Task: Implement sparse-matrix/umfpack/umf_get_memory.h for EinsteinAnalysis/AHFinderDirect thorn

thorn_name: EinsteinAnalysis/AHFinderDirect
src_filename: sparse-matrix/umfpack/umf_get_memory.h
interface: # Interface definition for thorn AHFinderDirect
# $Header$

implements: AHFinderDirect
inherits: Grid ADMBase StaticConformal SpaceMask SphericalSurface IO

# include file provided by SpaceMask
USES INCLUDE: SpaceMask.h

################################################################################

# we use a mask in order to find out whether we interpolate from the excised
# region.  (we need 3 time levels because Carpet might need to interpolate
# in time.)

REAL ahmask TYPE=gf TIMELEVELS=3 TAGS='tensortypealias="Scalar" Prolongation="None"'

################################################################################

# we use an aliased function maybe provided by DriftCorrect
SUBROUTINE SetDriftCorrectPosition					\
   (CCTK_POINTER_TO_CONST IN GH,					\
    CCTK_REAL IN x, CCTK_REAL IN y, CCTK_REAL IN z)
USES FUNCTION SetDriftCorrectPosition

################################################################################

#
# This thorn provides an aliased-function interface which other thorns
# can use to find out about the horizons.  Each function returns a status
# code which is >= 0 for ok, or < 0 for an error.
#

########################################

#
# This function returns the local coordinate origin for a given horizon.
#
CCTK_INT FUNCTION HorizonLocalCoordinateOrigin				\
   (CCTK_INT IN horizon_number,						\
    CCTK_REAL OUT origin_x, CCTK_REAL OUT origin_y, CCTK_REAL OUT origin_z)

#
# The following function queries whether or not the specified horizon
# was found the most recent time AHFinderDirect searched for it.
# The return value is:
#  1 if the horizon was found
#  0 if the horizon was not found
#  negative for an error
#
CCTK_INT FUNCTION HorizonWasFound(CCTK_INT IN horizon_number)

#
# The following function queries whether or not the specified horizon
# was found the most recent time AHFinderDirect searched for it, and
# if it was, returns the horizon centroid.  The returned value is the
# same as that of  HorizonWasFound()  (above), i.e.
#  1 if the horizon was found; in this case  centroid_[xyz]  are set
#    to the centroid position
#  0 if the horizon was not found; in this case  centroid_[xyz]  are
#    set to zeros
#  negative for an error
#
CCTK_INT FUNCTION HorizonCentroid					\
  (CCTK_INT IN horizon_number,						\
   CCTK_REAL OUT centroid_x, CCTK_REAL OUT centroid_y, CCTK_REAL OUT centroid_z)

#
# The following function computes the horizon radius in the direction
# of each (x,y,z) point, or -1.0 if this horizon wasn't found the most
# recent time AHFinderDirect searched for it.  More precisely, for each
# (x,y,z), consider the ray from the local coordinate origin through
# (x,y,z).  This function computes the Euclidean distance between the
# local coordinate origin and this ray's intersection with the horizon,
# or -1.0 if this horizon wasn't found the most recent time AHFinderDirect
# searched for it.  
#
CCTK_INT FUNCTION HorizonRadiusInDirection				\
   (CCTK_INT IN horizon_number,						\
    CCTK_INT IN N_points,						\
    CCTK_REAL IN ARRAY x, CCTK_REAL IN ARRAY y, CCTK_REAL IN ARRAY z,	\
    CCTK_REAL OUT ARRAY radius)

########################################

# all these functions are in src/driver/aliased_functions.cc
PROVIDES FUNCTION HorizonLocalCoordinateOrigin				\
	WITH AHFinderDirect_local_coordinate_origin			\
	LANGUAGE C
PROVIDES FUNCTION HorizonWasFound					\
	WITH AHFinderDirect_horizon_was_found				\
	LANGUAGE C
PROVIDES FUNCTION HorizonCentroid					\
	WITH AHFinderDirect_horizon_centroid				\
	LANGUAGE C
PROVIDES FUNCTION HorizonRadiusInDirection				\
	WITH AHFinderDirect_radius_in_direction				\
	LANGUAGE C

################################################################################

# Grid arrays to store the horizon shape, for easy output and checkpointing.
# These are similar and equivalent to SphericalSurface.
# Note that the horizons are numbered [0...N_horizon-1].

CCTK_REAL ah_radius[N_horizons] TYPE=array DIM=3 SIZE=max_N_zones_per_right_angle+1,max_N_zones_per_right_angle+1,6 DISTRIB=constant

CCTK_REAL ah_origin[N_horizons] TYPE=scalar
{
  ah_origin_x ah_origin_y ah_origin_z
}

CCTK_REAL ah_centroid[N_horizons] TYPE=scalar
{
  ah_centroid_x ah_centroid_y ah_centroid_z ah_centroid_t
  ah_centroid_x_p ah_centroid_y_p ah_centroid_z_p ah_centroid_t_p
}

CCTK_INT ah_flags[N_horizons] TYPE=scalar
{
  ah_initial_find_flag
  ah_really_initial_find_flag
  ah_search_flag
  ah_found_flag
  ah_centroid_valid
  ah_centroid_valid_p
  ah_centroid_iteration
  ah_centroid_iteration_p
}

################################################################################

# This IOUtil function is used to determine whether the diagnostics file
# should be truncated or appended (as in a recovery run )

CCTK_INT FUNCTION IO_TruncateOutputFiles \
  (CCTK_POINTER_TO_CONST IN GH)
USES FUNCTION IO_TruncateOutputFiles

################################################################################

# translate SphericalSurface name into ID
CCTK_INT \
FUNCTION sf_IdFromName (CCTK_INT IN id, CCTK_POINTER_TO_CONST IN name)
USES FUNCTION sf_IdFromName

################################################################################

schedule: # Schedule definitions for thorn AHFinderDirect
# $Header$

storage: ah_radius ah_origin ah_centroid ah_flags

if (metric_timelevels == 1)
{
  storage: ahmask[1]
}
else if (metric_timelevels == 2)
{
  storage: ahmask[2]
}
else if (metric_timelevels == 3)
{
  storage: ahmask[3]
}

#
# setup
#
schedule AHFinderDirect_setup at CCTK_BASEGRID        \
   after SpatialCoordinates
   {
   lang: C
   options: global
   } "setup data structures"

#
# checkpoint/recover support
#
schedule AHFinderDirect_recover at CCTK_POST_RECOVER_VARIABLES
   {
   lang: C
   options: global
   } "import horizon data from Cactus variables"

#
# find horizons
#
if (run_at_CCTK_ANALYSIS != 0)
  {
  schedule AHFinderDirect_find_horizons at CCTK_ANALYSIS
     {
     lang: C
     options: global-early
     } "find apparent horizon(s) after this time step"
  }
if (run_at_CCTK_POSTSTEP != 0)
  {
  schedule AHFinderDirect_find_horizons at CCTK_POSTSTEP
     {
     lang: C
     options: global-early
     } "find apparent horizon(s) after this time step"
  }
if (run_at_CCTK_POSTINITIAL != 0)
  {
  schedule AHFinderDirect_find_horizons at CCTK_POSTINITIAL
     {
     lang: C
     options: global-early
     } "find apparent horizon(s) after this time step"
  }
if (run_at_CCTK_POSTPOSTINITIAL != 0)
  {
  schedule AHFinderDirect_find_horizons at CCTK_POSTPOSTINITIAL
     {
     lang: C
     options: global-early
     } "find apparent horizon(s) after this time step"
  }
if (run_at_CCTK_POST_RECOVER_VARIABLES != 0)
  {
  schedule AHFinderDirect_find_horizons at CCTK_POST_RECOVER_VARIABLES \
           after MoL_PostStep
     {
     lang: C
     options: global-early
     } "find apparent horizon(s) after this time step"
  }

#
# find horizons, set excision mask, maybe announce centroid to other thorns
#
# FIXME: the name  AHFinderDirect_maybe_do_masks  is archaic,
#        AHFinderDirect_do_masks  would be more apt
#
# We use two Cactus Groups here:
#  group_for_mask_stuff
#    group_where_mask_is_set
#      AHFinderDirect_maybe_do_masks
# this way other thorns can schedule routines which need to run immediately
# before/after we set the mask in group_for_mask_stuff before/after
# group_where_mask_is_set, without having to know in what higher-level
# Cactus schedule bins/groups this thorn runs.
#
if (run_at_CCTK_ANALYSIS != 0)
  {
  schedule AHFinderDirect_import_mask at CCTK_ANALYSIS \
     before AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early loop-local
     } "import the excision mask"

  schedule AHFinderDirect_store at CCTK_ANALYSIS \
     after AHFinderDirect_find_horizons \
     before SphericalSurface_HasBeenSet
     {
     lang: C
     options: global-early
     } "store apparent horizon(s) into spherical surface(s)"

  schedule AHFinderDirect_save at CCTK_ANALYSIS \
     after AHFinderDirect_find_horizons \
     {
     lang: C
     options: global-early
     } "save apparent horizon(s) into Cactus variables"

  if (which_horizon_to_announce_centroid != 0)
    {
    schedule AHFinderDirect_announce at CCTK_ANALYSIS \
       before DriftCorrect \
       after AHFinderDirect_find_horizons
       {
       lang: C
       options: global-early
       } "announce horizon position(s) to other thorns"
    }

  schedule AHFinderDirect_maybe_do_masks at CCTK_ANALYSIS \
     after AHFinderDirect_find_horizons
     {
     lang: C
     } "set mask(s) based on apparent horizon position(s)"
  }
if (run_at_CCTK_POSTSTEP != 0)
  {
  schedule AHFinderDirect_import_mask at CCTK_POSTSTEP \
     before AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early loop-local
     } "import the excision mask"

  schedule AHFinderDirect_store at CCTK_POSTSTEP \
     after AHFinderDirect_find_horizons \
     before SphericalSurface_HasBeenSet
     {
     lang: C
     options: global-early
     } "store apparent horizon(s) into spherical surface(s)"

  schedule AHFinderDirect_save at CCTK_POSTSTEP \
     after AHFinderDirect_find_horizons \
     {
     lang: C
     options: global-early
     } "save apparent horizon(s) into Cactus variables"

  if (which_horizon_to_announce_centroid != 0)
    {
    schedule AHFinderDirect_announce at CCTK_POSTSTEP \
       before DriftCorrect \
       after AHFinderDirect_find_horizons
       {
       lang: C
       options: global-early
       } "announce horizon position(s) to other thorns"
    }

  schedule AHFinderDirect_maybe_do_masks at CCTK_POSTSTEP \
     after AHFinderDirect_find_horizons
     {
     lang: C
     } "set mask(s) based on apparent horizon position(s)"
  }
if (run_at_CCTK_POSTINITIAL != 0)
  {
  schedule AHFinderDirect_import_mask at CCTK_POSTINITIAL \
     before AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early loop-local
     } "import the excision mask"

  schedule AHFinderDirect_store at CCTK_POSTINITIAL \
     after AHFinderDirect_find_horizons \
     before SphericalSurface_HasBeenSet
     {
     lang: C
     options: global-early
     } "store apparent horizon(s) into spherical surface(s)"

  schedule AHFinderDirect_save at CCTK_POSTINITIAL \
     after AHFinderDirect_find_horizons \
     {
     lang: C
     options: global-early
     } "save apparent horizon(s) into Cactus variables"

  if (which_horizon_to_announce_centroid != 0)
    {
    schedule AHFinderDirect_announce at CCTK_POSTINITIAL \
       before DriftCorrect \
       after AHFinderDirect_find_horizons
       {
       lang: C
       options: global-early
       } "announce horizon position(s) to other thorns"
    }

  schedule AHFinderDirect_maybe_do_masks at CCTK_POSTINITIAL \
     after AHFinderDirect_find_horizons
     {
     lang: C
     } "set mask(s) based on apparent horizon position(s)"
  }
if (run_at_CCTK_POSTPOSTINITIAL != 0)
  {
  schedule AHFinderDirect_import_mask at CCTK_POSTPOSTINITIAL \
     before AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early loop-local
     } "import the excision mask"

  schedule AHFinderDirect_store at CCTK_POSTPOSTINITIAL \
     after AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early
     } "store apparent horizon(s) into spherical surface(s)"

  schedule AHFinderDirect_save at CCTK_POSTPOSTINITIAL \
     after AHFinderDirect_find_horizons \
     {
     lang: C
     options: global-early
     } "save apparent horizon(s) into Cactus variables"

  if (which_horizon_to_announce_centroid != 0)
    {
    schedule AHFinderDirect_announce at CCTK_POSTPOSTINITIAL \
       before DriftCorrect \
       after AHFinderDirect_find_horizons
       {
       lang: C
       options: global-early
       } "announce horizon position(s) to other thorns"
    }

  schedule AHFinderDirect_maybe_do_masks at CCTK_POSTPOSTINITIAL \
     after AHFinderDirect_find_horizons
     {
     lang: C
     } "set mask(s) based on apparent horizon position(s)"
  }
if (run_at_CCTK_POST_RECOVER_VARIABLES != 0)
  {
  schedule AHFinderDirect_import_mask at CCTK_POST_RECOVER_VARIABLES \
     before AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early loop-local
     } "import the excision mask"

  schedule AHFinderDirect_store at CCTK_POST_RECOVER_VARIABLES \
     after AHFinderDirect_find_horizons \
     before SphericalSurface_HasBeenSet
     {
     lang: C
     options: global-early
     } "store apparent horizon(s) into spherical surface(s)"

  schedule AHFinderDirect_save at CCTK_POST_RECOVER_VARIABLES \
     after AHFinderDirect_find_horizons
     {
     lang: C
     options: global-early
     } "save apparent horizon(s) into Cactus variables"

  if (which_horizon_to_announce_centroid != 0)
    {
    schedule AHFinderDirect_announce at CCTK_POST_RECOVER_VARIABLES \
       before DriftCorrect \
       after AHFinderDirect_find_horizons
       {
       lang: C
       options: global-early
       } "announce horizon position(s) to other thorns"
    }

  schedule AHFinderDirect_maybe_do_masks at CCTK_POST_RECOVER_VARIABLES \
     after AHFinderDirect_find_horizons
     {
     lang: C
     } "set mask(s) based on apparent horizon position(s)"
  }

# TODO: Don't call this, since no horizon has been found yet
schedule AHFinderDirect_maybe_do_masks at CCTK_POSTREGRIDINITIAL \
   after (MaskOne MaskZero)
   {
   lang: C
   } "set mask(s) based on apparent horizon position(s)"

schedule AHFinderDirect_maybe_do_masks at CCTK_POSTREGRID \
   after (MaskOne MaskZero)
   {
   lang: C
   } "set mask(s) based on apparent horizon position(s)"

########################################

##
## uncomment this if you want to use the test driver
##  src/util/test_patch_system.cc
##
##schedule test_patch_system at CCTK_POSTINITIAL
##{
##LANG: C
##OPTIONS: global
##} "test driver to verify that src/util/ and src/jtutil/ code works properly"

param: # Parameter definitions for thorn AHFinderDirect
# $Header$

################################################################################

#
# ***** interface to the rest of Cactus *****
#

# we may need to look at some grid:: parameters to choose our patch system type
# note our access is read-only
shares: grid
USES KEYWORD domain
USES KEYWORD bitant_plane
USES KEYWORD quadrant_direction
USES KEYWORD rotation_axis

# we need to look at ADMBase::metric_type and StaticConformal::conformal_state
# to check physical/conformal metric
# note our access is read-only
shares: ADMBase
USES KEYWORD metric_type
USES CCTK_INT metric_timelevels

# we need to look at SpaceMask::use_mask in order to find out about the
# excision region
shares: SpaceMask
USES BOOLEAN use_mask

# we need to look at IO::out_dir to set the default for our own
# output directory parameters
shares: IO
USES STRING out_dir

# we want to know how many surfaces there are, so that we don't access
# a non-existing one
shares: SphericalSurface
USES INT nsurfaces
USES INT maxntheta
USES INT maxnphi

# all remaining parameters are private to this thorn
private:

################################################################################

#
# ***** overall parameters *****
#

# set this to (try to) find apparent horizons at every time step
int find_every "how often should we try to find apparent horizons?" STEERABLE=always
{
0   :: "don't find AHs at all (this thorn is a no-op)"
1:* :: "any integer >= 1"
} 1

# set this to (try to) find individual apparent horizons at every time step
int find_every_individual[101] "how often should we try to find individual apparent horizons?  (overrides find_every)" STEERABLE=always
{
-1  :: "use the value of find_every"
0   :: "don't find this AH at all"
1:* :: "any integer >= 1"
} -1

# set this to (try to) find individual apparent horizons after a time step
int find_after_individual[101] "when should we start to find individual apparent horizons?" STEERABLE=always
{
0:* :: "after this iteration (inclusively)"
} 0

int dont_find_after_individual[101] "when should we stop finding individual apparent horizons?" STEERABLE=always
{
0:* :: "after this iteration (exclusively)"
-1  :: "do not use this parameter"
} -1

# set this to (try to) find individual apparent horizons after a certain time
real find_after_individual_time[101] "when should we start to find individual apparent horizons?" STEERABLE=always
{
*:* :: "after this time (inclusively)"
} 0.0

real dont_find_after_individual_time[101] "when should we stop finding individual apparent horizons?" STEERABLE=always
{
*:* :: "after this time (exclusively); ignore this value if it is less than or equal to find_after_individual_time"
} 0.0

# set this to disable finding individual horizons
boolean disable_horizon[101] "should this horizon be disabled?" STEERABLE=always
{
} "false"

#
# These parameters control which schedule bin (or even bins, if you
# want) AHFinderDirect runs in.  Historically, Erik's branch of
# AHFinderDirect has run at CCTK_POSTSTEP.  However, for some purposes
# it's more convenient to run at CCTK_ANALYSIS and/or CCTK_POSTINITIAL.
#
# In general we need to run at CCTK_POST_RECOVER_VARIABLES, since
# (a) parameters may have been steered at recovery, so we may need
#     to find a new horizon or horizons, and
# (b) we need to set the mask again to make sure it's correct
#     right away (since our next regular horizon-finding may not
#     be until some time steps later)
# Therefore the  run_at_CCTK_POST_RECOVER_VARIABLES  parameter should
# probably be left at its default setting of  true .
#
Boolean run_at_CCTK_ANALYSIS "should we run at CCTK_ANALYSIS?"
{
} false
Boolean run_at_CCTK_POSTSTEP "should we run at CCTK_POSTSTEP?"
{
} true
Boolean run_at_CCTK_POSTINITIAL "should we run at CCTK_POSTINITIAL?"
{
} false
Boolean run_at_CCTK_POSTPOSTINITIAL "should we run at CCTK_POSTPOSTINITIAL?"
{
} false
Boolean run_at_CCTK_POST_RECOVER_VARIABLES				\
  "should we run at CCTK_POST_RECOVER_VARIABLES?"
{
} false

keyword method "what should this thorn do for each apparent horizon?" \
  STEERABLE=recover
{
# these options are mostly for testing/debugging
# ... in a multiprocessor Cactus run, the horizons are done sequentually
#     on processor #0; the other processors do dummy computations
"evaluate expansions"	  :: "evaluate the LHS function Theta(h)"

# ... in a multiprocessor Cactus run, the Jacobian is computed on
#     processor #0; the other processors do dummy computations
"test expansion Jacobians" :: \
  "compute/print horizon 1's J[Theta(h)] Jacobian matrix (possibly in \
   multiple ways, depending on the  test_all_Jacobian_methods  parameter)"

# this is for normal apparent horizon finding
# ... in a multiprocessor Cactus run, the horizons are done in parallel
#     across processors; see src/driver/README.parallel for details
"find horizons"		  :: "find the apparent horizon"
} "find horizons"

#
# We support searching for up to N_horizons distinct apparent horizons
# (some of which may be nested inside others) in a slice.
# ***** IMPORTANT *****: The horizons are numbered 1 to N_horizons.
# Since Cactus arrays are 0-origin, we make the arrays be of size
# N_horizons+1, and don't use the [0] array elements.
#
# To change the N_horizons limit, just change the upper limit for
# N_horizons itself, change all the [N_horizons+1] array sizes in this
# paramter file, change the upper limit for which_horizon_to_announce_centroid,
# and recompile your configuration.  No changes are needed to the source code.
#
int N_horizons "number of apparent horizons to search for" \
  STEERABLE=recover
{
0     :: "turn this thorn into a fancy no-op :)"
1:100 :: "search for this many apparent horizons"
} 1

#
# This parameter controls whether or not (assuming we have found a
# horizon or horizons), we announce a selected horizon centroid to
# some other thorn.  In practice the other thorn will be DriftCorrect,
# which will use this information to adjust the coordinates to try to
# keep the horizon centroid at a constant coordinate position.
#
int which_horizon_to_announce_centroid \
  "for which horizon should we announce the centroid?" \
  STEERABLE=recover
{
0     :: "don't announce any centroid(s)"
1:100 :: "announce this horizon's centroid each time we find it"
} 0

#
# This parameter controls whether and (if) into which surface the horizon
# information will be stored.
#
int which_surface_to_store_info[101] \
  "into which surface should we store the info?" \
  STEERABLE=recover
{
-1  :: "don't store info"
0:* :: "store info into the corresponding surface"
} -1

string which_surface_to_store_info_by_name[101] \
  "into which surface should we store the info?" \
  STEERABLE=recover
{
""   :: "use which_surface_to_store_info"
".*" :: "any string"
} ""

#
# This parameter controls how verbose this thorn is in printing
# informational (non-error) messages describing what it's doing.
#
keyword verbose_level \
  "controls which (how many) messages to print describing AH finding" \
  STEERABLE=recover
{
# 1 line each time step giving number of horizons found and their masses
# ... this doesn't work yet :(
"physics highlights"   :: "just a few physics messages"

# 1 line for each horizon giving position/mass/area, + a summary line or two
"physics details"      :: "more detailed physics messages"

# 1 line giving Theta(h) norms at each Newton iteration
"algorithm highlights" :: \
  "physics details + a few messages about the AH-finding algorithm"

# lots of details tracing what the code is doing
"algorithm details"    :: \
  "physics details + lots of messages about the AH-finding algorithm"

# even more details tracing what the code is doing :)
"algorithm debug"    :: \
  "physics details + lots and lots of messages about the AH-finding algorithm"
} "physics details"

# n.b. printing timing stats is independent of  verbose_level
boolean print_timing_stats \
  "should we print timing stats for the whole apparent-horizon-finding process?" \
  STEERABLE=recover
{
} "false"

# Do we want the gradients of the surface expansion?
# (Increases run time)
boolean want_expansion_gradients "should we print the gradients of the expansions?" \
  STEERABLE=recover
{
} "false"

# By default, we find apparent horizons or CE surfaces.
# This parameter allows the equation to be change so that other surfaces
# are found instead.
keyword surface_definition[101] "search for what kind of surface?" STEERABLE=recover
{
"expansion"         :: "Theta_(l) (apparent horizons etc.)"
"inner expansion"   :: "Theta_(n) (expansion of ingoing null normal)"
"mean curvature"    :: "for CMC surfaces"
"expansion product" :: "Theta_(l) Theta_(n) (product of the expansions)"
} "expansion"

# By default, we find surfaces as defined above.
# This parameter allows the definition to be modified.
keyword surface_modification[101] "how to modify the surface definition" STEERABLE=recover
{
"none"         :: "no modification"
"radius"       :: "multiply with the coordinate radius"
"radius^2"     :: "multiply with the square of the coordinate radius"
"mean radius"  :: "multiply with the mean coordinate radius"
"areal radius" :: "multiply with the areal radius (does not converge nicely, because the Jacobian is only approximate)"
} "none"

# By default, we find apparent horizons by looking for a specified expansion.
# This parameter allows looking for a specified areal radius etc. instead.
keyword surface_selection[101] "search for a surface with this areal radius" STEERABLE=recover
{
"definition"                             :: "look for a surface as defined by 'surface_definition'"
"mean coordinate radius"                 :: "look for a surface with a certain mean coordinate radius (not covariant, but fast)"
"areal radius"                           :: "look for a surface with a certain areal radius"
"expansion times mean coordinate radius" :: "look for a surface with a certain product of expansion and mean coordiante radius"
"expansion times areal radius"           :: "look for a surface with a certain product of expansion and areal radius"
} "definition"

# By default, we find apparent horizons by solving the equation
#  Theta(h) = 0
# This parameter allows the RHS to be set to any specified constant,
# to find a surface of constant expansion.
real desired_value[101] "search for a surface with this (constant) value" STEERABLE=recover
{
*:* :: "any real number"
} 0.0



# By default, we just find apparent horizons.  This parameter uses
# pretracking to search for outermost apparent horizons instead.
boolean use_pretracking[101] "search for an outermost apparent horizon" STEERABLE=recover
{
} "no"

int pretracking_max_iterations[101] "maximum number of pretracking iterations" STEERABLE=recover
{
  0:* :: ""
} 100

real pretracking_value[101] "initial pretracking value" STEERABLE=recover
{
  *:* :: ""
} 1.0

real pretracking_minimum_value[101] "minimum pretracking value" STEERABLE=recover
{
  *:* :: ""
} 0.0

real pretracking_maximum_value[101] "maximum pretracking value (should be near the outer boundary)" STEERABLE=recover
{
  *:* :: ""
} 10.0

real pretracking_delta[101] "step size for value" STEERABLE=recover
{
  (0.0:* :: ""
} 1.0

real pretracking_minimum_delta[101] "minimum step size for value" STEERABLE=recover
{
  (0.0:* :: ""
} 1.0e-4

real pretracking_maximum_delta[101] "maximum step size for value" STEERABLE=recover
{
  (0.0:* :: ""
} 1.0

# Horizons can depend on other (usually pretracked) horizons
int depends_on[101] "the other horizon that this horizon depends on" STEERABLE=recover
{
  0     :: "the horizon is independent"
  1:100 :: "horizon index"
} 0

real desired_value_factor[101] "factor between this horizon's and the other horizon's desired value, applied before the offset" STEERABLE=recover
{
  *:* :: ""
} 1.0

real desired_value_offset[101] "difference between this horizon's and the other horizon's desired value, applied after the factor" STEERABLE=recover
{
  *:* :: ""
} 0.0



# By default, we keep the surface from the last time (if so desired).
# We can also modify it before each finding to try make it find inner
# or outer horizons.
real shiftout_factor[101] "enlarge the surface initial guess before finding" STEERABLE=recover
{
  (0:* :: "choose 1 for doing nothing, larger for enlarging, smaller for shrinking"
} 1.0

real smoothing_factor[101] "smoothen (remove higher multipole moments) the surface initial guess before finding" STEERABLE=recover
{
  *:* :: "choose 0 for no smoothing, 1 for complete smoothing, larger for over-smoothing, negative for roughening"
} 0.0

################################################################################

#
# ***** parameters for the initial guess for each apparent horizon shape *****
#

#
# (Note that if at any time we fail to find the (an) apparent horizon,
# then we reset our trial horizon surface to this initial guess before
# next attempting to find this horizon.)
#

keyword initial_guess_method[101] \
  "method used to set up initial guess for apparent horizon shape" \
  STEERABLE=recover
{
"read from named file"	:: "read from explicitly-named input file"
"read from h file"	:: \
  "read from input file named the same as the (later) h output file"
"Kerr/Kerr"		:: \
  "set to the (analytical) horizon of Kerr spacetime in Kerr coordinates \
   (n.b. Schwarzschild/EF is the special case spin=0 of this)"
"Kerr/Kerr-Schild"	:: \
  "set to the (analytical) horizon of Kerr spacetime in Kerr-Schild coordinates"
"coordinate sphere"	:: "set to a coordinate sphere"
"coordinate ellipsoid"	:: "set to a coordinate ellipsoid"
} "coordinate sphere"

# if a horizon could not be found, do we want to reset the horizon shape,
# or do we want to continue with the last shape?
boolean reset_horizon_after_not_finding[101] \
  "reset the horizon shape to the initial data if a horizon was not found" \
  STEERABLE=recover
{
} "yes"

# parameters for initial_guess_method = "read from named file"
string initial_guess__read_from_named_file__file_name[101] \
  "file name to read initial guess from" \
  STEERABLE=recover
{
".+" :: "file name to read initial guess from"
} "h.gp"

# parameters for initial_guess_method = "Kerr/Kerr"
real initial_guess__Kerr_Kerr__x_posn[101] "x coordinate of Kerr BH" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__Kerr_Kerr__y_posn[101] "y coordinate of Kerr BH" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__Kerr_Kerr__z_posn[101] "z coordinate of Kerr BH" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__Kerr_Kerr__mass[101] "mass of Kerr BH" STEERABLE=recover
{
(0.0:*		:: "BH mass = any real number > 0"
} 1.0
# n.b. my convention is that a=J/m^2 is dimensionless,
#      while MTW take a=J/m=m * (my a)
real initial_guess__Kerr_Kerr__spin[101] "dimensionless spin a=J/m^2 of Kerr BH" STEERABLE=recover
{
(-1.0:1.0)	:: \
  "dimensionless BH spin = J/m^2 = any real number with absolute value < 1"
} 0.6

# parameters for initial_guess_method = "Kerr/Kerr-Schild"
real initial_guess__Kerr_KerrSchild__x_posn[101] "x coordinate of Kerr BH" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__Kerr_KerrSchild__y_posn[101] "y coordinate of Kerr BH" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__Kerr_KerrSchild__z_posn[101] "z coordinate of Kerr BH" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__Kerr_KerrSchild__mass[101] "mass of Kerr BH" STEERABLE=recover
{
(0.0:*		:: "BH mass = any real number > 0"
} 1.0
# n.b. my convention is that a=J/m^2 is dimensionless,
#      while MTW take a=J/m=m * (my a)
real initial_guess__Kerr_KerrSchild__spin[101] "dimensionless spin a=J/m^2 of Kerr BH" STEERABLE=recover
{
(-1.0:1.0)	:: \
  "dimensionless BH spin = J/m^2 = any real number with absolute value < 1"
} 0.6

# parameters for initial_guess_method = "sphere"
real initial_guess__coord_sphere__x_center[101] "x coordinate of sphere center" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__coord_sphere__y_center[101] "y coordinate of sphere center" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__coord_sphere__z_center[101] "z coordinate of sphere center" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__coord_sphere__radius[101] "radius of sphere" STEERABLE=recover
{
(0.0:* :: "any real number > 0.0"
} 2.0

# parameters for initial_guess_method = "ellipsoid"
real initial_guess__coord_ellipsoid__x_center[101] \
  "x coordinate of ellipsoid center" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__coord_ellipsoid__y_center[101] \
  "y coordinate of ellipsoid center" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__coord_ellipsoid__z_center[101] \
  "z coordinate of ellipsoid center" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real initial_guess__coord_ellipsoid__x_radius[101] "x radius of ellipsoid" STEERABLE=recover
{
(0.0:* :: "any real number > 0.0"
} 2.0
real initial_guess__coord_ellipsoid__y_radius[101] "y radius of ellipsoid" STEERABLE=recover
{
(0.0:* :: "any real number > 0.0"
} 2.0
real initial_guess__coord_ellipsoid__z_radius[101] "z radius of ellipsoid" STEERABLE=recover
{
(0.0:* :: "any real number > 0.0"
} 2.0

################################################################################

#
# ***** I/O parameters *****
#

########################################

#
# parameters for BH diagnostics
#

#
# The file format is currently hard-wired to a simple ASCII format:
# - there is one file per AH
# - after some header comments (starting with '#'),
#   there is one line per successful-AH-finding
#   (there is *no* line if we fail to find an AH)
# - each line contains various tab-separated fields, as described by
#   the header comment
#
boolean output_BH_diagnostics \
  "should we output BH diagnostics to a data file for each AH found?" \
  STEERABLE=always
{
} "true"

#
# These file names are actually just "base" file names, with the full
# file names being given by a printf() format "%s/%s.ah%d.%s",
# where
# - the first %s is the BH diagnostics directory as set by the
#   BH_diagnostics_directory  parameter; this is automagically created
#   if it doesn't already exist
# - the second %s is the base file name,
# - the %d is the global Cactus time iteration number cctk_iteration,
# - the third %s is the file name extension as set by the
#   BH_diagnostics_file_name_extension  parameter
#
string BH_diagnostics_directory "directory for BH diagnostics output file(s)" \
  STEERABLE=always
{
".+" :: "any nonempty string"
"^$" :: "an empty string to default to IO::out_dir"
} ""
string BH_diagnostics_base_file_name \
  "base file name for BH diagnostics output file(s)" STEERABLE=always
{
".+" :: "any nonempty string"
} "BH_diagnostics"

string BH_diagnostics_file_name_extension \
  "extension for BH diagnostics data files" STEERABLE=always
{
".+" :: "any nonempty string"
} "gp"

########################################

#
# parameters for horizon-shape and other similar data files
#

# this parameter controls how often we write apparent-horizon-shape
# output files (format controlled by  horizon_file_format ); we still
# *find* apparent horizons at each time step, and compute BH diagnostics
# and (if desired) write them to the one-per-horizon BH diagnostics
# output files, but this parameter may be used to reduce the number
# of one-file-per-horizon-per-time-step apparent-horizon-shape output
# files
int output_h_every \
  "how often (in Cactus time steps) should we output h (0 to disable)?" \
  STEERABLE=always
{
0   :: "don't output h at all"
1:* :: "any integer >= 1"
} 1

# setting this > 0 is probably only of interest if the Newton iteration
# fails to converge, or if you're debugging AHFinderDirect internals
int output_Theta_every \
  "how often (in Cactus time steps) should we output the Theta(h) functions?" \
  STEERABLE=always
{
0   :: "don't output Theta(h) at all"
1:* :: "any integer >= 1"
} 0

int output_mean_curvature_every \
  "how often (in Cactus time steps) should we output the mean_curvature(h) functions?" \
  STEERABLE=always
{
0   :: "don't output mean_curvature(h) at all"
1:* :: "any integer >= 1"
} 0

boolean output_ASCII_files "output h and Theta(h) as ASCII files" STEERABLE=always
{
} "yes"

boolean output_HDF5_files "output h and Theta(h) as HDF5 files" STEERABLE=always
{
} "no"

# n.b. this refers to the patch system (angular) interpatch ghost zones,
#      *not* the Cactus interprocessor ghost zones
boolean output_ghost_zones_for_h \
  "should we include the patch system (angular) interpatch ghost zones \
   in h data files?" STEERABLE=always
{
} "false"

# n.b. making this "gnuplot" probably isn't a good idea,
#      since it could create confusion with gnuplot command files
string ASCII_gnuplot_file_name_extension \
  "extension for ASCII (gnuplot) data files" STEERABLE=always
{
".+" :: "any nonempty string"
} "gp"
string HDF5_file_name_extension "extension for HDF5 data files" STEERABLE=always
{
".+" :: "any nonempty string"
} "h5"

#
# These file names are actually just "base" file names, with the full
# file names being given by a printf() format "%s/%s.t%d.ah%d[.it%d].%s",
# where
# - the first %s is the output directory as set by the  h_directory
#   parameter (this same directory is used for all of these files);
#   this is automagically created if it doesn't already exist
# - the second %s is the base file name,
# - the first %d is the global Cactus time iteration number cctk_iteration,
# - the second %d is the apparent horizon number,
# - the optional third %d is the horizon finder iteration number
# - the third %s is the file name extension
#   {ASCII,HDF5}_data_file_name_extension
#
string h_directory \
  "directory for horizon shape and other similar output (or input) file(s)" \
   STEERABLE=always
{
".+" :: "any nonempty string"
"^$" :: "an empty string to default to IO::out_dir"
} ""

string h_base_file_name \
  "base file name for horizon shape h input/output file(s)" \
  STEERABLE=always
{
".+" :: "any nonempty string"
} "h"

string Theta_base_file_name "base file name for Theta(h) output file(s)" \
  STEERABLE=always
{
".+" :: "any nonempty string"
} "Theta"

string mean_curvature_base_file_name "base file name for mean_curvature(h) output file(s)" \
  STEERABLE=always
{
".+" :: "any nonempty string"
} "mean_curvature"

string Delta_h_base_file_name \
  "base file name for horizon-shape-update Delta_h output file(s)" \
  STEERABLE=always
{
".+" :: "any nonempty string"
} "Delta_h"

int h_min_digits \
  "minimum number of digits for the iteration number in the file name" STEERABLE=always
{
0:* :: ""
} 0

#
# Thomas Radke has written a set of OpenDX macros to read AHFinderDirect
# ASCII (gnuplot) format files.  These macros require a "control file"
# for each patch to specify some of the patch metadata.
#
# If we write them, the control file names are given by a printf() format
# "%s/%s.ah%d.%s", where
# - the first %s is the output directory as set by the  h_directory
#   parameter (this same directory is used for all of these files);
#   this is automagically created if it doesn't already exist
# - the second %s is h_base_file_name
# - the %d is the apparent horizon number
# - the third %s is the file name extension OpenDX_control_file_name_extension
#
boolean output_OpenDX_control_files \
  "should we output OpenDX control files to allow reading of AHFinderDirect \
   'ASCII (gnuplot)' format data files?" STEERABLE=always
{
} "true"
string OpenDX_control_file_name_extension \
  "file name extension for OpenDX control files" STEERABLE=always
{
".+" :: "any nonempty string"
} "dx"

########################################

#
# parameters mainly for debugging
#

# this is mainly useful for debugging purposes
boolean output_initial_guess \
  "should we output the initial guess back to the h data file?" \
  STEERABLE=always
{
} "false"

# for debugging convergence failures, we can optionally output
# h, Theta, and delta_h at each Newton iteration
# (the file names are the usual ones with ".it%d" appended)
boolean debugging_output_at_each_Newton_iteration \
  "should we output {h, Theta, delta_h} at each Newton iteration?" \
  STEERABLE=recover
{
} "false"

string Jacobian_base_file_name "base file name for Jacobian output file(s)" \
  STEERABLE=always
{
".+" :: "any valid file name"
} "Jacobian.dat"

################################################################################

#
# ***** parameters for setting a mask grid function ***
# ***** based on each apparent horizon shape *****
#

boolean set_mask_for_all_horizons \
  "should we set a mask grid function (or functions) for all horizons?" \
  STEERABLE=recover
{
} "false"

boolean set_mask_for_individual_horizon[101] \
  "should we set a mask grid function (or functions) for *this* horizon?" \
  STEERABLE=recover
{
} "false"

#
# If we set a mask, we do so based on partitioning the Cactus grid into
# 3 regions, an "inside", a "buffer", and an "outside" (typically the inside
# region is excised).  For a grid point a distance r[i] from horizon i's
# local coordinate origin, with horizon i's radius in this same direction
# (again, measured from its local coordinate origin) being r_horizon[i],
# the regions are defined as follows:
#	r[i] <= r_inner[i] for some i				==> inside
#	r[i] >  r_inner[i] for all i
#		and r[i] <= r_outer[i] for some i		==> buffer
#	r[i] >  r_outer[i] for all i				==> outside
# where
#	r_inner = mask_radius_multiplier * r_horizon
#		  +  mask_radius_offset * Delta_x
#	r_outer = r_innter  +  mask_buffer_thickness * Delta_x
# and where  Delta_x  is the Cactus base grid spacing (more precisely,
# the geometric mean of the base grid's x, y, and z Cactus grid spacings)
# (The "base grid" part here only matters if you're doing mesh refinement.)
#
real mask_radius_multiplier \
  "radius multiplier to define the 'inside' mask region" \
  STEERABLE=recover
{
(0:*) :: \
  "any positive real number; typically this will be slightly less than 1.0"
} 0.8
real mask_radius_offset \
  "radius offset (in Cactus base grid spacings) to define the 'inside' mask region" \
  STEERABLE=recover
{
*:* :: "any real number; typically this will be negative"
} -5.0
real mask_buffer_thickness \
  "thickness (in Cactus base grid spacings) of the 'buffer' mask region" \
  STEERABLE=recover
{
*:* :: "any real number; typically this will be positive"
} 5.0

#
# If this parameter is set to true, then we prevent the inside & buffer
#    regions from ever shrinking, i.e. if a point is ever set to the inside
#    value then we leave at that value forever, and if it's ever set to
#    the buffer value then we leave at that value or the inside value forever.
# If this parameter is set to false, then we set the inside/buffer/outside
#    values independently at each time step, so all three regions can grow
#    or shrink depending on the horizon positions/shapes.
#
boolean mask_is_noshrink \
  "should we prevent the inside & buffer regions from ever shrinking?" \
  STEERABLE=recover
{
} "true"

#
# If we're using mesh refinement (eg Carpet) it's possible to find a
# horizon on a fine grid, and then set a mask on a coarser grid where
# the masked-off region is only a few grid points across.  This then
# confuses various other thorns (eg LegoExcision) which require excised
# regions to be "not too small".
#
# So...  This parameter may be used to specify that the mask not be set
# for a given horizon (just as if that horizon wasn't found) on any grid
# where the inner mask radius  r_inner  is < this many grid spacings.
# More precisely, we skip masking for any horizon where
#	r_inner,min < min_horizon_radius_points_for_mask * Delta_x_current,max
# where
#	r_inner,min = the minimum over all angles of the inner mask radius
#		      r_inner
#	Delta_x_current,max = the maximum of the x,y,and z grid spacings
#			      in the *current* Cactus grid
#
# Note that since r_inner can be negative (if  mask_radius_offset < 0 ),
# the default setting for this parameter is effectively -infinity, to
# excise any horizon.
#
real min_horizon_radius_points_for_mask \
  "only set mask if min r_inner >= this number of local grid spacings" \
  STEERABLE=recover
{
*:* :: "any real number"
} -1.0e10

#
# This thorn supports two types of mask grid functions:
# * the "old" style is that defined by CactusEinstein/AHFinder:
#   the mask is a CCTK_REAL grid function, which is set to specified
#   (CCTK_REAL) values inside/outside the (adjusted) horizon
# * the "new" style is that defined by CactusEinstein/SpaceMask:
#   the mask is a CCTK_INT grid function, with a specified bit field
#   set to specified values inside/outside the (adjusted) horizon
#	[N.b. AHFinderDirect doesn't set up the bit fields --
#	you must arrange for some other thorn to do this.  At
#	present AHFinderDirect converts the names specified here
#	to integer bit masks only once, in its setup routine;
#	this may change to do this conversion each time AHFinderDirect
#	does horizon finding.]
# You can choose to set either (or even both!) of these.
#
boolean set_old_style_mask \
  "if we're setting a mask, should we set an old-style (CCTK_REAL) mask gridfn?" STEERABLE=recover
{
} "true"
boolean set_new_style_mask \
  "if we're setting a mask, should we set an new-style (CCTK_INT) mask gridfn?" STEERABLE=recover
{
} "false"

#
# mask info for  mask_type = "old-style (CCTK_REAL)"
# defaults match CactusEinstein/AHFinder
#
string old_style_mask_gridfn_name "name of the old-style mask grid function" STEERABLE=recover
{
".+" :: "any valid Cactus grid function name"
} "SpaceMask::emask"
real old_style_mask_inside_value \
  "set the old-style mask to this value in the 'inside' region" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real old_style_mask_buffer_value \
  "set the old-style mask to this value in the 'buffer' region" STEERABLE=recover
{
*:* :: "any real number"
} 0.5
real old_style_mask_outside_value \
  "set the old-style mask to this value in the 'outside' region" STEERABLE=recover
{
*:* :: "any real number"
} 1.0

#
# mask info for  mask_type = "new-style (CCTK_INT)"
#
string new_style_mask_gridfn_name "name of the new-style mask grid function" STEERABLE=recover
{
".+" :: "any valid Cactus grid function name"
} "SpaceMask::space_mask"
string new_style_mask_bitfield_name \
  "name of the new-style mask bit field ('type') as registered with SpaceMask" STEERABLE=recover
{
".+" :: "any valid bit field ('type') name registered with the SpaceMask thorn"
} "mask"
string new_style_mask_inside_value \
  "set the specified bit field of the new-style mask to this named state \
   at grid points in the 'inside' region" STEERABLE=recover
{
".+" :: "any state name registered with the SpaceMask thorn"
} "inside"
string new_style_mask_buffer_value \
  "set the specified bit field of the new-style mask to this named state \
   at grid points in the 'buffer' region" STEERABLE=recover
{
".+" :: "any state name registered with the SpaceMask thorn"
} "buffer"
string new_style_mask_outside_value \
  "set the specified bit field of the new-style mask to this named state \
   at grid points in the 'outside' region" STEERABLE=recover
{
".+" :: "any state name registered with the SpaceMask thorn"
} "outside"

################################################################################

#
# ***** parameters controlling how we handle errors/warnings *****
#

#
# We currently "handle" the following errors/warnings by CCTK_VWarn();
# these parameters give the warning levels (in the sense of the -W flag
# to Cactus, and the first argument to CCTK_VWarn()) for various conditions:
#

# point outside (or too close to boundary of) Cactus grid,
# i.e. geometry interpolator returns CCTK_ERROR_INTERP_POINT_OUTSIDE
# ... warning level if error occurs on first Newton iteration,
#     i.e. when evaluating expansion/Jacobian for initial guess
# ... this may mean that  driver::ghost_size  is too small, especially
#     if any horizon crosses a symmetry boundary
#     ==> visible message by default
int warn_level__point_outside__initial \
  "warning level for point outside (or too close to boundary of) Cactus grid \
   (error occurs on first Newton iteration)" STEERABLE=recover
{
-1:* :: "any valid Cactus warning level"
} 1
# ... warning level if error occurs on a subsequent Newton iteration
# ... this probably "just" means there's no horizon in the grid
#     ==> no visible message by default
int warn_level__point_outside__subsequent \
  "warning level for point outside (or too close to boundary of) Cactus grid \
   (error occurs on subsequent Newton iteration)" STEERABLE=recover
{
-1:* :: "any valid Cactus warning level"
} 2

# the Cactus configure process didn't find a finite() function,
# so we're skipping the "check that the geometry is finite" tests
# even though the user set  check_that_geometry_is_finite = "true"
int warn_level__skipping_finite_check \
  "warning level if the user sets  check_that_geometry_is_finite \
   but the Cactus configure process doesn't find a  finite()  function \
   so we have to skip the finite-geometry check" STEERABLE=recover
{
-1:* :: "any valid Cactus warning level"
} 3

# infinity and/or NaN in interpolated {g_ij, partial_k g_ij, K_ij}
int warn_level__nonfinite_geometry \
  "warning level if we find infinity and/or NaN in the interpolated geometry \
   values {g_ij, partial_k g_ij, K_ij}" STEERABLE=recover
{
-1:* :: "any valid Cactus warning level"
} 1

# interpolated g_{ij} isn't positive definite
# (usually this means we're too close to a singularity)
# ... warning level if error occurs on first Newton iteration,
#     i.e. when evaluating expansion/Jacobian for initial guess
int warn_level__gij_not_positive_definite__initial \
  "warning level if the interpolated g_{ij} isn't positive definite \
   (usually this means we're too close to a singularity) \
   (error occurs on first Newton iteration)" STEERABLE=recover
{
-1:* :: "any valid Cactus warning level"
} 2
# ... warning level if error occurs on a subsequent Newton iteration
int warn_level__gij_not_positive_definite__subsequent \
  "warning level if the interpolated g_{ij} isn't positive definite \
   (usually this means we're too close to a singularity) \
   (error occurs on subsequent Newton iteration)" STEERABLE=recover
{
-1:* :: "any valid Cactus warning level"
} 2

################################################################################

#
# ***** parameters for the Newton's-method solution of Theta(h) = 0 *****
#

#
# The first time we (try to) find a given horizon, our initial guess
# is likely to be rather inaccurate, so we may need a larger number of
# iterations.  But if we've found this horizon before, then we have its
# previous position as an initial guess, so (assuming that we've been
# scheduled at each time step) we shouldn't need as many iterations.
#
int max_Newton_iterations__initial \
  "maximum number of Newton iterations before giving up \
   when initially finding a given horizon" STEERABLE=recover
{
(0:* :: "any positive integer"
} 20
int max_Newton_iterations__subsequent \
  "maximum number of Newton iterations before giving up \
   when re-finding a given horizon after finding it before" STEERABLE=recover
{
(0:* :: "any positive integer"
} 10

#
# To avoid the Newton iteration diverging if there is no horizon present
# or if the initial guess is bad, we limit the distance the horizon may
# move in any single Newton iteration (i.e. the infinity-norm of Delta_h)
# to <= this fraction of the mean horizon radius
#
real max_allowable_Delta_h_over_h \
  "don't let horizon move > this fraction of mean radius in a Newton iteration" STEERABLE=recover
{
(0.0:* :: "any positive real number"
} 0.1

#
# To avoid the Newton iteration diverging (or more accurately, to detect
# this divergence faster) if there is no horizon present or if the initial
# guess is bad, we also deem any horizon with a mean radius (measured around
# its local coordinate origin) larger than this to be "not found".  The
# default is effectively "no limit"; AHFinderDirect will be more efficient
# if you (re)set this (for each horizon) to the coordinate radius of a
# sphere a bit larger than you expect the horizon to be (eg a sphere with
# areal radius 4m or so, where m is the ADM mass of the slice).
#
real max_allowable_horizon_radius[101] \
  "max mean-coordinate-radius allowed for any trial surface \
   before we give up and say we can't find this horizon" STEERABLE=recover
{
(0.0:* :: "any positive real number"
} 1.0e10

#
# convergence tolerance for the Newton iteration
#
real Theta_norm_for_convergence \
  "we declare the horizon to be found if ||Theta||_infinity <= this" STEERABLE=recover
{
(0.0:* :: "any positive real number"
} 1.0e-8

#
# To detect some divergence cases faster, we also deem any horizon where
# the infinity-norm of Theta exceeds this parameter, to be "not found".
# The default is effectively "no limit".
#
real max_allowable_Theta \
  "max ||Theta||_infinity allowed for any trial surface \
   before we give up and say we can't find this horizon" STEERABLE=recover
{
(0.0:* :: "any positive real number"
} 1.0e10

#
# To detect some divergence cases faster, we also do not allow the Theta
# norm to grow for more than a certain number of iterations.
#
int max_allowable_Theta_growth_iterations \
  "max number of consecutive iterations during which ||Theta||_infinity \
   is allowed to grow before we give up and say we can't find this horizon" STEERABLE=recover
{
0   :: "infinitly many"
1:* :: "that many"
} 0

#
# To detect some divergence cases faster, we also do not allow the Theta
# norm to oscillate without shrinking for more than a certain number of
# iterations.
#
int max_allowable_Theta_nonshrink_iterations \
  "max number of consecutive iterations during which ||Theta||_infinity \
   is allowed to oscillate without shrinking before we give up and say \
   we can't find this horizon" STEERABLE=recover
{
0   :: "infinitly many"
1:* :: "that many"
} 0

################################################################################

#
# ***** parameters to define the patch system(s) *****
#

#
# For each apparent horizon, you need to set these parameters to the
# Cactus xyz coordinates of a "local origin point" inside the horizon,
# which will serve as the origin for the apparent horizon finder's
# local angular coordinate system.
#
# The apparent horizon surface (and in fact all the trial surfaces the
# apparent horizon finder generates while iteratively solving the apparent
# horizon equation) is restricted to being a Strahlkoerper ("star-shaped
# region") about the origin point.  That is, each surface must be of
# the form r = h(angle) with h a single-valued function.
#
# If the origin point is too far from the actual horizon centroid, i.e.
# if it's too close to the horizon surface itself, then the apparent
# horizon finder's Newton iteration won't converge as quickly or robustly.
# However, in practice the tolerances on this are quite loose -- 1/4
# of the horizon radius is no problem, and even 1/2 the horizon radius
# only slows the convergence by an extra iteration or two.
#
real origin_x[101] "global x coordinate of patch system origin" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real origin_y[101] "global y coordinate of patch system origin" STEERABLE=recover
{
*:* :: "any real number"
} 0.0
real origin_z[101] "global z coordinate of patch system origin" STEERABLE=recover
{
*:* :: "any real number"
} 0.0

boolean move_origins "move the origins with the horizons" STEERABLE=recover
{
} "no"

boolean reshape_while_moving "reshape the horizons when moving them" STEERABLE=recover
{
} "no"

boolean predict_origin_movement "predict origin movement when moving the origins" STEERABLE=recover
{
} "no"

boolean track_origin_from_grid_scalar[101] "track horizon origin from given grid scalars" STEERABLE=recover
{
} "no"
string track_origin_source_x[101] "grid scalar containing the x component of the origin estimate" STEERABLE=recover
{
  "" :: "don't use this feature"
  "[a-zA-Z_][a-zA-Z0-9_]*[:][:][a-zA-Z_][a-zA-Z0-9_]*(\[0-9+\])" :: "name of a grid scalar"
} ""
string track_origin_source_y[101] "grid scalar containing the x component of the origin estimate" STEERABLE=recover
{
  "" :: "don't use this feature"
  "[a-zA-Z_][a-zA-Z0-9_]*[:][:][a-zA-Z_][a-zA-Z0-9_]*(\[0-9+\])" :: "name of a grid scalar"
} ""
string track_origin_source_z[101] "grid scalar containing the x component of the origin estimate" STEERABLE=recover
{
  "" :: "don't use this feature"
  "[a-zA-Z_][a-zA-Z0-9_]*[:][:][a-zA-Z_][a-zA-Z0-9_]*(\[0-9+\])" :: "name of a grid scalar"
} ""

#
# The "(rotating)" patch system types are ok for evaluating Theta(h),
# but alas they don't work yet for apparent horizon finding
# (the Jacobian computation doesn't yet grok the nonlocal rotation BCs).
#
keyword patch_system_type[101] "what type of patch system should we use?"
{
# choose this for normal use
"match Cactus grid symmetry" :: \
  "choose automagically based on grid symmetries and the patch system's origin"

"full sphere"	:: "full sphere, no symmetries"
"+z hemisphere"	:: "mirror symmetry across z=0 plane"

"+xy quadrant (rotating)" :: "90 degree periodic rotation symmetry about z axis"
"+xy quadrant (mirrored)" :: "mirror symmetry across x=0 and y=0 planes"

"+xz quadrant (mirrored)" :: "mirror symmetry across x=0 and z=0 planes"
"+xz quadrant (rotating)" :: \
  "180 degree periodic rotation symmetry about z axis \
   and mirror symmetry across z=0 plane"

"+xyz octant (rotating)"  :: \
  "90 degree periodic rotation symmetry about z axis \
   and mirror symmetry across z=0 plane"
"+xyz octant (mirrored)"  :: "mirror symmetry across x=0 and y=0 and z=0 planes"
} "match Cactus grid symmetry"

#
# This parameter sets the angular resolution of each patch systems:
# the angular grid spacing in degrees is 90.0/N_zones_per_right_angle.
#
# In practice the error in the horizon position is usually dominated
# by the errors from interpolating the Cactus gij and Kij to the horizon
# position, not by the angular finite differencing or interpatch interpolation
# errors.  Thus this parameter can be made fairly small (low resolution)
# for better performance, without seriously affecting the accuracy
# with which we can locate the horizon.
#
# *** IMPORTANT
# *** For any patch system type other than "full sphere", there are
# *** patches with 45 degree widths, so this parameter must be even.
#
# Normally we use Simpson's rule (in a variant which doesn't require the
# number of zones to be even) for angular integrations over the patch
# system.  However, if the number of angular zones in a patch is very
# small and odd (i.e. 3 or 5), then we have to use the trapezoid rule
# instead, so the integrations are less accurate.  This occurs for
# N_zones_per_right_angle = 3 or 5 for a full sphere patch system,
# or N_zones_per_right_angle = 6 or 10 for any other patch system type.
#
# For large values of this parameter, this thorn takes CPU time which
# scales roughly as the ...
# ... 6th power of this parameter
#     if Jacobian_store_solve_method = "dense matrix/LAPACK"
# ... 3rd power of this parameter
#     if Jacobian_store_solve_method = "row-oriented sparse matrix/ILUCG"
#
#int N_zones_per_right_angle[101] "sets angular resolution of patch systems" ACCUMULATOR-BASE max_N_zones_per_right_angle
int N_zones_per_right_angle[101] "sets angular resolution of patch systems"
{
1:* :: "any integer >= 1; must be even for patch systems other than full-sphere"
} 18

# Default parameter values do not trigger accumulator expressions.
# Therefore the default values here must be large enough for the default
# horizon shape.
#int max_N_zones_per_right_angle "the maximum of all N_zones_per_right_angle -- calculated automatically; do not set this parameter directly" ACCUMULATOR=((x>y)*x+!(x>y)*y)
int max_N_zones_per_right_angle "the maximum of all N_zones_per_right_angle"
{
1:* :: "must be at least the maximum of all N_zones_per_right_angle"
} 18

#
# This parameter sets the width of the interpatch ghost zones in the
# patch system.  Note that this thorn uses the terminology "ghost zone"
# for any of what Cactus in general now calls a "boundary zone" or a
# "symmetry zone" or a "patch zone".
#
# This parameter must be at least
# ... 2 if FINITE_DIFF_ORDER is set to 4 in "src/include/config.hh"
# ... 1 if FINITE_DIFF_ORDER is set to 2 in "src/include/config.hh"
# The code checks for this being too small, and reports a fatal error if so.
#
int ghost_zone_width "number of ghost zones on each side of a patch"
{
0:* :: "any integer >= 0"
} 2

#
# Our code that computes surface integrals over patches (used for
# computing BH diagnostics like centroids, areas, masses, etc) silently
# assumes that this parameter is == 1, so you should probably leave
# it at that setting.
#
int patch_overlap_width \
  "number of grid points that nominally-just-touching patches should overlap"
{
1:*:2 :: "any integer >= 0; current implementation requires that it be odd"
} 1

################################################################################

#
# ***** parameters for the Jacobian matrix *****
#

keyword Jacobian_compute_method "how do we compute the Jacobian matrix?" STEERABLE=recover
{
# for debugging only"
"numerical perturbation"    :: "n.b. this is *very* slow"

# use this for normal apparent horizon finding
"symbolic differentiation with finite diff d/dr" :: \
  "fast, tricky programming, uses only gij, dx gij, Kij"

# alas, this isn't implemented yet :(
"symbolic differentiation"  :: \
  "fast, tricky programming, uses gij, dx gij, dxx gij, Kij, dx Kij"
} "symbolic differentiation with finite diff d/dr"

########################################

#
# This parameter lists all known methods for storing the Jacobian and
# solving linear systems with the Jacobian as the LHS matrix.  See
# "src/include/config.hh" for which of these are actually compiled in at
# the moment.  N.b. each compiled-in method requires linking with the
# corresponding linear-solver library; see "src/make.configuration.defn"
# for details on these libraries.
#
keyword Jacobian_store_solve_method \
  "how do we store/linear-solve the Jacobian matrix?" STEERABLE=recover
{
# good for debugging, but very slow for large  N_zones_per_right_angle
"dense matrix/LAPACK"	:: \
  "store as (Fortran) dense matrix, solve with LAPACK routines"
# use this for normal runs
"row-oriented sparse matrix/ILUCG" :: \
  "store as sparse matrix (row-oriented storage format), \
   solve with ILUCG (incomplete LU decomposition - conjugate gradient) method"
# this will probably replace ILUCG, but it's not ready yet :(
"row-oriented sparse matrix/UMFPACK" :: \
  "store as sparse matrix (row-oriented storage format), \
   solve with UMFPACK (sparse LU decomposition) method"
} "row-oriented sparse matrix/UMFPACK"

#
# Extra parameters for individual linear solvers
#

# extra parameters for
#   Jacobian_store_solve_method == "row-oriented sparse matrix/ILUCG"
real ILUCG__error_tolerance "error tolerance for conjugate gradient iteration" STEERABLE=recover
{
(*:0.0)	:: \
  "negative ==> scale the absolute value by the floating point roundoff \
                threshold, e.g. -256.0 means to allow the last 8 bits of \
                the solution to be in error"
(0.0:*)	:: \
  "positive ==> error tolerance"
} 1.0e-10
boolean ILUCG__limit_CG_iterations \
  "should we limit the maximum number of conjugate gradient iterations allowed?" STEERABLE=recover
{
"false"	:: "no limit on CG iterations"
"true"	:: "limit to Neqns CG iterations"
} "true"

# extra parameters for
#   Jacobian_store_solve_method == "row-oriented sparse matrix/UMFPACK"
int UMFPACK__N_II_iterations \
  "number of iterative-improvement iterations to do inside UMFPACK \
   after the sparse LU decompose/solve, each time we solve a linear system" STEERABLE=recover
{
-1	:: "use the UMFPACK default"
0	:: "don't do iterative improvement"
1:*	:: "any positive integer \
	    (in practice a few iterations give almost all the benefit)"
} 0

########################################

#
# This parameter controls two different sorts of one-sided finite
# differencing:
# - numerical-perturbation Jacobian computations
# - the finite differencing part of the "symbolic differentiation
#   with finite diff d/dr" Jacobian computation
#
# Notes on this parameter:
# - don't set it too small or roundoff errors will become large
# - don't set it too large or finite differencing errors will become large
# In practice the default value should be fine
#
real Jacobian_perturbation_amplitude \
  "perturbation amplitude for 1-sided finite differencing for Jacobians" STEERABLE=recover
{
(0.0:* :: "any real number > 0"
} 1.0e-6

# if AHFinderDirect::method = "test Jacobian", should we test all
# known methods for computing the Jacobian, or just the numerical perturbation
# method (the latter may be useful of some other methods are broken)
boolean test_all_Jacobian_compute_methods \
  "should we test all Jacobian computation methods, or just NP?" STEERABLE=recover
{
} "true"

################################################################################

#
# ***** parameters for how we compute the slice's geometry *****
# ***** (gij, Kij, partial_k gij) *****
#

########################################

#
# parameters for geometry interpolation
#
# This 3D interpolator is used to interpolate gij and Kij from the
# Cactus grid to the position of each trial horizon surface, giving
# gij, Kij, and partial_x gij as outputs.  This interpolator must have
# the following properties:
# - It must support taking at least 1st derivatives as part of the
#   interpolation.
# - It should give at least $C^1$ interpolants for smooth data, otherwise
#   the Theta(h) function will have "spikes" and the Newton iteration may
#   fail to converge all the way down to tight error tolerances.  $C^2$
#   would be even better, but in practice a ($C^1$) Hermite interpolant
#   works well.
# In practice the default values for these parameters should work fine
# (with the AEIThorns/AEILocalInterp local interpolator).
#

string coordinate_system_name \
  "name under which the coordinate system is registered in Cactus" STEERABLE=recover
{
".+" :: "any nonempty string"
} "cart3d"

string geometry_interpolator_name \
  "name under which the geometry interpolation operator is registered in Cactus" STEERABLE=recover
{
".+" :: "any nonempty string"
} "Hermite polynomial interpolation"

#
# The defaults here are set up for 2nd order Hermite interpolation,
# which requires driver::ghost_size >= 2.
#
string geometry_interpolator_pars \
  "parameters for the geometry interpolator" STEERABLE=recover
{
".*" :: \
  "any string acceptable to Util_TableSetFromString() and to the interpolator"
} "order=2 \
   boundary_off_centering_tolerance={1.0e-10 1.0e-10 1.0e-10 1.0e-10 1.0e-10 1.0e-10} \
   boundary_extrapolation_tolerance={0.0 0.0 0.0 0.0 0.0 0.0}"

########################################

#
# for testing/debugging purposes, instead of interpolating the geometry
# from the Cactus grid, we can instead hardwire it to an Eddington-Finkelstein
# slice of Schwarzschild spacetime
#
boolean hardwire_Schwarzschild_EF_geometry \
  "should we hard-wire the geometry to Schwarzschild/EF instead of \
   interpolating from the Cactus grid?"
{
} "false"

real geometry__Schwarzschild_EF__mass "mass of Schwarzschild BH"
{
(0.0:*		:: "BH mass = any real number > 0"
} 1.0

real geometry__Schwarzschild_EF__x_posn "x coordinate of Schwarzschild BH"
{
*:* :: "any real number"
} 0.0
real geometry__Schwarzschild_EF__y_posn "y coordinate of Schwarzschild BH"
{
*:* :: "any real number"
} 0.0
real geometry__Schwarzschild_EF__z_posn "z coordinate of Schwarzschild BH"
{
*:* :: "any real number"
} 0.0

# some of the Schwarzschild/EF formulas have 0/0 limits on the z axis;
# this parameter controls where we switch from the generic formulas to
# the L'Hopital's-rule z axis limits
# - don't set this parameter too small or roundoff errors will be excessive
# - don't set this parameter too large or finite differencing errors will
#   be excessive
# in practice the default value should be fine
# n.b. this is used for centered finite differencing, unlike the Jacobian
real geometry__Schwarzschild_EF__epsilon \
  "threshold for sin^2 theta = (x^2+y^2)/r^2 below which we use z axis limits"
{
(0.0:*	:: "this should be somewhat above the floating-point roundoff level"
} 1.0e-9

# we compute partial_k g_ij by numerical finite differencing of the exact
# analytical g_ij values; this parameter sets the "grid spacing" for this
# - don't set this parameter too small or roundoff errors will be excessive
# - don't set this parameter too large or finite differencing errors will
#   be excessive
# in practice the default value should be fine
# ... n.b. this finite differencing is *centered*, unlike that in the
#          Jacobian computation
real geometry__Schwarzschild_EF__Delta_xyz \
  "finite diff pseuo-grid spacing for computing partial_k g_ij"
{
(0.0:* :: "any real number > 0"
} 1.0e-6

########################################

#
# These parameters control whether we check that various angular gridfns
# are finite (neither NaN nor infinity) at various points in evaluating
# the Theta(h) function.  These are pretty cheap tests, and they're quite
# useful in catching assorted wierdness, so it's probably worth leaving
# them enabled unless you're trying to squeeze every last nanosecond...
#
boolean check_that_h_is_finite \
  "should we check that horizon shape function h is finite?" \
  STEERABLE=recover
{
} "true"
boolean check_that_geometry_is_finite \
  "should we check the interpolated geometry variables are finite?" \
  STEERABLE=recover
{
} "true"

################################################################################

#
# ***** parameters for the interpatch interpolation *****
#

#
# This 1D local interpolator is used to interpolate the h function between
# angular patches.  Because any given patch boundary only interpolates
# from a single neighboring patch (this is to simplify the bookkeeping),
# near the patch corners this interpolator will have to be used off-centered.
# Thus it's desirable to use an interpolator which retains reasonable
# accuracy right up to the edge of the data range.  In practice a Lagrange
# polynomial interpolant works well.
#

string interpatch_interpolator_name \
  "name under which the interpatch interpolation operator is registered in Cactus" STEERABLE=recover
{
".+" :: "any string (in practice it should be nonempty)"
} "Lagrange polynomial interpolation"

string interpatch_interpolator_pars \
  "parameters for the interpatch interpolator" STEERABLE=recover
{
".*" :: \
  "any string acceptable to Util_TableSetFromString() and to the interpolator"
} "order=5"

################################################################################

#
# ***** parameters for the surface interpolation *****
#

#
# This 2D local interpolator is used to interpolate the horizon radius
# arbitrary angular coordinate, to find out whether a given xyz point is
# inside or outside the horizon.
#
# At present this interpolator is used only if  set_horizon_mask  is true.
# Otherwise, these parameters may both be set to empty strings.
#

string surface_interpolator_name \
  "name under which the surface interpolation operator is registered in Cactus" STEERABLE=recover
{
"^$" :: "the empty string if this interpolator isn't going to be used"
".+" :: "any string (in practice it should be nonempty)"
} "Lagrange polynomial interpolation"

string surface_interpolator_pars \
  "parameters for the surface interpolator" STEERABLE=recover
{
".*" :: \
  "any string acceptable to Util_TableSetFromString() and to the interpolator"
} "order=2 \
   boundary_off_centering_tolerance={1.0e-10 1.0e-10 1.0e-10 1.0e-10} \
   boundary_extrapolation_tolerance={0.0 0.0 0.0 0.0}"

################################################################################

#
# ***** parameters for computing line/surface integrals over the horizon *****
#

#
# In the descriptions, N is the number of grid zones in a patch
# in either the rho or the sigma direction, i.e. it's 45 or 90 degrees
# divided by delta_drho_dsigma.
#
keyword integral_method "how do we compute integrals over the horizon?" STEERABLE=recover
{
"trapezoid"		:: "alternate name for trapezoid rule"
"trapezoid rule"	:: "trapezoid rule (2nd order for smooth functions)"
"Simpson"		:: "alternate name for Simpson's rule"
"Simpson's rule"	:: \
  "Simpson's rule (4th order for smooth fns, requires N to be even)"
"Simpson (variant)"	:: "alternate name for Simpson's rule variant"
"Simpson's rule (variant)":: \
  "Simpson's rule variant (4th order for smooth fns, requires N >= 7)"

# choose this for normal use (assuming FINITE_DIFF_ORDER is set to 4
# in "src/include/config.hh")
"automatic choice"	:: \
  "choose Simpson's rule or variant if applicable, otherwise trapezoid rule"
} "automatic choice"

################################################################################

###
### ***** parameters for the test driver "src/patch/test_patch_system.cc" *****
###
### By default this test driver isn't compiled into the cactus executable,
### and these parameters are ignored.  To compile this test driver into
### the cactus executable (and have these parameters used),
### - edit the list of "source files in this directory" in
###   "src/patch/make.code.defn" to add  test_patch_system.cc
### - comment out the list of "subdirectories containing source files" in
###   "src/make.code.defn", and uncomment the alternate list which is
###   normally commented out there (this omits the higher-level directories
###   src/elliptic/, src/gr/, and src/driver/)
###
##keyword which_test "which test should we do?"
##{
##"gridfn"	:: "set up test fn(x,y,z), print it"
##"read gridfn"	:: "read in ghosted test fn(x,y,z), print it"
##"synchronize"	:: "set up test fn(x,y,z), synchronize it, print errors"
##"ghost zone Jacobian":: \
##  "set up test fn(x,y,z), compute Jacobian of gz.synchronize(), compare with NP"
##"derivatives"	:: "set up test fn(rho,sigma), take derivs, print errors"
##} "gridfn"
##
##int which_derivs "bit flags to specify which derivatives to test"
##{
##0:63 :: "any set of bit flags"
##} 63
##
### true ==> gives a more thorough test of the Jacobian,
###          but makes the test run much slower
### false ==> gives a slightly less thorough test, but runs faster
##boolean NP_Jacobian__perturb_all_y_patch_points \
##  "should we perturb at *all* points in the y patch, or just those with the \
##   iperp which is (supposedly) involved in the interpatch interpolation?"
##{
##} "true"
##
##################################################################################

configuration: REQUIRES THORNS: SpaceMask

OPTIONAL HDF5
{
}

context: sparse-matrix/umfpack/umfpack_report_numeric.h:
```
/* ========================================================================== */
/* === umfpack_report_numeric =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_report_numeric
(
    void *Numeric,
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_dl_report_numeric
(
    void *Numeric,
    const double Control [UMFPACK_CONTROL]
) ;

int umfpack_zi_report_numeric
(
    void *Numeric,
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_zl_report_numeric
(
    void *Numeric,
    const double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    double Control [UMFPACK_CONTROL] ;
    int status ;
    status = umfpack_di_report_numeric (Numeric, Control) ;

double long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    double Control [UMFPACK_CONTROL] ;
    long status ;
    status = umfpack_dl_report_numeric (Numeric, Control) ;

complex int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    double Control [UMFPACK_CONTROL] ;
    int status ;
    status = umfpack_zi_report_numeric (Numeric, Control) ;

complex long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    double Control [UMFPACK_CONTROL] ;
    long status ;
    status = umfpack_zl_report_numeric (Numeric, Control) ;

Purpose:

    Verifies and prints a Numeric object (the LU factorization, both its pattern
    numerical values, and permutation vectors P and Q).  This routine checks the
    object more carefully than the computational routines.  Normally, this check
    is not required, since umfpack_*_numeric either returns (void *) NULL, or a
    valid Numeric object.  However, if you suspect that your own code has
    corrupted the Numeric object (by overruning memory bounds, for example),
    then this routine might be able to detect a corrupted Numeric object.  Since
    this is a complex object, not all such user-generated errors are guaranteed
    to be caught by this routine.

Returns:

    UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked).

    Otherwise:

    UMFPACK_OK if the Numeric object is valid.
    UMFPACK_ERROR_invalid_Numeric_object if the Numeric object is invalid.
    UMFPACK_ERROR_out_of_memory if out of memory.

Arguments:

    void *Numeric ;			Input argument, not modified.

	The Numeric object, which holds the numeric factorization computed by
	umfpack_*_numeric.

    double Control [UMFPACK_CONTROL] ;	Input argument, not modified.

	If a (double *) NULL pointer is passed, then the default control
	settings are used.  Otherwise, the settings are determined from the
	Control array.  See umfpack_*_defaults on how to fill the Control
	array with the default settings.  If Control contains NaN's, the
	defaults are used.  The following Control parameters are used:

	Control [UMFPACK_PRL]:  printing level.

	    2 or less: no output.  returns silently without checking anything.
	    3: fully check input, and print a short summary of its status
	    4: as 3, but print first few entries of the input
	    5: as 3, but print all of the input
	    Default: 1
*/

```

sparse-matrix/umfpack/umf_create_element.c:
```
/* ========================================================================== */
/* === UMF_create_element =================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Factorization of a frontal matrix is complete.  Create a new element for
    later assembly into a subsequent frontal matrix.  Returns TRUE if
    successful, FALSE if out of memory.
*/

#include "umf_internal.h"
#include "umf_mem_alloc_element.h"
#include "umf_mem_alloc_tail_block.h"
#include "umf_mem_free_tail_block.h"
#include "umf_get_memory.h"
#include "umf_blas3_update.h"

GLOBAL Int UMF_create_element
(
    NumericType *Numeric,
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int j, col, row, *Fcols, *Frows, fnrows, fncols, *Cols, len, needunits, t1,
	t2, size, e, i, *E, *Fcpos, *Frpos, *Rows, eloc, fnrows_max, f,
	got_memory, *Row_tuples, *Row_degree, *Row_tlen, *Col_tuples, max_mark,
	*Col_degree, *Col_tlen, nn, n_row, n_col ;
    Entry *C, *Fcol ;
    Element *ep ;
    Unit *p, *Memory ;
    Tuple *tp, *tp1, *tp2, tuple, *tpend ;
#ifndef NDEBUG
    double gprob_save ;
#endif

    /* ---------------------------------------------------------------------- */
    /* apply pending updates, if any */
    /* ---------------------------------------------------------------------- */

    if (Work->fnpiv > 0)
    {
	UMF_blas3_update (Work) ;
    }

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

    DEBUG2 (("FRONTAL WRAPUP\n")) ;
    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;
    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;
    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;
    n_row = Work->n_row ;
    n_col = Work->n_col ;
    nn = MAX (n_row, n_col) ;
    Fcols = Work->Fcols ;
    Frows = Work->Frows ;
    Fcpos = Work->Fcpos ;
    Frpos = Work->Frpos ;
    Memory = Numeric->Memory ;
    fncols = Work->fncols ;
    fnrows = Work->fnrows ;
    fnrows_max = Work->fnrows_max ;

    tp = (Tuple *) NULL ;
    tp1 = (Tuple *) NULL ;
    tp2 = (Tuple *) NULL ;

    /* ---------------------------------------------------------------------- */
    /* add the current frontal matrix to the degrees of each column, */
    /* and place the modified columns back in the degree lists */
    /* ---------------------------------------------------------------------- */

    for (j = 0 ; j < fncols ; j++)
    {
	/* add the current frontal matrix to the degree */
	ASSERT (Fcols [j] >= 0 && Fcols [j] < n_col) ;
	Col_degree [Fcols [j]] += fnrows ;
    }

    /* ---------------------------------------------------------------------- */
    /* add the current frontal matrix to the degrees of each row */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i < fnrows ; i++)
    {
	/* add the current frontal matrix to the degree */
	ASSERT (Frows [i] >= 0 && Frows [i] < n_row) ;
	Row_degree [Frows [i]] += fncols ;
    }

    /* ---------------------------------------------------------------------- */
    /* Reset the external degree counters */
    /* ---------------------------------------------------------------------- */

    E = Work->E ;
    max_mark = MAX_MARK (nn) ;

    if (!Work->pivcol_in_front)
    {
	/* clear the external column degrees. no more Usons of current front */
	Work->cdeg0 += (nn + 1) ;
	if (Work->cdeg0 >= max_mark)
	{
	    /* guard against integer overflow.  This is very rare */
	    Work->cdeg0 = 1 ;
	    for (e = 0 ; e <= Work->nel ; e++)
	    {
		if (E [e])
		{
		    ep = (Element *) (Memory + E [e]) ;
		    ep->cdeg = 0 ;
		}
	    }
	}
    }

    if (!Work->pivrow_in_front)
    {
	/* clear the external row degrees.  no more Lsons of current front */
	Work->rdeg0 += (nn + 1) ;
	if (Work->rdeg0 >= max_mark)
	{
	    /* guard against integer overflow.  This is very rare */
	    Work->rdeg0 = 1 ;
	    for (e = 0 ; e <= Work->nel ; e++)
	    {
		if (E [e])
		{
		    ep = (Element *) (Memory + E [e]) ;
		    ep->rdeg = 0 ;
		}
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* clear row/col offsets */
    /* ---------------------------------------------------------------------- */

    if (!Work->pivrow_in_front)
    {
	for (j = 0 ; j < fncols ; j++)
	{
	    Fcpos [Fcols [j]] = EMPTY ;
	}
    }

    if (!Work->pivcol_in_front)
    {
	for (i = 0 ; i < fnrows ; i++)
	{
	    Frpos [Frows [i]] = EMPTY ;
	}
    }

    if (fncols <= 0 || fnrows <= 0)
    {
	/* no element to create */
	DEBUG2 (("Element evaporation\n")) ;
	Work->prior_element = EMPTY ;
	return (TRUE) ;
    }

    /* ---------------------------------------------------------------------- */
    /* create element for later assembly */
    /* ---------------------------------------------------------------------- */

    needunits = 0 ;
    got_memory = FALSE ;
    eloc = UMF_mem_alloc_element (Numeric, fnrows, fncols, &Rows, &Cols, &C,
	&needunits, &ep) ;

    if (!eloc)
    {
	/* do garbage collection, realloc, and try again */
	if (!UMF_get_memory (Numeric, Work, needunits))
	{
	    return (FALSE) ;	/* out of memory */
	}
	got_memory = TRUE ;
	Memory = Numeric->Memory ;
#ifndef NDEBUG
	gprob_save = UMF_gprob ;
	UMF_gprob = 0 ;
#endif
	eloc = UMF_mem_alloc_element (Numeric, fnrows, fncols, &Rows, &Cols, &C,
	    &needunits, &ep) ;
	ASSERT (eloc >= 0) ;
	if (!eloc)
	{
	    return (FALSE) ;	/* out of memory */
	}
#ifndef NDEBUG
	UMF_gprob = gprob_save ;
#endif
    }

    e = ++(Work->nel) ;	/* get the name of this new frontal matrix */
    Work->prior_element = e ;
    DEBUG8 (("wrapup e "ID" nel "ID"\n", e, Work->nel)) ;

    ASSERT (e > 0 && e <= n_col + MIN (n_row,n_col)) ;
    ASSERT (E [e] == 0) ;
    E [e] = eloc ;

    if (Work->pivcol_in_front)
    {
	/* the new element is a Uson of the next frontal matrix */
	ep->cdeg = Work->cdeg0 ;
    }

    if (Work->pivrow_in_front)
    {
	/* the new element is an Lson of the next frontal matrix */
	ep->rdeg = Work->rdeg0 ;
    }

    /* ---------------------------------------------------------------------- */
    /* copy frontal matrix into the new element */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i < fnrows ; i++)
    {
	Rows [i] = Frows [i] ;
    }
    for (i = 0 ; i < fncols ; i++)
    {
	Cols [i] = Fcols [i] ;
    }
    Fcol = Work->Fx ;
    for (j = 0 ; j < fncols ; j++)
    {
	for (i = 0 ; i < fnrows ; i++)
	{
	    C [i] = Fcol [i] ;
	}
	Fcol += fnrows_max ;
	C += fnrows ;
    }

    /* ---------------------------------------------------------------------- */
    /* add tuples for the new element */
    /* ---------------------------------------------------------------------- */

    tuple.e = e ;

    if (got_memory)
    {

	/* ------------------------------------------------------------------ */
	/* UMF_get_memory ensures enough space exists for each new tuple */
	/* ------------------------------------------------------------------ */

	/* place (e,f) in the element list of each column */
	for (tuple.f = 0 ; tuple.f < fncols ; tuple.f++)
	{
	    col = Fcols [tuple.f] ;
	    ASSERT (col >= 0 && col < n_col) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    ASSERT (Col_tuples [col]) ;
	    tp = ((Tuple *) (Memory + Col_tuples [col])) + Col_tlen [col]++ ;
	    *tp = tuple ;
	}

	/* ------------------------------------------------------------------ */

	/* place (e,f) in the element list of each row */
	for (tuple.f = 0 ; tuple.f < fnrows ; tuple.f++)
	{
	    row = Frows [tuple.f] ;
	    ASSERT (row >= 0 && row < n_row) ;
	    ASSERT (NON_PIVOTAL_ROW (row)) ;
	    ASSERT (Row_tuples [row]) ;
	    tp = ((Tuple *) (Memory + Row_tuples [row])) + Row_tlen [row]++ ;
	    *tp = tuple ;
	}

    }
    else
    {

	/* ------------------------------------------------------------------ */
	/* might not have enough space for each tuple */
	/* ------------------------------------------------------------------ */

	/* ------------------------------------------------------------------ */
	/* place (e,f) in the element list of each column */
	/* ------------------------------------------------------------------ */

	for (tuple.f = 0 ; tuple.f < fncols ; tuple.f++)
	{
	    col = Fcols [tuple.f] ;
	    ASSERT (col >= 0 && col < n_col) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    t1 = Col_tuples [col] ;
	    DEBUG1 (("Placing on col:"ID" , tuples at "ID"\n",
		col, Col_tuples [col])) ;

	    size = 0 ;
	    len = 0 ;

	    if (t1)
	    {
		p = Memory + t1 ;
		tp = (Tuple *) p ;
		size = GET_BLOCK_SIZE (p) ;
		len = Col_tlen [col] ;
		tp2 = tp + len ;
	    }

	    needunits = UNITS (Tuple, len + 1) ;
	    DEBUG1 (("len: "ID" size: "ID" needunits: "ID"\n",
		len, size, needunits));

	    if (needunits > size && t1)
	    {
		/* prune the tuples */
		tp1 = tp ;
		tp2 = tp ;
		tpend = tp + len ;
		for ( ; tp < tpend ; tp++)
		{
		    e = tp->e ;
		    ASSERT (e > 0 && e <= Work->nel) ;
		    if (!E [e]) continue ;   /* element already deallocated */
		    f = tp->f ;
		    p = Memory + E [e] ;
		    ep = (Element *) p ;
		    p += UNITS (Element, 1) ;
		    Cols = (Int *) p ;
		    ;
		    if (Cols [f] == EMPTY) continue ;	/* already assembled */
		    ASSERT (col == Cols [f]) ;
		    *tp2++ = *tp ;	/* leave the tuple in the list */
		}
		len = tp2 - tp1 ;
		Col_tlen [col] = len ;
		needunits = UNITS (Tuple, len + 1) ;
	    }

	    if (needunits > size)
	    {
		/* no room exists - reallocate elsewhere */
		DEBUG1 (("REALLOCATE Col: "ID", size "ID" to "ID"\n",
		    col, size, 2*needunits)) ;
#ifndef NDEBUG
		UMF_allocfail = FALSE ;
		if (UMF_gprob > 0)  /* a double relop, but ignore NaN case */
		{
		    double rrr = ((double) (rand ( ))) /
		    	(((double) RAND_MAX) + 1) ;
		    DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ;
		    UMF_allocfail = rrr < UMF_gprob ;
		    if (UMF_allocfail) DEBUG1 (("Random gar. (col tuple)\n")) ;
		}
#endif
		needunits = MIN (2*needunits, (Int) UNITS (Tuple, nn)) ;
		t2 = UMF_mem_alloc_tail_block (Numeric, needunits) ;
		if (!t2)
		{
		    /* get memory, reconstruct all tuple lists, and return */
		    return (UMF_get_memory (Numeric, Work, 0)) ;
		}
		Col_tuples [col] = t2 ;
		tp2 = (Tuple *) (Memory + t2) ;
		if (t1)
		{
		    for (i = 0 ; i < len ; i++)
		    {
			*tp2++ = *tp1++ ;
		    }
		    UMF_mem_free_tail_block (Numeric, t1) ;
		}
	    }

	    /* place the new (e,f) tuple in the element list of the column */
	    Col_tlen [col]++ ;
	    *tp2 = tuple ;
	}

	/* ------------------------------------------------------------------ */
	/* place (e,f) in the element list of each row */
	/* ------------------------------------------------------------------ */

	for (tuple.f = 0 ; tuple.f < fnrows ; tuple.f++)
	{
	    row = Frows [tuple.f] ;
	    ASSERT (row >= 0 && row < n_row) ;
	    ASSERT (NON_PIVOTAL_ROW (row)) ;
	    t1 = Row_tuples [row] ;
	    DEBUG1 (("Placing on row:"ID" , tuples at "ID"\n",
		row, Row_tuples [row])) ;

	    size = 0 ;
	    len = 0 ;
	    if (t1)
	    {
		p = Memory + t1 ;
		tp = (Tuple *) p ;
		size = GET_BLOCK_SIZE (p) ;
		len = Row_tlen [row] ;
		tp2 = tp + len ;
	    }

	    needunits = UNITS (Tuple, len + 1) ;
	    DEBUG1 (("len: "ID" size: "ID" needunits: "ID"\n",
		len, size, needunits)) ;

	    if (needunits > size && t1)
	    {
		/* prune the tuples */
		tp1 = tp ;
		tp2 = tp ;
		tpend = tp + len ;
		for ( ; tp < tpend ; tp++)
		{
		    e = tp->e ;
		    ASSERT (e > 0 && e <= Work->nel) ;
		    if (!E [e])
		    {
			continue ;	/* element already deallocated */
		    }
		    f = tp->f ;
		    p = Memory + E [e] ;
		    ep = (Element *) p ;
		    p += UNITS (Element, 1) ;
		    Cols = (Int *) p ;
		    Rows = Cols + (ep->ncols) ;
		    if (Rows [f] == EMPTY) continue ;	/* already assembled */
		    ASSERT (row == Rows [f]) ;
		    *tp2++ = *tp ;	/* leave the tuple in the list */
		}
		len = tp2 - tp1 ;
		Row_tlen [row] = len ;
		needunits = UNITS (Tuple, len + 1) ;
	    }

	    if (needunits > size)
	    {
		/* no room exists - reallocate elsewhere */
		DEBUG1 (("REALLOCATE Row: "ID", size "ID" to "ID"\n",
		    row, size, 2*needunits)) ;
#ifndef NDEBUG
		UMF_allocfail = FALSE ;
		if (UMF_gprob > 0)  /* a double relop, but ignore NaN case */
		{
		    double rrr = ((double) (rand ( ))) /
		    	(((double) RAND_MAX) + 1) ;
		    DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ;
		    UMF_allocfail = rrr < UMF_gprob ;
		    if (UMF_allocfail) DEBUG1 (("Random gar. (row tuple)\n")) ;
		}
#endif
		needunits = MIN (2*needunits, (Int) UNITS (Tuple, nn)) ;
		t2 = UMF_mem_alloc_tail_block (Numeric, needunits) ;
		if (!t2)
		{
		    /* get memory, reconstruct all tuple lists, and return */
		    return (UMF_get_memory (Numeric, Work, 0)) ;
		}
		Row_tuples [row] = t2 ;
		tp2 = (Tuple *) (Memory + t2) ;
		if (t1)
		{
		    for (i = 0 ; i < len ; i++)
		    {
			*tp2++ = *tp1++ ;
		    }
		    UMF_mem_free_tail_block (Numeric, t1) ;
		}
	    }

	    /* place the new (e,f) tuple in the element list of the row */
	    Row_tlen [row]++ ;
	    *tp2 = tuple ;
	}

    }

    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG1 (("Done extending\nFINAL: element row pattern: len="ID"\n", fncols));
    for (j = 0 ; j < fncols ; j++) DEBUG1 ((""ID"\n", Fcols [j])) ;
    DEBUG1 (("FINAL: element col pattern:  len="ID"\n", fnrows)) ;
    for (j = 0 ; j < fnrows ; j++) DEBUG1 ((""ID"\n", Frows [j])) ;
    for (j = 0 ; j < fncols ; j++)
    {
	col = Fcols [j] ;
	ASSERT (col >= 0 && col < n_col) ;
	UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ;
    }
    for (j = 0 ; j < fnrows ; j++)
    {
	row = Frows [j] ;
	ASSERT (row >= 0 && row < n_row) ;
	UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
    }

    if (n_row < 1000 && n_col < 1000)
    {
	UMF_dump_memory (Numeric) ;
    }

    DEBUG1 (("New element, after filling with stuff: "ID"\n", e)) ;
    UMF_dump_element (Numeric, Work, e, TRUE) ;

    if (nn < 1000)
    {
	DEBUG4 (("Matrix dump, after New element: "ID"\n", e)) ;
	UMF_dump_matrix (Numeric, Work, TRUE) ;
    }

    DEBUG3 (("FRONTAL WRAPUP DONE\n")) ;
#endif

    return (TRUE) ;
}


```

sparse-matrix/umfpack/umf_symbolic_usage.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL double UMF_symbolic_usage
(
    Int n_row,
    Int n_col,
    Int nchains,
    Int nfr
) ;


```

sparse-matrix/umfpack/umf_garbage_collection.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void UMF_garbage_collection
(
    NumericType *Numeric,
    WorkType *Work
) ;


```

sparse-matrix/umfpack/umf_utsolve.c:
```
/* ========================================================================== */
/* === UMF_utsolve ========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*  solves U'x = b or U.'x=b, where U is the upper triangular factor of a */
/*  matrix.  B is overwritten with the solution X. */
/*  Returns the floating point operation count */

#include "umf_internal.h"

GLOBAL double
#ifdef CONJUGATE_SOLVE
UMF_uhsolve			/* solve U'x=b  (complex conjugate transpose) */
#else
UMF_utsolve			/* solve U.'x=b (array transpose) */
#endif
(
    NumericType *Numeric,
    Entry X [ ],		/* b on input, solution x on output */
    Int Pattern [ ]		/* a work array of size n */
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int k, deg, j, *ip, col, *Upos, *Uilen, kstart, kend, up,
	*Uip, n, uhead, ulen, pos, npiv ;
    Entry *xp, xk, *D ;

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

    if (Numeric->n_row != Numeric->n_col) return (0.) ;
    n = Numeric->n_row ;
    npiv = Numeric->npiv ;
    Upos = Numeric->Upos ;
    Uilen = Numeric->Uilen ;
    Uip = Numeric->Uip ;
    D = Numeric->D ;
    kend = 0 ;

#ifndef NDEBUG
    DEBUG4 (("Utsolve start: npiv "ID" n "ID"\n", npiv, n)) ;
    for (j = 0 ; j < n ; j++)
    {
	DEBUG4 (("Utsolve start "ID": ", j)) ;
	EDEBUG4 (X [j]) ;
	DEBUG4 (("\n")) ;
    }
#endif

    for (kstart = 0 ; kstart < npiv ; kstart = kend + 1)
    {

	/* ------------------------------------------------------------------ */
	/* find the end of this Uchain */
	/* ------------------------------------------------------------------ */

	DEBUG4 (("kstart "ID" kend "ID"\n", kstart, kend)) ;
	/* for (kend = kstart ; kend < npiv && Uip [kend+1] > 0 ; kend++) ; */
	kend = kstart ;
	while (kend < npiv && Uip [kend+1] > 0)
	{
	    kend++ ;
	}

	/* ------------------------------------------------------------------ */
	/* scan the whole Uchain to find the pattern of the first row of U */
	/* ------------------------------------------------------------------ */

	k = kend+1 ;
	DEBUG4 (("\nKend "ID" K "ID"\n", kend, k)) ;

	/* ------------------------------------------------------------------ */
	/* start with last row in Uchain of U in Pattern [0..deg-1] */
	/* ------------------------------------------------------------------ */

	if (k == npiv)
	{
	    deg = Numeric->ulen ;
	    if (deg > 0)
	    {
		/* make last pivot row of U (singular matrices only) */
		for (j = 0 ; j < deg ; j++)
		{
		    Pattern [j] = Numeric->Upattern [j] ;
		}
	    }
	}
	else
	{
	    ASSERT (k >= 0 && k < npiv) ;
	    up = -Uip [k] ;
	    ASSERT (up > 0) ;
	    deg = Uilen [k] ;
	    DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ;
	    ip = (Int *) (Numeric->Memory + up) ;
	    for (j = 0 ; j < deg ; j++)
	    {
		col = *ip++ ;
		DEBUG4 (("  k "ID" col "ID"\n", k-1, col)) ;
		ASSERT (k <= col) ;
		Pattern [j] = col ;
	    }
	}

	/* empty the stack at the bottom of Pattern */
	uhead = n ;

	for (k = kend ; k > kstart ; k--)
	{
	    /* Pattern [0..deg-1] is the pattern of row k of U */

	    /* -------------------------------------------------------------- */
	    /* make row k-1 of U in Pattern [0..deg-1] */
	    /* -------------------------------------------------------------- */

	    ASSERT (k >= 0 && k < npiv) ;
	    ulen = Uilen [k] ;
	    /* delete, and push on the stack */
	    for (j = 0 ; j < ulen ; j++)
	    {
		ASSERT (uhead >= deg) ;
		Pattern [--uhead] = Pattern [--deg] ;
	    }
	    DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k, deg)) ;
	    ASSERT (deg >= 0) ;

	    pos = Upos [k] ;
	    if (pos != EMPTY)
	    {
		/* add the pivot column */
		DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ;
		ASSERT (pos >= 0 && pos <= deg) ;
		Pattern [deg++] = Pattern [pos] ;
		Pattern [pos] = k ;
	    }
	}

	/* Pattern [0..deg-1] is now the pattern of the first row in Uchain */

	/* ------------------------------------------------------------------ */
	/* solve using this Uchain, in reverse order */
	/* ------------------------------------------------------------------ */

	DEBUG4 (("Unwinding Uchain\n")) ;
	for (k = kstart ; k <= kend ; k++)
	{

	    /* -------------------------------------------------------------- */
	    /* construct row k */
	    /* -------------------------------------------------------------- */

	    ASSERT (k >= 0 && k < npiv) ;
	    pos = Upos [k] ;
	    if (pos != EMPTY)
	    {
		/* remove the pivot column */
		DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ;
		ASSERT (k > kstart) ;
		ASSERT (pos >= 0 && pos < deg) ;
		ASSERT (Pattern [pos] == k) ;
		Pattern [pos] = Pattern [--deg] ;
	    }

	    up = Uip [k] ;
	    ulen = Uilen [k] ;
	    if (k > kstart)
	    {
		/* concatenate the deleted pattern; pop from the stack */
		for (j = 0 ; j < ulen ; j++)
		{
		    ASSERT (deg <= uhead && uhead < n) ;
		    Pattern [deg++] = Pattern [uhead++] ;
		}
		DEBUG4 (("middle of chain, row of U "ID" deg "ID"\n", k, deg)) ;
		ASSERT (deg >= 0) ;
	    }

	    /* -------------------------------------------------------------- */
	    /* use row k of U */
	    /* -------------------------------------------------------------- */

	    /* Go ahead and divide by zero if D [k] is zero. */
#ifdef CONJUGATE_SOLVE
	    /* xk = X [k] / conjugate (D [k]) ; */
	    DIV_CONJ (xk, X [k], D [k]) ;
#else
	    /* xk = X [k] / D [k] ; */
	    DIV (xk, X [k], D [k]) ;
#endif
	    X [k] = xk ;

	    if (IS_NONZERO (xk))
	    {
		if (k == kstart)
		{
		    up = -up ;
		    xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ;
		}
		else
		{
		    xp = (Entry *) (Numeric->Memory + up) ;
		}
		for (j = 0 ; j < deg ; j++)
		{
		    DEBUG4 (("  k "ID" col "ID" value", k, Pattern [j])) ;
		    EDEBUG4 (*xp) ;
		    DEBUG4 (("\n")) ;
#ifdef CONJUGATE_SOLVE
		    /* X [Pattern [j]] -= xk * conjugate (*xp) ; */
		    MULT_SUB_CONJ (X [Pattern [j]], xk, *xp) ;
#else
		    /* X [Pattern [j]] -= xk * (*xp) ; */
		    MULT_SUB (X [Pattern [j]], xk, *xp) ;
#endif
		    xp++ ;
		}
	    }
	}
	ASSERT (uhead == n) ;
    }

    for (k = npiv ; k < n ; k++)
    {
	/* Note that D [k] is identically zero. */
	ASSERT (IS_ZERO (D [k])) ;
	/* For conjugate solve, D [k] == conjugate (D [k]), in this case */
	/* xk = X [k] / D [k] ; */
	DIV (xk, X [k], D [k]) ;
	X [k] = xk ;
    }

#ifndef NDEBUG
    for (j = 0 ; j < n ; j++)
    {
	DEBUG4 (("Utsolve done "ID": ", j)) ;
	EDEBUG4 (X [j]) ;
	DEBUG4 (("\n")) ;
    }
    DEBUG4 (("Utsolve done.\n")) ;
#endif

    return (DIV_FLOPS * ((double) n) + MULTSUB_FLOPS * ((double) Numeric->unz));
}


```

sparse-matrix/umfpack/umf_tuple_lengths.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_tuple_lengths
(
    NumericType *Numeric,
    WorkType *Work,
    double *dusage
) ;


```

sparse-matrix/umfpack/umf_blas3_update.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void UMF_blas3_update
(
    WorkType *Work
) ;


```

sparse-matrix/umfpack/umf_build_tuples_usage.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_build_tuples_usage
(
    const Int Col_tlen [ ],
    const Int Col_degree [ ],
    const Int Row_tlen [ ],
    const Int Row_degree [ ],
    Int n_row,
    Int n_col,
    double *dusage
) ;


```

sparse-matrix/umfpack/umfpack_col_to_triplet.h:
```
/* ========================================================================== */
/* === umfpack_col_to_triplet =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_col_to_triplet
(
    int n_col,
    const int Ap [ ],
    int Tj [ ]
) ;

long umfpack_dl_col_to_triplet
(
    long n_col,
    const long Ap [ ],
    long Tj [ ]
) ;

int umfpack_zi_col_to_triplet
(
    int n_col,
    const int Ap [ ],
    int Tj [ ]
) ;

long umfpack_zl_col_to_triplet
(
    long n_col,
    const long Ap [ ],
    long Tj [ ]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int n_col, *Tj, *Ap, status ;
    status = umfpack_di_col_to_triplet (n_col, Ap, Tj) ;

double long Syntax:

    #include "umfpack.h"
    long n_col, *Tj, *Ap, status ;
    status = umfpack_dl_col_to_triplet (n_col, Ap, Tj) ;

complex int Syntax:

    #include "umfpack.h"
    int n_col, *Tj, *Ap, status ;
    status = umfpack_zi_col_to_triplet (n_col, Ap, Tj) ;

complex long Syntax:

    #include "umfpack.h"
    long n_col, *Tj, *Ap, status ;
    status = umfpack_zl_col_to_triplet (n_col, Ap, Tj) ;

Purpose:

    Converts a column-oriented matrix to a triplet form.  Only the column
    pointers, Ap, are required, and only the column indices of the triplet form
    are constructed.   This routine is the opposite of umfpack_*_triplet_to_col.
    The matrix may be singular and/or rectangular.  Analogous to [i, Tj, x] =
    find (A) in MATLAB, except that zero entries present in the column-form of
    A are present in the output, and i and x are not created (those are just Ai
    and Ax+Az*1i, respectively, for a column-form matrix A).

Returns:

    UMFPACK_OK if successful
    UMFPACK_ERROR_argument_missing if Ap or Tj is missing
    UMFPACK_ERROR_n_nonpositive if n_col <= 0
    UMFPACK_ERROR_Ap0_nonzero if Ap [0] != 0
    UMFPACK_ERROR_nz_negative if Ap [n_col] < 0
    UMFPACK_ERROR_col_length_negative if Ap [j] > Ap [j+1] for any j in the
	range 0 to n-1.
    Unsorted columns and duplicate entries do not cause an error (these would
    only be evident by examining Ai).  Empty rows and columns are OK.

Arguments:

    Int n_col ;		Input argument, not modified.

	A is an n_row-by-n_col matrix.  Restriction: n_col > 0.
	(n_row is not required)

    Int Ap [n_col+1] ;	Input argument, not modified.

	The column pointers of the column-oriented form of the matrix.  See
	umfpack_*_*symbolic for a description.  The number of entries in
	the matrix is nz = Ap [n_col].  Restrictions on Ap are the same as those
	for umfpack_*_transpose.  Ap [0] must be zero, nz must be >= 0, and
	Ap [j] <= Ap [j+1] and Ap [j] <= Ap [n_col] must be true for all j in
	the range 0 to n_col-1.  Empty columns are OK (that is, Ap [j] may equal
	Ap [j+1] for any j in the range 0 to n_col-1).

    Int Tj [nz] ;	Output argument.

	Tj is an integer array of size nz on input, where nz = Ap [n_col].
	Suppose the column-form of the matrix is held in Ap, Ai, Ax, and Az
	(see umfpack_*_*symbolic for a description).  Then on output, the
	triplet form of the same matrix is held in Ai (row indices), Tj (column
	indices), and Ax (numerical values).  Note, however, that this routine
	does not require Ai and Ax (or Az for the complex version) in order to
	do the conversion.
*/

```

sparse-matrix/umfpack/umfpack_defaults.c:
```
/* ========================================================================== */
/* === UMFPACK_defaults ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Sets default control parameters.  See umfpack_defaults.h
    for details.
*/

#include "umf_internal.h"

GLOBAL void UMFPACK_defaults
(
    double Control [UMFPACK_CONTROL]
)
{
    Int i ;

    if (!Control)
    {
	/* silently return if no Control array */
	return ;
    }

    for (i = 0 ; i < UMFPACK_CONTROL ; i++)
    {
	Control [i] = 0 ;
    }

    /* ---------------------------------------------------------------------- */
    /* default control settings: can be modified at run-time */
    /* ---------------------------------------------------------------------- */

    /* used in UMFPACK_report_* routines: */
    Control [UMFPACK_PRL] = UMFPACK_DEFAULT_PRL ;

    /* used in UMFPACK_*symbolic: */
    Control [UMFPACK_DENSE_ROW] = UMFPACK_DEFAULT_DENSE_ROW ;
    Control [UMFPACK_DENSE_COL] = UMFPACK_DEFAULT_DENSE_COL ;

    /* used in UMFPACK_numeric: */
    Control [UMFPACK_PIVOT_TOLERANCE] = UMFPACK_DEFAULT_PIVOT_TOLERANCE ;
    Control [UMFPACK_BLOCK_SIZE] = UMFPACK_DEFAULT_BLOCK_SIZE ;
    Control [UMFPACK_RELAXED_AMALGAMATION] =
	UMFPACK_DEFAULT_RELAXED_AMALGAMATION ;
    Control [UMFPACK_RELAXED2_AMALGAMATION] =
	UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ;
    Control [UMFPACK_RELAXED3_AMALGAMATION] =
	UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ;
    Control [UMFPACK_ALLOC_INIT] = UMFPACK_DEFAULT_ALLOC_INIT ;

    /* used in UMFPACK_*solve: */
    Control [UMFPACK_IRSTEP] = UMFPACK_DEFAULT_IRSTEP ;

    /* ---------------------------------------------------------------------- */
    /* compile-time settings: cannot be modified at run-time */
    /* ---------------------------------------------------------------------- */

#ifdef USE_NO_BLAS
    /* use externally-provided BLAS (dgemm, dger, dgemv, zgemm, zgeru, zgemv) */
    Control [UMFPACK_COMPILED_WITH_BLAS] = 1 ;
#else
    /* do not use the BLAS - use in-line C code instead */
    Control [UMFPACK_COMPILED_WITH_BLAS] = 0 ;
#endif

#ifdef MATLAB_MEX_FILE
    /* use mxMalloc, mxFree, mxRealloc, and mexPrintf */
    /* use mxAssert if debugging is enabled */
    Control [UMFPACK_COMPILED_FOR_MATLAB] = 1 ;
#else
#ifdef MATHWORKS
    /* use internal utMalloc, utFree, utRealloc, and utPrintf routines. */
    /* use utDivideComplex and utFdlibm_hypot for complex version. */
    /* use utAssert if debugging is enabled. */
    Control [UMFPACK_COMPILED_FOR_MATLAB] = 2 ;
#else
    /* use ANSI C malloc, free, realloc, and print */
    /* use ANSI C assert if debugging is enabled */
    Control [UMFPACK_COMPILED_FOR_MATLAB] = 0 ;
#endif
#endif

#ifdef GETRUSAGE
    /* uses the non-standard getrusage to get CPU time (Solaris) */
    Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 1 ;
#else
    /* uses the ANSI standard clock routine to get CPU time */
    /* this may wrap around */
    Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 0 ;
#endif

#ifndef NDEBUG
    /* UMFPACK is compiled in debug mode. */
    /* This is exceedingly slow. */
    PRINTF (("UMFPACK is running in debug mode.  This is very slow!\n")) ;
    Control [UMFPACK_COMPILED_IN_DEBUG_MODE] = 1 ;
#else
    /* UMFPACK is compiled in normal (non-debug) mode */
    Control [UMFPACK_COMPILED_IN_DEBUG_MODE] = 0 ;
#endif

}


```

sparse-matrix/umfpack/umf_valid_numeric.c:
```
/* ========================================================================== */
/* === UMF_valid_numeric ==================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Returns TRUE if the Numeric object is valid, FALSE otherwise. */
/* Does not check everything.  UMFPACK_report_numeric checks more. */

#include "umf_internal.h"

GLOBAL Int UMF_valid_numeric
(
    NumericType *Numeric
)
{
    /* This routine does not check the contents of the individual arrays, so */
    /* it can miss some errors.  All it checks for is the presence of the */
    /* arrays, and the Numeric "valid" entry. */

    if (!Numeric)
    {
	return (FALSE) ;
    }

    if (Numeric->valid != NUMERIC_VALID)
    {
	/* Numeric does not point to a NumericType object */
	return (FALSE) ;
    }

    if (Numeric->n_row <= 0 || Numeric->n_col <= 0 || !Numeric->D ||
	!Numeric->Rperm || !Numeric->Cperm ||
	!Numeric->Lpos || !Numeric->Upos ||
	!Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip || !Numeric->Uip ||
	!Numeric->Memory || (Numeric->ulen > 0 && !Numeric->Upattern))
    {
	return (FALSE) ;
    }

    return (TRUE) ;
}


```

sparse-matrix/umfpack/umfpack_timer.c:
```
/* ========================================================================== */
/* === umfpack_timer ======================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Returns the time in seconds used by the process.  BE
    CAREFUL:  if you compare the run time of UMFPACK with other sparse matrix
    packages, be sure to use the same timer.  See umfpack_timer.h for details.
*/

#ifdef GETRUSAGE

/* -------------------------------------------------------------------------- */
/* use getrusage for accurate process times (and no overflow) */
/* -------------------------------------------------------------------------- */

/*
    This works under Solaris, SGI Irix, Linux, IBM RS 6000 (AIX), and Compaq
    Alpha.  It might work on other Unix systems, too.  Includes both the "user
    time" and the "system time".  The system time is the time spent by the
    operating system on behalf of the process, and thus should be charged to
    the process.
*/

#include <sys/time.h>
#include <sys/resource.h>

double umfpack_timer ( void )
{
    struct rusage ru ;
    double user_time, sys_time ;

    (void) getrusage (RUSAGE_SELF, &ru) ;

    user_time =
    ru.ru_utime.tv_sec			/* user time (seconds) */
    + 1e-6 * ru.ru_utime.tv_usec ;	/* user time (microseconds) */

    sys_time =
    ru.ru_stime.tv_sec			/* system time (seconds) */
    + 1e-6 * ru.ru_stime.tv_usec ;	/* system time (microseconds) */

    return (user_time + sys_time) ;
}

#else

/* -------------------------------------------------------------------------- */
/* Generic ANSI C: use the ANSI clock function */
/* -------------------------------------------------------------------------- */

/* This is portable, but may overflow.  On Sun Solaris, when compiling in */
/* 32-bit mode, the overflow occurs in only 2147 seconds (about 36 minutes). */

#include <time.h>

double umfpack_timer ( void )
{
    return (((double) (clock ( ))) / ((double) (CLOCKS_PER_SEC))) ;
}

#endif


```

sparse-matrix/umfpack/umf_realloc.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void *UMF_realloc
(
    void *p,
    Int n_objects,
    size_t size_of_object
) ;


```

sparse-matrix/umfpack/umf_report_perm.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_report_perm
(
    Int n,
    const Int P [ ],
    Int W [ ],
    Int prl,
    Int user
) ;


```

sparse-matrix/umfpack/License:
```
UMFPACK Version 4.0 (Apr 11, 2002).  Copyright (c) 2002 by Timothy A.
Davis.  All Rights Reserved.

UMFPACK License:

    Your use or distribution of UMFPACK or any modified version of
    UMFPACK implies that you agree to this License.

    THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
    EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.

    Permission is hereby granted to use or copy this program, 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 UMFPACK or any modified version of UMFPACK 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.  This
    software was developed with support from the National Science Foundation,
    and is provided to you free of charge.

Availability:

    http://www.cise.ufl.edu/research/sparse/umfpack


```

sparse-matrix/umfpack/umf_colamd.c:
```
/* ========================================================================== */
/* === UMF_colamd =========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
UMF_colamd:  an approximate minimum degree column ordering algorithm,
	used as a preordering for UMFPACK.

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.  By default, the \
    and / operators in MATLAB perform a column ordering (using colmmd
    or colamd) prior to LU factorization using sparse partial pivoting,
    in the built-in MATLAB lu(A) routine.

    This code is derived from Colamd Version 2.0.

Authors:

    The authors of the COLAMD code itself are Stefan I. Larimore and Timothy A.
    Davis, University of Florida.  The algorithm was developed in collaboration
    with John Gilbert, Xerox PARC, and Esmond Ng, Oak Ridge National Laboratory.
    The AMD metric on which this is based is by Patrick Amestoy, T. Davis,
    and Iain Duff.

Date:

    UMFPACK Version: see above.
    COLAMD Version 2.0 was released on January 31, 2000.

Acknowledgements:

    This work was supported by the National Science Foundation, under
    grants DMS-9504974 and DMS-9803599.

UMFPACK:  Copyright (c) 2002 by Timothy A. Davis.  All Rights Reserved.

See the UMFPACK README file for the License for your use of this code.

Availability:

    Both UMFPACK and the original unmodified colamd/symamd library are
    available at http://www.cise.ufl.edu/research/sparse.

Changes for inclusion in UMFPACK:

    * symamd, symamd_report, and colamd_report removed

    * additional terms added to RowInfo, ColInfo, and stats

    * Frontal matrix information computed for UMFPACK

    * routines renamed

    * column elimination tree post-ordering incorporated.  In the original
	version 2.0, this was performed in colamd.m.

For more information, see:

	Amestoy, P. R. and Davis, T. A. and Duff, I. S.,
        An approximate minimum degree ordering algorithm,
	SIAM J. Matrix Analysis and Applic, vol 17, no 4., pp 886-905, 1996.

	Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G.,
	A column approximate minimum degree ordering algorithm,
	Univ. of Florida, CISE Dept., TR-00-005, Gainesville, FL
	Oct. 2000.  Submitted to ACM Trans. Math. Softw.

*/

/* ========================================================================== */
/* === Description of user-callable routines ================================ */
/* ========================================================================== */

/*
    ----------------------------------------------------------------------------
    colamd_recommended: removed for UMFPACK
    ----------------------------------------------------------------------------

    ----------------------------------------------------------------------------
    colamd_set_defaults:
    ----------------------------------------------------------------------------

	C syntax:

	    #include "colamd.h"
	    colamd_set_defaults (Int knobs [COLAMD_KNOBS]) ;

	Purpose:

	    Sets the default parameters.  The use of this routine is optional.

	Arguments:

	    double knobs [COLAMD_KNOBS] ;	Output only.

		Let c = knobs [COLAMD_DENSE_COL], r = knobs [COLAMD_DENSE_ROW].
		Colamd: rows with more than max (16, r*16*sqrt(n_col))
		entries are removed prior to ordering.  Columns with more than
		max (16, c*16*sqrt(n_row)) entries are removed prior to
		ordering, and placed last in the output column ordering.

		Symamd: removed for UMFPACK.

		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 0.5.  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.

    ----------------------------------------------------------------------------
    colamd:
    ----------------------------------------------------------------------------

	C syntax:

	    #include "colamd.h"
	    Int UMF_colamd (Int n_row, Int n_col, Int Alen, Int *A, Int *p,
		double knobs [COLAMD_KNOBS], Int 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 + 8*(n_col+1) + 6*(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 >= UMF_COLAMD_RECOMMENDED (nnz, n_row, n_col)

		will be sufficient.

	    Int A [Alen] ;	Input and output argument.

		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
		UMF_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.

		A holds the inverse permutation 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.
		The behavior is undefined if knobs contains NaN's.
		(UMFPACK does not call umf_colamd with NaN-valued knobs).

	    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.

			-11	Columns of input matrix jumbled
				(unsorted columns or duplicate entries).

					stats [4]: the bad column index
					stats [5]: the bad row index

			-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 zero or negative number of
				entries (changed for UMFPACK)

					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

			-999	(unused; see symamd.c)

		Future versions may return more statistics in the stats array.

	Example:

	    See http://www.cise.ufl.edu/~davis/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 UMF_COLAMD_RECOMMENDED (11, 5, 4)
		Int A [ALEN] = {1, 2, 5, 3, 5, 1, 2, 3, 4, 2, 4} ;
		Int p [ ] = {0, 3, 5, 9, 11} ;
		Int stats [COLAMD_STATS] ;
		UMF_colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ;

	    The permutation is returned in the array p, and A is destroyed.


    ----------------------------------------------------------------------------
    symamd:  does not appear in this version for UMFPACK
    ----------------------------------------------------------------------------

    ----------------------------------------------------------------------------
    colamd_report: does not appear in this version for UMFPACK
    ----------------------------------------------------------------------------

    ----------------------------------------------------------------------------
    symamd_report: does not appear in this version for UMFPACK
    ----------------------------------------------------------------------------

*/

/* ========================================================================== */
/* === Scaffolding code definitions  ======================================== */
/* ========================================================================== */

/* ------------------ */
/* UMFPACK debugging control is in umf_config.h */
/* ------------------ */

/*
   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...

    To enable debugging, comment out the "#define NDEBUG" above.  For a MATLAB
    mexFunction, you will also need to modify mexopts.sh to remove the -DNDEBUG
    definition.  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 ======================================================== */
/* ========================================================================== */

/* ------------------ */
/* modified for UMFPACK: */
#include "umf_internal.h"
#include "umf_colamd.h"
#include "umf_order_front_tree.h"
#include "umf_apply_order.h"
/* ------------------ */

/* ========================================================================== */
/* === Definitions ========================================================== */
/* ========================================================================== */

/* ------------------ */
/* UMFPACK: duplicate definitions moved to umf_internal.h */
/* ------------------ */

/* 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 ; }

/* ------------------ */
/* UMFPACK: Colamd reporting mechanism moved to umf_internal.h */
/* ------------------ */

/* ========================================================================== */
/* === 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
    /* ------------------ */
    /* added for UMFPACK */
    , Int *p_ndense_row		/* number of dense rows */
    , Int *p_nempty_row		/* number of original empty rows */
    , Int *p_nnewlyempty_row	/* number of newly empty rows */
    , Int *p_ndense_col		/* number of dense cols (excl "empty" cols) */
    , Int *p_nempty_col		/* number of original empty cols */
    , Int *p_nnewlyempty_col	/* number of newly empty cols */
) ;

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
    /* ------------------ */
    /* added for UMFPACK: */
    , Int Front_npivcol [ ]
    , Int Front_nrows [ ]
    , Int Front_ncols [ ]
    , Int Front_parent [ ]
    , Int Front_cols [ ]
    , Int *p_nfr
    /* ------------------ */
) ;

/* ------------------ */
/* order_children deleted for UMFPACK: */
/* ------------------ */

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 n_row,
    Colamd_Row Row []
) ;

/* ------------------ */
/* print_report deleted for UMFPACK */
/* ------------------ */

/* ========================================================================== */
/* === Debugging prototypes and definitions ================================= */
/* ========================================================================== */

#ifndef NDEBUG

/* ------------------ */
/* debugging macros moved for UMFPACK */
/* ------------------ */

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
) ;

/* ------------------ */
/* dump_super added for UMFPACK: */
PRIVATE void dump_super
(
    Int super_c,
    Colamd_Col Col [],
    Int n_col
) ;
/* ------------------ */

#endif /* NDEBUG */

/* ========================================================================== */



/* ========================================================================== */
/* === USER-CALLABLE ROUTINES: ============================================== */
/* ========================================================================== */


/* ========================================================================== */
/* === colamd_set_defaults ================================================== */
/* ========================================================================== */

/*
    The colamd_set_defaults routine sets the default values of the user-
    controllable parameters for colamd:

	knobs [0]	rows with knobs[0]*n_col entries or more are removed
			prior to ordering in colamd.  Rows and columns with
			knobs[0]*n_col entries or more are removed prior to
			ordering in symamd and placed last in the output
			ordering.

	knobs [1]	columns with knobs[1]*n_row entries or more are removed
			prior to ordering in colamd, and placed last in the
			column permutation.  Symamd ignores this knob.

	knobs [2..19]	unused, but future versions might use this
*/

GLOBAL void UMF_colamd_set_defaults
(
    /* === Parameters ======================================================= */

    double knobs [COLAMD_KNOBS]		/* knob array */
)
{
    /* === Local variables ================================================== */

    Int i ;

    if (!knobs)
    {
	return ;			/* UMFPACK always passes knobs array */
    }
    for (i = 0 ; i < COLAMD_KNOBS ; i++)
    {
	knobs [i] = 0 ;
    }
    knobs [COLAMD_DENSE_ROW] = 0.2 ;	/* default changed for UMFPACK */
    knobs [COLAMD_DENSE_COL] = 0.2 ;	/* default changed for UMFPACK */
}


/* ========================================================================== */
/* === symamd removed for UMFPACK =========================================== */
/* ========================================================================== */



/* ========================================================================== */
/* === 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.
*/

GLOBAL Int UMF_colamd		/* 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 */

    /* ------------------ */
    /* added for UMFPACK: each Front_ array is of size n_col */
    , Int Front_npivcol [ ]	/* # pivot cols in each front */
    , Int Front_nrows [ ]	/* # of rows in each front (incl. pivot rows) */
    , Int Front_ncols [ ]	/* # of cols in each front (incl. pivot cols) */
    , Int Front_parent [ ]	/* parent of each front */
    , Int Front_cols [ ]	/* link list of pivot columns for each front */
    , Int *p_nfr		/* total number of frontal matrices */
    /* ------------------ */
)
{
    /* === Local variables ================================================== */

    Int i ;			/* loop index */
    Int nnz ;			/* nonzeros in A */
    Int Row_size ;		/* size of Row [], in integers */
    Int Col_size ;		/* size of Col [], in integers */
    Int 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 */

    /* ------------------ */
    /* debugging initializations moved for UMFPACK */
    /* ------------------ */

    /* ------------------ */
    /* added for UMFPACK: */
    Int f, j, nchild, ndense_row, nempty_row, parent,
	nrows, ncols, frsize, ndense_col, nempty_col, k, col, nfr,
	*Front_child, *Front_sibling, *Front_maxfr, *Front_order,
	fprev, maxfrsize, bigf, fnext, bigfprev, *Stack ;
    Int nnewlyempty_col, nnewlyempty_row ;
    /* ------------------ */

    /* === Check the input arguments ======================================== */

    if (!stats)
    {
	DEBUG0 (("colamd: stats not present\n")) ;
	return (FALSE) ;	/* UMFPACK:  always passes stats [ ] */
    }
    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 */
    {
	/* UMFPACK:  always passes A [ ] */
	DEBUG0 (("colamd: A not present\n")) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ;
	return (FALSE) ;
    }

    if (!p)		/* p is not present */
    {
	/* UMFPACK:  always passes p [ ] */
	DEBUG0 (("colamd: p not present\n")) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ;
	return (FALSE) ;
    }

    if (n_row < 0)	/* n_row must be >= 0 */
    {
	/* UMFPACK:  does not call UMF_colamd if n <= 0 */
	DEBUG0 (("colamd: nrow negative "ID"\n", n_row)) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ;
	stats [COLAMD_INFO1] = n_row ;
	return (FALSE) ;
    }

    if (n_col < 0)	/* n_col must be >= 0 */
    {
	/* UMFPACK:  does not call UMF_colamd if n <= 0 */
	DEBUG0 (("colamd: ncol negative "ID"\n", n_col)) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ;
	stats [COLAMD_INFO1] = n_col ;
	return (FALSE) ;
    }

    nnz = p [n_col] ;
    if (nnz < 0)	/* nnz must be >= 0 */
    {
	/* UMFPACK:  does not call UMF_colamd if nnz < 0 */
	DEBUG0 (("colamd: number of entries negative "ID"\n", nnz)) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ;
	stats [COLAMD_INFO1] = nnz ;
	return (FALSE) ;
    }

    if (p [0] != 0)	/* p [0] must be exactly zero */
    {
	DEBUG0 (("colamd: p[0] not zero "ID"\n", p [0])) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero	;
	stats [COLAMD_INFO1] = p [0] ;
	return (FALSE) ;
    }

    /* === If no knobs, set default knobs =================================== */

    if (!knobs)
    {
	/* UMFPACK:  always passes the knobs */
	UMF_colamd_set_defaults (default_knobs) ;
	knobs = default_knobs ;
    }

    /* === Allocate the Row and Col arrays from array A ===================== */

    Col_size = UMF_COLAMD_C (n_col) ;
    Row_size = UMF_COLAMD_R (n_row) ;
    need = 2*nnz + n_col + Col_size + Row_size ;

    if (need > Alen)
    {
	/* UMFPACK: always passes enough space */
	/* not enough space in array A to perform the ordering */
	DEBUG0 (("colamd: Need Alen >= "ID", given only Alen = "ID"\n",
	    need, Alen)) ;
	stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ;
	stats [COLAMD_INFO1] = need ;
	stats [COLAMD_INFO2] = 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) ;
    }

    /* === UMFPACK: Initialize front info =================================== */

    for (col = 0 ; col < n_col ; col++)
    {
	Front_npivcol [col] = 0 ;
	Front_nrows [col] = 0 ;
	Front_ncols [col] = 0 ;
	Front_parent [col] = EMPTY ;
	Front_cols [col] = EMPTY ;
    }

    /* === Initialize scores, kill dense rows/columns ======================= */

    init_scoring (n_row, n_col, Row, Col, A, p, knobs,
	&n_row2, &n_col2, &max_deg
	/* ------------------ */
	/* added for UMFPACK: */
	, &ndense_row, &nempty_row, &nnewlyempty_row
	, &ndense_col, &nempty_col, &nnewlyempty_col
	/* ------------------ */
	) ;
    ASSERT (n_row2 == n_row - nempty_row - nnewlyempty_row - ndense_row) ;
    ASSERT (n_col2 == n_col - nempty_col - nnewlyempty_col - ndense_col) ;

    /* === Order the supercolumns =========================================== */

    ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p,
	n_col2, max_deg, 2*nnz
	/* ------------------ */
	/* added for UMFPACK: */
	, Front_npivcol, Front_nrows, Front_ncols, Front_parent, Front_cols
	, &nfr
	/* ------------------ */
	) ;

    /* ------------------ */
    /* changed for UMFPACK: */

    /* use A [0..4*nfr-1] as workspace [ [ [ */
    Front_child   = A ;
    Front_sibling = Front_child + nfr ;
    Front_maxfr   = Front_sibling + nfr ;
    Front_order   = Front_maxfr + nfr ;

    for (j = 0 ; j < nfr ; j++)
    {
	Front_child [j] = EMPTY ;
	Front_sibling [j] = EMPTY ;
	Front_maxfr [j] = EMPTY ;
    }

    /* === find maxfr for each front ======================================== */

    DEBUG1 (("\n\n========================================FRONTS:\n")) ;
    for (j = 0 ; j < nfr ; j++)
    {
	parent = Front_parent [j] ;
	nrows = Front_nrows [j] ;
	ncols = Front_ncols [j] ;
	frsize = nrows * ncols ;
	DEBUG1 ((""ID" :  npiv "ID" nrows "ID" ncols "ID" parent "ID" ",
		j, Front_npivcol [j], nrows, ncols, parent)) ;
	Front_maxfr [j] = MAX (Front_maxfr [j], frsize) ;
	DEBUG1 (("Front_maxfr [j = "ID"] = "ID"\n", j, Front_maxfr[j]));
	if (parent != EMPTY)
	{
	    /* find the maximum frontsize of self and children */
	    Front_maxfr [parent] = MAX (Front_maxfr [parent], Front_maxfr [j]) ;
	    DEBUG1 (("Front_maxfr [parent = "ID"] = "ID"\n",
		parent, Front_maxfr [parent])) ;
	}
    }

    /* === put the fronts in the child lists ================================ */

    /* try to put them in order of smaller to larger */
    for (j = nfr-1 ; j >= 0 ; j--)
    {
	parent = Front_parent [j] ;
	if (parent != EMPTY)
	{
	    /* place the front in the link list of the children its parent */
	    /* bigger fronts will tend to be at the end of the list */
	    Front_sibling [j] = Front_child [parent] ;
	    Front_child [parent] = j ;
	}
    }

    /* === Order the front tree via depth-first-search ====================== */

    for (i = 0 ; i < nfr ; i++)
    {
	if (Front_child [i] != EMPTY)
	{
	    DEBUG1 (("Before partial sort, front "ID"\n", i)) ;
	    nchild = 0 ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		DEBUG1 (("        "ID"  "ID"\n", f, Front_maxfr [f])) ;
		nchild++ ;
	    }

	    /* find the biggest front in the child list */
	    fprev = EMPTY ;
	    maxfrsize = EMPTY ;
	    bigfprev = EMPTY ;
	    bigf = EMPTY ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		frsize = Front_maxfr [f] ;
		if (frsize >= maxfrsize)
		{
		    /* this is the biggest seen so far */
		    maxfrsize = frsize ;
		    bigfprev = fprev ;
		    bigf = f ;
		}
		fprev = f ;
	    }
	    ASSERT (bigf != EMPTY) ;

	    fnext = Front_sibling [bigf] ;

	    DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID" fprev"
		ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ;

	    if (fnext != EMPTY)
	    {
		/* if fnext is EMPTY, then bigf is already at the end of list */

		if (bigfprev == EMPTY)
		{
		    /* delete bigf from the front of the list */
		    Front_child [i] = fnext ;
		}
		else
		{
		    /* delete bigf from the middle of the list */
		    Front_sibling [bigfprev] = fnext ;
		}

		/* put bigf at the end of the list */
		Front_sibling [bigf] = EMPTY ;
		ASSERT (Front_child [i] != EMPTY) ;
		ASSERT (fprev != bigf) ;
		ASSERT (fprev != EMPTY) ;
		Front_sibling [fprev] = bigf ;
	    }

	    DEBUG1 (("After partial sort, front "ID"\n", i)) ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		DEBUG1 (("        "ID"  "ID"\n", f, Front_maxfr [f])) ;
		ASSERT (Front_npivcol [f] > 0) ;
		nchild-- ;
	    }
	    ASSERT (nchild == 0) ;
	}
    }

    /* Front_maxfr no longer needed ] */

#ifndef NDEBUG
    UMF_nbug = nfr ;	/* frontal id's are in the range 0..nfr-1 */
    UMF_fbug = nfr ;	/* total number of frontal matrices */
#endif

    /* use Front_maxfr as workspace for stack */
    Stack = Front_maxfr ;
    k = 0 ;
    for (i = 0 ; i < nfr ; i++)
    {
	if (Front_parent [i] == EMPTY)
	{
	    DEBUG1 (("Root of front tree "ID"\n", i)) ;
	    k = UMF_order_front_tree (i, k, Front_child, Front_sibling,
		Front_order, Stack) ;
	}
    }

    /* Front_child, Front_sibling no longer needed ] */

    /* use A [0..nfr-1] as workspace */
    UMF_apply_order (Front_npivcol, Front_order, A, nfr, nfr) ;
    UMF_apply_order (Front_nrows,   Front_order, A, nfr, nfr) ;
    UMF_apply_order (Front_ncols,   Front_order, A, nfr, nfr) ;
    UMF_apply_order (Front_parent,  Front_order, A, nfr, nfr) ;
    UMF_apply_order (Front_cols,    Front_order, A, nfr, nfr) ;

    /* fix the parent to refer to the new numbering */
    for (i = 0 ; i < nfr ; i++)
    {
	parent = Front_parent [i] ;
	if (parent != EMPTY)
	{
	    Front_parent [i] = Front_order [parent] ;
	}
    }

    /* Front_order longer needed ] */

    /* === Order the columns in the fronts ================================== */

    /* use A [0..n_col-1] as inverse permutation */
    for (i = 0 ; i < n_col ; i++)
    {
	A [i] = -1 ;
    }
    k = 0 ;
    for (i = 0 ; i < nfr ; i++)
    {
	ASSERT (Front_npivcol [i] > 0) ;
	for (col = Front_cols [i] ; col != EMPTY ; col = Col [col].nextcol)
	{
	    ASSERT (col >= 0 && col < n_col) ;
	    p [k++] = col ;
	    ASSERT (A [col] == -1) ;
	    A [col] = k ;
	}
    }

    /* === Order the "dense" and null columns =============================== */

    ASSERT (k == n_col2) ;
    if (n_col2 < n_col)
    {
	for (col = 0 ; col < n_col ; col++)
	{
	    if (A [col] == -1)
	    {
		DEBUG1 (("colamd dense or null col "ID" k: "ID"\n", col, k)) ;
		k = Col [col].shared2.order ;
		ASSERT (k >= n_col2 && k < n_col) ;
		p [k] = col ;
		A [col] = k ;
	    }
	}
    }

    /* ------------------ */

    /* === Return statistics in stats ======================================= */

    /* ------------------ */
    /* modified for UMFPACK */
    stats [COLAMD_DENSE_ROW] = ndense_row ;
    stats [COLAMD_EMPTY_ROW] = nempty_row ;
    stats [COLAMD_NEWLY_EMPTY_ROW] = nnewlyempty_row ;
    stats [COLAMD_DENSE_COL] = ndense_col ;
    stats [COLAMD_EMPTY_COL] = nempty_col ;
    stats [COLAMD_NEWLY_EMPTY_COL] = nnewlyempty_col ;
    ASSERT (ndense_col + nempty_col + nnewlyempty_col == n_col - n_col2) ;
    /* ------------------ */
    stats [COLAMD_DEFRAG_COUNT] = ngarbage ;
    *p_nfr = nfr ;
    DEBUG1 (("colamd: done.\n")) ;
    return (TRUE) ;
}




/* ========================================================================== */
/* === colamd_report removed for UMFPACK ==================================== */
/* ========================================================================== */

/* ========================================================================== */
/* === symamd_report removed for UMFPACK ==================================== */
/* ========================================================================== */



/* ========================================================================== */
/* === 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 */
    /* rp and rp_end not used for UMFPACK */
    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 "ID" length "ID" <= 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 ;

	/* ------------------ */
	/* added for UMFPACK: */
	Col [col].nextcol = EMPTY ;
	Col [col].lastcol = col ;
	/* ------------------ */
    }

    /* 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 - not computed in UMFPACK */
    /* ------------------ */

    for (row = 0 ; row < n_row ; row++)
    {
	Row [row].length = 0 ;
	/* ------------------ */
	/* removed for UMFPACK */
	/* Row [row].shared2.mark = -1 ; */
	/* ------------------ */
	/* ------------------ */
	/* added for UMFPACK: */
	Row [row].thickness = 1 ;
	Row [row].front = EMPTY ;
	/* ------------------ */
    }

    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 ;
		/* ------------------ */
		/* not needed in UMFPACK: */
		/* stats [COLAMD_INFO3] = n_row ; */
		/* ------------------ */
		DEBUG0 (("colamd: row "ID" col "ID" out of bounds\n", row,col));
		return (FALSE) ;
	    }

	    /* ------------------ */
	    /* changed for UMFPACK */
	    if (row <= last_row)
	    {
		/* row index are unsorted or repeated (or both), thus col */
		/* is jumbled.  This is an error condition for UMFPACK */
		stats [COLAMD_STATUS] = COLAMD_ERROR_jumbled_matrix ;
		stats [COLAMD_INFO1] = col ;
		stats [COLAMD_INFO2] = row ;
		DEBUG1 (("colamd: row "ID" col "ID" unsorted/duplicate\n",
		    row, col)) ;
		return (FALSE) ;
	    }
	    /* ------------------ */

	    /* ------------------ */
	    /* changed for UMFPACK - jumbled columns not tolerated */
	    Row [row].length++ ;
	    /* ------------------ */

	    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 ;
    /* ------------------ */
    /* removed for UMFPACK */
    /* 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 ;
	/* ------------------ */
	/* removed for UMFPACK */
	/* Row [row].shared2.mark = -1 ; */
	/* ------------------ */
    }

    /* === Create row form ================================================== */

    /* ------------------ */
    /* jumbled matrix case removed for UMFPACK */
    /* ------------------ */

    ASSERT (stats [COLAMD_STATUS] == COLAMD_OK) ;

	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 ;
    }

    /* ------------------ */
    /* recreate columns for jumbled matrix case removed for UMFPACK */
    /* ------------------ */

    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 */
    /* ------------------ */
    /* added for UMFPACK */
    , Int *p_ndense_row		/* number of dense rows */
    , Int *p_nempty_row		/* number of original empty rows */
    , Int *p_nnewlyempty_row	/* number of newly empty rows */
    , Int *p_ndense_col		/* number of dense cols (excl "empty" cols) */
    , Int *p_nempty_col		/* number of original empty cols */
    , Int *p_nnewlyempty_col	/* number of newly empty cols */
    /* ------------------ */
)
{
    /* === 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.*/

    /* ------------------ */
    /* added for UMFPACK */
    Int ndense_row ;		/* number of dense rows */
    Int nempty_row ;		/* number of empty rows */
    Int nnewlyempty_row ;	/* number of newly empty rows */
    Int ndense_col ;		/* number of dense cols (excl "empty" cols) */
    Int nempty_col ;		/* number of original empty cols */
    Int nnewlyempty_col ;	/* number of newly empty cols */
    Int ne ;
    /* ------------------ */

#ifndef NDEBUG
    Int debug_count ;		/* debug only. */
#endif /* NDEBUG */

    /* === Extract knobs ==================================================== */

    /* --------------------- */
    /* old dense row/column knobs:
    dense_row_count = MAX (0, MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ;
    dense_col_count = MAX (0, MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ;
    */
    /* new, for UMFPACK: */
    /* Note: if knobs contains a NaN, this is undefined: */
    dense_row_count =
	UMFPACK_DENSE_DEGREE_THRESHOLD (knobs [COLAMD_DENSE_ROW], n_col) ;
    dense_col_count =
	UMFPACK_DENSE_DEGREE_THRESHOLD (knobs [COLAMD_DENSE_COL], n_row) ;
    /* Make sure dense_*_count is between 0 and n: */
    dense_row_count = MAX (0, MIN (dense_row_count, n_col)) ;
    dense_col_count = MAX (0, MIN (dense_col_count, n_row)) ;
    /* --------------------- */

    DEBUG1 (("colamd: densecount: "ID" "ID"\n",
	dense_row_count, dense_col_count)) ;
    max_deg = 0 ;
    n_col2 = n_col ;
    n_row2 = n_row ;

    /* --------------------- */
    /* added for UMFPACK */
    ndense_col = 0 ;
    nempty_col = 0 ;
    nnewlyempty_col = 0 ;
    ndense_row = 0 ;
    nempty_row = 0 ;
    nnewlyempty_row = 0 ;
    /* --------------------- */

    /* === 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) ;
	    /* --------------------- */
	    /* added for UMFPACK */
	    nempty_col++ ;
	    /* --------------------- */
	}
    }
    DEBUG1 (("colamd: null columns killed: "ID"\n", n_col - n_col2)) ;

    /* === Count null rows ================================================== */

    for (r = 0 ; r < n_row ; r++)
    {
	deg = Row [r].shared1.degree ;
	if (deg == 0)
	{
	    /* this is an original empty row */
	    nempty_row++ ;
	}
    }

    /* === 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 ;
	    /* --------------------- */
	    /* added for UMFPACK */
	    ndense_col++ ;
	    /* --------------------- */
	    /* 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: "ID"\n", n_col - n_col2)) ;

    /* === Kill dense and empty rows ======================================== */

    ne = 0 ;
    for (r = 0 ; r < n_row ; r++)
    {
	deg = Row [r].shared1.degree ;
	ASSERT (deg >= 0 && deg <= n_col) ;
	/* --------------------- */
	/* added for UMFPACK */
	if (deg > dense_row_count)
	{
	    /* There is at least one dense row.  Continue ordering, but */
	    /* symbolic factorization will be redone after UMF_colamd is done.*/
	    ndense_row++ ;
	}
	if (deg == 0)
	{
	    /* this is a newly empty row, or original empty row */
	    ne++ ;
	}
	/* --------------------- */
	if (deg > dense_row_count || deg == 0)
	{
	    /* kill a dense or empty row */
	    KILL_ROW (r) ;
	    /* --------------------- */
	    /* added for UMFPACK */
	    Row [r].thickness = 0 ;
	    /* --------------------- */
	    --n_row2 ;
	}
	else
	{
	    /* keep track of max degree of remaining rows */
	    max_deg = MAX (max_deg, deg) ;
	}
    }
    nnewlyempty_row = ne - nempty_row ;
    DEBUG1 (("colamd: Dense rows killed: "ID"\n", ndense_row)) ;
    DEBUG1 (("colamd: Dense and null rows killed: "ID"\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: "ID"\n", c)) ;
	    Col [c].shared2.order = --n_col2 ;
	    KILL_PRINCIPAL_COL (c) ;
	    /* --------------------- */
	    /* added for UMFPACK */
	    nnewlyempty_col++ ;
	    /* --------------------- */
	}
	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: "ID"\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 "ID" score "ID" minscore "ID" ncol "ID"\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 "ID" out of "ID", non-princ: "ID"\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 ;

    /* --------------------- */
    /* added for UMFPACK */
    *p_ndense_row = ndense_row ;
    *p_nempty_row = nempty_row ;	/* original empty rows */
    *p_nnewlyempty_row = nnewlyempty_row ;
    *p_ndense_col = ndense_col ;
    *p_nempty_col = nempty_col ;	/* original empty cols */
    *p_nnewlyempty_col = nnewlyempty_col ;
    /* --------------------- */
}


/* ========================================================================== */
/* === 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) */
    /* ------------------ */
    /* added for UMFPACK: */
    , Int Front_npivcol [ ]
    , Int Front_nrows [ ]
    , Int Front_ncols [ ]
    , Int Front_parent [ ]
    , Int Front_cols [ ]
    , Int *p_nfr		/* number of fronts */
    /* ------------------ */
)
{
    /* === 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 */

    /* ------------------ */
    /* added for UMFPACK: */
    Int pivot_row_thickness ;	/* number of rows represented by pivot row */
    Int nfr = 0 ;		/* number of fronts */
    Int child ;
    /* ------------------ */

    /* === Initialization and clear mark ==================================== */

    max_mark = MAX_MARK (n_col) ;	/* defined in umfpack.h */
    tag_mark = clear_mark (n_row, Row) ;
    min_score = 0 ;
    ngarbage = 0 ;
    DEBUG1 (("colamd: Ordering, n_col2="ID"\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: "ID" out of n_col2: "ID"\n", k, n_col2)) ;
	}
	else
	{
	    DEBUG3 (("\n-----Step k: "ID" out of n_col2: "ID"\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)) ;
	DEBUG3 (("Pivot col: "ID"\n", 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 ;
	/* ------------------ */
	/* changed for UMFPACK: */
	k += pivot_col_thickness ;
	/* ------------------ */
	ASSERT (pivot_col_thickness > 0) ;

	/* === 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 (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 ;

	/* ------------------ */
	/* added for UMFPACK: */
	pivot_row_thickness = 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 "ID" "ID"\n", ROW_IS_ALIVE(row), row)) ;
	    /* skip if row is dead */
	    if (ROW_IS_DEAD (row))
	    {
		continue ;
	    }

	    /* ------------------ */
	    /* added for UMFPACK: */
	    /* sum the thicknesses of all the rows */
	    ASSERT (Row [row].thickness > 0) ;
	    pivot_row_thickness += Row [row].thickness ;
	    /* ------------------ */

	    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 ;
		    /* ------------------ */
		    /* added for UMFPACK: */
		    DEBUG4 (("\t\t\tNew live column in pivot row: "ID"\n",col));
		    /* ------------------ */
		}
/* ------------------ */
/* added for UMFPACK */
#ifndef NDEBUG
		if (col_thickness < 0 && COL_IS_ALIVE (col))
		{
		    DEBUG4 (("\t\t\tOld live column in pivot row: "ID"\n",col));
		}
#endif
/* ------------------ */
	    }
	}

	/* ------------------ */
	/* added for UMFPACK: */
	/* pivot_row_thickness is the number of rows in frontal matrix */
	/* both pivotal rows and nonpivotal rows */
	/* ------------------ */

	/* 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++ ;

	    /* ------------------ */
	    /* added for UMFPACK: */
	    DEBUG2 (("Kill row in pivot col: "ID" alive? "ID", front "ID"\n",
		row, ROW_IS_ALIVE (row), Row [row].front)) ;

	    if (ROW_IS_ALIVE (row) && Row [row].front != EMPTY)
	    {
		/* Row [row].front is a child of current front */
		child = Row [row].front ;
		Front_parent [child] = nfr ;
	    }
	    /* ------------------ */

	    KILL_ROW (row) ;

	    /* ------------------ */
	    /* added for UMFPACK: */
	    Row [row].thickness = 0 ;
	    /* ------------------ */
	}

	/* === 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 "ID"\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: \n")) ;
	/* 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: "ID"\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) ;

		/* ------------------ */
		/* aggressive row absorption removed for UMFPACK */
		/* ------------------ */

		    /* 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: "ID".\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))
		{
		    /* ------------------ */
		    /* changed for UMFPACK: */
		    DEBUG4 ((" Row "ID", dead\n", row)) ;
		    /* ------------------ */
		    continue ;
		}
		/* ------------------ */
		/* changed for UMFPACK: */
		/* ASSERT (row_mark > tag_mark) ; */
		DEBUG4 ((" Row "ID", set diff "ID"\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: "ID"\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 ;

		/* ------------------ */
		/* added for UMFPACK: */
		pivot_col_thickness += Col [col].shared1.thickness ;

		/* add to column list of front ... */
#ifndef NDEBUG
		DEBUG1 (("Mass")) ;
		dump_super (col, Col, n_col) ;
#endif
		Col [Col [col].lastcol].nextcol = Front_cols [nfr] ;
		Front_cols [nfr] = col ;
		/* ------------------ */

	    }
	    else
	    {
		/* === Prepare for supercolumn detection ==================== */

		DEBUG4 (("Preparing supercol detection for Col: "ID".\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 = "ID", n_col = "ID".\n", hash, n_col)) ;
		ASSERT (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) ;

	/* ------------------ */
	/* added for UMFPACK: */
	/* add columns to column list of front */
#ifndef NDEBUG
	DEBUG1 (("Pivot")) ;
	dump_super (pivot_col, Col, n_col) ;
#endif
	Col [Col [pivot_col].lastcol].nextcol = Front_cols [nfr] ;
	Front_cols [nfr] = pivot_col ;
	/* ------------------ */

	/* === Clear mark =================================================== */

	tag_mark += (max_deg + 1) ;
	if (tag_mark >= max_mark)
	{
	    DEBUG2 (("clearing tag_mark\n")) ;
	    tag_mark = clear_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")) ;
	DEBUG3 (("pivot_row_degree "ID"\n", pivot_row_degree)) ;

	/* 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++ ;
	    DEBUG3 (("Col "ID" \n", col)) ;
	    /* skip dead columns */
	    if (COL_IS_DEAD (col))
	    {
		DEBUG3 (("dead\n")) ;
		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). */
	    DEBUG3 ((" cur_score "ID" ", cur_score)) ;
	    cur_score = Col [col].shared2.score + pivot_row_degree ;
	    DEBUG3 (("ID", cur_score)) ;

	    /* 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 ;
	    DEBUG3 ((" max_score "ID" ", max_score)) ;

	    /* make the score the external degree of the union-of-rows */
	    cur_score -= Col [col].shared1.thickness ;
	    DEBUG3 ((" cur_score "ID" ", cur_score)) ;

	    /* 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 ;
	    DEBUG3 ((" "ID"\n", 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 */

	/* ------------------ */
	/* added for UMFPACK: */
	/* frontal matrix can have more pivot cols than pivot rows for */
	/* singular matrices. */

	/* number of candidate pivot columns */
	Front_npivcol [nfr] = pivot_col_thickness ;

	/* all rows (not just size of contrib. block) */
	Front_nrows [nfr] = pivot_row_thickness ;

	/* all cols */
	Front_ncols [nfr] = pivot_col_thickness + pivot_row_degree ;

	Front_parent [nfr] = EMPTY ;

	pivot_row_thickness -= pivot_col_thickness ;
	DEBUG1 (("Front "ID" Pivot_row_thickness after pivot cols elim: "ID"\n",
	    nfr, pivot_row_thickness)) ;
	pivot_row_thickness = MAX (0, pivot_row_thickness) ;
	/* ------------------ */

	/* === Resurrect the new pivot row ================================== */

	if (pivot_row_degree > 0
	/* ------------------ */
	/* added for UMFPACK: */
	&& pivot_row_thickness > 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]) ;
	    Row [pivot_row].shared1.degree = pivot_row_degree ;
	    Row [pivot_row].shared2.mark = 0 ;
	    /* ------------------ */
	    /* added for UMFPACK: */
	    Row [pivot_row].thickness = pivot_row_thickness ;
	    Row [pivot_row].front = nfr ;
	    /* ------------------ */
	    /* pivot row is no longer dead */
	}

	/* ------------------ */
	/* added for UMFPACK: */

#ifndef NDEBUG
	DEBUG1 (("Front "ID" : "ID" "ID" "ID" ", nfr,
		Front_npivcol [nfr], Front_nrows [nfr], Front_ncols [nfr])) ;
	DEBUG1 ((" cols:[ ")) ;
	debug_d = 0 ;
	for (col = Front_cols [nfr] ; col != EMPTY ; col = Col [col].nextcol)
	{
	    DEBUG1 ((" "ID, col)) ;
	    ASSERT (col >= 0 && col < n_col) ;
	    ASSERT (COL_IS_DEAD (col)) ;
	    debug_d++ ;
	    ASSERT (debug_d <= pivot_col_thickness) ;
	}
	ASSERT (debug_d == pivot_col_thickness) ;
	DEBUG1 ((" ]\n ")) ;
#endif
	nfr++ ; /* one more front */
	/* ------------------ */

    }

    /* === All principal columns have now been ordered ====================== */

    /* ------------------ */
    /* added for UMFPACK: */
    *p_nfr = nfr ;
    /* ------------------ */

    return (ngarbage) ;
}


/* ========================================================================== */
/* === order_children deleted for UMFPACK =================================== */
/* ========================================================================== */

/* ========================================================================== */
/* === 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) ;

		Col [c].shared2.order = EMPTY ;
		/* remove c from hash bucket */
		Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ;

		/* ------------------ */
		/* added for UMFPACK: */
		/* add c to end of list of super_c */
		ASSERT (Col [super_c].lastcol >= 0) ;
		ASSERT (Col [super_c].lastcol < n_col) ;
		Col [Col [super_c].lastcol].nextcol = c ;
		Col [super_c].lastcol = Col [c].lastcol ;
#ifndef NDEBUG
		/* dump the supercolumn */
		DEBUG1 (("Super")) ;
		dump_super (super_c, Col, n_col) ;
#endif
		/* ------------------ */

	    }
	}

	/* === 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_ALIVE (r))
	{
	    if (Row [r].length == 0)
	    {
		/* this row is of zero length.  cannot compact it, so kill it */
		DEBUG3 (("Defrag row kill\n")) ;
		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)) ;

	    /* 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]) ;

#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 n_row,		/* number of rows in A */
    Colamd_Row Row []	/* Row [0 ... n-1].shared2.mark is set to zero */
)
{
    /* === Local variables ================================================== */

    Int r ;

    for (r = 0 ; r < n_row ; r++)
    {
	if (ROW_IS_ALIVE (r))
	{
	    Row [r].shared2.mark = 0 ;
	}
    }

    /* ------------------ */
    return (1) ;
    /* ------------------ */

}


/* ========================================================================== */
/* === print_report removed for UMFPACK ===================================== */
/* ========================================================================== */



/* ========================================================================== */
/* === 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 "ID" "ID" "ID"\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 && UMF_debug <= 0)
    {
	return ;
    }
    have = 0 ;
    DEBUG4 (("Degree lists: "ID"\n", min_score)) ;
    for (deg = 0 ; deg <= n_col ; deg++)
    {
	col = head [deg] ;
	if (col == EMPTY)
	{
	    continue ;
	}
	DEBUG4 ((ID":", deg)) ;
	while (col != EMPTY)
	{
	    DEBUG4 ((" "ID, col)) ;
	    have += Col [col].shared1.thickness ;
	    ASSERT (COL_IS_ALIVE (col)) ;
	    col = Col [col].shared4.degree_next ;
	}
	DEBUG4 (("\n")) ;
    }
    DEBUG4 (("should "ID" have "ID"\n", should, have)) ;
    ASSERT (should == have) ;

    /* === Check the row degrees ============================================ */

    if (n_row > 10000 && UMF_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 && UMF_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 (UMF_debug < 3)
    {
	return ;
    }
    DEBUG3 (("DUMP MATRIX:\n")) ;
    for (r = 0 ; r < n_row ; r++)
    {
	DEBUG3 (("Row "ID" alive? "ID"\n", r, ROW_IS_ALIVE (r))) ;
	if (ROW_IS_DEAD (r))
	{
	    continue ;
	}

	/* ------------------ */
	/* changed for UMFPACK: */
	DEBUG3 (("start "ID" length "ID" degree "ID" thickness "ID"\n",
		Row [r].start, Row [r].length, Row [r].shared1.degree,
		Row [r].thickness)) ;
	/* ------------------ */

	rp = &A [Row [r].start] ;
	rp_end = rp + Row [r].length ;
	while (rp < rp_end)
	{
	    c = *rp++ ;
	    DEBUG4 (("	"ID" col "ID"\n", COL_IS_ALIVE (c), c)) ;
	}
    }

    for (c = 0 ; c < n_col ; c++)
    {
	DEBUG3 (("Col "ID" alive? "ID"\n", c, COL_IS_ALIVE (c))) ;
	if (COL_IS_DEAD (c))
	{
	    continue ;
	}
	/* ------------------ */
	/* changed for UMFPACK: */
	DEBUG3 (("start "ID" length "ID" shared1[thickness,parent] "ID
		" shared2 [order,score] "ID"\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 (("	"ID" row "ID"\n", ROW_IS_ALIVE (r), r)) ;
	}

	/* ------------------ */
	/* added for UMFPACK: */
	DEBUG1 (("Col")) ;
	dump_super (c, Col, n_col) ;
	/* ------------------ */

    }
}

/* ------------------ */
/* dump_super added for UMFPACK: */
PRIVATE void dump_super
(
    Int super_c,
    Colamd_Col Col [],
    Int n_col
)
{
    Int col, ncols ;

    DEBUG1 ((" =[ ")) ;
    ncols = 0 ;
    for (col = super_c ; col != EMPTY ; col = Col [col].nextcol)
    {
	DEBUG1 ((" "ID, col)) ;
	ASSERT (col >= 0 && col < n_col) ;
	if (col != super_c)
	{
	    ASSERT (COL_IS_DEAD (col)) ;
	}
	if (Col [col].nextcol == EMPTY)
	{
	    ASSERT (col == Col [super_c].lastcol) ;
	}
	ncols++ ;
	ASSERT (ncols <= Col [super_c].shared1.thickness) ;
    }
    ASSERT (ncols == Col [super_c].shared1.thickness) ;
    DEBUG1 (("]\n")) ;
}
/* ------------------ */


#endif /* NDEBUG */


```

sparse-matrix/umfpack/umfpack_wsolve.h:
```
/* ========================================================================== */
/* === umfpack_wsolve ======================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_wsolve
(
    int sys,
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ],
    double X [ ],
    const double B [ ],
    void *Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO],
    int Wi [ ],
    double W [ ]
) ;

long umfpack_dl_wsolve
(
    long sys,
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ],
    double X [ ],
    const double B [ ],
    void *Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO],
    long Wi [ ],
    double W [ ]
) ;

int umfpack_zi_wsolve
(
    int sys,
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ], const double Az [ ],
    double Xx [ ],	 double Xz [ ],
    const double Bx [ ], const double Bz [ ],
    void *Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO],
    int Wi [ ],
    double W [ ]
) ;

long umfpack_zl_wsolve
(
    long sys,
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ], const double Az [ ],
    double Xx [ ],	 double Xz [ ],
    const double Bx [ ], const double Bz [ ],
    void *Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO],
    long Wi [ ],
    double W [ ]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    int status, *Ap, *Ai, *Wi, sys ;
    double *B, *X, *Ax, *W, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ;
    status = umfpack_di_wsolve (sys, Ap, Ai, Ax, X, B, Numeric,
	Control, Info, Wi, W) ;

double long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    long status, *Ap, *Ai, *Wi, sys ;
    double *B, *X, *Ax, *W, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ;
    status = umfpack_dl_wsolve (sys, Ap, Ai, Ax, X, B, Numeric,
	Control, Info, Wi, W) ;

complex int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    int status, *Ap, *Ai, *Wi, sys ;
    double *Bx, *Bz, *Xx, *Xz, *Ax, *Az, *W,
	Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ;
    status = umfpack_zi_wsolve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric,
	Control, Info, Wi, W) ;

complex long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    long status, *Ap, *Ai, *Wi, sys ;
    double *Bx, *Bz, *Xx, *Xz, *Ax, *Az, *W,
	Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ;
    status = umfpack_zl_wsolve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric,
	Control, Info, Wi, W) ;

Purpose:

    Given LU factors computed by umfpack_*_numeric (PAQ=LU) and the
    right-hand-side, B, solve a linear system for the solution X.  Iterative
    refinement is optionally performed.  This routine is identical to
    umfpack_*_solve, except that it does not dynamically allocate any workspace.
    When you have many linear systems to solve, this routine is faster than
    umfpack_*_solve, since the workspace (Wi, W) needs to be allocated only
    once, prior to calling umfpack_*_wsolve.

Returns:

    The status code is returned.  See Info [UMFPACK_STATUS], below.

Arguments:

    Int sys ;		Input argument, not modified.
    Int Ap [n+1] ;	Input argument, not modified.
    Int Ai [nz] ;	Input argument, not modified.
    double Ax [nz] ;	Input argument, not modified.
    double X [n] ;	Output argument.
    double B [n] ;	Input argument, not modified.
    void *Numeric ;	Input argument, not modified.
    double Control [UMFPACK_CONTROL] ;	Input argument, not modified.
    double Info [UMFPACK_INFO] ;	Output argument.

    for complex versions:
    double Az [nz] ;	Input argument, not modified, imaginary part
    double Xx [n] ;	Output argument, real part.
    double Xz [n] ;	Output argument, imaginary part
    double Bx [n] ;	Input argument, not modified, real part
    double Bz [n] ;	Input argument, not modified, imaginary part

	The above arguments are identical to umfpack_*_solve, except that the
	error code UMFPACK_ERROR_out_of_memory will not be returned in
	Info [UMFPACK_STATUS], since umfpack_*_wsolve does not allocate any
	memory.

    Int Wi [n] ;		Workspace.
    double W [c*n] ;		Workspace, where c is defined below.

	The Wi and W arguments are workspace used by umfpack_*_wsolve.  They
	need not be initialized on input, and their contents are undefined on
	output.  The size of W depends on whether or not iterative refinement is
	used, and which version (real or complex) is called.  Iterative
	refinement is performed if Ax=b, A'x=b, or A.'x=b is being solved,
	Control [UMFPACK_IRSTEP] > 0, and A is nonsingular.  The size of W is
	given below:

				no iter.	with iter.
				refinement	refinement
	umfpack_di_wsolve	n		5*n
	umfpack_dl_wsolve	n		5*n
	umfpack_zi_wsolve	4*n		10*n
	umfpack_zl_wsolve	4*n		10*n
*/

```

sparse-matrix/umfpack/umfpack_report_control.h:
```
/* ========================================================================== */
/* === umfpack_report_control =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

void umfpack_di_report_control
(
    const double Control [UMFPACK_CONTROL]
) ;

void umfpack_dl_report_control
(
    const double Control [UMFPACK_CONTROL]
) ;

void umfpack_zi_report_control
(
    const double Control [UMFPACK_CONTROL]
) ;

void umfpack_zl_report_control
(
    const double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL] ;
    umfpack_di_report_control (Control) ;

double long Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL] ;
    umfpack_dl_report_control (Control) ;

complex int Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL] ;
    umfpack_zi_report_control (Control) ;

double long Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL] ;
    umfpack_zl_report_control (Control) ;

Purpose:

    Prints the current control settings.  Note that with the default print
    level, nothing is printed.  Does nothing if Control is (double *) NULL.

Arguments:

    double Control [UMFPACK_CONTROL] ;   Input argument, not modified.

	If a (double *) NULL pointer is passed, then the default control
	settings are used.  Otherwise, the settings are determined from the
	Control array.  See umfpack_*_defaults on how to fill the Control
	array with the default settings.  If Control contains NaN's, the
	defaults are used.  The following Control parameters are used:

	Control [UMFPACK_PRL]:  printing level.

	    1 or less: no output
	    2 or more: print all of Control
	    Default: 1
*/

```

sparse-matrix/umfpack/umf_extend_front.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void UMF_extend_front
(
    WorkType *Work
) ;


```

sparse-matrix/umfpack/ChangeLog:
```
Apr 11, 2002:

    * bug fix:  the Microsoft compiler doesn't handle NaN's properly.
	utIsNaN, and other ut* routines, added for MathWorks version
	to handle this properly.

Apr 1, 2002:

    * bug fix:  if a column was all NaN's, then UMFPACK would fail
	to find a pivot row.  umf_row_search.c and umf_internal.h
	modified to fix this problem.

Mar 9, 2002:  V4.0beta released

    * Map argument added to umfpack_*_triplet_to_col.  New files
	(umf_triplet.[ch]) added.
    * minor changes made so that UMFPACK can be compiled with g++ 
    * additional error checking added to umfpack_*_numeric, for
	detecting more changes in pattern (Ap, Ai) since last
	call to umfpack_*_symbolic

Feb 21, 2002:

    * User Guide explains the Makefile vs. GNUmakefile

    * umf_config.h modified, so that the complex SCSL C-BLAS uses
	(void *) arguments instead of (scsl_zomplex *).  gcc generates
	some spurious warnings (cc doesn't complain).  Affects the SGI
	IRIX only.

    * ported to Compaq Alpha

Feb 20, 2002: V4.0 (alpha) released.

    * V4.0 not yet ported to the Compaq Alpha (V3.2 was ported).

Feb 6 to Feb 19, 2002:

    * Relaxed restrictions on sizes of arrays for umfpack_*_transpose and
	umfpack_*_triplet_to_col.  Size of "max(n,nz)" now just size nz.

    * workspace for umfpack_*_wsolve increased in size.

    * two user arrays for umfpack_*_get_symbolic increased in size,
	by 1 (Chain_maxrows, Chain_maxcols).

    * lu_normest.m added.

Jan 18 to Feb 5, 2002:

    * The matrix A can be complex, singular, and/or rectangular.
	The solve step that uses the LU factors can only handle
	matrices that are complex or real, singuluar or non-singular, 
	and *** square ***, however.

    * Estimate of the condition number computed:
	(min (abs (diag (U))) / (max (abs (diag (U)))))

    * Forward/backsolves can solve with A.' as well as A'.

    * char * arguments removed from user-callable routines to make it
	easier for Fortran to call UMFPACK.  No Fortran interface is (yet)
	provided, however.

	The solve codes for umfpack_*_*solve changed to #define'd
	integers:

	    UMFPACK_A       Ax=b
	    UMFPACK_At      A'x=b
	    UMFPACK_Aat     A.'x=b
	    UMFPACK_Pt_L    P'Lx=b
	    UMFPACK_L       Lx=b
	    UMFPACK_Lt_P    L'Px=b
	    UMFPACK_Lat_P   L.'Px=b
	    UMFPACK_Lt      L'x=b
	    UMFPACK_U_Qt    UQ'x=b
	    UMFPACK_U       Ux=b
	    UMFPACK_Q_Ut    QU'x=b
	    UMFPACK_Q_Uat   QU.'x=b
	    UMFPACK_Ut      U'x=b
	    UMFPACK_Uat     U.'x=b

	All arguments are now either int, long scalars (pass by value),
	or int, long, double arrays (pass by reference), or void * pointers
	(pass by value or reference).  A void * pointer is of size 32 or 64
	bits on most machines.  There is no need for the caller (C or Fortran)
	to dereference the void * pointers, so these can be treated as
	integer*4 or integer*8 in Fortran.  A Fortran interface would have to
	have all arguments passed by reference.

    * All user-callable routine names changed.  The four sets are now:
	umfpack_di_*	real (double precision), int's as integers
	umfpack_dl_*	real (double precision), longs's as integers
	umfpack_zi_*	real (double precision), int's as integers
	umfpack_zl_*	real (double precision), longs's as integers

    * Ptree (row preordering) and info on pivotal rows for each front
	added to Symbolic object (extracted by umfpack_*_get_symbolic).
	Ptree added as output argument to "umfpack (A, 'symbolic')"
	mexFunction.

    * umfpack_*_transpose can do A' or A.'

    * umfpack_wsolve.c file removed (now generated from umfpack_solve.c).

    * Can now extract just the diagonal of U with umfpack_*_get_numeric,
	without having to extract the entire matrix U.

    * UMFPACK_ERROR_singular_matrix (-2) removed.

    * UMFPACK_WARNING_singular_matrix (1) added.

    * Control [UMFPACK_PIVOT_OPTION] removed.  No longer any symmetric
	pivot option (conflicts with the handling of singular and
	rectangular matrices).

    * Iterative refinement can do Ax=b, A'x=b, or A.'x=b.

    * Most floating-point operations done in macros, to support the complex
	versions.

    * Info [UMFPACK_N] is now Info [UMFPACK_NROW]

    * Info [UMFPACK_NCOL], Info [UMFPACK_UDIAG_NZ], Info [UMFPACK_UDIAG_NZ]
	added.

    * umfpack_* routines with "n" as input now use two arguments,
	n_row and n_col.

    * umfpack mexFunction now explicitly transposes A for b/A.  It computes
	it using the array transpose as (A.'\b.').'

January 1, 2002:  UMFPACK Version 3.2 released.  Submitted to ACM Trans.
	on Mathematical Software.

    * The umfpack mexFunction now returns the Info array when the matrix
	is singular.  Returned an empty array prior to this change.

    * Renamed variable that conflicted with system library routines
    	(system and j1).

    * Added a #ifdef MATHWORKS definition, so the built-in UMFPACK routine
	(in a future release of MATLAB) can use the internal ut* memory
	allocation routines, ut* assertion routine, and utPrintf.

    * MAX and MIN are not defined if they are already defined.

    * A bug fix in umf_kernel_init (a variable was not properly initialized).

    * Removed unused variables.

October 8, 2001:  UMFPACK Version 3.1 released.

August-October, 2001:

    * added umfpack_btf M-file.

    * modified the BLAS update in the frontal matrix.  If there are only
      a few pivots in remaining in the current front, then the BLAS3 update
      is delayed to include pivots in the next front.

    * Removed the special-case handling of dense columns from the numerical
      factorization (kept it in the colamd preordering).  This improves the
      performance of UMFPACK on dense matrices by a factor of 5 or so, and
      simplifies the code.

    * Added a symmetric-preference pivoting option.  The option slightly
      (but uniformly) improves the ordering when factorizing matrices with
      symmetric nonzero pattern.  That class of matrix is better handled by
      the symmetric-pattern multifrontal method (MA41 in the Harwell
      Subroutine Library), however.

    * Fixed the detection of integer overflow.  The 32-bit version cannot
      make use of more than 2GB of main memory (use the 64-bit version
      in that case, instead).  The 32-bit version did not correctly detect
      when it was trying to factorize too large of a matrix.

May 4, 2001:

    * SGI port extended.  It can now call the SCSL Scientific Library, with
	64-bit BLAS.  Make.sgi and umf_config.h modified.

April 30, 2001:  UMFPACK Version 3.0 released.  Changes since 3.0Beta release:

    * Long integer version added (umfpack_l_* user-callable routines).

    * Peak memory usage in the numerical factorization reduced by a total of
	12n integers (8n temporary workspace used during numerical
	factorization, and 4n for the permanent LU factors which was allocated
	at the beginning of factorization).

    * Ported to the IBM RS 6000 and Compaq Alpha, with help from Anshul Gupta
	and Friedrich Grund, respectively.

    * 64-bit version added.  Uses dgemm_64, dgemv_64, and dger_64 in the Sun
	Performance Library.  64-bit versions with the BLAS might not work on
	any other platform, because they take int's as their integer input
	arguments instead of long's.  Unfortunately, the proposed ANSI
	definition of the C-BLAS also uses int's as input integer arguments.
	It ought to use long's, or include a version that uses long's, just
	like the Sun Performance Library BLAS.

    * Additional statistics returned in Info:
	Info [UMFPACK_SIZE_OF_INT]	sizeof (int)
	Info [UMFPACK_SIZE_OF_LONG]	sizeof (long)
	Info [UMFPACK_SIZE_OF_POINTER]	sizeof (void *)
	Info [UMFPACK_SIZE_OF_ENTRY]	(was Info [UMFPACK_WORD])
	Info [UMFPACK_MAX_FRONT_SIZE_ESTIMATE]	est. front matrix size
	Info [UMFPACK_MAX_FRONT_SIZE]	actual max frontal matrix size.
	Contents of Info rearranged.

    * UMFPACK_ERROR_bad_configurution error code replaced with
	UMFPACK_ERROR_problem_too_large error code.  The "bad configuration"
	error occured when sizeof (int) < sizeof (size_t).  Now, the int
	version of UMFPACK can use 32-bit int's and 64-bit pointers, and the
	long version can use 64-bit long's and 64-bit pointers.  Both versions
	check to see if the array sizes allocated are larger than what can be
	accessed by an integer index variable (int or long, depending on the
	version), and returns UMFPACK_ERROR_problem_too_large if they become
	too large.

March 15, 2001:  UMFPACK Version 3.0Beta released.


```

sparse-matrix/umfpack/umf_row_search.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_row_search
(
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic,
    Int cdeg,
    const Int Pattern [ ],
    Int pivrow [2],
    Int rdeg [2],
    Int W_i [ ],
    Int W_o [ ],
    Int prior_pivrow [2],
    const Entry Fcol [ ],
    Int pivcol,
    Int freebie [2]
) ;


```

sparse-matrix/umfpack/umfpack_qsymbolic.h:
```
/* ========================================================================== */
/* === umfpack_qsymbolic ==================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_qsymbolic
(
    int n_row,
    int n_col,
    const int Ap [ ],
    const int Ai [ ],
    const int Qinit [ ],
    void **Symbolic,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

long umfpack_dl_qsymbolic
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    const long Qinit [ ],
    void **Symbolic,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

int umfpack_zi_qsymbolic
(
    int n_row,
    int n_col,
    const int Ap [ ],
    const int Ai [ ],
    const int Qinit [ ],
    void **Symbolic,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

long umfpack_zl_qsymbolic
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    const long Qinit [ ],
    void **Symbolic,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    int n_row, n_col, *Ap, *Ai, *Qinit, status ;
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_di_qsymbolic (n_row, n_col, Ap, Ai, Qinit, &Symbolic,
	Control, Info) ;

double long Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    long n_row, n_col, *Ap, *Ai, *Qinit, status ;
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_dl_qsymbolic (n_row, n_col, Ap, Ai, Qinit, &Symbolic,
	Control, Info) ;

complex int Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    int n_row, n_col, *Ap, *Ai, *Qinit, status ;
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_zi_qsymbolic (n_row, n_col, Ap, Ai, Qinit, &Symbolic,
	Control, Info) ;

complex long Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    long n_row, n_col, *Ap, *Ai, *Qinit, status ;
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_zl_qsymbolic (n_row, n_col, Ap, Ai, Qinit, &Symbolic,
	Control, Info) ;

Purpose:

    Given the nonzero pattern of a sparse matrix A in column-oriented form, and
    a sparsity preserving column preordering Qinit, umfpack_*_qsymbolic performs
    the symbolic factorization of A*Qinit (or A (:,Qinit) in MATLAB notation).
    It also computes the column elimination tree post-ordering.  This is
    identical to umfpack_*_symbolic, except that colamd is not called and the
    user input column order Qinit is used instead.  Note that in general, the
    Qinit passed to umfpack_*_qsymbolic will differ from the final Q found in
    umfpack_*_numeric, because of the column etree postordering done in
    umfpack_*_qsymbolic and sparsity-preserving modifications made within each
    frontal matrix during umfpack_*_numeric.

    *** WARNING ***  A poor choice of Qinit can easily cause umfpack_*_numeric
    to use a huge amount of memory and do a lot of work.  The "default" symbolic
    analysis method is umfpack_*_symbolic, not this routine.  If you use this
    routine, the performance of UMFPACK is your responsibility;  UMFPACK will
    not try to second-guess a poor choice of Qinit.  If you are unsure about
    the quality of your Qinit, then call both umfpack_*_symbolic and
    umfpack_*_qsymbolic, and pick the one with lower estimates of work and
    memory usage (Info [UMFPACK_FLOPS_ESTIMATE] and
    Info [UMFPACK_PEAK_MEMORY_ESTIMATE]).  Don't forget to call
    umfpack_*_free_symbolic to free the Symbolic object that you don't need.

Returns:

    The value of Info [UMFPACK_STATUS]; see below.

Arguments:

    All arguments are the same as umfpack_*_symbolic, except for the following:

    Int Qinit [n_col] ;		Input argument, not modified.

	The user's fill-reducing initial column preordering.  This must be a
	permutation of 0..n_col-1.  If Qinit [k] = j, then column j is the kth
	column of the matrix A (:,Qinit) to be factorized.  If Qinit is an
	(Int *) NULL pointer, then colamd is called instead.  In fact,

	Symbolic = umfpack_*_symbolic (n_row, n_col, Ap, Ai, Control, Info) ;

	is identical to

	Symbolic = umfpack_*_qsymbolic (n_row, n_col, Ap, Ai, (Int *) NULL,
	    Control, Info) ;

    double Control [UMFPACK_CONTROL] ;	Input argument, not modified.

	Identical to umfpack_*_symbolic if Qinit is (Int *) NULL.  Otherwise,
	if Qinit is present, it is identical to umfpack_*_symbolic except for
	the following:

	Control [UMFPACK_DENSE_ROW]:  ignored.

	Control [UMFPACK_DENSE_COL]:  ignored.

    double Info [UMFPACK_INFO] ;	Output argument, not defined on input.

	Identical to umfpack_*_symbolic if Qinit is (Int *) NULL.  Otherwise,
	if Qinit is present, it is identical to umfpack_*_symbolic except for
	the following:

	Info [UMFPACK_NDENSE_ROW]:  zero
	Info [UMFPACK_NEMPTY_ROW]:  number of empty rows.
	Info [UMFPACK_NDENSE_COL]:  zero
	Info [UMFPACK_NEMPTY_COL]:  number of empty columns.
*/

```

sparse-matrix/umfpack/umfpack_symbolic.c:
```
/* ========================================================================== */
/* === UMFPACK_symbolic ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Performs a symbolic factorization.
    See umfpack_symbolic.h for details.
*/

#include "umf_internal.h"

GLOBAL Int UMFPACK_symbolic
(
    Int n_row,
    Int n_col,
    const Int Ap [ ],
    const Int Ai [ ],
    void **SymbolicHandle,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
)
{
    Int *Qinit = (Int *) NULL ;
    return (UMFPACK_qsymbolic (n_row, n_col, Ap, Ai, Qinit, SymbolicHandle,
	Control, Info)) ;
}


```

sparse-matrix/umfpack/umf_kernel_init.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_kernel_init
(
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
#ifdef COMPLEX
    const double Az [ ],
#endif
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
) ;


```

sparse-matrix/umfpack/umf_scale_column.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_scale_column
(
    NumericType *Numeric,
    WorkType *Work
) ;


```

sparse-matrix/umfpack/umfpack_get_lunz.h:
```
/* ========================================================================== */
/* === umfpack_get_lunz ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_get_lunz
(
    int *lnz,
    int *unz,
    int *n_row,
    int *n_col,
    int *nz_udiag,
    void *Numeric
) ;

long umfpack_dl_get_lunz
(
    long *lnz,
    long *unz,
    long *n_row,
    long *n_col,
    long *nz_udiag,
    void *Numeric
) ;

int umfpack_zi_get_lunz
(
    int *lnz,
    int *unz,
    int *n_row,
    int *n_col,
    int *nz_udiag,
    void *Numeric
) ;

long umfpack_zl_get_lunz
(
    long *lnz,
    long *unz,
    long *n_row,
    long *n_col,
    long *nz_udiag,
    void *Numeric
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    int status, lnz, unz, n_row, n_col ;
    status = umfpack_di_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ;

double long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    long status, lnz, unz, n_row, n_col ;
    status = umfpack_dl_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ;

complex int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    int status, lnz, unz, n_row, n_col ;
    status = umfpack_zi_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ;

complex long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    long status, lnz, unz, n_row, n_col ;
    status = umfpack_zl_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ;

Purpose:

    Determines the size and number of nonzeros in the LU factors held by the
    Numeric object.  These are also the sizes of the output arrays required
    by umfpack_*_get_numeric.

    The matrix L is n_row -by- min(n_row,n_col), with lnz nonzeros, including
    the entries on the unit diagonal of L.

    The matrix U is min(n_row,n_col) -by- n_col, with unz nonzeros, including
    nonzeros on the diagonal of U.

Returns:

    UMFPACK_OK if successful.
    UMFPACK_ERROR_invalid_Numeric_object if Numeric is not a valid object.
    UMFPACK_ERROR_argument_missing if any other argument is (Int *) NULL.

Arguments:

    Int *lnz ;		Output argument.

	The number of nonzeros in L, including the diagonal (which is all
	one's).  This value is the required size of the Lj and Lx arrays as
	computed by umfpack_*_get_numeric.  The value of lnz is identical to
	Info [UMFPACK_LNZ], if that value was returned by umfpack_*_numeric.

    Int *unz ;		Output argument.

	The number of nonzeros in U, including the diagonal.  This value is the
	required size of the Ui and Ux arrays as computed by
	umfpack_*_get_numeric.  The value of unz is identical to
	Info [UMFPACK_UNZ], if that value was returned by umfpack_*_numeric.

    Int *n_row ;	Output argument.
    Int *n_col ;	Output argument.

	The order of the L and U matrices.  L is n_row -by- min(n_row,n_col)
	and U is min(n_row,n_col) -by- n_col.

    Int *nz_udiag ;	Output argument.

	The number of numerically nonzero values on the diagonal of U.  The
	matrix is singular if nz_diag < min(n_row,n_col).  A divide-by-zero
	will occur if nz_diag < n_row == n_col when solving a sparse system
	involving the matrix U in umfpack_*_*solve.  The value of nz_udiag is
	identical to Info [UMFPACK_UDIAG_NZ] if that value was returned by
	umfpack_*_numeric.

    void *Numeric ;	Input argument, not modified.

	Numeric must point to a valid Numeric object, computed by
	umfpack_*_numeric.
*/

```

sparse-matrix/umfpack/umf_order_front_tree.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_order_front_tree
(
    Int root,
    Int k,
    Int Front_child [ ],
    const Int Front_sibling [ ],
    Int Front_order [ ],
    Int Stack [ ]
) ;


```

sparse-matrix/umfpack/umf_is_permutation.c:
```
/* ========================================================================== */
/* === UMF_is_permutation =================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Return TRUE if P is a r-permutation vector, FALSE otherwise */
/* P [0..r-1] must be an r-permutation of 0..n-1 */

#include "umf_internal.h"

GLOBAL Int UMF_is_permutation
(
    const Int P [ ],	/* permutation of size r */
    Int W [ ],		/* workspace of size n */
    Int n,
    Int r
)
{
    Int i, k ;

    if (!P)
    {
	/* if P is (Int *) NULL, this is the identity permutation */
	return (TRUE) ;
    }

    ASSERT (W) ;

    for (i = 0 ; i < n ; i++)
    {
	W [i] = FALSE ;
    }
    for (k = 0 ; k < r ; k++)
    {
	i = P [k] ;
	if (i < 0 || i >= n)
	{
	    return (FALSE) ;
	}
	if (W [i])
	{
	    return (FALSE) ;
	}
	W [i] = TRUE ;
    }
    return (TRUE) ;
}


```

sparse-matrix/umfpack/umf_lsolve.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL double UMF_lsolve
(
    NumericType *Numeric,
    Entry X [ ],
    Int Pattern [ ]
) ;


```

sparse-matrix/umfpack/wrap_umfpack_wsolve.c:
```
#define	WSOLVE
#include "umfpack_solve.c"

```

sparse-matrix/umfpack/umfpack_report_matrix.h:
```
/* ========================================================================== */
/* === umfpack_report_matrix ================================================ */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_report_matrix
(
    int n_row,
    int n_col,
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ],
    int col_form,
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_dl_report_matrix
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ],
    long col_form,
    const double Control [UMFPACK_CONTROL]
) ;

int umfpack_zi_report_matrix
(
    int n_row,
    int n_col,
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ], const double Az [ ],
    int col_form,
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_zl_report_matrix
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ], const double Az [ ],
    long col_form,
    const double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int n_row, n_col, *Ap, *Ai, status ;
    double *Ax, Control [UMFPACK_CONTROL] ;
    status = umfpack_di_report_matrix (n_row, n_col, Ap, Ai, Ax, 1, Control) ;
or:
    status = umfpack_di_report_matrix (n_row, n_col, Ap, Ai, Ax, 0, Control) ;

double long Syntax:

    #include "umfpack.h"
    long n_row, n_col, *Ap, *Ai, status ;
    double *Ax, Control [UMFPACK_CONTROL] ;
    status = umfpack_dl_report_matrix (n_row, n_col, Ap, Ai, Ax, 1, Control) ;
or:
    status = umfpack_dl_report_matrix (n_row, n_col, Ap, Ai, Ax, 0, Control) ;

complex int Syntax:

    #include "umfpack.h"
    int n_row, n_col, *Ap, *Ai, status ;
    double *Ax, *Az, Control [UMFPACK_CONTROL] ;
    status = umfpack_zi_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 1,
        Control) ;
or:
    status = umfpack_zi_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 0,
        Control) ;

complex long Syntax:

    #include "umfpack.h"
    long n_row, n_col, *Ap, *Ai, status ;
    double *Ax, Control [UMFPACK_CONTROL] ;
    status = umfpack_zl_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 1,
	Control) ;
or:
    status = umfpack_zl_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 0,
	Control) ;

Purpose:

    Verifies and prints a row or column-oriented sparse matrix.

Returns:

    UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked).

    Otherwise (where n is n_col for the column form and n_row for row
    and let ni be n_row for the column form and n_col for row):

    UMFPACK_OK if the matrix is valid.

    UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0.
    UMFPACK_ERROR_argument_missing if Ap and/or Ai are missing.
    UMFPACK_ERROR_nz_negative if Ap [n] < 0.
    UMFPACK_ERROR_Ap0_nonzero if Ap [0] is not zero.
    UMFPACK_ERROR_col_length_negative if Ap [j+1] < Ap [j] for any j in the
	range 0 to n-1.
    UMFPACK_ERROR_out_of_memory if out of memory.
    UMFPACK_ERROR_row_index_out_of_bounds if any row index in Ai is not in
	the range 0 to ni-1.
    UMFPACK_ERROR_jumbled_matrix if the row indices in any column are not in
	ascending order, or contain duplicates.

Arguments:

    Int n_row ;		Input argument, not modified.
    Int n_col ;		Input argument, not modified.

	A is an n_row-by-n_row matrix.  Restriction: n_row > 0 and n_col > 0.

    Int Ap [n+1] ;	Input argument, not modified.

	n is n_row for a row-form matrix, and n_col for a column-form matrix.

	Ap is an integer array of size n+1.  If col_form is true (nonzero),
	then on input, it holds the "pointers" for the column form of the
	sparse matrix A.  The row indices of column j of the matrix A are held
	in Ai [(Ap [j]) ... (Ap [j+1]-1)].  Otherwise, Ap holds the
	row pointers, and the column indices of row j of the matrix are held
	in Ai [(Ap [j]) ... (Ap [j+1]-1)].

	The first entry, Ap [0], must be zero, and Ap [j] <= Ap [j+1] must hold
	for all j in the range 0 to n-1.  The value nz = Ap [n] is thus the
	total number of entries in the pattern of the matrix A.

    Int Ai [nz] ;	Input argument, not modified, of size nz = Ap [n].

	If col_form is true (nonzero), then the nonzero pattern (row indices)
	for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)].  Row indices
	must be in the range 0 to n_row-1 (the matrix is 0-based).

	Otherwise, the nonzero pattern (column indices) for row j is stored in
	Ai [(Ap [j]) ... (Ap [j+1]-1)]. Column indices must be in the range 0
	to n_col-1 (the matrix is 0-based).

    double Ax [nz] ;	Input argument, not modified, of size nz = Ap [n].

	The numerical values of the sparse matrix A.

	If col_form is true (nonzero), then the nonzero pattern (row indices)
	for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and the
	corresponding (real) numerical values are stored in
	Ax [(Ap [j]) ... (Ap [j+1]-1)].  The imaginary parts are stored in
	Az [(Ap [j]) ... (Ap [j+1]-1)], for the complex versions.

	Otherwise, the nonzero pattern (column indices) for row j
	is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and the corresponding
	(real) numerical values are stored in Ax [(Ap [j]) ... (Ap [j+1]-1)].
	The imaginary parts are stored in Az [(Ap [j]) ... (Ap [j+1]-1)],
	for the complex versions.

	No numerical values are printed if Ax or Az are (double *) NULL.

    double Az [nz] ;	Input argument, not modified, for complex versions.

	The imaginary values of the sparse matrix A.   See the description
	of Ax, above.  No numerical values are printed if Az is NULL. 

	Future complex version:  if Ax is present and Az is NULL, then both real
	and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k]
	and Ax[2*k+1] being the real and imaginary part of the kth entry.

    Int col_form ;	Input argument, not modified.

	The matrix is in row-oriented form if form is col_form is false (0).
	Otherwise, the matrix is in column-oriented form.

    double Control [UMFPACK_CONTROL] ;	Input argument, not modified.

	If a (double *) NULL pointer is passed, then the default control
	settings are used.  Otherwise, the settings are determined from the
	Control array.  See umfpack_*_defaults on how to fill the Control
	array with the default settings.  If Control contains NaN's, the
	defaults are used.  The following Control parameters are used:

	Control [UMFPACK_PRL]:  printing level.

	    2 or less: no output.  returns silently without checking anything.
	    3: fully check input, and print a short summary of its status
	    4: as 3, but print first few entries of the input
	    5: as 3, but print all of the input
	    Default: 1
*/

```

sparse-matrix/umfpack/umf_init_front.c:
```
/* ========================================================================== */
/* === UMF_init_front ======================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

#include "umf_internal.h"

GLOBAL void UMF_init_front
(
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int i, fsize, pivrow, pivcol, j, fnrows_max, fncols_max,
	row, col, *Frows, *Fcols, *Fcpos, *Frpos, fncols, fnrows, src, dest,
	*Wrow ;
    Entry *Fx, *F ;

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

    /* current front is defined by pivot row and column */

    pivrow = Work->pivrow ;
    pivcol = Work->pivcol ;

    Frows = Work->Frows ;
    Fcols = Work->Fcols ;
    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;

    Work->fnpiv = 1 ;
    Work->fnzeros = 0 ;

    /* dynamic front dimensions, but fixed across one chain */
    fnrows_max = Work->fnrows_max ;
    fncols_max = Work->fncols_max ;

    fsize = fnrows_max * fncols_max ;

    /* ---------------------------------------------------------------------- */
    /* place pivot column pattern in frontal matrix */
    /* ---------------------------------------------------------------------- */

    if (Work->pivcol_in_front)
    {
	/* append the pivot column extension */
	/* note that all we need to do is increment the size, since the */
	/* candidate pivot column pattern is already in place in */
	/* Frows [0 ... Work->fnrows-1] (the old pattern), and */
	/* Frows [Work->fnrows ... Work->fnrows + Work->ccdeg - 1] (the new */
	/* pattern). */

	/* if both pivrow and pivcol are in front, then we extend the old one */
	/* in UMF_extend_front, rather than starting a new one here. */
	ASSERT (!Work->pivrow_in_front) ;

	dest = Work->fnrows  ;
	Work->fscan_row = dest ;	/* only scan the new rows */
	dest += Work->ccdeg ;
    }
    else
    {
	/* this is a completely new column */
	dest = 0 ;
	Work->fscan_row = 0 ;		/* scan all the rows */

	ASSERT (Work->Wcol == Work->Wm) ;
	for (i = 0 ; i < Work->ccdeg ; i++)
	{
	    row = Work->Wcol [i] ;
	    Frows [dest] = row ;
	    Frpos [row] = dest ;
	    dest++ ;
	}
    }

    Work->fnrows = dest ;

    /* place pivot row index into position */
    Frpos [pivrow] = fnrows_max - 1 ;

#ifndef NDEBUG
    DEBUG3 (("New Pivot col "ID" now in front, length "ID"\n",
	pivcol, Work->fnrows)) ;
    for (i = 0 ; i < Work->fnrows ; i++)
    {
	DEBUG4 (("row "ID" position "ID"\n", Frows [i], Frpos [Frows [i]])) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* place pivot row pattern in frontal matrix */
    /* ---------------------------------------------------------------------- */

    if (Work->pivrow_in_front)
    {
	/* append the pivot row extension */
	src = Work->fncols ;
	dest = Work->fncols ;
	Work->fscan_col = dest ;	/* only scan the new columns */
    }
    else
    {
	/* this is a completely new row */
	src = 0 ;
	dest = 0 ;
	Work->fscan_col = 0 ;		/* scan all the columns */
    }

    Wrow = Work->Wrow ;
    for ( ; src < Work->rrdeg ; src++)
    {
	col = Wrow [src] ;
	if (col != pivcol)
	{
	    Fcols [dest] = col ;
	    Fcpos [col] = dest * fnrows_max ;
	    dest++ ;
	}
    }

    Work->fncols = dest ;

    /* place pivot column index into position */
    Fcpos [pivcol] = (fncols_max - 1) * fnrows_max ;

#ifndef NDEBUG
    DEBUG3 (("New Pivot row "ID" now in front, length "ID" fnrows_max "ID"\n",
		pivrow, Work->fncols, fnrows_max)) ;
    for (j = 0 ; j < Work->fncols ; j++)
    {
	DEBUG4 (("col "ID" position "ID"  ("ID")\n",
	    Fcols [j], Fcpos [Fcols [j]], j*fnrows_max)) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* clear the frontal matrix */
    /* ---------------------------------------------------------------------- */

    fncols = Work->fncols ;
    fnrows = Work->fnrows ;
    Fx = Work->Fx ;

    /* clear the contribution block and the pivot row */
    F = Fx ;
    for (j = 0 ; j < fncols ; j++)
    {
	for (i = 0 ; i < fnrows ; i++)
	{
	    ASSERT ((&F[i] >= Fx) && (&F[i] < Fx+fsize)) ;
	    CLEAR (F [i]) ;
	}
	ASSERT ((&F[fnrows_max-1] >= Fx) && (&F[fnrows_max-1] < Fx+fsize)) ;
	CLEAR (F [fnrows_max - 1]) ;
	F += fnrows_max ;
    }

    /* clear the pivot column (excl pivot itself) */
    F = Fx + Fcpos [pivcol] ;
    for (j = 0 ; j < fnrows ; j++)
    {
	ASSERT ((&F[j] >= Fx) && (&F[j] < Fx+fsize)) ;
	CLEAR (F [j]) ;
    }

    /* clear the pivot entry */
    j = fsize-1 ;
    DEBUG2 (("j "ID" fsize "ID"\n", j, fsize)) ;
    ASSERT ((&Fx[j] >= Fx) && (&Fx[j] < Fx+fsize)) ;
    CLEAR (Fx [j]) ;

    /* ---------------------------------------------------------------------- */
    /* current workspace usage: */
    /* ---------------------------------------------------------------------- */

    /* Fx [0..fnrows_max-1, 0..fncols_max-1]: */
    /*	space for the new frontal matrix. */
    /* Fx (i,j) is located at Fx [i+j*fnrows_max] */

    /* ---------------------------------------------------------------------- */
    /* make sure the pivot row and column are scanned in assembly phase */
    /* ---------------------------------------------------------------------- */

    Work->scan_pivrow = TRUE ;
    Work->scan_pivcol = TRUE ;

}

```

sparse-matrix/umfpack/umf_usolve.c:
```
/* ========================================================================== */
/* === UMF_usolve =========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*  solves Ux = b, where U is the upper triangular factor of a matrix. */
/*  B is overwritten with the solution X. */
/*  Returns the floating point operation count */

#include "umf_internal.h"

GLOBAL double UMF_usolve
(
    NumericType *Numeric,
    Entry X [ ],		/* b on input, solution x on output */
    Int Pattern [ ]		/* a work array of size n */
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int k, deg, j, *ip, col, *Upos, *Uilen, pos,
	*Uip, n, ulen, up, newUchain, npiv ;
    Entry *xp, xk, *D ;
    double flops ;

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

    if (Numeric->n_row != Numeric->n_col) return (0.) ;
    n = Numeric->n_row ;
    npiv = Numeric->npiv ;
    Upos = Numeric->Upos ;
    Uilen = Numeric->Uilen ;
    Uip = Numeric->Uip ;
    D = Numeric->D ;

    /* start by counting the division by D [0..n-1] */
    flops = n ;

#ifndef NDEBUG
    DEBUG4 (("Usolve start:  npiv = "ID" n = "ID"\n", npiv, n)) ;
    for (j = 0 ; j < n ; j++)
    {
	DEBUG4 (("Usolve start "ID": ", j)) ;
	EDEBUG4 (X [j]) ;
	DEBUG4 (("\n")) ;
    }
#endif

    /* handle the singular part of D, up to just before the last pivot */
    flops += (n-npiv) ;
    for (k = n-1 ; k >= npiv ; k--)
    {
	/* Note that D [k] is identically zero. */
	ASSERT (IS_ZERO (D [k])) ;
	xk = X [k] ;
	/* X [k] = xk / D [k] ; */
	DIV (X [k], xk, D [k]) ;
    }

    deg = Numeric->ulen ;
    if (deg > 0)
    {
	/* make last pivot row of U (singular matrices only) */
	for (j = 0 ; j < deg ; j++)
	{
	    DEBUG1 (("Last row of U: j="ID"\n", j)) ;
	    DEBUG1 (("Last row of U: Upattern[j]="ID"\n",
		Numeric->Upattern [j]) );
	    Pattern [j] = Numeric->Upattern [j] ;
	}
    }

    for (k = npiv-1 ; k >= 0 ; k--)
    {

	/* ------------------------------------------------------------------ */
	/* use row k of U */
	/* ------------------------------------------------------------------ */

	up = Uip [k] ;
	ulen = Uilen [k] ;
	newUchain = (up < 0) ;
	if (newUchain)
	{
	    up = -up ;
	    xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ;
	}
	else
	{
	    xp = (Entry *) (Numeric->Memory + up) ;
	}

	xk = X [k] ;
	flops += 2*deg ;
	for (j = 0 ; j < deg ; j++)
	{
	    DEBUG4 (("  k "ID" col "ID" value", k, Pattern [j])) ;
	    EDEBUG4 (*xp) ;
	    DEBUG4 (("\n")) ;
	    /* xk -= X [Pattern [j]] * (*xp) ; */
	    MULT_SUB (xk, X [Pattern [j]], *xp) ;
	    xp++ ;
	}

	/* Go ahead and divide by zero if D [k] is zero */
	/* X [k] = xk / D [k] ; */
	DIV (X [k], xk, D [k]) ;

	/* ------------------------------------------------------------------ */
	/* make row k-1 of U in Pattern [0..deg-1] */
	/* ------------------------------------------------------------------ */

	if (newUchain)
	{
	    /* next row is a new Uchain */
	    deg = ulen ;
	    ASSERT (IMPLIES (k == 0, deg == 0)) ;
	    DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ;
	    ip = (Int *) (Numeric->Memory + up) ;
	    for (j = 0 ; j < deg ; j++)
	    {
		col = *ip++ ;
		DEBUG4 (("  k "ID" col "ID"\n", k-1, col)) ;
		ASSERT (k <= col) ;
		Pattern [j] = col ;
	    }
	}
	else
	{
	    deg -= ulen ;
	    DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k, deg)) ;
	    ASSERT (deg >= 0) ;
	    pos = Upos [k] ;
	    if (pos != EMPTY)
	    {
		/* add the pivot column */
		DEBUG4 (("k "ID" add pivot entry at pos "ID"\n", k, pos)) ;
		ASSERT (pos >= 0 && pos <= deg) ;
		Pattern [deg++] = Pattern [pos] ;
		Pattern [pos] = k ;
	    }
	}
    }

#ifndef NDEBUG
    for (j = 0 ; j < n ; j++)
    {
	DEBUG4 (("Usolve done "ID": ", j)) ;
	EDEBUG4 (X [j]) ;
	DEBUG4 (("\n")) ;
    }
    DEBUG4 (("Usolve done.\n")) ;
#endif

    return (DIV_FLOPS * ((double) n) + MULTSUB_FLOPS * ((double) Numeric->unz));
}


```

sparse-matrix/umfpack/umf_kernel_init_usage.c:
```
/* ========================================================================== */
/* === UMF_kernel_init_usage ================================================ */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Return number of Units needed for initial elements by UMF_kernel_init */
/* (including the tuples). */

#include "umf_internal.h"
#include "umf_build_tuples_usage.h"

GLOBAL Int UMF_kernel_init_usage
(
    const Int Ap [ ],
    const Int Row_degree [ ],
    Int n_row,
    Int n_col,
    double *dusage		/* input and output argument */
)
{
    Int col, cdeg, usage ;

    /* elements: */
    usage = 0 ;
    for (col = 0 ; col < n_col ; col++)
    {
	DEBUG2 (("col "ID" Ap["ID"] = "ID"  Ap ["ID"] "ID"\n",
	    col, col+1, Ap [col+1], col, Ap [col])) ;
	cdeg = Ap [col+1] - Ap [col] ;
	ASSERT (cdeg >= 0) ;
	if (cdeg > 0)
	{
	    usage   += GET_ELEMENT_SIZE (cdeg, 1) + 1 ;
	    *dusage += DGET_ELEMENT_SIZE (cdeg, 1) + 1 ;
	}
    }

    DEBUG0 (("UMF_kernel_init_usage : original elements: "ID" %g\n",
	usage, *dusage)) ;

    /* tuples: */
    usage += UMF_build_tuples_usage ((Int *) NULL, (Int *) NULL,
	Row_degree, Row_degree, n_row, n_col, dusage) ;

    DEBUG0 (("UMF_kernel_init_usage : all: "ID" %g\n", usage, *dusage)) ;
    return (usage) ;
}


```

sparse-matrix/umfpack/umf_transpose.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_transpose
(
    Int n_row,
    Int n_col,
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
    const Int P [ ],
    const Int Q [ ],
    Int nq,
    Int Rp [ ],
    Int Ri [ ],
    double Rx [ ],
    Int W [ ],
    Int check
#ifdef COMPLEX
    , const double Az [ ]
    , double Rz [ ]
    , Int do_conjugate
#endif
) ;


```

sparse-matrix/umfpack/umf_assemble.c:
```
/* ========================================================================== */
/* === UMF_assemble ========================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*  Degree update and numerical assembly */

#include "umf_internal.h"
#include "umf_mem_free_tail_block.h"

GLOBAL void UMF_assemble
(
    NumericType *Numeric,
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int e, i, row, col, i2, nrows, ncols, f, tpi, extcdeg, extrdeg, rdeg0,
	cdeg0, son_list, next, scan_pivrow, scan_pivcol, nrows_to_assemble,
	ncols_to_assemble, ngetrows, j, j2,
	nrowsleft,	/* number of rows remaining in C */
	ncolsleft,	/* number of columns remaining in C  */
	prior_Lson, prior_Uson, *E, *Cols, *Rows, *Wm, *Woo,
	*Row_tuples, *Row_degree, *Row_tlen,
	*Col_tuples, *Col_degree, *Col_tlen ;
    Unit *Memory, *p ;
    Element *ep ;
    Tuple *tp, *tp1, *tp2, *tpend ;

    Entry
	*C,		/* a pointer into the contribution block */
	*Fx,		/* Fx [0..fnrows_max-1, 0..fncols_max-1] working array*/
	*Fcol,		/* a column of F */
	*Frow ;		/* a row of F */

    Int
	*Frows,		/* Frows [0.. ]: row indices of F */
	*Fcols,		/* Fcols [0.. ]: column indices of F */
	*Frpos,
	*Fcpos,
	fnrows,		/* number of rows in contribution block in F */
	fncols ;	/* number of columns in contribution block in F */

#ifndef NDEBUG
    Int j3, n_row, n_col ;
    n_row = Work->n_row ;
    n_col = Work->n_col ;
    DEBUG3 (("::Assemble SCANS 1-4\n")) ;
    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif

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

    fncols = Work->fncols ;
    fnrows = Work->fnrows ;
    Fcols = Work->Fcols ;
    Frows = Work->Frows ;
    Fcpos = Work->Fcpos ;
    Frpos = Work->Frpos ;
    Fx = Work->Fx ;
    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;
    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;
    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;
    E = Work->E ;
    Memory = Numeric->Memory ;
    Wm = Work->Wm ;
    Woo = Work->Woo ;		/* must be of size at least fnrows_max */
    rdeg0 = Work->rdeg0 ;
    cdeg0 = Work->cdeg0 ;

#ifndef NDEBUG
    DEBUG6 (("============================================\n")) ;
    DEBUG6 (("Degree update, assembly.\n")) ;
    DEBUG6 (("pivot row pattern:  fncols="ID"\n", fncols)) ;
    for (j3 = 0 ; j3 < fncols ; j3++) DEBUG6 ((ID, Fcols [j3])) ;
    DEBUG6 (("\npivot col pattern:  fnrows="ID"\n", fnrows)) ;
    for (j3 = 0 ; j3 < fnrows ; j3++) DEBUG6 ((ID, Frows [j3])) ;
    DEBUG6 (("\n")) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* determine the largest actual frontal matrix size */
    /* ---------------------------------------------------------------------- */

    Numeric->maxfrsize = MAX (Numeric->maxfrsize,
	(fnrows + Work->fnpiv) * (fncols + Work->fnpiv)) ;

    /* ---------------------------------------------------------------------- */
    /* assemble from prior elements into the current frontal matrix */
    /* ---------------------------------------------------------------------- */

    DEBUG2 (("New assemble start [\n")) ;

    /* Currently no rows or columns are marked.  No elements are scanned, */
    /* that is, (ep->next == EMPTY) is true for all elements */

    son_list = 0 ;	/* start creating son_list [ */

    /* ---------------------------------------------------------------------- */
    /* determine if most recent element is Lson or Uson of current front */
    /* ---------------------------------------------------------------------- */

    if (!Work->do_extend)
    {
	prior_Uson = ( Work->pivcol_in_front && !Work->pivrow_in_front) ;
	prior_Lson = (!Work->pivcol_in_front &&  Work->pivrow_in_front) ;
	if (prior_Uson || prior_Lson)
	{
	    e = Work->prior_element ;
	    if (e != EMPTY)
	    {
		ASSERT (E [e]) ;
		p = Memory + E [e] ;
		ep = (Element *) p ;
		ep->next = son_list ;
		son_list = e ;
#ifndef NDEBUG
		DEBUG2 (("e "ID" is Prior son "ID" "ID"\n",
		    e, prior_Uson, prior_Lson)) ;
		UMF_dump_element (Numeric, Work, e, FALSE) ;
#endif
		ASSERT (E [e]) ;
	    }
	}
    }
    Work->prior_element = EMPTY ;

    /* ---------------------------------------------------------------------- */
    /* SCAN1-row:  scan the element lists of each new row in the pivot col */
    /* and compute the external column degree for each frontal */
    /* ---------------------------------------------------------------------- */

    scan_pivrow = Work->scan_pivrow ;

    for (i2 = Work->fscan_row ; i2 < fnrows || scan_pivrow ; )
    {
	/* Get a row */
	if (scan_pivrow)
	{
	    /* pivot row is new to this front.  Scan it */
	    row = Work->pivrow ;
	    scan_pivrow = FALSE ;
	}
	else
	{
	    row = Frows [i2++] ;
	}

	DEBUG6 (("SCAN1-row: "ID"\n", row)) ;
#ifndef NDEBUG
	UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ;
#endif

	ASSERT (NON_PIVOTAL_ROW (row)) ;
	tpi = Row_tuples [row] ;
	if (!tpi) continue ;
	tp = (Tuple *) (Memory + tpi) ;
	tp1 = tp ;
	tp2 = tp ;
	tpend = tp + Row_tlen [row] ;
	for ( ; tp < tpend ; tp++)
	{
	    e = tp->e ;
	    ASSERT (e > 0 && e <= Work->nel) ;
	    if (!E [e]) continue ;	/* element already deallocated */
	    f = tp->f ;
	    p = Memory + E [e] ;
	    ep = (Element *) p ;
	    p += UNITS (Element, 1) ;
	    Rows = ((Int *) p) + ep->ncols ;
	    if (Rows [f] == EMPTY) continue ;	/* row already assembled */
	    ASSERT (row == Rows [f]) ;

	    if (ep->cdeg < cdeg0)
	    {
		/* first time seen in scan1-row */
		ep->cdeg = ep->nrowsleft + cdeg0 ;
		DEBUG6 (("e "ID" First seen: cdeg: "ID" ", e, ep->cdeg-cdeg0)) ;
		ASSERT (ep->ncolsleft > 0 && ep->nrowsleft > 0) ;
	    }

	    ep->cdeg-- ;	/* decrement external column degree */
	    DEBUG6 (("e "ID" New ext col deg: "ID"\n", e, ep->cdeg - cdeg0)) ;

	    /* this element is not yet in the new son list */
	    if (ep->cdeg == cdeg0 && ep->next == EMPTY)
	    {
		/* A new LUson or Uson has been found */
		ep->next = son_list ;
		son_list = e ;
	    }

	    ASSERT (ep->cdeg >= cdeg0) ;
	    *tp2++ = *tp ;	/* leave the tuple in the list */
	}
	Row_tlen [row] = tp2 - tp1 ;
    }

    /* ---------------------------------------------------------------------- */
    /* SCAN1-col:  scan the element lists of each new col in the pivot row */
    /*	 and compute the external row degree for each frontal */
    /* ---------------------------------------------------------------------- */

    scan_pivcol = Work->scan_pivcol ;

    for (j2 = Work->fscan_col ; j2 < fncols || scan_pivcol ; )
    {
	/* Get a column */
	if (scan_pivcol)
	{
	    /* pivot col is new to this front.  Scan it */
	    col = Work->pivcol ;
	    scan_pivcol = FALSE ;
	}
	else
	{
	    col = Fcols [j2++] ;
	}
	ASSERT (col >= 0 && col < n_col) ;

	DEBUG6 (("SCAN 1-col: "ID"\n", col)) ;
#ifndef NDEBUG
	UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ;
#endif

	ASSERT (NON_PIVOTAL_COL (col)) ;
	tpi = Col_tuples [col] ;
	if (!tpi) continue ;
	tp = (Tuple *) (Memory + tpi) ;
	tp1 = tp ;
	tp2 = tp ;
	tpend = tp + Col_tlen [col] ;
	for ( ; tp < tpend ; tp++)
	{
	    e = tp->e ;
	    ASSERT (e > 0 && e <= Work->nel) ;
	    if (!E [e]) continue ;	/* element already deallocated */
	    f = tp->f ;
	    p = Memory + E [e] ;
	    ep = (Element *) p ;
	    p += UNITS (Element, 1) ;
	    Cols = (Int *) p ;
	    if (Cols [f] == EMPTY) continue ;	/* column already assembled */
	    ASSERT (col == Cols [f]) ;

	    if (ep->rdeg < rdeg0)
	    {
		/* first time seen in scan1-col */
		ep->rdeg = ep->ncolsleft + rdeg0 ;
		DEBUG6 (("e "ID" First seen: rdeg: "ID" ", e, ep->rdeg-rdeg0)) ;
		ASSERT (ep->ncolsleft > 0 && ep->nrowsleft > 0) ;
	    }

	    ep->rdeg-- ;	/* decrement external row degree */
	    DEBUG6 (("e "ID" New ext row degree: "ID"\n", e, ep->rdeg-rdeg0)) ;

	    if (ep->rdeg == rdeg0 && ep->next == EMPTY)
	    {
		/* A new LUson or Lson has been found */
		ep->next = son_list ;
		son_list = e ;
	    }

	    ASSERT (ep->rdeg >= rdeg0) ;
	    *tp2++ = *tp ;	/* leave the tuple in the list */
	}
	Col_tlen [col] = tp2 - tp1 ;
    }

    /* ---------------------------------------------------------------------- */
    /* assemble new sons via full scans */
    /* ---------------------------------------------------------------------- */

    next = EMPTY ;

    for (e = son_list ; e > 0 ; e = next)
    {
	ASSERT (e > 0 && e <= Work->nel && E [e]) ;
	p = Memory + E [e] ;
	DEBUG2 (("New son: "ID"\n", e)) ;
#ifndef NDEBUG
	UMF_dump_element (Numeric, Work, e, FALSE) ;
#endif
	GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ;
	nrowsleft = ep->nrowsleft ;
	ncolsleft = ep->ncolsleft ;
	next = ep->next ;
	ep->next = EMPTY ;

	extrdeg = (ep->rdeg < rdeg0) ? ncolsleft : (ep->rdeg - rdeg0) ;
	extcdeg = (ep->cdeg < cdeg0) ? nrowsleft : (ep->cdeg - cdeg0) ;
	ncols_to_assemble = ncolsleft - extrdeg ;
	nrows_to_assemble = nrowsleft - extcdeg ;

	if (extrdeg == 0 && extcdeg == 0)
	{

	    /* -------------------------------------------------------------- */
	    /* this is an LUson - assemble an entire contribution block */
	    /* -------------------------------------------------------------- */

	    DEBUG6 (("LUson found: "ID"\n", e)) ;

	    if (nrows == nrowsleft)
	    {
		/* ---------------------------------------------------------- */
		/* no rows assembled out of this LUson yet */
		/* ---------------------------------------------------------- */

		/* compute the compressed column offset vector*/
		/* [ use Wm [0..nrows-1] for offsets */
		for (i = 0 ; i < nrows ; i++)
		{
		    row = Rows [i] ;
		    Row_degree [row] -= ncolsleft ;
		    Wm [i] = Frpos [row] ;
		}

		if (ncols == ncolsleft)
		{
		    /* ------------------------------------------------------ */
		    /* no rows or cols assembled out of LUson yet */
		    /* ------------------------------------------------------ */

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			Col_degree [col] -= nrowsleft ;
			Fcol = Fx + Fcpos [col] ;
			for (i = 0 ; i < nrows ; i++)
			{
			    /* Fcol [Wm [i]] += C [i] ; */
			    ASSEMBLE (Fcol [Wm [i]], C [i]) ;
			}
			C += nrows ;
		    }
		}
		else
		{
		    /* ------------------------------------------------------ */
		    /* only cols have been assembled out of LUson */
		    /* ------------------------------------------------------ */

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			if (col >= 0)
			{
			    Col_degree [col] -= nrowsleft ;
			    Fcol = Fx + Fcpos [col] ;
			    for (i = 0 ; i < nrows ; i++)
			    {
				/* Fcol [Wm [i]] += C [i] ; */
				ASSEMBLE (Fcol [Wm [i]], C [i]) ;
			    }
			}
			C += nrows ;
		    }
		}
		/* ] done using Wm [0..nrows-1] for offsets */
	    }
	    else
	    {
		/* ---------------------------------------------------------- */
		/* some rows have been assembled out of this LUson */
		/* ---------------------------------------------------------- */

		/* compute the compressed column offset vector*/
		/* [ use Woo,Wm [0..nrowsleft-1] for offsets */
		ngetrows = 0 ;
		for (i = 0 ; i < nrows ; i++)
		{
		    row = Rows [i] ;
		    if (row >= 0)
		    {
			Row_degree [row] -= ncolsleft ;
			Woo [ngetrows] = i ;
			Wm [ngetrows++] = Frpos [row] ;
		    }
		}
		ASSERT (ngetrows == nrowsleft) ;

		if (ncols == ncolsleft)
		{
		    /* ------------------------------------------------------ */
		    /* only rows have been assembled out of this LUson */
		    /* ------------------------------------------------------ */

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			Col_degree [col] -= nrowsleft ;
			Fcol = Fx + Fcpos [col] ;
			for (i = 0 ; i < nrowsleft ; i++)
			{
			    /* Fcol [Wm [i]] += C [Woo [i]] ; */
			    ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ;
			}
			C += nrows ;
		    }

		}
		else
		{
		    /* ------------------------------------------------------ */
		    /* both rows and columns have been assembled out of LUson */
		    /* ------------------------------------------------------ */

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			if (col >= 0)
			{
			    Col_degree [col] -= nrowsleft ;
			    Fcol = Fx + Fcpos [col] ;
			    for (i = 0 ; i < nrowsleft ; i++)
			    {
				/* Fcol [Wm [i]] += C [Woo [i]] ; */
				ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ;
			    }
			}
			C += nrows ;
		    }
		}
		/* ] done using Woo,Wm [0..nrowsleft-1] */
	    }

	    /* deallocate the element: remove from ordered list */
	    UMF_mem_free_tail_block (Numeric, E [e]) ;
	    E [e] = 0 ;

	}
	else if (extcdeg == 0)
	{

	    /* -------------------------------------------------------------- */
	    /* this is a Uson - assemble all possible columns */
	    /* -------------------------------------------------------------- */

	    DEBUG6 (("New USON: "ID"\n", e)) ;
	    ASSERT (extrdeg > 0) ;

	    DEBUG6 (("New uson "ID" cols to do "ID"\n", e, ncols_to_assemble)) ;

	    if (ncols_to_assemble > 0)
	    {

		if (nrows == nrowsleft)
		{
		    /* ------------------------------------------------------ */
		    /* no rows have been assembled out of this Uson yet */
		    /* ------------------------------------------------------ */

		    /* compute the compressed column offset vector */
		    /* [ use Wm [0..nrows-1] for offsets */
		    for (i = 0 ; i < nrows ; i++)
		    {
			row = Rows [i] ;
			ASSERT (row >= 0 && row < n_row) ;
			Row_degree [row] -= ncols_to_assemble ;
			Wm [i] = Frpos [row] ;
		    }
		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			if ((col >= 0) && (Fcpos [col] >= 0))
			{
			    Col_degree [col] -= nrowsleft ;
			    Fcol = Fx + Fcpos [col] ;
			    for (i = 0 ; i < nrows ; i++)
			    {
				/* Fcol [Wm [i]] += C [i] ; */
				ASSEMBLE (Fcol [Wm [i]], C [i]) ;
			    }
			    /* flag the column as assembled from Uson */
			    Cols [j] = EMPTY ;
			}
			C += nrows ;
		    }
		    /* ] done using Wm [0..nrows-1] for offsets */
		}
		else
		{
		    /* ------------------------------------------------------ */
		    /* some rows have been assembled out of this Uson */
		    /* ------------------------------------------------------ */

		    /* compute the compressed column offset vector*/
		    /* [ use Woo,Wm [0..nrows-1] for offsets */
		    ngetrows = 0 ;
		    for (i = 0 ; i < nrows ; i++)
		    {
			row = Rows [i] ;
			if (row >= 0)
			{
			    Row_degree [row] -= ncols_to_assemble ;
			    ASSERT (row < n_row && Frpos [row] >= 0) ;
			    Woo [ngetrows] = i ;
			    Wm [ngetrows++] = Frpos [row] ;
			}
		    }
		    ASSERT (ngetrows == nrowsleft) ;

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			if ((col >= 0) && (Fcpos [col] >= 0))
			{
			    Col_degree [col] -= nrowsleft ;
			    Fcol = Fx + Fcpos [col] ;
			    for (i = 0 ; i < nrowsleft ; i++)
			    {
				/* Fcol [Wm [i]] += C [Woo [i]] ; */
				ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ;
			    }
			    /* flag the column as assembled from Uson */
			    Cols [j] = EMPTY ;
			}
			C += nrows ;
		    }
		    /* ] done using Woo,Wm */
		}
		ep->ncolsleft = extrdeg ;
	    }

	}
	else
	{

	    /* -------------------------------------------------------------- */
	    /* this is an Lson - assemble all possible rows */
	    /* -------------------------------------------------------------- */

	    DEBUG6 (("New LSON: "ID"\n", e)) ;
	    ASSERT (extrdeg == 0 && extcdeg > 0) ;

	    DEBUG6 (("New lson "ID" rows to do "ID"\n", e, nrows_to_assemble)) ;

	    if (nrows_to_assemble > 0)
	    {

		/* compute the compressed column offset vector */
		/* [ use Woo,Wm [0..nrows-1] for offsets */
		ngetrows = 0 ;
		for (i = 0 ; i < nrows ; i++)
		{
		    row = Rows [i] ;
		    if ((row >= 0) && (Frpos [row] >= 0))
		    {
			ASSERT (row < n_row) ;
			Row_degree [row] -= ncolsleft ;
			Woo [ngetrows] = i ;
			Wm [ngetrows++] = Frpos [row] ;
			/* flag the row as assembled from the Lson */
			Rows [i] = EMPTY ;
		    }
		}
		ASSERT (nrowsleft - ngetrows == extcdeg) ;
		ASSERT (ngetrows == nrows_to_assemble) ;

		if (ncols == ncolsleft)
		{
		    /* ------------------------------------------------------ */
		    /* no columns assembled out this Lson yet */
		    /* ------------------------------------------------------ */

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			ASSERT (col >= 0 && col < n_col) ;
			Col_degree [col] -= nrows_to_assemble ;
			Fcol = Fx + Fcpos [col] ;
			for (i = 0 ; i < nrows_to_assemble ; i++)
			{
			    /* Fcol [Wm [i]] += C [Woo [i]] ; */
			    ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ;
			}
			C += nrows ;
		    }
		}
		else
		{
		    /* ------------------------------------------------------ */
		    /* some columns have been assembled out of this Lson */
		    /* ------------------------------------------------------ */

		    for (j = 0 ; j < ncols ; j++)
		    {
			col = Cols [j] ;
			ASSERT (col < n_col) ;
			if (col >= 0)
			{
			    Col_degree [col] -= nrows_to_assemble ;
			    Fcol = Fx + Fcpos [col] ;
			    for (i = 0 ; i < nrows_to_assemble ; i++)
			    {
				/* Fcol [Wm [i]] += C [Woo [i]] ; */
				ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ;
			    }
			}
			C += nrows ;
		    }
		}

		/* ] done using Woo,Wm */

		ep->nrowsleft = extcdeg ;
	    }
	}
    }

    /* Note that garbage collection, and build tuples */
    /* both destroy the son list. */

    /* ] son_list now empty */

    /* ---------------------------------------------------------------------- */
    /* If frontal matrix extended, assemble old L/Usons from new rows/cols */
    /* ---------------------------------------------------------------------- */

    /* ---------------------------------------------------------------------- */
    /* SCAN2-row:  assemble rows of old Lsons from the new rows */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG7 (("Current frontal matrix: (prior to scan2-row)\n")) ;
    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif

    if (Work->do_scan2row)
    {

	scan_pivrow = Work->scan_pivrow ;

	for (i2 = Work->fscan_row ; i2 < fnrows || scan_pivrow ; )
	{
	    /* Get a row */
	    if (scan_pivrow)
	    {
		/* pivot row is new to this front.  Scan it */
		row = Work->pivrow ;
		scan_pivrow = FALSE ;
	    }
	    else
	    {
		row = Frows [i2++] ;
	    }
	    ASSERT (row >= 0 && row < n_row) ;

#ifndef NDEBUG
	    DEBUG6 (("SCAN2-row: "ID"\n", row)) ;
	    UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ;
#endif

	    ASSERT (NON_PIVOTAL_ROW (row)) ;
	    tpi = Row_tuples [row] ;
	    if (!tpi) continue ;
	    tp = (Tuple *) (Memory + tpi) ;
	    tp1 = tp ;
	    tp2 = tp ;
	    tpend = tp + Row_tlen [row] ;
	    for ( ; tp < tpend ; tp++)
	    {
		e = tp->e ;
		ASSERT (e > 0 && e <= Work->nel) ;
		if (!E [e]) continue ;	/* element already deallocated */
		f = tp->f ;
		p = Memory + E [e] ;
		ep = (Element *) p ;
		p += UNITS (Element, 1) ;
		Cols = (Int *) p ;
		Rows = Cols + ep->ncols ;
		if (Rows [f] == EMPTY) continue ;    /* row already assembled */
		ASSERT (row == Rows [f] && row >= 0 && row < n_row) ;

		if (ep->rdeg == rdeg0)
		{
		    /* ------------------------------------------------------ */
		    /* this is an old Lson - assemble just one row */
		    /* ------------------------------------------------------ */

		    /* flag the row as assembled from the Lson */
		    Rows [f] = EMPTY ;

		    nrows = ep->nrows ;
		    ncols = ep->ncols ;
		    p += UNITS (Int, ncols + nrows) ;
		    C = ((Entry *) p) + f ;

		    DEBUG6 (("Old LSON: "ID"\n", e)) ;
#ifndef NDEBUG
		    UMF_dump_element (Numeric, Work, e, FALSE) ;
#endif

		    nrowsleft = ep->nrowsleft ;
		    ncolsleft = ep->ncolsleft ;

		    Frow = Fx + Frpos [row] ;
		    DEBUG6 (("LSON found (in scan2-row): "ID"\n", e)) ;

		    Row_degree [row] -= ncolsleft ;

		    if (ncols == ncolsleft)
		    {
			/* -------------------------------------------------- */
			/* no columns assembled out this Lson yet */
			/* -------------------------------------------------- */

			for (j = 0 ; j < ncols ; j++)
			{
			    col = Cols [j] ;
			    ASSERT (col >= 0 && col < n_col) ;
			    Col_degree [col] -- ;
			    /* Frow [Fcpos [col]] += *C ; */
			    ASSEMBLE (Frow [Fcpos [col]], *C) ;
			    C += nrows ;
			}
		    }
		    else
		    {
			/* -------------------------------------------------- */
			/* some columns have been assembled out of this Lson */
			/* -------------------------------------------------- */

			for (j = 0 ; j < ncols ; j++)
			{
			    col = Cols [j] ;
			    if (col >= 0)
			    {
				ASSERT (col < n_col) ;
				Col_degree [col] -- ;
				/* Frow [Fcpos [col]] += *C ; */
				ASSEMBLE (Frow [Fcpos [col]], *C) ;
			    }
			    C += nrows ;
			}
		    }
		    ep->nrowsleft-- ;
		    ASSERT (ep->nrowsleft > 0) ;
		}
		else
		{
		    *tp2++ = *tp ;	/* leave the tuple in the list */
		}
	    }
	    Row_tlen [row] = tp2 - tp1 ;

#ifndef NDEBUG
	    DEBUG7 (("row assembled in scan2-row: "ID"\n", row)) ;
	    if (row != Work->pivrow)
	    {
		UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ;
	    }
	    DEBUG7 (("Current frontal matrix: (scan 1b)\n")) ;
	    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif
	}
    }

    /* ---------------------------------------------------------------------- */
    /* SCAN2-col:  assemble columns of old Usons from the new columns */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG7 (("Current frontal matrix: (prior to scan2-col)\n")) ;
    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif

    if (Work->do_scan2col)
    {

	scan_pivcol = Work->scan_pivcol ;

	for (j2 = Work->fscan_col ; j2 < fncols || scan_pivcol ; )
	{
	    /* Get a column */
	    if (scan_pivcol)
	    {
		/* pivot col is new to this front.  Scan it */
		col = Work->pivcol ;
		scan_pivcol = FALSE ;
	    }
	    else
	    {
		col = Fcols [j2++] ;
	    }
	    ASSERT (col >= 0 && col < n_col) ;

	    DEBUG6 (("SCAN2-col: "ID"\n", col)) ;
#ifndef NDEBUG
	    UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ;
#endif

	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    tpi = Col_tuples [col] ;
	    if (!tpi) continue ;
	    tp = (Tuple *) (Memory + tpi) ;
	    tp1 = tp ;
	    tp2 = tp ;
	    tpend = tp + Col_tlen [col] ;
	    for ( ; tp < tpend ; tp++)
	    {

		e = tp->e ;
		ASSERT (e > 0 && e <= Work->nel) ;
		if (!E [e]) continue ;	/* element already deallocated */
		f = tp->f ;
		p = Memory + E [e] ;
		ep = (Element *) p ;
		p += UNITS (Element, 1) ;
		Cols = (Int *) p ;
		if (Cols [f] == EMPTY)
		{
		    continue ;	/* column already assembled */
		}
		ASSERT (col == Cols [f] && col >= 0 && col < n_col) ;

		if (ep->cdeg == cdeg0)
		{

		    /* ------------------------------------------------------ */
		    /* this is an old Uson - assemble just one column */
		    /* ------------------------------------------------------ */

		    /* flag the column as assembled from the Uson */
		    Cols [f] = EMPTY ;

		    nrows = ep->nrows ;
		    ncols = ep->ncols ;
		    Rows = Cols + ncols ;
		    p += UNITS (Int, ep->ncols + nrows) ;
		    C = ((Entry *) p) + f * nrows ;

		    DEBUG6 (("Old USON: "ID"\n", e)) ;
#ifndef NDEBUG
		    UMF_dump_element (Numeric, Work, e, FALSE) ;
#endif

		    nrowsleft = ep->nrowsleft ;
		    ncolsleft = ep->ncolsleft ;

		    Fcol = Fx + Fcpos [col] ;
		    DEBUG6 (("USON found (in scan2-col): "ID"\n", e)) ;

		    Col_degree [col] -= nrowsleft ;

		    if (nrows == nrowsleft)
		    {
			/* -------------------------------------------------- */
			/* no rows assembled out of this Uson yet */
			/* -------------------------------------------------- */

			for (i = 0 ; i < nrows ; i++)
			{
			    row = Rows [i] ;
			    ASSERT (row >= 0 && row < n_row) ;
			    Row_degree [row]-- ;
			    /* Fcol [Frpos [row]] += C [i] ; */
			    ASSEMBLE (Fcol [Frpos [row]], C [i]) ;
			}
		    }
		    else
		    {
			/* -------------------------------------------------- */
			/* some rows have been assembled out of this Uson */
			/* -------------------------------------------------- */

			for (i = 0 ; i < nrows ; i++)
			{
			    row = Rows [i] ;
			    if (row >= 0)
			    {
				ASSERT (row < n_row) ;
				Row_degree [row]-- ;
				/* Fcol [Frpos [row]] += C [i] ; */
				ASSEMBLE (Fcol [Frpos [row]], C [i]) ;
			    }
			}
		    }
		    ep->ncolsleft-- ;
		    ASSERT (ep->ncolsleft > 0) ;
		}
		else
		{
		    *tp2++ = *tp ;	/* leave the tuple in the list */
		}
	    }
	    Col_tlen [col] = tp2 - tp1 ;

#ifndef NDEBUG
	    DEBUG7 (("Column assembled in scan2-col: "ID"\n", col)) ;
	    if (col != Work->pivcol)
	    {
		UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ;
	    }
	    DEBUG7 (("Current frontal matrix: after scan2-col\n")) ;
	    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif

	}

    }

    /* ---------------------------------------------------------------------- */
    /* done.  the remainder of this routine is used only when in debug mode */
    /* ---------------------------------------------------------------------- */



#ifndef NDEBUG

    /* ---------------------------------------------------------------------- */
    /* when debugging: make sure the assembly did everything that it could */
    /* ---------------------------------------------------------------------- */

    DEBUG3 (("::Assemble done\n")) ;

    scan_pivrow = TRUE ;

    for (i2 = 0 ; i2 < fnrows || scan_pivrow ; )
    {
	/* Get a row */
	if (scan_pivrow)
	{
	    /* pivot row is new to this front.  Scan it */
	    row = Work->pivrow ;
	    scan_pivrow = FALSE ;
	}
	else
	{
	    row = Frows [i2++] ;
	}
	ASSERT (row >= 0 && row < n_row) ;

	DEBUG6 (("DEBUG SCAN 1: "ID"\n", row)) ;
	UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;

	ASSERT (NON_PIVOTAL_ROW (row)) ;
	tpi = Row_tuples [row] ;
	if (!tpi) continue ;
	tp = (Tuple *) (Memory + tpi) ;
	tpend = tp + Row_tlen [row] ;
	for ( ; tp < tpend ; tp++)
	{
	    e = tp->e ;
	    ASSERT (e > 0 && e <= Work->nel) ;
	    if (!E [e]) continue ;	/* element already deallocated */
	    f = tp->f ;
	    p = Memory + E [e] ;
	    ep = (Element *) p ;
	    p += UNITS (Element, 1) ;
	    Rows = ((Int *) p) + ep->ncols ;
	    if (Rows [f] == EMPTY) continue ;	/* row already assembled */
	    ASSERT (row == Rows [f]) ;
	    extrdeg = (ep->rdeg < rdeg0) ? ep->ncolsleft : (ep->rdeg - rdeg0) ;
	    extcdeg = (ep->cdeg < cdeg0) ? ep->nrowsleft : (ep->cdeg - cdeg0) ;
	    DEBUG6 ((
		"e "ID" After assembly ext row deg: "ID" ext col degree "ID"\n",
		e, extrdeg, extcdeg)) ;

	    /* no Lsons in any row */
	    ASSERT (extrdeg > 0) ;

	    /* Uson external row degree is = number of cols left */
	    ASSERT (IMPLIES (extcdeg == 0, extrdeg == ep->ncolsleft)) ;
	}
    }

    /* ---------------------------------------------------------------------- */

    scan_pivcol = TRUE ;

    for (j2 = 0 ; j2 < fncols || scan_pivcol ; )
    {
	/* Get a column */
	if (scan_pivcol)
	{
	    /* pivot col is new to this front.  Scan it */
	    col = Work->pivcol ;
	    scan_pivcol = FALSE ;
	}
	else
	{
	    col = Fcols [j2++] ;
	}
	ASSERT (col >= 0 && col < n_col) ;

	DEBUG6 (("DEBUG SCAN 2: "ID"\n", col)) ;
	UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ;

	ASSERT (NON_PIVOTAL_COL (col)) ;
	tpi = Col_tuples [col] ;
	if (!tpi) continue ;
	tp = (Tuple *) (Memory + tpi) ;
	tpend = tp + Col_tlen [col] ;
	for ( ; tp < tpend ; tp++)
	{
	    e = tp->e ;
	    ASSERT (e > 0 && e <= Work->nel) ;
	    if (!E [e]) continue ;	/* element already deallocated */
	    f = tp->f ;
	    p = Memory + E [e] ;
	    ep = (Element *) p ;
	    p += UNITS (Element, 1) ;
	    Cols = (Int *) p ;
	    if (Cols [f] == EMPTY) continue ;	/* column already assembled */
	    ASSERT (col == Cols [f]) ;
	    extrdeg = (ep->rdeg < rdeg0) ? ep->ncolsleft : (ep->rdeg - rdeg0) ;
	    extcdeg = (ep->cdeg < cdeg0) ? ep->nrowsleft : (ep->cdeg - cdeg0) ;
	    DEBUG6 (("e "ID" After assembly ext col deg: "ID"\n", e, extcdeg)) ;

	    /* no Usons in any column */
	    ASSERT (extcdeg > 0) ;

	    /* Lson external column degree is = number of rows left */
	    ASSERT (IMPLIES (extrdeg == 0, extcdeg == ep->nrowsleft)) ;
	}
    }

#endif /* NDEBUG */

}


```

sparse-matrix/umfpack/umf_dump.h:
```
/* ========================================================================== */
/* === UMF_dump ============================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Debugging definitions. */

#include <assert.h>

#ifndef NDEBUG

GLOBAL void UMF_dump_dense
(
    Entry *Fx,
    Int dim,
    Int m,
    Int n
) ;

GLOBAL void UMF_dump_element
(
    NumericType *Numeric,
    WorkType *Work,
    Int e,
    Int clean
) ;

GLOBAL void UMF_dump_rowcol
(
    Int dump_which,
    NumericType *Numeric,
    WorkType *Work,
    Int dump_index,
    Int check_degree
) ;

GLOBAL void UMF_dump_matrix
(
    NumericType *Numeric,
    WorkType *Work,
    Int check_degree
) ;

GLOBAL void UMF_dump_current_front
(
    NumericType *Numeric,
    WorkType *Work,
    Int check
) ;

GLOBAL void UMF_dump_lu
(
    NumericType *Numeric
) ;

GLOBAL void UMF_dump_memory
(
    NumericType *Numeric
) ;

GLOBAL void UMF_dump_packed_memory
(
    NumericType *Numeric,
    WorkType *Work
) ;

GLOBAL void UMF_dump_col_matrix
(
    const double Ax [ ],
#ifdef COMPLEX
    const double Az [ ],
#endif
    const Int Ai [ ],
    const Int Ap [ ],
    Int n_row,
    Int n_col,
    Int nz
) ;

GLOBAL void UMF_dump_chain
(
    Int frontid,
    Int Front_parent [ ],
    Int Front_npivcol [ ],
    Int Front_nrows [ ],
    Int Front_ncols [ ],
    Int nfr
) ;

GLOBAL void UMF_dump_rowmerge
(
    NumericType *Numeric,
    SymbolicType *Symbolic,
    WorkType *Work
) ;

GLOBAL void UMF_dump_start
(
    void
) ;


#define UMF_DBMAX 50000
GLOBAL extern Int UMF_debug ;
GLOBAL extern Int UMF_nbug ;
GLOBAL extern Int UMF_fbug ;
GLOBAL extern Int UMF_allocfail ;
GLOBAL extern double UMF_gprob ;

#define DEBUGk(k,params) { if (UMF_debug >= (k)) { PRINTF (params) ; } }

#define DEBUG0(params) DEBUGk (0, params)
#define DEBUG1(params) DEBUGk (1, params)
#define DEBUG2(params) DEBUGk (2, params)
#define DEBUG3(params) DEBUGk (3, params)
#define DEBUG4(params) DEBUGk (4, params)
#define DEBUG5(params) DEBUGk (5, params)
#define DEBUG6(params) DEBUGk (6, params)
#define DEBUG7(params) DEBUGk (7, params)
#define DEBUG8(params) DEBUGk (8, params)
#define DEBUG9(params) DEBUGk (9, params)

#define EDEBUGk(k,a) { if (UMF_debug >= (k)) { PRINT_ENTRY (a) ; } }

#define EDEBUG0(a) EDEBUGk (0, a)
#define EDEBUG1(a) EDEBUGk (1, a)
#define EDEBUG2(a) EDEBUGk (2, a)
#define EDEBUG3(a) EDEBUGk (3, a)
#define EDEBUG4(a) EDEBUGk (4, a)
#define EDEBUG5(a) EDEBUGk (5, a)
#define EDEBUG6(a) EDEBUGk (6, a)
#define EDEBUG7(a) EDEBUGk (7, a)
#define EDEBUG8(a) EDEBUGk (8, a)
#define EDEBUG9(a) EDEBUGk (9, a)

#ifdef MATLAB_MEX_FILE
#define ASSERT(expression) (mxAssert ((expression), ""))
#else
#ifdef MATHWORKS
#define ASSERT(expression) (utAssert (expression))
#else
#define ASSERT(expression) (assert (expression))
#endif
#endif /* MATLAB_MEX_FILE */

#else

/* ========================================================================== */
/* === No debugging ========================================================= */
/* ========================================================================== */

/* turn off all debugging macros */

#define DEBUGk(k,params)

#define DEBUG0(params)
#define DEBUG1(params)
#define DEBUG2(params)
#define DEBUG3(params)
#define DEBUG4(params)
#define DEBUG5(params)
#define DEBUG6(params)
#define DEBUG7(params)
#define DEBUG8(params)
#define DEBUG9(params)

#define EDEBUG0(a)
#define EDEBUG1(a)
#define EDEBUG2(a)
#define EDEBUG3(a)
#define EDEBUG4(a)
#define EDEBUG5(a)
#define EDEBUG6(a)
#define EDEBUG7(a)
#define EDEBUG8(a)
#define EDEBUG9(a)

#define ASSERT(expression)

#endif /* NDEBUG */


```

sparse-matrix/umfpack/umf_local_search.c:
```
/* ========================================================================== */
/* === UMF_local_search ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Perform pivot search to find pivot row and pivot column.

    The pivot column is selected from the candidate set (a supercolumn from
    colamd), and then removed from that set.

    Called by kernel.

    Modifies front, but keeps current Frows and Fcols unchanged (new entries
    are appended).  This can be undone if the current front must be written
    out after the pivot is found.  The numerical values of the front are
    unchanged.

    The current frontal matrix might be empty (Fx not allocated, Work->fnrows
    and Work->fncols are both zero).

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

*/

/* ========================================================================== */

#include "umf_internal.h"
#include "umf_row_search.h"
#include "umf_mem_free_tail_block.h"

#define IN 0
#define OUT 1

#define IN_IN 0
#define IN_OUT 1
#define OUT_IN 2
#define OUT_OUT 3

/* ========================================================================== */

GLOBAL Int UMF_local_search
(
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Entry *Fx, *Fl, *Fu, *Fs, *C, *Fd ;
    double relax, relax2, relax3 ;
    Int pos, nrows, *Cols, *Rows, e, f, jmax, status, max_cdeg, fnzeros,
	j, col, i, row, cdeg [2], rdeg [2][2], fnpiv, nothing [2], new_LUsize,
	pivrow [2][2], pivcol [2], *Wp, *Fcpos, *Frpos, new_fnzeros,
	*Wm, *Wio, *Woi, *Woo, *Frows, *Fcols, fnrows, fncols, *E,
	deg, nr_in, nc, src, dest, thiscost, bestcost, nr_out, *Wcol, do_update,
	extra_cols, extra_rows, extra_zeros, relaxed_front, do_extend,
	fnrows_max, fncols_max, tpi, *Col_tuples, *Col_degree, *Col_tlen,
	jj, jcand [2], freebie [2], did_rowmerge ;
    Unit *Memory, *p ;
    Tuple *tp, *tpend, *tp1, *tp2 ;
    Element *ep ;

#ifndef NDEBUG
    Int debug_ok, n_row, n_col, *Row_degree ;
    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro only */
#endif

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

    Memory = Numeric->Memory ;
    E = Work->E ;
    Col_degree = Numeric->Cperm ;

    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;

    Wp = Work->Wp ;
    Wm = Work->Wm ;
    Woi = Work->Woi ;
    Wio = Work->Wio ;
    Woo = Work->Woo ;	/* size at least fncols_max */
    Fx = Work->Fx ;
    Fcpos = Work->Fcpos ;
    Frpos = Work->Frpos ;
    Frows = Work->Frows ;
    Fcols = Work->Fcols ;
    fnrows = Work->fnrows ;
    fncols = Work->fncols ;
    fnrows_max = Work->fnrows_max ;
    fncols_max = Work->fncols_max ;
    fnpiv = Work->fnpiv ;
    nothing [0] = EMPTY ;
    nothing [1] = EMPTY ;
    relax = Numeric->relax ;
    relax2 = Numeric->relax2 ;
    relax3 = Numeric->relax3 ;
    fnzeros = Work->fnzeros ;
    new_fnzeros = fnzeros ;
    jj = EMPTY ;

    /* The pivot column degree cannot exceed max_cdeg */
    max_cdeg = fnrows_max - fnpiv ;

#ifndef NDEBUG
    n_row = Work->n_row ;
    n_col = Work->n_col ;
    DEBUG2 (("LOCAL SEARCH:  current frontal matrix: # candidates: "ID"\n",
	Work->ncand)) ;
    UMF_dump_current_front (Numeric, Work, TRUE) ;
    if (UMF_debug > 0 || MAX (n_row, n_col) < 1000)
    {
	for (i = 0 ; i < MAX (n_row, n_col) ; i++)
	{
	    ASSERT (Wp [i] < 0) ;
	    ASSERT (Wp [i] > Work->Wpflag) ;
	}
    }
    ASSERT (Work->Wpflag < EMPTY) ;
    DEBUG2 (("candidates: ")) ;
    for (j = 0 ; j < Work->ncand ; j++) DEBUG2 ((ID" ", Work->Candidates [j]));
    DEBUG2 (("\n")) ;
#endif

    /* ====================================================================== */
    /* === PIVOT SEARCH ===================================================== */
    /* ====================================================================== */

    /* initialize */

    pivcol [IN] = EMPTY ;
    pivcol [OUT] = EMPTY ;

    /* Int_MAX is defined in umfpack.h */
    cdeg [IN] = Int_MAX ;
    cdeg [OUT] = Int_MAX ;

    pivrow [IN][IN] = EMPTY ;
    pivrow [IN][OUT] = EMPTY ;
    pivrow [OUT][IN] = EMPTY ;
    pivrow [OUT][OUT] = EMPTY ;

    rdeg [IN][IN] = Int_MAX ;
    rdeg [IN][OUT] = Int_MAX ;
    rdeg [OUT][IN] = Int_MAX ;
    rdeg [OUT][OUT] = Int_MAX ;

    freebie [IN] = FALSE ;
    freebie [OUT] = FALSE ;

    Work->pivot_case = EMPTY ;
    bestcost = EMPTY ;

    nr_out = EMPTY ;
    nr_in = EMPTY ;

    jcand [IN] = EMPTY ;
    jcand [OUT] = EMPTY ;

    /* ---------------------------------------------------------------------- */
    /* find shortest column in the front, and shortest column not in the */
    /* front, from the candidate pivot column set */
    /* ---------------------------------------------------------------------- */

    /* If there are too many candidates, then only look at the first */
    /* MAX_CANDIDATES of them.   Otherwise, if there are O(n) candidates, */
    /* this code could take O(n^2) time. */

    jmax = MIN (MAX_CANDIDATES, Work->ncand) ;

#ifndef NDEBUG
    DEBUG2 (("Max candidates "ID", Work->ncand "ID" jmax "ID"\n",
	MAX_CANDIDATES, Work->ncand, jmax)) ;
#endif

    /* get the first candidate */

    col = Work->Candidates [0] ;
    DEBUG3 (("Pivot column candidate: "ID" j = "ID"\n", col, j)) ;
    ASSERT (col >= 0 && col < n_col) ;
    deg = Col_degree [col] ;

#ifndef NDEBUG
    DEBUG3 (("Pivot column candidate: "ID" cost: "ID"  Fcpos[col] "ID"\n",
	col, deg, Fcpos [col])) ;
    UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ;
#endif

    if (Fcpos [col] >= 0)
    {
	/* best column in front, so far */
	pivcol [IN] = col ;
	cdeg [IN] = deg ;
	jcand [IN] = 0 ;
    }
    else
    {
	/* best column not in front, so far */
	pivcol [OUT] = col ;
	cdeg [OUT] = deg ;
	jcand [OUT] = 0 ;
    }

    /* look at the rest of the candidates */

    for (j = 1 ; j < jmax ; j++)
    {
	col = Work->Candidates [j] ;
	DEBUG3 (("Pivot column candidate: "ID" j = "ID"\n", col, j)) ;
	ASSERT (col >= 0 && col < n_col) ;
	deg = Col_degree [col] ;
#ifndef NDEBUG
	DEBUG3 (("Pivot column candidate: "ID" cost: "ID"  Fcpos[col] "ID"\n",
		col, deg, Fcpos [col])) ;
	UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ;
#endif
	if (Fcpos [col] >= 0)
	{
#ifndef NDEBUG
	    Int fs ;
	    fs = Fcpos [col] / fnrows_max ;
	    ASSERT (fs >= 0 && fs < fncols) ;
#endif
	    if (deg < cdeg [IN] || (deg == cdeg [IN] && col < pivcol [IN]))
	    {
		/* best column in front, so far */
		pivcol [IN] = col ;
		cdeg [IN] = deg ;
		jcand [IN] = j ;
	    }
	}
	else
	{
	    if (deg < cdeg [OUT] || (deg == cdeg [OUT] && col < pivcol [OUT]))
	    {
		/* best column not in front, so far */
		pivcol [OUT] = col ;
		cdeg [OUT] = deg ;
		jcand [OUT] = j ;
	    }
	}
    }

    DEBUG2 (("Pivcol in "ID" out "ID"\n", pivcol [IN], pivcol [OUT])) ;
    ASSERT ((pivcol [IN] >= 0 && pivcol [IN] < n_col)
	|| (pivcol [OUT] >= 0 && pivcol [OUT] < n_col)) ;

    cdeg [IN] = EMPTY ;
    cdeg [OUT] = EMPTY ;

    /* ---------------------------------------------------------------------- */
    /* construct candidate column in front, and search for pivot rows */
    /* ---------------------------------------------------------------------- */

    if (pivcol [IN] != EMPTY)
    {

#ifndef NDEBUG
	DEBUG2 (("col[IN] column "ID" in front at position = "ID"\n",
		pivcol [IN], Fcpos [pivcol [IN]])) ;
	UMF_dump_rowcol (1, Numeric, Work, pivcol [IN], TRUE) ;
#endif

	/* the only way we can have a pivcol[IN] is if the front is not empty */
	ASSERT (fnrows > 0 && fncols > 0) ;

	Fs = Fx + Fcpos [pivcol [IN]] ;

	/* ------------------------------------------------------------------ */
	/* update column in front (permanent) */
	/* ------------------------------------------------------------------ */

	DEBUG6 (("Update pivot column:\n")) ;
	Fl = Fx + (fncols_max - fnpiv) * fnrows_max ;
	Fu = Fs + (fnrows_max - fnpiv) ;

#ifdef USE_NO_BLAS

	/* no BLAS available - use plain C code instead */
	for (j = 0 ; j < fnpiv ; j++)
	{
	    Entry Fuj ;
	    Fuj = Fu [j] ;
	    if (IS_NONZERO (Fuj))
	    {
		for (i = 0 ; i < fnrows ; i++)
		{
		    /* Fs [i] -= Fuj * Fl [i+j*fnrows_max] ; */
		    MULT_SUB (Fs [i], Fuj, Fl [i+j*fnrows_max]) ;
		}
	    }
	}

#else

	BLAS_GEMV_COL (fnrows, fnpiv, Fl, Fu, Fs, fnrows_max) ;

#endif /* USE_NO_BLAS */

	/* zero out the column of U, in case this doesn't become pivot column */
	for (j = 0 ; j < fnpiv ; j++)
	{
	    CLEAR (Fu [j]) ;
	}

	/* ------------------------------------------------------------------ */

#ifndef NDEBUG
	DEBUG6 (("Fs after update: fnrows="ID"\n", fnrows)) ;
	DEBUG6 ((" Work->fnpiv="ID" \n", fnpiv)) ;
	for (i = 0 ; i < fnrows ; i++)
	{
	    DEBUG6 ((ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ;
	    EDEBUG6 (Fs [i]) ;
	    DEBUG6 (("\n")) ;
	}
#endif

	/* ------------------------------------------------------------------ */
	/* construct the candidate column in the front */
	/* ------------------------------------------------------------------ */

	cdeg [IN] = fnrows ;

#ifndef NDEBUG
	/* check Frpos */
	DEBUG5 (("COL ASSEMBLE: cdeg "ID"\nREDUCE COL in "ID" max_cdeg "ID"\n",
		cdeg [IN], pivcol [IN], max_cdeg)) ;
	for (i = 0 ; i < cdeg [IN] ; i++)
	{
	    row = Frows [i] ;
	    ASSERT (row >= 0 && row < n_row) ;
	    ASSERT (Frpos [row] == i) ;
	}
	if (UMF_debug > 0 || n_row < 1000)
	{
	    Int cnt = cdeg [IN] ;
	    for (row = 0 ; row < n_row ; row++)
	    {
		if (Frpos [row] == EMPTY) cnt++ ;
	    }
	    ASSERT (cnt == n_row) ;
	}
#endif

	ASSERT (pivcol [IN] >= 0 && pivcol [IN] < n_col) ;
	ASSERT (NON_PIVOTAL_COL (pivcol [IN])) ;

	tpi = Col_tuples [pivcol [IN]] ;
	if (tpi)
	{
	    tp = (Tuple *) (Memory + tpi) ;
	    tp1 = tp ;
	    tp2 = tp ;
	    tpend = tp + Col_tlen [pivcol [IN]] ;
	    for ( ; tp < tpend ; tp++)
	    {
		e = tp->e ;
		ASSERT (e > 0 && e <= Work->nel) ;
		if (!E [e]) continue ;	/* element already deallocated */
		f = tp->f ;
		p = Memory + E [e] ;
		ep = (Element *) p ;
		p += UNITS (Element, 1) ;
		Cols = (Int *) p ;
		if (Cols [f] == EMPTY) continue ; /* column already assembled */
		ASSERT (pivcol [IN] == Cols [f]) ;

		Rows = Cols + ep->ncols ;
		nrows = ep->nrows ;
		p += UNITS (Int, ep->ncols + nrows) ;
		C = ((Entry *) p) + f * nrows ;

		for (i = 0 ; i < nrows ; i++)
		{
		    row = Rows [i] ;
		    if (row >= 0) /* skip this if already gone from element */
		    {
			ASSERT (row < n_row) ;
			pos = Frpos [row] ;
			if (pos < 0)
			{
			    if (cdeg [IN] >= max_cdeg)
			    {
				return (UMFPACK_ERROR_different_pattern) ;
			    }
			    /* new entry in the pattern - save Frpos */
			    Frpos [row] = cdeg [IN] ;
			    Frows [cdeg [IN]] = row ;

			    /* this will be discarded */
			    Fs [cdeg [IN]++] = C [i] ;
			}
			else
			{
			    /* entry already in pattern - sum the values */
			    /* Fs [pos] += C [i] ; */
			    ASSEMBLE (Fs [pos], C [i]) ;
			    if (pos < fnrows)
			    {
				/* just did a permanent assembly of a single */
				/* entry into the current front */
				CLEAR (C [i]) ;
			    }
			}
		    }
		}

		*tp2++ = *tp ;	/* leave the tuple in the list */

	    }
	    Col_tlen [pivcol [IN]] = tp2 - tp1 ;
	}

	/* ------------------------------------------------------------------ */

#ifndef NDEBUG
	DEBUG4 (("Reduced column: cdeg in  "ID"\n", cdeg [IN])) ;
	for (i = 0 ; i < cdeg [IN] ; i++)
	{
	    DEBUG6 ((" "ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ;
	    EDEBUG6 (Fs [i]) ;
	    DEBUG6 (("\n")) ;
	    ASSERT (i == Frpos [Frows [i]]) ;
	}
	ASSERT (cdeg [IN] + fnpiv <= fnrows_max) ;
#endif

	/* ------------------------------------------------------------------ */
	/* cdeg [IN] is now the exact degree of this column */
	/* ------------------------------------------------------------------ */

	nr_in = cdeg [IN] - fnrows ;

	/* since there are no 0-by-x fronts, if there is a pivcol [IN] the */
	/* front must have at least one row. */
	ASSERT (cdeg [IN] > 0) ;

	/* new degree of pivcol [IN], excluding current front is nr_in */
	/* column expands by nr_in rows */

	/* ------------------------------------------------------------------ */
	/* search for two candidate pivot rows */
	/* ------------------------------------------------------------------ */

	/* for the IN_IN pivot row (if any), */
	/* extend the pattern in place, using Fcols */
	status = UMF_row_search (Numeric, Work, Symbolic, cdeg [IN], Frows,
	    pivrow [IN], rdeg [IN], Fcols, Wio, nothing, Fs, pivcol [IN],
	    freebie) ;
	ASSERT (!freebie [IN] && !freebie [OUT]) ;

	/* ------------------------------------------------------------------ */
	/* fatal error if matrix pattern has changed since symbolic analysis */
	/* ------------------------------------------------------------------ */

	if (status == UMFPACK_ERROR_different_pattern)
	{
	    return (UMFPACK_ERROR_different_pattern) ;
	}

	/* ------------------------------------------------------------------ */
	/* we now must have a structural pivot */
	/* ------------------------------------------------------------------ */

	/* Since the pivcol[IN] exists, there must be at least one row in the */
	/* current frontal matrix, and so we must have found a structural */
	/* pivot.  The numerical value might be zero, of course. */

	ASSERT (status != UMFPACK_WARNING_singular_matrix) ;

	/* ------------------------------------------------------------------ */
	/* evaluate IN_IN option */
	/* ------------------------------------------------------------------ */

	if (pivrow [IN][IN] != EMPTY)
	{
	    /* the current front would become an (implicit) LUson */
	    /* cost is how much the current front would expand */

	    /* pivrow[IN][IN] candidates are not found via row merge search */

	    ASSERT (cdeg [IN] > 0) ;
	    nc = rdeg [IN][IN] - fncols ;

	    thiscost =
		/* each column in front (except pivot column) grows by nr_in: */
		(nr_in * (fncols - 1)) +
		/* new columns not in old front: */
		(nc * (cdeg [IN] - 1)) ;

	    /* no extra cost to relaxed amalgamation */

	    ASSERT (fnrows + nr_in == cdeg [IN]) ;
	    ASSERT (fncols + nc == rdeg [IN][IN]) ;

	    /* relaxed_front = ((fncols-1) + nc) * ((fnrows-1) + nr_in) ; */
	    do_extend = TRUE ;

	    DEBUG2 (("Evaluating option IN-IN:\n")) ;
	    DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n",
	    	Work->fnzeros, fnpiv, nr_in, nc)) ;
	    DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;

	    /* determine if BLAS-3 update should be applied before extending. */
	    /* update if too many zero entries accumulate in the LU block */
	    fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ;

	    DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;

	    new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ;

	    DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
	    DEBUG2 (("relax2 %g\n", relax2)) ;

	    /* relax2 parameter uses a double relop, but ignore NaN case: */
	    do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax2 ;

	    DEBUG2 (("do_update "ID"\n", do_update))

	    DEBUG2 (("option IN  IN : nr "ID" nc "ID" cost "ID"(0) relax "ID
		"\n", nr_in, nc, thiscost, do_extend)) ;

	    /* this is the best option seen so far */
	    Work->pivot_case = IN_IN ;
	    bestcost = thiscost ;

	    /* do the amalgamation and extend the front */
	    Work->do_extend = do_extend ;
	    Work->do_update = do_update ;
	    new_fnzeros = fnzeros ;

	}

	/* ------------------------------------------------------------------ */
	/* evaluate IN_OUT option */
	/* ------------------------------------------------------------------ */

	if (pivrow [IN][OUT] != EMPTY)
	{
	    /* the current front would become a Uson of the new front */

	    ASSERT (cdeg [IN] > 0) ;

	    /* must be at least one row outside the front */
	    /* (the pivrow [IN][OUT] itself) */
	    ASSERT (nr_in >= 1) ;

	    /* count columns not in current front */
	    nc = 0 ;
#ifndef NDEBUG
	    debug_ok = FALSE ;
#endif
	    for (i = 0 ; i < rdeg [IN][OUT] ; i++)
	    {
		col = Wio [i] ;
		DEBUG6 (("counting col "ID" Fcpos[] = "ID"\n", col,
		    Fcpos [col])) ;
		ASSERT (col >= 0 && col < n_col) ;
		ASSERT (NON_PIVOTAL_COL (col)) ;
		if (Fcpos [col] < 0) nc++ ;
#ifndef NDEBUG
		/* we must see the pivot column somewhere */
		if (col == pivcol [IN])
		{
		    ASSERT (Fcpos [col] >= 0) ;
		    debug_ok = TRUE ;
		}
#endif
	    }
	    ASSERT (debug_ok) ;

	    thiscost =
		/* each row in front grows by nc: */
		(nc * fnrows) +
		/* new rows not affected by front: */
		((nr_in-1) * (rdeg [IN][OUT]-1)) ;

	    /* check the cost of relaxed IN_OUT amalgamation */

	    extra_cols = ((fncols-1) + nc ) - (rdeg [IN][OUT] - 1) ;
	    ASSERT (extra_cols >= 0) ;
	    ASSERT (fncols + nc == extra_cols + rdeg [IN][OUT]) ;
	    extra_zeros = (nr_in-1) * extra_cols ;	/* symbolic fill-in */

	    ASSERT (fnrows + nr_in == cdeg [IN]) ;
	    ASSERT (fncols + nc == rdeg [IN][OUT] + extra_cols) ;

	    /* size of relaxed front: */
	    relaxed_front = ((fncols-1) + nc) * (fnrows + (nr_in-1)) ;

	    /* do relaxed amalgamation if the extra zeros are no more */
	    /* than a fraction (default 0.25) of the relaxed front */
	    /* if relax = 0: no extra zeros allowed */
	    /* if relax = +inf: always amalgamate */

	    /* relax parameter uses a double relop, but ignore NaN case: */
	    do_extend = ((double) extra_zeros)
		< (relax * (double) relaxed_front) ;

	    if (do_extend)
	    {
		/* count the cost of relaxed amalgamation */
		thiscost += extra_zeros ;

	        DEBUG2 (("Evaluating option IN-OUT:\n")) ;
	        DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n",
	    	Work->fnzeros, fnpiv, nr_in, nc)) ;
	        DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;

	        /* determine if BLAS-3 update to be applied before extending. */
	        /* update if too many zero entries accumulate in the LU block */
	        fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ;

	        DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;

	        new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ;

	        DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
	        DEBUG2 (("relax3 %g\n", relax3)) ;

		/* relax3 parameter uses a double relop, but ignore NaN case: */
	        do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax3 ;

	        DEBUG2 (("do_update "ID"\n", do_update))
	    }
	    else
	    {
	    	do_update = TRUE ;
		fnzeros = 0 ;
	        DEBUG2 (("IN-OUT do_update forced true: "ID"\n", do_update))
	    }

	    DEBUG2 (("option IN  OUT: nr "ID" nc "ID" cost "ID"("ID") relax "ID
		"\n", nr_in, nc, thiscost, extra_zeros, do_extend)) ;

	    if (bestcost == EMPTY || thiscost < bestcost)
	    {
		/* this is the best option seen so far */
		Work->pivot_case = IN_OUT ;
		bestcost = thiscost ;
		Work->do_extend = do_extend ;
		Work->do_update = do_update ;
		new_fnzeros = fnzeros ;
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* construct candidate column not in front, and search for pivot rows */
    /* ---------------------------------------------------------------------- */

    if (bestcost != 0 && pivcol [OUT] != EMPTY)
    {

#ifndef NDEBUG
	DEBUG2 (("out_col column "ID" NOT in front at position = "ID"\n",
		pivcol [OUT], Fcpos [pivcol [OUT]])) ;
	UMF_dump_rowcol (1, Numeric, Work, pivcol [OUT], TRUE) ;
#endif

	/* Find an empty column in the current frontal matrix to use */
	/* as workspace.  There must be one; otherwise, there couldn't be a */
	/* candidate pivot column outside the current front. */

	/* Fd: destination of final pivot column, currently unoccupied */
	/* Use Fd as temporary workspace to construct the pivcol [OUT] */
	Fd = Fx + (fncols_max - fnpiv - 1) * fnrows_max ;

	ASSERT (fncols + fnpiv < fncols_max) ;

	/* ------------------------------------------------------------------ */
	/* construct the candidate column (currently not in the front) */
	/* ------------------------------------------------------------------ */

	/* Construct the column in Fd, Wm, using Wp for the positions: */
	/* Wm [0..cdeg [OUT]-1]	list of row indices in the column */
	/* Fd [0..cdeg [OUT]-1]	list of corresponding numerical values */
	/* Wp [0..n-1] starts as all negative, and ends that way too. */

	cdeg [OUT] = 0 ;

#ifndef NDEBUG
	/* check Wp */
	DEBUG5 (("COL ASSEMBLE: cdeg 0\nREDUCE COL out "ID"\n", pivcol [OUT])) ;
	if (UMF_debug > 0 || MAX (n_row, n_col) < 1000)
	{
	    for (i = 0 ; i < MAX (n_row, n_col) ; i++)
	    {
		ASSERT (Wp [i] < 0) ;
		ASSERT (Wp [i] > Work->Wpflag) ;
	    }
	}
	DEBUG5 (("max_cdeg: "ID"\n", max_cdeg)) ;
#endif

	ASSERT (pivcol [OUT] >= 0 && pivcol [OUT] < n_col) ;
	ASSERT (NON_PIVOTAL_COL (pivcol [OUT])) ;

	tpi = Col_tuples [pivcol [OUT]] ;
	if (tpi)
	{
	    tp = (Tuple *) (Memory + tpi) ;
	    tp1 = tp ;
	    tp2 = tp ;
	    tpend = tp + Col_tlen [pivcol [OUT]] ;
	    for ( ; tp < tpend ; tp++)
	    {
		e = tp->e ;
		ASSERT (e > 0 && e <= Work->nel) ;
		if (!E [e]) continue ;	/* element already deallocated */
		f = tp->f ;
		p = Memory + E [e] ;
		ep = (Element *) p ;
		p += UNITS (Element, 1) ;
		Cols = (Int *) p ;
		if (Cols [f] == EMPTY) continue ; /* column already assembled */
		ASSERT (pivcol [OUT] == Cols [f]) ;

		Rows = Cols + ep->ncols ;
		nrows = ep->nrows ;
		p += UNITS (Int, ep->ncols + nrows) ;
		C = ((Entry *) p) + f * nrows ;

		for (i = 0 ; i < nrows ; i++)
		{
		    row = Rows [i] ;
		    if (row >= 0) /* skip this if already gone from element */
		    {
			ASSERT (row < n_row) ;
			pos = Wp [row] ;
			if (pos < 0)
			{
			    if (cdeg [OUT] >= max_cdeg)
			    {
				return (UMFPACK_ERROR_different_pattern) ;
			    }
			    /* new entry in the pattern - save Wp */
			    Wp [row] = cdeg [OUT] ;
			    Wm [cdeg [OUT]] = row ;
			    Fd [cdeg [OUT]++] = C [i] ;
			}
			else
			{
			    /* entry already in pattern - sum the values */
			    /* Fd [pos] += C [i] ; */
			    ASSEMBLE (Fd [pos], C [i]) ;
			}
		    }
		}

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

	/* ------------------------------------------------------------------ */

#ifndef NDEBUG
	DEBUG4 (("Reduced column: cdeg out "ID"\n", cdeg [OUT])) ;
	for (i = 0 ; i < cdeg [OUT] ; i++)
	{
	    DEBUG6 ((" "ID" "ID" "ID, i, Wm [i], Wp [Wm [i]])) ;
	    EDEBUG6 (Fd [i]) ;
	    DEBUG6 (("\n")) ;
	    ASSERT (i == Wp [Wm [i]]) ;
	}
#endif

	/* ------------------------------------------------------------------ */
	/* Clear Wp */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < cdeg [OUT] ; i++)
	{
	    Wp [Wm [i]] = EMPTY ;	/* clear Wp */
	}

	/* ------------------------------------------------------------------ */
	/* new degree of pivcol [OUT] is cdeg [OUT] */
	/* ------------------------------------------------------------------ */

	/* search for two candidate pivot rows */
	status = UMF_row_search (Numeric, Work, Symbolic, cdeg [OUT], Wm,
	    pivrow [OUT], rdeg [OUT], Woi, Woo, pivrow [IN], Fd, pivcol [OUT],
	    freebie) ;

	/* ------------------------------------------------------------------ */
	/* fatal error if matrix pattern has changed since symbolic analysis */
	/* ------------------------------------------------------------------ */

	if (status == UMFPACK_ERROR_different_pattern)
	{
	    return (UMFPACK_ERROR_different_pattern) ;
	}

	/* ------------------------------------------------------------------ */
	/* check for rectangular, singular matrix */
	/* ------------------------------------------------------------------ */

	if (status == UMFPACK_WARNING_singular_matrix)
	{
	    /* Pivot column is empty, and row-merge set is empty too. */
	    /* The matrix is structurally singular.  */

	    DEBUG0 (("Warning: pivcol [OUT]: "ID" discard\n", pivcol [OUT])) ;

	    /* remove the failed pivcol [OUT] from candidate set */
	    jj = jcand [OUT] ;
	    ASSERT (jj >= 0 && jj < jmax) ;
	    ASSERT (pivcol [OUT] == Work->Candidates [jj]) ;
	    if (Work->ncand > MAX_CANDIDATES)
	    {
		Work->Candidates [jj] = Work->nextcand++ ;
	    }
	    else
	    {
		col = Work->Candidates [Work->ncand - 1] ;
		Work->Candidates [jj] = col ;
		Work->Candidates [Work->ncand - 1] = 0 ;
		if (col == pivcol [IN])
		{
		    ASSERT (jcand [IN] == Work->ncand-1) ;
		    jcand [IN] = jj ;
		}
	    }
	    Work->ncand-- ;

	    /* delete all of the tuples, and all contributions to this column */
	    DEBUG1 (("Prune tuples of dead outcol: "ID"\n", pivcol [OUT])) ;
	    Col_tlen [pivcol [OUT]] = 0 ;
	    UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol [OUT]]) ;
	    Col_tuples [pivcol [OUT]] = 0 ;

	    if (pivcol [IN] == EMPTY)
	    {
		/* no pivot found at all */
		return (UMFPACK_WARNING_singular_matrix) ;
	    }
	}

	/* ------------------------------------------------------------------ */

	/* Fd [0 .. cdeg[OUT]-1], the workspace column in the frontal matrix, */
	/* no longer needed */

	if (freebie [IN])
	{
	    /* the "in" row is the same as the "in" row for the "in" column */
	    Woi = Fcols ;
	    rdeg [OUT][IN] = rdeg [IN][IN] ;
	    DEBUG4 (("Freebie in, row "ID"\n", pivrow [IN][IN])) ;
	}

	if (freebie [OUT])
	{
	    /* the "out" row is the same as the "out" row for the "in" column */
	    Woo = Wio ;
	    rdeg [OUT][OUT] = rdeg [IN][OUT] ;
	    DEBUG4 (("Freebie out, row "ID"\n", pivrow [IN][OUT])) ;
	}

	/* ------------------------------------------------------------------ */
	/* evaluate OUT_IN option */
	/* ------------------------------------------------------------------ */

	if (pivrow [OUT][IN] != EMPTY)
	{
	    /* the current front would become an Lson of the new front */

	    did_rowmerge = (cdeg [OUT] == 0) ;
	    if (did_rowmerge)
	    {
		/* pivrow [OUT][IN] was found via row merge search */
		/* it is not (yet) in the pivot column pattern (add it now) */
		if (cdeg [OUT] >= max_cdeg)
		{
		    return (UMFPACK_ERROR_different_pattern) ;
		}
		/* new entry in the pattern */
		Wm [0] = pivrow [OUT][IN] ;
		cdeg [OUT] = 1 ;
		ASSERT (nr_out == EMPTY) ;
	    }

	    nc = rdeg [OUT][IN] - fncols ;
	    ASSERT (nc >= 1) ;

	    /* count rows not in current front */
	    nr_out = 0 ;
#ifndef NDEBUG
	    debug_ok = FALSE ;
#endif
	    for (i = 0 ; i < cdeg [OUT] ; i++)
	    {
		row = Wm [i] ;
		ASSERT (row >= 0 && row < n_row) ;
		ASSERT (NON_PIVOTAL_ROW (row)) ;
		if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ;
#ifndef NDEBUG
		/* we must see the pivot row somewhere */
		if (row == pivrow [OUT][IN])
		{
		    ASSERT (Frpos [row] >= 0) ;
		    debug_ok = TRUE ;
		}
#endif
	    }
	    ASSERT (debug_ok) ;

	    thiscost =
		/* each column in front grows by nr_out: */
		(nr_out * fncols) +
		/* new cols not affected by front: */
		((nc-1) * (cdeg [OUT]-1)) ;

	    /* check the cost of relaxed OUT_IN amalgamation */

	    extra_rows = ((fnrows-1) + nr_out) - (cdeg [OUT] - 1) ;
	    ASSERT (extra_rows >= 0) ;
	    ASSERT (fnrows + nr_out == extra_rows + cdeg [OUT]) ;
	    extra_zeros = (nc-1) * extra_rows ;	/* symbolic fill-in */

	    ASSERT (fnrows + nr_out == cdeg [OUT] + extra_rows) ;
	    ASSERT (fncols + nc == rdeg [OUT][IN]) ;

	    /* size of relaxed front: */
	    relaxed_front = (fncols + (nc-1)) * ((fnrows-1) + nr_out) ;

	    /* do relaxed amalgamation if the extra zeros are no more */
	    /* than a fraction (default 0.25) of the relaxed front */
	    /* if relax = 0: no extra zeros allowed */
	    /* if relax = +inf: always amalgamate */
	    if (did_rowmerge)
	    {
		do_extend = FALSE ;
	    }
	    else
	    {
		/* relax parameter uses a double relop, but ignore NaN case: */
		do_extend = ((double) extra_zeros)
		< (relax * (double) relaxed_front) ;
	    }

	    if (do_extend)
	    {
		/* count the cost of relaxed amalgamation */
		thiscost += extra_zeros ;

	        /* determine if BLAS-3 update to be applied before extending. */
	        /* update if too many zero entries accumulate in the LU block */

	        DEBUG2 (("Evaluating option OUT-IN:\n")) ;
	        DEBUG2 ((" Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n",
	    	Work->fnzeros, fnpiv, nr_out, nc)) ;
	        DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;

	        fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ;

	        DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;

	        new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ;

	        DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
	        DEBUG2 (("relax3 %g\n", relax3)) ;

		/* relax3 parameter uses a double relop, but ignore NaN case: */
	        do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax3 ;

	        DEBUG2 (("do_update "ID"\n", do_update))

	    }
	    else
	    {
	    	do_update = TRUE ;
		fnzeros = 0 ;
	        DEBUG2 (("OUT-IN do_update forced true: "ID"\n", do_update))
	    }

	    DEBUG2 (("option OUT IN : nr "ID" nc "ID" cost "ID"("ID") relax "ID
		"\n", nr_out, nc, thiscost, extra_zeros, do_extend)) ;

	    if (bestcost == EMPTY || thiscost < bestcost)
	    {
		/* this is the best option seen so far */
		Work->pivot_case = OUT_IN ;
		bestcost = thiscost ;
		Work->do_extend = do_extend ;
		Work->do_update = do_update ;
		new_fnzeros = fnzeros ;
	    }
	}

	/* ------------------------------------------------------------------ */
	/* evaluate OUT_OUT option */
	/* ------------------------------------------------------------------ */

	if (pivrow [OUT][OUT] != EMPTY)
	{

	    did_rowmerge = (cdeg [OUT] == 0) ;
	    if (did_rowmerge)
	    {
		/* pivrow [OUT][OUT] was found via row merge search */
		/* it is not (yet) in the pivot column pattern (add it now) */
		if (cdeg [OUT] >= max_cdeg)
		{
		    return (UMFPACK_ERROR_different_pattern) ;
		}
		/* new entry in the pattern */
		Wm [0] = pivrow [OUT][OUT] ;
		cdeg [OUT] = 1 ;
		ASSERT (nr_out == EMPTY) ;
		nr_out = 1 ;
	    }

	    /* count rows not in current front */
	    if (nr_out == EMPTY)
	    {
		nr_out = 0 ;
#ifndef NDEBUG
		debug_ok = FALSE ;
#endif
		for (i = 0 ; i < cdeg [OUT] ; i++)
		{
		    row = Wm [i] ;
		    ASSERT (row >= 0 && row < n_row) ;
		    ASSERT (NON_PIVOTAL_ROW (row)) ;
		    if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ;
#ifndef NDEBUG
		    /* we must see the pivot row somewhere */
		    if (row == pivrow [OUT][OUT])
		    {
			ASSERT (Frpos [row] < 0 || Frpos [row] >= fnrows) ;
			debug_ok = TRUE ;
		    }
#endif
		}
		ASSERT (debug_ok) ;
	    }

	    /* count columns not in current front */
	    nc = 0 ;
#ifndef NDEBUG
	    debug_ok = FALSE ;
#endif
	    for (i = 0 ; i < rdeg [OUT][OUT] ; i++)
	    {
		col = Woo [i] ;
		ASSERT (col >= 0 && col < n_col) ;
		ASSERT (NON_PIVOTAL_COL (col)) ;
		if (Fcpos [col] < 0) nc++ ;
#ifndef NDEBUG
		/* we must see the pivot column somewhere */
		if (col == pivcol [OUT])
		{
		    ASSERT (Fcpos [col] < 0) ;
		    debug_ok = TRUE ;
		}
#endif
	    }
	    ASSERT (debug_ok) ;

	    extra_cols = (fncols + (nc-1)) - (rdeg [OUT][OUT] - 1) ;
	    extra_rows = (fnrows + (nr_out-1)) - (cdeg [OUT] - 1) ;
	    ASSERT (extra_rows >= 0) ;
	    ASSERT (extra_cols >= 0) ;
	    extra_zeros = ((nc-1) * extra_rows) + ((nr_out-1) * extra_cols) ;

	    ASSERT (fnrows + nr_out == cdeg [OUT] + extra_rows) ;
	    ASSERT (fncols + nc == rdeg [OUT][OUT] + extra_cols) ;

	    thiscost =
		/* new columns: */
		((nc-1) * (cdeg [OUT]-1)) +
		/* old columns in front grow by nr_out-1: */
		((nr_out-1) * (fncols - extra_cols)) ;

	    /* size of relaxed front: */
	    relaxed_front = (fncols + (nc-1)) * (fnrows + (nr_out-1)) ;

	    /* do relaxed amalgamation if the extra zeros are no more */
	    /* than a fraction (default 0.25) of the relaxed front */
	    /* if relax = 0: no extra zeros allowed */
	    /* if relax = +inf: always amalgamate */
	    if (did_rowmerge)
	    {
	    	do_extend = FALSE ;
	    }
	    else
	    {
		/* relax parameter uses a double relop, but ignore NaN case: */
		do_extend = ((double) extra_zeros)
		< (relax * (double) relaxed_front) ;
	    }

	    if (do_extend)
	    {
		/* count the cost of relaxed amalgamation */
		thiscost += extra_zeros ;

	        DEBUG2 (("Evaluating option OUT-OUT:\n")) ;
	        DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n",
	    	Work->fnzeros, fnpiv, nr_out, nc)) ;
	        DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;

	        /* determine if BLAS-3 update to be applied before extending. */
	        /* update if too many zero entries accumulate in the LU block */
	        fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ;

	        DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;

	        new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ;

	        DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
	        DEBUG2 (("relax3 %g\n", relax3)) ;

		/* relax3 parameter uses a double relop, but ignore NaN case: */
	    	do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax3 ;

	        DEBUG2 (("do_update "ID"\n", do_update))

	    }
	    else
	    {
	    	do_update = TRUE ;
		fnzeros = 0 ;
	        DEBUG2 (("OUT-OUT do_update forced true: "ID"\n", do_update))
	    }

	    DEBUG2 (("option OUT OUT: nr "ID" nc "ID" cost "ID"\n",
		rdeg [OUT][OUT], cdeg[OUT], thiscost)) ;

	    if (bestcost == EMPTY || thiscost < bestcost)
	    {
		/* this is the best option seen so far */
		Work->pivot_case = OUT_OUT ;
		bestcost = thiscost ;
		Work->do_extend = do_extend ;
		Work->do_update = do_update ;
		new_fnzeros = fnzeros ;
	    }
	}
    }

    /* At this point, a structural pivot has been found. */
    /* It may be numerically zero, however. */
    ASSERT (Work->pivot_case != EMPTY) ;
    DEBUG2 (("local seach, best option "ID", best cost "ID"\n",
	Work->pivot_case, bestcost)) ;

    /* ====================================================================== */
    /* Pivot row and column, and extension, now determined */
    /* ====================================================================== */

    Work->fnzeros = new_fnzeros ;

    /* ---------------------------------------------------------------------- */
    /* finalize the pivot row and column */
    /* ---------------------------------------------------------------------- */

    switch (Work->pivot_case)
    {
	case IN_IN:
	    DEBUG2 (("IN-IN option selected\n")) ;
	    Work->pivcol_in_front = TRUE ;
	    Work->pivrow_in_front = TRUE ;
	    Work->pivcol = pivcol [IN] ;
	    Work->pivrow = pivrow [IN][IN] ;
	    Work->Wcol = (Int *) NULL ;	/* not accessed */
	    Work->ccdeg = nr_in ;
	    Work->Wrow = Fcols ;
	    Work->rrdeg = rdeg [IN][IN] ;
	    jj = jcand [IN] ;
	    break ;

	case IN_OUT:
	    DEBUG2 (("IN-OUT option selected\n")) ;
	    Work->pivcol_in_front = TRUE ;
	    Work->pivrow_in_front = FALSE ;
	    Work->pivcol = pivcol [IN] ;
	    Work->pivrow = pivrow [IN][OUT] ;
	    Work->Wcol = (Int *) NULL ;	/* not accessed */
	    Work->ccdeg = nr_in ;
	    Work->Wrow = Wio ;
	    Work->rrdeg = rdeg [IN][OUT] ;
	    jj = jcand [IN] ;
	    break ;

	case OUT_IN:
	    DEBUG2 (("OUT-IN option selected\n")) ;
	    Work->pivcol_in_front = FALSE ;
	    Work->pivrow_in_front = TRUE ;
	    Work->pivcol = pivcol [OUT] ;
	    Work->pivrow = pivrow [OUT][IN] ;
	    Work->Wcol = Wm ;
	    Work->ccdeg = cdeg [OUT] ;
	    /* Wrow might be equivalenced to Fcols (Freebie in): */
	    Work->Wrow = Woi ;
	    Work->rrdeg = rdeg [OUT][IN] ;
	    /* Work->Wrow[0..fncols-1] is not there.  See Fcols instead */
	    jj = jcand [OUT] ;
	    break ;

	case OUT_OUT:
	    DEBUG2 (("OUT-OUT option selected\n")) ;
	    Work->pivcol_in_front = FALSE ;
	    Work->pivrow_in_front = FALSE ;
	    Work->pivcol = pivcol [OUT] ;
	    Work->pivrow = pivrow [OUT][OUT] ;
	    Work->Wcol = Wm ;
	    Work->ccdeg = cdeg [OUT] ;
	    /* Wrow might be equivalenced to Wio (Freebie out): */
	    Work->Wrow = Woo ;
	    Work->rrdeg = rdeg [OUT][OUT] ;
	    jj = jcand [OUT] ;
	    break ;

    }

    Wcol = Work->Wcol ;

    if (!Work->pivcol_in_front && pivcol [IN] != EMPTY)
    {
	/* clear Frpos if pivcol [IN] was searched, but not selected */
	for (i = fnrows ; i < cdeg [IN] ; i++)
	{
	    Frpos [Frows [i]] = EMPTY;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* remove pivot column from candidate pivot column list */
    /* ---------------------------------------------------------------------- */

    ASSERT (jj >= 0 && jj < jmax) ;
    ASSERT (Work->pivcol == Work->Candidates [jj]) ;
    if (Work->ncand > MAX_CANDIDATES)
    {
	Work->Candidates [jj] = Work->nextcand++ ;
    }
    else
    {
	Work->Candidates [jj] = Work->Candidates [Work->ncand - 1] ;
	Work->Candidates [Work->ncand - 1] = 0 ;
    }
    Work->ncand-- ;

    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    if (!Work->pivcol_in_front)
    {
	DEBUG2 (("All of Wcol, size "ID"\n", Work->ccdeg)) ;
	ASSERT (Wcol == Work->Wm) ;
	for (i = 0 ; i < Work->ccdeg ; i++)
	{
	    row = Wcol [i] ;
	    DEBUG2 (("Wcol row "ID" Frpos[row] "ID"\n", row, Frpos [row])) ;
	}
    }
    else
    {
	DEBUG2 (("All of old Frows, size "ID"\n", fnrows)) ;
	for (i = 0 ; i < fnrows ; i++)
	{
	    row = Frows [i] ;
	    DEBUG2 (("old Frows row "ID" Frpos[row] "ID"\n", row, Frpos [row]));
	}
	DEBUG2 (("All of (new part of Frows), size "ID"\n",Work->ccdeg));
	for (i = fnrows ; i < fnrows + Work->ccdeg ; i++)
	{
	    row = Frows [i] ;
	    DEBUG2 (("new Frows row "ID" Frpos[row] "ID"\n", row, Frpos [row])) ;
	}
    }
    DEBUG2 (("All of Wrow, size "ID"\n", Work->rrdeg)) ;
    if (Work->pivrow_in_front)
    {
	for (i = 0 ; i < fncols ; i++)
	{
	    col = Fcols [i] ;
	    DEBUG2 (("Wrow col "ID" Fcpos[col] "ID"\n", col, Fcpos [col])) ;
	}
	DEBUG2 (("---\n")) ;
	for (i = fncols ; i < Work->rrdeg ; i++)
	{
	    col = Work->Wrow [i] ;
	    DEBUG2 (("Wrow col "ID" Fcpos[col] "ID"\n", col, Fcpos [col])) ;
	}
    }
    else
    {
	for (i = 0 ; i < Work->rrdeg ; i++)
	{
	    col = Work->Wrow [i] ;
	    DEBUG2 (("Wrow col "ID" Fcpos[col] "ID"\n", col, Fcpos [col])) ;
	}
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* remove pivot row index from pattern of pivot column pattern, */
    /* unless extend_front needs it.  Compress the pivot column pattern. */
    /* ---------------------------------------------------------------------- */

    src = 0 ;
    dest = 0 ;

    if (Work->pivcol_in_front)
    {

	pos = Frpos [Work->pivrow] ;
#ifndef NDEBUG
	if ( Work->pivrow_in_front)
	{
	    ASSERT (pos < fnrows && pos >= 0) ;
	}
	else
	{
	    ASSERT (pos < cdeg [IN] && pos >= fnrows) ;
	}
#endif

	if (!Work->pivrow_in_front)
	{
	    ASSERT (Work->ccdeg > 0) ;
	    ASSERT (cdeg [IN] > fnrows) ;

	    /* remove the pivot row index by shifting the last entry into */
	    /* its position */
	    cdeg [IN]-- ;
	    Work->ccdeg-- ;
	    row = Frows [cdeg [IN]] ;
	    Frows [pos] = row ;
	    Frpos [row] = pos ;

	}

    }
    else
    {
	if (Work->do_extend)
	{
	    /* all cases, when front is being extended */
	    for ( ; src < Work->ccdeg ; src++)
	    {
		ASSERT (Wcol == Work->Wm) ;
		row = Wcol [src] ;
		DEBUG2 (("Pruning Wcol, row "ID" (do_extend)", row)) ;
		if (row != Work->pivrow && Frpos [row] < 0)
		{
		    DEBUG2 ((" keep")) ;
		    Wcol [dest++] = row ;
		}
		DEBUG2 (("\n")) ;
		ASSERT (IMPLIES (Work->pivcol_in_front, Frpos [row] < 0)) ;
	    }
	    /* now Wcol contains only the new rows */
	}
	else
	{
	    for ( ; src < Work->ccdeg ; src++)
	    {
		ASSERT (Wcol == Work->Wm) ;
		row = Wcol [src] ;
		DEBUG2 (("Pruning Wcol, row "ID" (no extend)", row)) ;
		if (row != Work->pivrow)
		{
		    DEBUG2 ((" keep")) ;
		    Wcol [dest++] = row ;
		}
		DEBUG2 (("\n")) ;
	    }
	}
	Work->ccdeg = dest ;
    }


    /* ---------------------------------------------------------------------- */
    /* determine whether to do scan2-row and scan2-col */
    /* ---------------------------------------------------------------------- */

    if (Work->do_extend)
    {
	Work->do_scan2row = (fncols > 0) ;
	Work->do_scan2col = (fnrows > 0) ;
    }
    else
    {
	Work->do_scan2row = (fncols > 0) && Work->pivrow_in_front ;
	Work->do_scan2col = (fnrows > 0) && Work->pivcol_in_front ;
    }

    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG2 (("LOCAL SEARCH DONE: pivot column "ID" pivot row: "ID,
	Work->pivcol, Work->pivrow)) ;
    DEBUG2 ((" do_extend: "ID"\n", Work->do_extend)) ;
    DEBUG2 (("do_update: "ID"\n", Work->do_update)) ;
    UMF_dump_rowcol (0, Numeric, Work, Work->pivrow, TRUE) ;
    DEBUG2 (("Pivot Wrow "ID":\n", Work->pivrow)) ;
    if (Work->pivrow_in_front || Work->do_extend)
    {
	for (i = 0 ; i < fncols ; i++)
	{
	    DEBUG3 ((" col:: "ID" \n", Fcols [i])) ;
	}
    }
    for (i = ((Work->pivrow_in_front) ? fncols : 0) ; i < Work->rrdeg ; i++)
    {
	if (col != Work->pivcol && Fcpos [col] < 0)
	{
	    DEBUG3 ((" col:: "ID" (new)\n", Work->Wrow [i])) ;
	}
    }
    UMF_dump_rowcol (1, Numeric, Work, Work->pivcol, TRUE) ;
    DEBUG2 (("Pivot "ID":\n", Work->pivcol)) ;
    if (Work->pivcol_in_front || Work->do_extend)
    {
	for (i = 0 ; i < fnrows ; i++)
	{
	    DEBUG3 ((" row:: "ID" \n", Frows [i])) ;
	}
    }
    if (Work->pivcol_in_front)
    {
	for (i = fnrows ; i < fnrows + Work->ccdeg ; i++)
	{
	    row = Frows [i] ;
	    DEBUG3 ((" row:: "ID" (new, in Frows already)\n", Frows [i])) ;
	    ASSERT (row != Work->pivrow) ;
	}
    }
    else
    {
	for (i = 0 ; i < Work->ccdeg ; i++)
	{
	    ASSERT (Wcol == Work->Wm) ;
	    row = Wcol [i] ;
	    DEBUG3 ((" row:: "ID" (new)\n", row)) ;
	    ASSERT (row != Work->pivrow) ;
	}
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* Pivot row and column have been found */
    /* ---------------------------------------------------------------------- */

    /*
	If pivot column index is in the front:
		pivot column pattern (or just extension) is in
		Work->Wcol [0..Work->ccdeg-1]
	otherwise
		pivot column pattern is in Frows [0.. fnrows + Work->ccdeg-1],
		and Frpos [row] is already set properly.
	In both cases, the pivot row index has been removed from
	the pivot column pattern.

	------------------------------------------------------------------------

	If pivot row index is in the front:
		pivot row pattern (or just extension) is in
		Work->Wrow [fncols..Work->rrdeg-1]
	otherwise
		pivot row pattern is in Work->Wrow [0..Work->rrdeg-1]
	The pivot row pattern has not been pruned.

    */

    return (UMFPACK_OK) ;
}


```

sparse-matrix/umfpack/umf_apply_order.c:
```
/* ========================================================================== */
/* === UMF_apply_order ====================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Apply post-ordering of supernodal elimination tree.
*/

#include "umf_internal.h"

GLOBAL void UMF_apply_order
(
    Int Front [ ],
    const Int Order [ ],
    Int Temp [ ],
    Int n_col,
    Int nfr
)
{
    Int i, k ;
    for (i = 0 ; i < n_col ; i++)
    {
	k = Order [i] ;
	if (k != EMPTY)
	{
	    Temp [k] = Front [i] ;
	}
    }

    for (k = 0 ; k < nfr ; k++)
    {
	Front [k] = Temp [k] ;
    }
}


```

sparse-matrix/umfpack/umfpack_transpose.h:
```
/* ========================================================================== */
/* === umfpack_transpose ==================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_transpose
(
    int n_row,
    int n_col,
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ],
    const int P [ ],
    const int Q [ ],
    int Rp [ ],
    int Ri [ ],
    double Rx [ ]
) ;

long umfpack_dl_transpose
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ],
    const long P [ ],
    const long Q [ ],
    long Rp [ ],
    long Ri [ ],
    double Rx [ ]
) ;

int umfpack_zi_transpose
(
    int n_row,
    int n_col,
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ], const double Az [ ],
    const int P [ ],
    const int Q [ ],
    int Rp [ ],
    int Ri [ ],
    double Rx [ ], double Rz [ ],
    int do_conjugate
) ;

long umfpack_zl_transpose
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ], const double Az [ ],
    const long P [ ],
    const long Q [ ],
    long Rp [ ],
    long Ri [ ],
    double Rx [ ], double Rz [ ],
    long do_conjugate
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri ;
    double *Ax, *Rx ;
    status = umfpack_di_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, Rp, Ri, Rx) ;

double long Syntax:

    #include "umfpack.h"
    long n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri ;
    double *Ax, *Rx ;
    status = umfpack_dl_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, Rp, Ri, Rx) ;

complex int Syntax:

    #include "umfpack.h"
    int n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri, do_conjugate ;
    double *Ax, *Az, *Rx, *Rz ;
    status = umfpack_zi_transpose (n_row, n_col, Ap, Ai, Ax, Az, P, Q,
	Rp, Ri, Rx, Rz, do_conjugate) ;

complex long Syntax:

    #include "umfpack.h"
    long n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri, do_conjugate ;
    double *Ax, *Az, *Rx, *Rz ;
    status = umfpack_zl_transpose (n_row, n_col, Ap, Ai, Ax, Az, P, Q,
	Rp, Ri, Rx, Rz, do_conjugate) ;

Purpose:

    Transposes and optionally permutes a sparse matrix in row or column-form,
    R = (PAQ)'.  In MATLAB notation, R = (A (P,Q))' or R = (A (P,Q)).' doing
    either the linear algebraic transpose or the array transpose. Alternatively,
    this routine can be viewed as converting A (P,Q) from column-form to
    row-form, or visa versa (for the array transpose).  Empty rows and columns
    may exist.  The matrix A may be singular and/or rectangular.

    umfpack_*_transpose is useful if you want to factorize A' or A.' instead of
    A.  Factorizing A' or A.' instead of A can be much better, particularly if
    AA' is much sparser than A'A.  You can still solve Ax=b if you factorize
    A' or A.', by solving with the sys argument UMFPACK_At or UMFPACK_Aat,
    respectively, in umfpack_*_*solve.  The umfpack mexFunction (umfpackmex.c)
    is one example.  To compute x = A/b, it computes x = (A.'\b.').' instead,
    by factorizing A.'.  It then uses the regular solve, since b.' and x.' are
    stored identically as b and x, respectively (both b.' and b are dense
    vectors).  If b and x were arrays, the umfpack mexFunction would need to
    first compute b.' and then transpose the resulting solution.

Returns:

    UMFPACK_OK if successful.
    UMFPACK_ERROR_out_of_memory if umfpack_*_transpose fails to allocate a
	size-max (n_row,n_col) workspace.
    UMFPACK_ERROR_argument_missing if Ai, Ap, Ri, and/or Rp are missing.
    UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0
    UMFPACK_ERROR_invalid_permutation if P and/or Q are invalid.
    UMFPACK_ERROR_nz_negative if Ap [n_col] < 0.
    UMFPACK_ERROR_Ap0_nonzero if Ap [0] != 0.
    UMFPACK_ERROR_col_length_negative if Ap [j] > Ap [j+1] for any j in the
	range 0 to n_col-1.
    UMFPACK_ERROR_row_index_out_of_bounds if any row index i is < 0 or >= n_row.
    UMFPACK_ERROR_jumbled_matrix if the row indices in any column are not in
	ascending order.

Arguments:

    Int n_row ;		Input argument, not modified.
    Int n_col ;		Input argument, not modified.

	A is an n_row-by-n_col matrix.  Restriction: n_row > 0 and n_col > 0.

    Int Ap [n_col+1] ;	Input argument, not modified.

	The column pointers of the column-oriented form of the matrix A.  See
	umfpack_*_symbolic for a description.  The number of entries in
	the matrix is nz = Ap [n_col].  Ap [0] must be zero, Ap [n_col] must be
	=> 0, and Ap [j] <= Ap [j+1] and Ap [j] <= Ap [n_col] must be true for
	all j in the range 0 to n_col-1.  Empty columns are OK (that is, Ap [j]
	may equal Ap [j+1] for any j in the range 0 to n_col-1).

    Int Ai [nz] ;	Input argument, not modified, of size nz = Ap [n_col].

	The nonzero pattern (row indices) for column j is stored in
	Ai [(Ap [j]) ... (Ap [j+1]-1)].  The row indices in a given column j
	must be in ascending order, and no duplicate row indices may be present.
	Row indices must be in the range 0 to n_row-1 (the matrix is 0-based).

    double Ax [nz] ;	Input argument, not modified, of size nz = Ap [n_col].
    double Az [nz] ;	Input argument, not modified, for complex versions.

	If present, these are the numerical values of the sparse matrix A.
	The nonzero pattern (row indices) for column j is stored in
	Ai [(Ap [j]) ... (Ap [j+1]-1)], and the corresponding real numerical
	values are stored in Ax [(Ap [j]) ... (Ap [j+1]-1)].  The imaginary
	values are stored in Az [(Ap [j]) ... (Ap [j+1]-1)].  The values are
	transposed only if Ax and Rx are present (for the real version), and
	only if all four (Ax, Az, Rx, and Rz) are present for the complex
	version.  These are not an error conditions; you are able to transpose
	and permute just the pattern of a matrix.

	Future complex version:  if Ax is present and Az is NULL, then both real
	and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k]
	and Ax[2*k+1] being the real and imaginary part of the kth entry.

    Int P [n_row] ;		Input argument, not modified.

	The permutation vector P is defined as P [k] = i, where the original
	row i of A is the kth row of PAQ.  If you want to use the identity
	permutation for P, simply pass (Int *) NULL for P.  This is not an error
	condition.  P is a complete permutation of all the rows of A; this
	routine does not support the creation of a transposed submatrix of A
	(R = A (1:3,:)' where A has more than 3 rows, for example, cannot be
	done; a future version might support this operation).

    Int Q [n_col] ;		Input argument, not modified.

	The permutation vector Q is defined as Q [k] = j, where the original
	column j of A is the kth column of PAQ.  If you want to use the identity
	permutation for Q, simply pass (Int *) NULL for Q.  This is not an error
	condition.  Q is a complete permutation of all the columns of A; this
	routine does not support the creation of a transposed submatrix of A.

    Int Rp [n_row+1] ;	Output argument.

	The column pointers of the matrix R = (A (P,Q))' or (A (P,Q)).', in the
	same form as the column pointers Ap for the matrix A.

    Int Ri [nz] ;	Output argument.

	The row indices of the matrix R = (A (P,Q))' or (A (P,Q)).' , in the
	same form as the row indices Ai for the matrix A.

    double Rx [nz] ;	Output argument.
    double Rz [nz] ;	Output argument, imaginary part for complex versions.

	If present, these are the numerical values of the sparse matrix R,
	in the same form as the values Ax and Az of the matrix A.

	Future complex version:  if Rx is present and Rz is NULL, then both real
	and imaginary parts will be contained in Rx[0..2*nz-1], with Rx[2*k]
	and Rx[2*k+1] being the real and imaginary part of the kth entry.

    Int do_conjugate ;	Input argument for complex versions only.

	If true, and if Ax, Az, Rx, and Rz are all present, then the linear
	algebraic transpose is computed (complex conjugate).  If false, the
	array transpose is computed instead.
*/

```

sparse-matrix/umfpack/umf_ltsolve.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL double UMF_ltsolve
(
    NumericType *Numeric,
    Entry X [ ],
    Int Pattern [ ]
) ;


GLOBAL double UMF_lhsolve
(
    NumericType *Numeric,
    Entry X [ ],
    Int Pattern [ ]
) ;


```

sparse-matrix/umfpack/umf_free.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void *UMF_free
(
    void *p
) ;


```

sparse-matrix/umfpack/umfpack_get_symbolic.h:
```
/* ========================================================================== */
/* === umfpack_get_symbolic ================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_get_symbolic
(
    int *n_row,
    int *n_col,
    int *nz,
    int *nfr,
    int *nchains,
    int Ptree [ ],
    int Qtree [ ],
    int Front_npivcol [ ],
    int Front_parent [ ],
    int Front_1strow [ ],
    int Front_leftmostdesc [ ],
    int Chain_start [ ],
    int Chain_maxrows [ ],
    int Chain_maxcols [ ],
    void *Symbolic
) ;

long umfpack_dl_get_symbolic
(
    long *n_row,
    long *n_col,
    long *nz,
    long *nfr,
    long *nchains,
    long Ptree [ ],
    long Qtree [ ],
    long Front_npivcol [ ],
    long Front_parent [ ],
    long Front_1strow [ ],
    long Front_leftmostdesc [ ],
    long Chain_start [ ],
    long Chain_maxrows [ ],
    long Chain_maxcols [ ],
    void *Symbolic
) ;

int umfpack_zi_get_symbolic
(
    int *n_row,
    int *n_col,
    int *nz,
    int *nfr,
    int *nchains,
    int Ptree [ ],
    int Qtree [ ],
    int Front_npivcol [ ],
    int Front_parent [ ],
    int Front_1strow [ ],
    int Front_leftmostdesc [ ],
    int Chain_start [ ],
    int Chain_maxrows [ ],
    int Chain_maxcols [ ],
    void *Symbolic
) ;

long umfpack_zl_get_symbolic
(
    long *n_row,
    long *n_col,
    long *nz,
    long *nfr,
    long *nchains,
    long Ptree [ ],
    long Qtree [ ],
    long Front_npivcol [ ],
    long Front_parent [ ],
    long Front_1strow [ ],
    long Front_leftmostdesc [ ],
    long Chain_start [ ],
    long Chain_maxrows [ ],
    long Chain_maxcols [ ],
    void *Symbolic
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int status, n_row, n_col, nz, nfr, nchains, *Ptree, *Qtree,
	*Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc,
	*Chain_start, *Chain_maxrows, *Chain_maxcols ;
    void *Symbolic ;
    status = umfpack_di_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains,
	Ptree, Qtree, Front_npivcol, Front_parent, Front_1strow,
	Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols,
	Symbolic) ;

double long Syntax:

    #include "umfpack.h"
    long status, n_row, n_col, nz, nfr, nchains, *Ptree, *Qtree,
	*Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc,
	*Chain_start, *Chain_maxrows, *Chain_maxcols ;
    void *Symbolic ;
    status = umfpack_dl_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains,
	Ptree, Qtree, Front_npivcol, Front_parent, Front_1strow,
	Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols,
	Symbolic) ;

complex int Syntax:

    #include "umfpack.h"
    int status, n_row, n_col, nz, nfr, nchains, *Ptree, *Qtree,
	*Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc,
	*Chain_start, *Chain_maxrows, *Chain_maxcols ;
    void *Symbolic ;
    status = umfpack_zi_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains,
	Ptree, Qtree, Front_npivcol, Front_parent, Front_1strow,
	Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols,
	Symbolic) ;

complex long Syntax:

    #include "umfpack.h"
    long status, n_row, n_col, nz, nfr, nchains, *Ptree, *Qtree,
	*Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc,
	*Chain_start, *Chain_maxrows, *Chain_maxcols ;
    void *Symbolic ;
    status = umfpack_zl_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains,
	Ptree, Qtree, Front_npivcol, Front_parent, Front_1strow,
	Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols,
	Symbolic) ;

Purpose:

    Copies the contents of the Symbolic object into simple integer arrays
    accessible to the user.  This routine is not needed to factorize and/or
    solve a sparse linear system using UMFPACK.  Note that the output arrays
    Ptree, Qtree, Front_npivcol, Front_parent, Front_1strow, Front_leftmostdesc,
    Chain_start, Chain_maxrows, and Chain_maxcols are not allocated by
    umfpack_*_get_symbolic; they must exist on input.

    The Symbolic object is small.  Its size for an n-by-n square matrix is about
    (3*nchains + 2*n + 4*nfr + 30) integers plus 5 double's, which is no greater
    than about 9*n+40 int's.  The object holds the initial column permutation,
    the supernodal column elimination tree, and information about each frontal
    matrix.  You can print it with umfpack_*_report_symbolic.

Returns:

    Returns UMFPACK_OK if successful, UMFPACK_ERROR_invalid_Symbolic_object
    if Symbolic is an invalid object.

Arguments:

    Note that if any of the output arguments are (Int *) NULL pointers, then
    that argument is not returned.  This is not an error condition.  Thus,
    if you do not want a particular part of the Symbolic object to be
    returned to you, simply pass a (Int *) NULL pointer for that particular
    output argument.

    Int *n_row ;		Output argument.
    Int *n_col ;		Output argument.

	The dimensions of the matrix A analyzed by the call to
	umfpack_*_symbolic that generated the Symbolic object.

    Int *nz ;		Output argument.

	The number of nonzeros in A.

    Int *nfr ;	Output argument.

	The number of frontal matrices that will be used by umfpack_*_numeric
	to factorize the matrix A.  It is in the range 0 to n_col.

    Int *nchains ;	Output argument.

	The frontal matrices are related to one another by the supernodal
	column elimination tree.  Each node in this tree is one frontal matrix.
	The tree is partitioned into a set of disjoint paths, and a frontal
	matrix chain is one path in this tree.  Each chain is factorized using
	a unifrontal technique, with a single working array that holds each
	frontal matrix in the chain, one at a time.  nchains is in the range
	0 to nfr.

    Int Ptree [n_row] ;	Output argument.

	The initial row permutation.  If Ptree [k] = i, then this means that
	row i is the kth row in the preordered matrix.  Row i typically will
	not be the kth pivot row, however.  Ptree is the result of a sorting of
	the rows according to the smallest column index of entries in the
	matrix permuted by Qtree, below.

    Int Qtree [n_col] ;	Output argument.

	The initial column permutation.  If Qtree [k] = j, then this means that
	column j is the kth pivot column in the preordered matrix.  Qtree is
	not necessarily the same as the final column permutation Q, computed by
	umfpack_*_numeric.  The numeric factorization may reorder the pivot
	columns within each frontal matrix to reduce fill-in.

    Int Front_npivcol [n_col+1] ;	Output argument.

	This array should be of size at least n_col+1, in order to guarantee
	that it will be large enough to hold the output.  Only the first nfr+1
	entries are used, however.

	The kth frontal matrix holds Front_npivcol [k] pivot columns.  Thus, the
	first frontal matrix, front 0, is used to factorize the first
	Front_npivcol [0] columns; these correspond to the original columns
	Qtree [0] through Qtree [Front_npivcol [0]-1].  The next frontal matrix
	is used to factorize the next Front_npivcol [1] columns, which are thus
	the original columns Qtree [Front_npivcol [0]] through
	Qtree [Front_npivcol [0] + Front_npivcol [1] - 1], and so on.  Columns
	with no entries at all are put in a placeholder "front",
	Front_npivcol [nfr].  The sum of Front_npivcol [0..nfr] is equal to
	n_col.

	Any modifications that umfpack_*_numeric makes to the initial column
	permutation are constrained to within each frontal matrix.  Thus, for
	the first frontal matrix, Q [0] through Q [Front_npivcol [0]-1] is some
	permutation of the columns Qtree [0] through
	Qtree [Front_npivcol [0]-1].  For second frontal matrix,
	Q [Front_npivcol [0]] through Q [Front_npivcol [0] + Front_npivcol[1]-1]
	is some permutation of the same portion of Qtree, and so on.  All pivot
	columns are numerically factorized within the frontal matrix originally
	determined by the symbolic factorization; there is no delayed pivoting
	across frontal matrices.

    Int Front_parent [n_col+1] ;	Output argument.

	This array should be of size at least n_col+1, in order to guarantee
	that it will be large enough to hold the output.  Only the first nfr+1
	entries are used, however.

	Front_parent [0..nfr] holds the supernodal column elimination tree
	(including the placeholder front nfr, which may be empty).  Each node in
	the tree corresponds to a single frontal matrix.  The parent of node f
	is Front_parent [f].

    Int Front_1strow [n_col+1] ;	Output argument.

	This array should be of size at least n_col+1, in order to guarantee
	that it will be large enough to hold the output.  Only the first nfr+1
	entries are used, however.

	Front_1strow [k] is the row index of the first row in A (Ptree,Qtree)
	whose leftmost entry is in a pivot column for the kth front.  This is
	necessary only to properly factorize singular matrices.  It is new to
	Version 4.0.  Rows in the range Front_1strow [k] to
	Front_1strow [k+1]-1 first become pivot row candidates at the kth front.
	Any rows not eliminated in the kth front may be selected as pivot rows
	in the parent of k (Front_parent [k]) and so on up the tree.

    Int Front_leftmostdesc [n_col+1] ;	Output argument.

	This array should be of size at least n_col+1, in order to guarantee
	that it will be large enough to hold the output.  Only the first nfr+1
	entries are used, however.

	Front_leftmostdesc [k] is the leftmost descendant of front k, or k
	if the front has no children in the tree.  Since the rows and columns
	(Ptree and Qtree) have been post-ordered via a depth-first-search of
	the tree, rows in the range Front_1strow [Front_leftmostdesc [k]] to
	Front_1strow [k+1]-1 form the entire set of candidate pivot rows for
	the kth front (some of these will typically have already been selected
	by fronts in the range Front_leftmostdesc [k] to front k-1, before
	the factorization reaches front k).

    Chain_start [n_col+1] ;	Output argument.

	This array should be of size at least n_col+1, in order to guarantee
	that it will be large enough to hold the output.  Only the first
	nchains+1 entries are used, however.

	The kth frontal matrix chain consists of frontal matrices Chain_start[k]
	through Chain_start [k+1]-1.  Thus, Chain_start [0] is always 0, and
	Chain_start [nchains] is the total number of frontal matrices, nfr.  For
	two adjacent fronts f and f+1 within a single chain, f+1 is always the
	parent of f (that is, Front_parent [f] = f+1).

    Int Chain_maxrows [n_col+1] ;	Output argument.
    Int Chain_maxcols [n_col+1] ;	Output argument.

	These arrays should be of size at least n_col+1, in order to guarantee
	that they will be large enough to hold the output.  Only the first
	nchains entries are used, however.

	The kth frontal matrix chain requires a single working array of
	dimension Chain_maxrows [k] by Chain_maxcols [k], for the unifrontal
	technique that factorizes the frontal matrix chain.  Since the symbolic
	factorization only provides an upper bound on the size of each frontal
	matrix, not all of the working array is necessarily used during the
	numerical factorization.

	Note that the upper bound on the number of rows and columns of each
	frontal matrix is computed by umfpack_*_symbolic, but all that is
	required by umfpack_*_numeric is the maximum of these two sets of
	values for each frontal matrix chain.  Thus, the size of each
	individual frontal matrix is not preserved in the Symbolic object.

    void *Symbolic ;			Input argument, not modified.

	The Symbolic object, which holds the symbolic factorization computed by
	umfpack_*_symbolic.  The Symbolic object is not modified by
	umfpack_*_get_symbolic.
*/

```

sparse-matrix/umfpack/umf_solve.c:
```
/* ========================================================================== */
/* === UMF_solve ============================================================ */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Not user-callable.  Solves a linear system using the numerical factorization
    computed by UMFPACK_numeric.  No workspace is dynamically allocated.  Counts
    flops, but excludes floating-point comparisons (thus real abs (...) are
    zero flops, but complex abs (...) takes 6 flops).

    Returns UMFPACK_OK if successful, UMFPACK_ERROR_argument_missing if
    required arguments are missing, UMFPACK_ERROR_invalid_system if the sys
    string is not valid or if the matrix A is not square.

    Uses the sparse backward error method of Arioli, Demmel, and Duff
    (Solving sparse linear systems with sparse backward error, SIAM J. Matrix
    Analysis and Applic., vol 10, pp. 165-190).
*/

#include "umf_internal.h"
#include "umf_lsolve.h"
#include "umf_usolve.h"
#include "umf_ltsolve.h"
#include "umf_utsolve.h"

PRIVATE Int do_step
(
    double omega [3],
    Int step,
    const double B2 [ ],
    Entry X [ ],
    const Entry W [ ],
    const double Y [ ],
    const double Z2 [ ],
    Entry S [ ],
    Int n,
    double Info [UMFPACK_INFO]
) ;

/* ========================================================================== */
/* === UMF_solve ============================================================ */
/* ========================================================================== */

GLOBAL Int UMF_solve
(
    Int sys,
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
    double Xx [ ],
    const double Bx [ ],
#ifdef COMPLEX
    const double Az [ ],
    double Xz [ ],
    const double Bz [ ],
#endif
    NumericType *Numeric,
    Int irstep,
    double Info [UMFPACK_INFO],
    Int Pattern [ ],		/* size n */
    double SolveWork [ ]	/* if irstep>0 real:  size 5*n.  complex:10*n */
				/* otherwise   real:  size   n.  complex: 4*n */
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int *Rperm, *Cperm, i, n, p, step, j, nz, status ;
    Entry axx, wi, xj, zi, xi, aij, *W, *Z, *S, *X, bi ;
    double omega [3], d, *Z2, *Y, z2i, yi, *B2 ;

    /* ---------------------------------------------------------------------- */
    /* initializations */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    UMF_dump_lu (Numeric) ;
    ASSERT (Numeric && Xx && Bx && Pattern && SolveWork && Info) ;
#ifdef COMPLEX
    ASSERT (Xz && Bz) ;
#endif
#endif

    nz = 0 ;
    omega [0] = 0. ;
    omega [1] = 0. ;
    omega [2] = 0. ;
    Rperm = Numeric->Rperm ;
    Cperm = Numeric->Cperm ;
    Info [UMFPACK_SOLVE_FLOPS] = 0. ;
    Info [UMFPACK_IR_TAKEN] = 0 ;
    Info [UMFPACK_IR_ATTEMPTED] = 0 ;

    /* UMFPACK_solve does not call this routine if A is rectangular */
    ASSERT (Numeric->n_row == Numeric->n_col) ;
    n = Numeric->n_row ;
    if (Numeric->nnzpiv < n
	|| SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond))
    {
	/* Note that systems involving just L return UMFPACK_OK, even if */
	/* A is singular (L is always has a unit diagonal). */ 
	DEBUG0 (("Warning, matrix is singular in umf_solve\n")) ;
	status = UMFPACK_WARNING_singular_matrix ;
	irstep = 0 ;
    }
    else
    {
	status = UMFPACK_OK ;
    }
    irstep = MAX (0, irstep) ;			/* make sure irstep is >= 0 */

    W = (Entry *) SolveWork ;			/* Entry W [0..n-1] */

    Z = (Entry *) NULL ;	/* unused if no iterative refinement */
    S = (Entry *) NULL ;
    Y = (double *) NULL ;
    Z2 = (double *) NULL ;
    B2 = (double *) NULL ;

#ifdef COMPLEX
    X = (Entry *) (SolveWork + 2*n) ;		/* Entry X [0..n-1] */
    if (irstep > 0)
    {
	if (!Ap || !Ai || !Ax || !Az)
	{
	    return (UMFPACK_ERROR_argument_missing) ;
	}
	Z = (Entry *) (SolveWork + 4*n) ;	/* Entry Z [0..n-1] */
	S = (Entry *) (SolveWork + 6*n) ;	/* Entry S [0..n-1] */
	Y = (double *) (SolveWork + 8*n) ;	/* double Y [0..n-1] */
	B2 = (double *) (SolveWork + 9*n) ;	/* double B2 [0..n-1] */
    	Z2 = (double *) Z ;		/* double Z2 [0..n-1], equiv. to Z */
    }
#else
    X = (Entry *) Xx ;				/* Entry X [0..n-1] */
    if (irstep > 0)
    {
	if (!Ap || !Ai || !Ax)
	{
	    return (UMFPACK_ERROR_argument_missing) ;
	}
	Z = (Entry *) (SolveWork + n) ;		/* Entry Z [0..n-1] */
	S = (Entry *) (SolveWork + 2*n) ;	/* Entry S [0..n-1] */
	Y = (double *) (SolveWork + 3*n) ;	/* double Y [0..n-1] */
	B2 = (double *) (SolveWork + 4*n) ;	/* double B2 [0..n-1] */
    	Z2 = (double *) Z ;		/* double Z2 [0..n-1], equiv. to Z */
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* determine which system to solve */
    /* ---------------------------------------------------------------------- */

    if (sys == UMFPACK_A)
    {

	/* ------------------------------------------------------------------ */
	/* solve Ax=b with optional iterative refinement */
	/* ------------------------------------------------------------------ */

	if (irstep > 0)
	{

	    /* -------------------------------------------------------------- */
	    /* using iterative refinement:  compute Y and B2 */
	    /* -------------------------------------------------------------- */

	    nz = Ap [n] ;
	    Info [UMFPACK_NZ] = nz ;

	    /* A is stored by column */
	    /* Y (i) = ||A_i||, 1-norm of row i of A */
	    for (i = 0 ; i < n ; i++)
	    {
		Y [i] = 0. ;
	    }
	    Info [UMFPACK_SOLVE_FLOPS] += (ABS_FLOPS + 1) * nz ;
	    for (j = 0 ; j < n ; j++)
	    {
		for (p = Ap [j] ; p < Ap [j+1] ; p++)
		{
		    /* Y [Ai [p]] += ABS (Ax [p]) ; */
		    ASSIGN (aij, Ax [p], Az [p]) ;
		    ABS (d, aij) ;
		    Y [Ai [p]] += d ;
		}
	    }

	    /* B2 = abs (B) */
	    Info [UMFPACK_SOLVE_FLOPS] += ABS_FLOPS * n ;
	    for (i = 0 ; i < n ; i++)
	    {
		/* B2 [i] = ABS (B [i]) ; */
		ASSIGN (bi, Bx [i], Bz [i]) ;
		ABS (B2 [i], bi) ;
	    }

	}

	for (step = 0 ; step <= irstep ; step++)
	{

	    /* -------------------------------------------------------------- */
	    /* Solve Ax=b (step 0): */
	    /*  x = Q  (U \ (L \ (Pb))) */
	    /* and then perform iterative refinement (step > 0): */
	    /*  x = x + Q (U \ (L \ (P (b-Ax)))) */
	    /* -------------------------------------------------------------- */

	    if (step == 0)
	    {
		for (i = 0 ; i < n ; i++)
		{
		    /* W [i] = B [Rperm [i]] ; */
		    ASSIGN (W [i], Bx [Rperm [i]], Bz [Rperm [i]]) ;
		}
	    }
	    else
	    {
		for (i = 0 ; i < n ; i++)
		{
		    /* Z [i] = B [i] ; */
		    ASSIGN (Z [i], Bx [i], Bz [i]) ;
		}
		Info [UMFPACK_SOLVE_FLOPS] += MULTSUB_FLOPS * nz ;
		for (i = 0 ; i < n ; i++)
		{
		    xi = X [i] ;
		    for (p = Ap [i] ; p < Ap [i+1] ; p++)
		    {
			/* Z [Ai [p]] -= Ax [p] * xi ; */
			ASSIGN (aij, Ax [p], Az [p]) ;
			MULT_SUB (Z [Ai [p]], aij, xi) ;
		    }
		}
		for (i = 0 ; i < n ; i++)
		{
		    W [i] = Z [Rperm [i]] ;
		}
	    }
	    Info [UMFPACK_SOLVE_FLOPS] += UMF_lsolve (Numeric, W, Pattern) ;
	    Info [UMFPACK_SOLVE_FLOPS] += UMF_usolve (Numeric, W, Pattern) ;
	    if (step == 0)
	    {
		for (i = 0 ; i < n ; i++)
		{
		    X [Cperm [i]] = W [i] ;
		}
	    }
	    else
	    {
		Info [UMFPACK_SOLVE_FLOPS] += ASSEMBLE_FLOPS * n ;
		for (i = 0 ; i < n ; i++)
		{
		    /* X [Cperm [i]] += W [i] ; */
		    ASSEMBLE (X [Cperm [i]], W [i]) ;
		}
	    }

	    /* -------------------------------------------------------------- */
	    /* sparse backward error estimate */
	    /* -------------------------------------------------------------- */

	    if (irstep > 0)
	    {

		/* ---------------------------------------------------------- */
		/* A is stored by column */
		/* W (i) = (b-Ax)_i, residual */
		/* Z2 (i) = (|A||x|)_i */
		/* ---------------------------------------------------------- */

		for (i = 0 ; i < n ; i++)
		{
		    /* W [i] = B [i] ; */
		    ASSIGN (W [i], Bx [i], Bz [i]) ;
		    Z2 [i] = 0. ;
		}
		Info [UMFPACK_SOLVE_FLOPS] +=
		    (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ;
		for (j = 0 ; j < n ; j++)
		{
		    xj = X [j] ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			i = Ai [p] ;

			/* axx = Ax [p] * xj ; */
			ASSIGN (aij, Ax [p], Az [p]) ;
			MULT (axx, aij, xj) ;

			/* W [i] -= axx ; */
			DECREMENT (W [i], axx) ;

			/* Z2 [i] += ABS (axx) ; */
			ABS (d, axx) ;
			Z2 [i] += d ;
		    }
		}

		if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info))
		{
		    /* iterative refinement is done */
		    break ;
		}

	    }

	}

    }
    else if (sys == UMFPACK_At)
    {

	/* ------------------------------------------------------------------ */
	/* solve A'x=b with optional iterative refinement */
	/* ------------------------------------------------------------------ */

	/* A' is the complex conjugate transpose */

	if (irstep > 0)
	{
	
	    /* -------------------------------------------------------------- */
	    /* using iterative refinement:  compute Y */
	    /* -------------------------------------------------------------- */

	    nz = Ap [n] ;
	    Info [UMFPACK_NZ] = nz ;

	    /* A' is stored by row */
	    /* Y (i) = ||A'_i||, 1-norm of row i of A' */
	    Info [UMFPACK_SOLVE_FLOPS] += (ABS_FLOPS + 1) * nz ;
	    for (i = 0 ; i < n ; i++)
	    {
		yi = 0. ;
		for (p = Ap [i] ; p < Ap [i+1] ; p++)
		{
		    /* yi += ABS (Ax [p]) ; */
		    /* note that abs (aij) is the same as abs (conj (aij)) */
		    ASSIGN (aij, Ax [p], Az [p]) ;
		    ABS (d, aij) ;
		    yi += d ;
		}
		Y [i] = yi ;
	    }

	    /* B2 = abs (B) */
	    for (i = 0 ; i < n ; i++)
	    {
		/* B2 [i] = ABS (B [i]) ; */
		ASSIGN (bi, Bx [i], Bz [i]) ;
		ABS (B2 [i], bi) ;
	    }

	}

	for (step = 0 ; step <= irstep ; step++)
	{

	    /* -------------------------------------------------------------- */
	    /* Solve A'x=b (step 0): */
	    /*	x = P' (L' \ (U' \ (Q'b))) */
	    /* and then perform iterative refinement (step > 0): */
	    /*	x = x + P' (L' \ (U' \ (Q' (b-A'x)))) */
	    /* -------------------------------------------------------------- */

	    if (step == 0)
	    {
		for (i = 0 ; i < n ; i++)
		{
		    /* W [i] = B [Cperm [i]] ; */
		    ASSIGN (W [i], Bx [Cperm [i]], Bz [Cperm [i]]) ;
		}
	    }
	    else
	    {
		for (i = 0 ; i < n ; i++)
		{
		    /* Z [i] = B [i] ; */
		    ASSIGN (Z [i], Bx [i], Bz [i]) ;
		}
		Info [UMFPACK_SOLVE_FLOPS] += MULTSUB_FLOPS * nz ;
		for (i = 0 ; i < n ; i++)
		{
		    zi = Z [i] ;
		    for (p = Ap [i] ; p < Ap [i+1] ; p++)
		    {
			/* zi -= conjugate (Ax [p]) * X [Ai [p]] ; */
			ASSIGN (aij, Ax [p], Az [p]) ;
			MULT_SUB_CONJ (zi, X [Ai [p]], aij) ;
		    }
		    Z [i] = zi ;
		}
		for (i = 0 ; i < n ; i++)
		{
		    W [i] = Z [Cperm [i]] ;
		}
	    }
	    Info [UMFPACK_SOLVE_FLOPS] += UMF_uhsolve (Numeric, W, Pattern) ;
	    Info [UMFPACK_SOLVE_FLOPS] += UMF_lhsolve (Numeric, W, Pattern) ;
	    if (step == 0)
	    {
		for (i = 0 ; i < n ; i++)
		{
		    X [Rperm [i]] = W [i] ;
		}
	    }
	    else
	    {
		Info [UMFPACK_SOLVE_FLOPS] += ASSEMBLE_FLOPS * n ;
		for (i = 0 ; i < n ; i++)
		{
		    /* X [Rperm [i]] += W [i] ; */
		    ASSEMBLE (X [Rperm [i]], W [i]) ;
		}
	    }

	    /* -------------------------------------------------------------- */
	    /* sparse backward error estimate */
	    /* -------------------------------------------------------------- */

	    if (irstep > 0)
	    {

		/* ---------------------------------------------------------- */
		/* A' is stored by row */
		/* W (i) = (b-A'x)_i, residual */
		/* Z (i) = (|A'||x|)_i */
		/* ---------------------------------------------------------- */

		Info [UMFPACK_SOLVE_FLOPS] +=
		    (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ;
		for (i = 0 ; i < n ; i++)
		{
		    /* wi = B [i] ; */
		    ASSIGN (wi, Bx [i], Bz [i]) ;
		    z2i = 0. ;
		    for (p = Ap [i] ; p < Ap [i+1] ; p++)
		    {
			/* axx = conjugate (Ax [p]) * X [Ai [p]] ; */
			ASSIGN (aij, Ax [p], Az [p]) ;
			MULT_CONJ (axx, X [Ai [p]], aij) ;

			/* wi -= axx ; */
			DECREMENT (wi, axx) ;

			/* z2i += ABS (axx) ; */
			ABS (d, axx) ;
			z2i += d ;
		    }
		    W [i] = wi ;
		    Z2 [i] = z2i ;
		}

		if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info))
		{
		    /* iterative refinement is done */
		    break ;
		}

	    }

	}

    }
    else if (sys == UMFPACK_Aat)
    {

	/* ------------------------------------------------------------------ */
	/* solve A.'x=b with optional iterative refinement */
	/* ------------------------------------------------------------------ */

	/* A' is the array transpose */

	if (irstep > 0)
	{
	
	    /* -------------------------------------------------------------- */
	    /* using iterative refinement:  compute Y */
	    /* -------------------------------------------------------------- */

	    nz = Ap [n] ;
	    Info [UMFPACK_NZ] = nz ;

	    /* A.' is stored by row */
	    /* Y (i) = ||A.'_i||, 1-norm of row i of A.' */
	    Info [UMFPACK_SOLVE_FLOPS] += (ABS_FLOPS + 1) * nz ;
	    for (i = 0 ; i < n ; i++)
	    {
		yi = 0. ;
		for (p = Ap [i] ; p < Ap [i+1] ; p++)
		{
		    /* yi += ABS (Ax [p]) ; */
		    ASSIGN (aij, Ax [p], Az [p]) ;
		    ABS (d, aij) ;
		    yi += d ;
		}
		Y [i] = yi ;
	    }

	    /* B2 = abs (B) */
	    for (i = 0 ; i < n ; i++)
	    {
		/* B2 [i] = ABS (B [i]) ; */
		ASSIGN (bi, Bx [i], Bz [i]) ;
		ABS (B2 [i], bi) ;
	    }

	}

	for (step = 0 ; step <= irstep ; step++)
	{

	    /* -------------------------------------------------------------- */
	    /* Solve A.'x=b (step 0): */
	    /*	x = P' (L.' \ (U.' \ (Q'b))) */
	    /* and then perform iterative refinement (step > 0): */
	    /*	x = x + P' (L.' \ (U.' \ (Q' (b-A.'x)))) */
	    /* -------------------------------------------------------------- */

	    if (step == 0)
	    {
		for (i = 0 ; i < n ; i++)
		{
		    /* W [i] = B [Cperm [i]] ; */
		    ASSIGN (W [i], Bx [Cperm [i]], Bz [Cperm [i]]) ;
		}
	    }
	    else
	    {
		for (i = 0 ; i < n ; i++)
		{
		    /* Z [i] = B [i] ; */
		    ASSIGN (Z [i], Bx [i], Bz [i]) ;
		}
		Info [UMFPACK_SOLVE_FLOPS] += MULTSUB_FLOPS * nz ;
		for (i = 0 ; i < n ; i++)
		{
		    zi = Z [i] ;
		    for (p = Ap [i] ; p < Ap [i+1] ; p++)
		    {
			/* zi -= Ax [p] * X [Ai [p]] ; */
			ASSIGN (aij, Ax [p], Az [p]) ;
			MULT_SUB (zi, aij, X [Ai [p]]) ;
		    }
		    Z [i] = zi ;
		}
		for (i = 0 ; i < n ; i++)
		{
		    W [i] = Z [Cperm [i]] ;
		}
	    }
	    Info [UMFPACK_SOLVE_FLOPS] += UMF_utsolve (Numeric, W, Pattern) ;
	    Info [UMFPACK_SOLVE_FLOPS] += UMF_ltsolve (Numeric, W, Pattern) ;
	    if (step == 0)
	    {
		for (i = 0 ; i < n ; i++)
		{
		    X [Rperm [i]] = W [i] ;
		}
	    }
	    else
	    {
		Info [UMFPACK_SOLVE_FLOPS] += ASSEMBLE_FLOPS * n ;
		for (i = 0 ; i < n ; i++)
		{
		    /* X [Rperm [i]] += W [i] ; */
		    ASSEMBLE (X [Rperm [i]], W [i]) ;
		}
	    }

	    /* -------------------------------------------------------------- */
	    /* sparse backward error estimate */
	    /* -------------------------------------------------------------- */

	    if (irstep > 0)
	    {

		/* ---------------------------------------------------------- */
		/* A.' is stored by row */
		/* W (i) = (b-A.'x)_i, residual */
		/* Z (i) = (|A.'||x|)_i */
		/* ---------------------------------------------------------- */

		Info [UMFPACK_SOLVE_FLOPS] +=
		    (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ;
		for (i = 0 ; i < n ; i++)
		{
		    /* wi = B [i] ; */
		    ASSIGN (wi, Bx [i], Bz [i]) ;
		    z2i = 0. ;
		    for (p = Ap [i] ; p < Ap [i+1] ; p++)
		    {
			/* axx = Ax [p] * X [Ai [p]] ; */
			ASSIGN (aij, Ax [p], Az [p]) ;
			MULT (axx, aij, X [Ai [p]]) ;

			/* wi -= axx ; */
			DECREMENT (wi, axx) ;

			/* z2i += ABS (axx) ; */
			ABS (d, axx) ;
			z2i += d ;
		    }
		    W [i] = wi ;
		    Z2 [i] = z2i ;
		}

		if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info))
		{
		    /* iterative refinement is done */
		    break ;
		}

	    }

	}

    }
    else if (sys == UMFPACK_Pt_L)
    {

	/* ------------------------------------------------------------------ */
	/* Solve P'Lx=b:  x = L \ Pb */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [Rperm [i]] ; */
	    ASSIGN (X [i], Bx [Rperm [i]], Bz [Rperm [i]]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_lsolve (Numeric, X, Pattern) ;
	status = UMFPACK_OK ;

    }
    else if (sys == UMFPACK_L)
    {

	/* ------------------------------------------------------------------ */
	/* Solve Lx=b:  x = L \ b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [i] ; */
	    ASSIGN (X [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_lsolve (Numeric, X, Pattern) ;
	status = UMFPACK_OK ;

    }
    else if (sys == UMFPACK_Lt_P)
    {

	/* ------------------------------------------------------------------ */
	/* Solve L'Px=b:  x = P' (L' \ b) */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* W [i] = B [i] ; */
	    ASSIGN (W [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_lhsolve (Numeric, W, Pattern) ;
	for (i = 0 ; i < n ; i++)
	{
	    X [Rperm [i]] = W [i] ;
	}
	status = UMFPACK_OK ;

    }
    else if (sys == UMFPACK_Lat_P)
    {

	/* ------------------------------------------------------------------ */
	/* Solve L.'Px=b:  x = P' (L.' \ b) */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* W [i] = B [i] ; */
	    ASSIGN (W [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_ltsolve (Numeric, W, Pattern) ;
	for (i = 0 ; i < n ; i++)
	{
	    X [Rperm [i]] = W [i] ;
	}
	status = UMFPACK_OK ;

    }
    else if (sys == UMFPACK_Lt)
    {

	/* ------------------------------------------------------------------ */
	/* Solve L'x=b:  x = L' \ b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [i] ; */
	    ASSIGN (X [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_lhsolve (Numeric, X, Pattern) ;
	status = UMFPACK_OK ;

    }
    else if (sys == UMFPACK_Lat)
    {

	/* ------------------------------------------------------------------ */
	/* Solve L.'x=b:  x = L.' \ b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [i] ; */
	    ASSIGN (X [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_ltsolve (Numeric, X, Pattern) ;
	status = UMFPACK_OK ;

    }
    else if (sys == UMFPACK_U_Qt)
    {

	/* ------------------------------------------------------------------ */
	/* Solve UQ'x=b:  x = Q (U \ b) */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* W [i] = B [i] ; */
	    ASSIGN (W [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_usolve (Numeric, W, Pattern) ;
	for (i = 0 ; i < n ; i++)
	{
	    X [Cperm [i]] = W [i] ;
	}

    }
    else if (sys == UMFPACK_U)
    {

	/* ------------------------------------------------------------------ */
	/* Solve Ux=b:  x = U \ b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [i] ; */
	    ASSIGN (X [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_usolve (Numeric, X, Pattern) ;

    }
    else if (sys == UMFPACK_Q_Ut)
    {

	/* ------------------------------------------------------------------ */
	/* Solve QU'x=b:  x = U' \ Q'b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [Cperm [i]] ; */
	    ASSIGN (X [i], Bx [Cperm [i]], Bz [Cperm [i]]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_uhsolve (Numeric, X, Pattern) ;

    }
    else if (sys == UMFPACK_Q_Uat)
    {

	/* ------------------------------------------------------------------ */
	/* Solve QU.'x=b:  x = U.' \ Q'b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [Cperm [i]] ; */
	    ASSIGN (X [i], Bx [Cperm [i]], Bz [Cperm [i]]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_utsolve (Numeric, X, Pattern) ;

    }
    else if (sys == UMFPACK_Ut)
    {

	/* ------------------------------------------------------------------ */
	/* Solve U'x=b:  x = U' \ b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [i] ; */
	    ASSIGN (X [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_uhsolve (Numeric, X, Pattern) ;

    }
    else if (sys == UMFPACK_Uat)
    {

	/* ------------------------------------------------------------------ */
	/* Solve U'x=b:  x = U' \ b */
	/* ------------------------------------------------------------------ */

	for (i = 0 ; i < n ; i++)
	{
	    /* X [i] = B [i] ; */
	    ASSIGN (X [i], Bx [i], Bz [i]) ;
	}
	Info [UMFPACK_SOLVE_FLOPS] = UMF_utsolve (Numeric, X, Pattern) ;

    }
    else
    {
	return (UMFPACK_ERROR_invalid_system) ;
    }

#ifdef COMPLEX
    /* copy the solution back, from Entry X [ ] to double Xx [ ]and Xz [ ]*/
    for (i = 0 ; i < n ; i++)
    {
	Xx [i] = REAL_COMPONENT (X [i]) ;
	Xz [i] = IMAG_COMPONENT (X [i]) ;
    }
#endif

    /* return UMFPACK_OK, or UMFPACK_WARNING_singular_matrix */
    /* Note that systems involving just L will return UMFPACK_OK */
    return (status) ;
}


/* ========================================================================== */
/* === do_step ============================================================== */
/* ========================================================================== */

/* Perform one step of iterative refinement, for Ax=b or A'x=b */

PRIVATE Int do_step		/* return TRUE if iterative refinement done */
(
    double omega [3],
    Int step,			/* which step of iterative refinement to do */
    const double B2 [ ],	/* abs (B) */
    Entry X [ ],
    const Entry W [ ],
    const double Y [ ],
    const double Z2 [ ],
    Entry S [ ],
    Int n,
    double Info [UMFPACK_INFO]
)
{
    double last_omega [3], tau, nctau, d1, wd1, d2, wd2, xi, yix, wi, xnorm ;
    Int i ;

    /* DBL_EPSILON is a standard ANSI C term defined in <float.h> */
    /* It is the smallest positive x such that 1.0+x != 1.0 */

    nctau = 1000 * n * DBL_EPSILON ;
    DEBUG0 (("nctau = %30.20e\n", nctau)) ;

    /* for approximate flop count, assume d1 > tau is always true */
    Info [UMFPACK_SOLVE_FLOPS] += (2*ABS_FLOPS + 5) * n ;

    /* ---------------------------------------------------------------------- */
    /* save the last iteration in case we need to reinstate it */
    /* ---------------------------------------------------------------------- */

    last_omega [0] = omega [0] ;
    last_omega [1] = omega [1] ;
    last_omega [2] = omega [2] ;

    /* ---------------------------------------------------------------------- */
    /* compute sparse backward errors: omega [1] and omega [2] */
    /* ---------------------------------------------------------------------- */

    /* xnorm = ||x|| maxnorm */
    xnorm = 0.0 ;
    for (i = 0 ; i < n ; i++)
    {
	/* xi = ABS (X [i]) ; */
	ABS (xi, X [i]) ;
	if (SCALAR_IS_NAN (xi))
	{
	    xnorm = xi ;
	    break ;
	}
	/* no NaN's to consider here: */
	xnorm = MAX (xnorm, xi) ;
    }

    omega [1] = 0. ;
    omega [2] = 0. ;
    for (i = 0 ; i < n ; i++)
    {
	yix = Y [i] * xnorm ;
	tau = (yix + B2 [i]) * nctau ;
	d1 = Z2 [i] + B2 [i] ;
	/* wi = ABS (W [i]) ; */
	ABS (wi, W [i]) ;
	if (SCALAR_IS_NAN (d1))
	{
	    omega [1] = d1 ;
	    omega [2] = d1 ;
	    break ;
	}
	if (SCALAR_IS_NAN (tau))
	{
	    omega [1] = tau ;
	    omega [2] = tau ;
	    break ;
	}
	if (d1 > tau)		/* a double relop, but no NaN's here */
	{
	    wd1 = wi / d1 ;
	    if (SCALAR_IS_NAN (wd1))
	    {
		omega [1] = wd1 ;
		break ;
	    }
	    /* no NaN's to consider here: */
	    omega [1] = MAX (omega [1], wd1) ;
	}
	else if (tau > 0.0)	/* a double relop, but no NaN's here */
	{
	    d2 = Z2 [i] + yix ;
	    wd2 = wi / d2 ;
	    if (SCALAR_IS_NAN (wd2))
	    {
		omega [2] = wd2 ;
		break ;
	    }
	    /* no NaN's to consider here: */
	    omega [2] = MAX (omega [2], wd2) ;
	}
    }

    omega [0] = omega [1] + omega [2] ;
    Info [UMFPACK_OMEGA1] = omega [1] ;
    Info [UMFPACK_OMEGA2] = omega [2] ;

    /* ---------------------------------------------------------------------- */
    /* stop the iterations if the backward error is small, or NaN */
    /* ---------------------------------------------------------------------- */

    Info [UMFPACK_IR_TAKEN] = step ;
    Info [UMFPACK_IR_ATTEMPTED] = step ;

    if (SCALAR_IS_NAN (omega [0]))
    {
	DEBUG0 (("omega[0] is NaN - done.\n")) ;
	if (step > 0)
	{
	    DEBUG0 (("last iteration better\n")) ;
	    for (i = 0 ; i < n ; i++)
	    {
		X [i] = S [i] ;
	    }
	    Info [UMFPACK_OMEGA1] = last_omega [1] ;
	    Info [UMFPACK_OMEGA2] = last_omega [2] ;
	    Info [UMFPACK_IR_TAKEN] = step - 1 ;
	}
	return (TRUE) ;
    } 

    if (omega [0] < DBL_EPSILON)    /* double relop, but no NaN case here */
    {
	DEBUG0 (("omega[0] too small - done.\n")) ;
	return (TRUE) ;
    }

    /* ---------------------------------------------------------------------- */
    /* stop if insufficient decrease in omega */
    /* ---------------------------------------------------------------------- */

    /* double relop, but no NaN case here: */
    if (step > 0 && omega [0] > last_omega [0] / 2)
    {
	DEBUG0 (("stop refinement\n")) ;
	if (omega [0] > last_omega [0])
	{
	    /* last iteration better than this one, reinstate it */
	    DEBUG0 (("last iteration better\n")) ;
	    for (i = 0 ; i < n ; i++)
	    {
		X [i] = S [i] ;
	    }
	    Info [UMFPACK_OMEGA1] = last_omega [1] ;
	    Info [UMFPACK_OMEGA2] = last_omega [2] ;
	}
	Info [UMFPACK_IR_TAKEN] = step - 1 ;
	return (TRUE) ;
    }

    /* ---------------------------------------------------------------------- */
    /* save current solution in case we need to reinstate */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i < n ; i++)
    {
	S [i] = X [i] ;
    }

    /* ---------------------------------------------------------------------- */
    /* iterative refinement continues */
    /* ---------------------------------------------------------------------- */

    return (FALSE) ;
}


```

sparse-matrix/umfpack/umf_mem_free_tail_block.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void UMF_mem_free_tail_block
(
    NumericType *Numeric,
    Int i
) ;


```

sparse-matrix/umfpack/umf_mem_alloc_tail_block.c:
```
/* ========================================================================== */
/* === UMF_mem_alloc_tail_block ============================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* The UMF_mem_* routines manage the Numeric->Memory memory space. */

#include "umf_internal.h"

/* allocate nunits from tail of Numeric->Memory */
/* (requires nunits+1, for header). */
/* Returns the index into Numeric->Memory if successful, or 0 on failure. */

GLOBAL Int UMF_mem_alloc_tail_block
(
    NumericType *Numeric,
    Int nunits
)
{
    Int bigsize, usage ;
    Unit *p, *pnext, *pbig ;

    ASSERT (Numeric) ;
    ASSERT (Numeric->Memory) ;

#ifndef NDEBUG
    if (UMF_allocfail)
    {
	/* pretend to fail, to test garbage_collection */
	UMF_allocfail = FALSE ;	/* don't fail the next time */
	return (0) ;
    }
    DEBUG2 (("UMF_mem_alloc_tail_block, size: "ID" + 1 = "ID":  ",
	nunits, nunits+1)) ;
#endif

    bigsize = 0 ;
    pbig = (Unit *) NULL ;

    ASSERT (nunits > 0) ;	/* size must be positive */
    if (Numeric->ibig != EMPTY)
    {
	ASSERT (Numeric->ibig > Numeric->itail) ;
	ASSERT (Numeric->ibig < Numeric->size) ;
	pbig = Numeric->Memory + Numeric->ibig ;
	bigsize = -pbig->header.size ;
	ASSERT (bigsize > 0) ;	/* Numeric->ibig is free */
	ASSERT (pbig->header.prevsize >= 0) ;	/* prev. is not free */
    }

    if (pbig && bigsize >= nunits)
    {

	/* use the biggest block, somewhere in middle of memory */
	p = pbig ;
	pnext = p + 1 + bigsize ;
	/* next is in range */
	ASSERT (pnext < Numeric->Memory + Numeric->size) ;
	/* prevsize of next = this size */
	ASSERT (pnext->header.prevsize == bigsize) ;
	/* next is not free */
	ASSERT (pnext->header.size > 0) ;
	bigsize -= nunits + 1 ;

	if (bigsize < 4)
	{
	    /* internal fragmentation would be too small */
	    /* allocate the entire free block */
	    p->header.size = -p->header.size ;
	    DEBUG2 (("GET  BLOCK: p: "ID" size: "ID", all of big: "ID" size: "
		ID"\n", p-Numeric->Memory, nunits, Numeric->ibig,
		p->header.size)) ;
	    /* no more biggest block */
	    Numeric->ibig = EMPTY ;

	}
	else
	{

	    /* allocate just the first nunits Units of the free block */
	    p->header.size = nunits ;
	    /* make a new free block */
	    Numeric->ibig += nunits + 1 ;
	    pbig = Numeric->Memory + Numeric->ibig ;
	    pbig->header.size = -bigsize ;
	    pbig->header.prevsize = nunits ;
	    pnext->header.prevsize = bigsize ;
	    DEBUG2 (("GET  BLOCK: p: "ID" size: "ID", some of big: "ID" left: "
		ID"\n", p-Numeric->Memory, nunits, Numeric->ibig, bigsize)) ;
	}

    }
    else
    {

	/* allocate from the top of tail */
	pnext = Numeric->Memory + Numeric->itail ;
	DEBUG2 (("GET  BLOCK: from tail ")) ;
	if ((nunits + 1) > (Numeric->itail - Numeric->ihead))
	{
	    DEBUG2 (("\n")) ;
	    return (0) ;
	}
	Numeric->itail -= (nunits + 1) ;
	p = Numeric->Memory + Numeric->itail ;
	p->header.size = nunits ;
	p->header.prevsize = 0 ;
	pnext->header.prevsize = nunits ;
	DEBUG2 (("p: "ID" size: "ID", new tail "ID"\n",
		p-Numeric->Memory, nunits, Numeric->itail)) ;
    }

    Numeric->tail_usage += p->header.size + 1 ;
    usage = Numeric->ihead + Numeric->tail_usage ;
    Numeric->max_usage = MAX (Numeric->max_usage, usage) ;

#ifndef NDEBUG
    UMF_debug -= 10 ;
    UMF_dump_memory (Numeric) ;
    UMF_debug += 10 ;
#endif

    /* p points to the header.  Add one to point to the usable block itself. */
    /* return the offset into Numeric->Memory */
    return ((p - Numeric->Memory) + 1) ;
}


```

sparse-matrix/umfpack/umfpack_report_symbolic.h:
```
/* ========================================================================== */
/* === umfpack_report_symbolic ============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_report_symbolic
(
    void *Symbolic,
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_dl_report_symbolic
(
    void *Symbolic,
    const double Control [UMFPACK_CONTROL]
) ;

int umfpack_zi_report_symbolic
(
    void *Symbolic,
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_zl_report_symbolic
(
    void *Symbolic,
    const double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    double Control [UMFPACK_CONTROL] ;
    int status ;
    status = umfpack_di_report_symbolic (Symbolic, Control) ;

double long Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    double Control [UMFPACK_CONTROL] ;
    long status ;
    status = umfpack_dl_report_symbolic (Symbolic, Control) ;

complex int Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    double Control [UMFPACK_CONTROL] ;
    int status ;
    status = umfpack_zi_report_symbolic (Symbolic, Control) ;

complex long Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    double Control [UMFPACK_CONTROL] ;
    long status ;
    status = umfpack_zl_report_symbolic (Symbolic, Control) ;

Purpose:

    Verifies and prints a Symbolic object.  This routine checks the object more
    carefully than the computational routines.  Normally, this check is not
    required, since umfpack_*_*symbolic either returns (void *) NULL, or a valid
    Symbolic object.  However, if you suspect that your own code has corrupted
    the Symbolic object (by overruning memory bounds, for example), then this
    routine might be able to detect a corrupted Symbolic object.  Since this is
    a complex object, not all such user-generated errors are guaranteed to be
    caught by this routine.

Returns:

    UMFPACK_OK if Control [UMFPACK_PRL] is <= 2 (no inputs are checked).

    Otherwise:

    UMFPACK_OK if the Symbolic object is valid.
    UMFPACK_ERROR_invalid_Symbolic_object if the Symbolic object is invalid.
    UMFPACK_ERROR_out_of_memory if out of memory.

Arguments:

    void *Symbolic ;			Input argument, not modified.

	The Symbolic object, which holds the symbolic factorization computed by
	umfpack_*_*symbolic.

    double Control [UMFPACK_CONTROL] ;	Input argument, not modified.

	If a (double *) NULL pointer is passed, then the default control
	settings are used.  Otherwise, the settings are determined from the
	Control array.  See umfpack_*_defaults on how to fill the Control
	array with the default settings.  If Control contains NaN's, the
	defaults are used.  The following Control parameters are used:

	Control [UMFPACK_PRL]:  printing level.

	    2 or less: no output.  returns silently without checking anything.
	    3: fully check input, and print a short summary of its status
	    4: as 3, but print first few entries of the input
	    5: as 3, but print all of the input
	    Default: 1
*/

```

sparse-matrix/umfpack/umf_valid_symbolic.c:
```
/* ========================================================================== */
/* === UMF_valid_symbolic =================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

#include "umf_internal.h"

/* Returns TRUE if the Symbolic object is valid, FALSE otherwise. */
/* The UMFPACK_report_symbolic routine does a more thorough check. */

GLOBAL Int UMF_valid_symbolic
(
    SymbolicType *Symbolic
)
{
    /* This routine does not check the contents of the individual arrays, so */
    /* it can miss some errors.  All it checks for is the presence of the */
    /* arrays, and the Symbolic "valid" entry. */

    if (!Symbolic)
    {
	return (FALSE) ;
    }

    if (Symbolic->valid != SYMBOLIC_VALID)
    {
	/* Symbolic does not point to a SymbolicType object */
	return (FALSE) ;
    }

    if (!Symbolic->Cperm_init || !Symbolic->Rperm_init ||
    	!Symbolic->Front_npivcol || !Symbolic->Front_1strow ||
	!Symbolic->Front_leftmostdesc ||
	!Symbolic->Front_parent || !Symbolic->Chain_start ||
	!Symbolic->Chain_maxrows || !Symbolic->Chain_maxcols ||
	Symbolic->n_row <= 0 || Symbolic->n_col <= 0)
    {
	return (FALSE) ;
    }

    return (TRUE) ;
}


```

sparse-matrix/umfpack/umf_analyze.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_analyze
(
    Int n_row,
    Int n_col,
    Int Ai [ ],
    Int Ap [ ],
    Int Up [ ],
    Int Front_ncols [ ],
    Int W [ ],
    Int Link [ ],
    Int Front_nrows [ ],
    Int Front_npivcol [ ],
    Int Front_parent [ ],
    Int *nfr_out,
    Int *p_ncompactions
) ;


```

sparse-matrix/umfpack/umfpack_numeric.h:
```
/* ========================================================================== */
/* === umfpack_numeric ====================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_numeric
(
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ],
    void *Symbolic,
    void **Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

long umfpack_dl_numeric
(
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ],
    void *Symbolic,
    void **Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

int umfpack_zi_numeric
(
    const int Ap [ ],
    const int Ai [ ],
    const double Ax [ ], const double Az [ ],
    void *Symbolic,
    void **Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

long umfpack_zl_numeric
(
    const long Ap [ ],
    const long Ai [ ],
    const double Ax [ ], const double Az [ ],
    void *Symbolic,
    void **Numeric,
    const double Control [UMFPACK_CONTROL],
    double Info [UMFPACK_INFO]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Symbolic, *Numeric ;
    int *Ap, *Ai, status ;
    double *Ax, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric,
	Control, Info) ;

double long Syntax:

    #include "umfpack.h"
    void *Symbolic, *Numeric ;
    long *Ap, *Ai, status ;
    double *Ax, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_dl_numeric (Ap, Ai, Ax, Symbolic, &Numeric,
	Control, Info) ;

complex int Syntax:

    #include "umfpack.h"
    void *Symbolic, *Numeric ;
    int *Ap, *Ai, status ;
    double *Ax, *Az, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_zi_numeric (Ap, Ai, Ax, Az, Symbolic, &Numeric,
	Control, Info) ;

complex long Syntax:

    #include "umfpack.h"
    void *Symbolic, *Numeric ;
    long *Ap, *Ai, status ;
    double *Ax, *Az, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_zl_numeric (Ap, Ai, Ax, Symbolic, &Numeric,
	Control, Info) ;

Purpose:

    Given a sparse matrix A in column-oriented form, and a symbolic analysis
    computed by umfpack_*_*symbolic, the umfpack_*_numeric routine performs the
    numerical factorization, PAQ=LU, where P and Q are permutation matrices
    (represented as permutation vectors), L is unit-lower triangular, and U
    is upper triangular.  This is required before the system Ax=b (or other
    related linear systems) can be solved.  umfpack_*_numeric can be called
    multiple times for each call to umfpack_*_symbolic, to factorize a sequence
    of matrices with identical nonzero pattern.  Simply compute the Symbolic
    object once, with umfpack_*_*symbolic, and reuse it for subsequent matrices.
    umfpack_*_numeric safely detects if the pattern changes, and sets an
    appropriate error code.

Returns:

    The status code is returned.  See Info [UMFPACK_STATUS], below.

Arguments:

    Int Ap [n_col+1] ;	Input argument, not modified.

	This must be identical to the Ap array passed to umfpack_*_*symbolic.
	The value of n_col is what was passed to umfpack_*_*symbolic (this is
	held in the Symbolic object).

    Int Ai [nz] ;	Input argument, not modified, of size nz = Ap [n_col].

	This must be identical to the Ai array passed to umfpack_*_*symbolic.

    double Ax [nz] ;	Input argument, not modified, of size nz = Ap [n_col].

	The numerical values of the sparse matrix A.  The nonzero pattern (row
	indices) for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and
	the corresponding numerical values are stored in
	Ax [(Ap [j]) ... (Ap [j+1]-1)].

    double Az [nz] ;	Input argument, not modified, for complex versions.

	For the complex versions, this holds the imaginary part of A.  The
	imaginary part of column j is held in Az [(Ap [j]) ... (Ap [j+1]-1)].

	Future complex version:  if Ax is present and Az is NULL, then both real
	and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k]
	and Ax[2*k+1] being the real and imaginary part of the kth entry.

    void *Symbolic ;	Input argument, not modified.

	The Symbolic object, which holds the symbolic factorization computed by
	umfpack_*_*symbolic.  The Symbolic object is not modified by
	umfpack_*_numeric.

    void **Numeric ;	Output argument.

	**Numeric is the address of a (void *) pointer variable in the user's
	calling routine (see Syntax, above).  On input, the contents of this
	variable are not defined.  On output, this variable holds a (void *)
	pointer to the Numeric object (if successful), or (void *) NULL if
	a failure occurred.

    double Control [UMFPACK_CONTROL] ;   Input argument, not modified.

	If a (double *) NULL pointer is passed, then the default control
	settings are used.  Otherwise, the settings are determined from the
	Control array.  See umfpack_*_defaults on how to fill the Control
	array with the default settings.  If Control contains NaN's, the
	defaults are used.  The following Control parameters are used:

	Control [UMFPACK_PIVOT_TOLERANCE]:  relative pivot tolerance for
	    threshold partial pivoting with row interchanges.  In any given
	    column, an entry is numerically acceptable if its absolute value is
	    greater than or equal to Control [UMFPACK_PIVOT_TOLERANCE] times
	    the largest absolute value in the column.  A value of 1.0 gives true
	    partial pivoting.  A value of zero is treated as 1.0.  Default: 0.1.
	    Smaller values tend to lead to sparser LU factors, but the solution
	    to the linear system can become inaccurate.  Larger values can lead
	    to a more accurate solution (but not always), and usually an
	    increase in the total work.

	    For complex matrices, a cheap approximate of the absolute value
	    is used for the threshold partial pivoting test (|a_real| + |a_imag|
	    instead of the more expensive-to-compute exact absolute value
	    sqrt (a_real^2 + a_imag^2)).

	Control [UMFPACK_RELAXED_AMALGAMATION]:  This controls the creation of
	    "elements" (small dense submatrices) that are formed when a frontal
	    matrix is factorized.  A new element is created if the current one,
	    plus the new pivot, contains "too many" explicitly zero numerical
	    entries.  The two elements are merged if the number of extra zero
	    entries is < Control [UMFPACK_RELAXED_AMALGAMATION] times the
	    size of the merged element.  A lower setting decreases fill-in, but
	    run-time and memory usage can increase.  A larger setting increases
	    fill-in (because the extra zeros are treated as normal entries
	    during pivot selection), but this can lead to an increase in
	    run-time but (paradoxically) a decrease in memory usage (one merged
	    elements can take less space than two separate elements).  Except
	    for the initial column ordering, this parameter has the most impact
	    on the run-time, fill-in, operation count, and memory usage.
	    Default: 0.25, which is fine for nearly all matrices.
	    (For nearly all matrices that I've tested, different values of this
	    parameter can decrease the run-time by at most 5%, but can also
	    dramatically increase the run time for some matrices).

	Control [UMFPACK_RELAXED2_AMALGAMATION]:  This, along with the block
	    size (Control [UMFPACK_BLOCK_SIZE]), controls how often the
	    pending updates are applied when the next pivot entry resides in
	    the current frontal matrix.  If the number of zero entries in the
	    LU part of the current frontal matrix would exceed this parameter
	    times the size of the LU part, then the pending updates are applied
	    before the next pivot is included in the frontal matrix.
	    Default: 0.20 (that is, more than 20% zero entries causes the
	    pending updates to be applied).  This input parameter is new
	    since Version 3.1.

	Control [UMFPACK_RELAXED3_AMALGAMATION]:  This, along with the block
	    size (Control [UMFPACK_BLOCK_SIZE]), controls how often the pending
	    updates are applied when the next pivot entry does NOT reside
	    in the current frontal matrix.  If the number of zero entries in the
	    LU part of the current frontal matrix would exceed this parameter
	    times the size of the LU part, then the pending updates are applied
	    before the next pivot is included in the frontal matrix.
	    Default: 0.10 (that is, more than 10% zero entries causes the
	    pending updates to be applied).  This input parameter is new
	    since Version 3.1.

	Control [UMFPACK_ALLOC_INIT]: When umfpack_*_numeric starts, it
	    allocates memory for the Numeric object.  Part of this is of fixed
	    size (approximately n double's + 12*n integers).  The remainder is
	    of variable size, which grows to hold the LU factors and the frontal
	    matrices created during factorization.  A estimate of the upper
	    bound is computed by umfpack_*_*symbolic, and returned by
	    umfpack_*_*symbolic in Info [UMFPACK_VARIABLE_PEAK_ESTIMATE].
	    umfpack_*_numeric initially allocates space for the variable-sized
	    part equal to this estimate times Control [UMFPACK_ALLOC_INIT].
	    Typically, umfpack_*_numeric needs only about half the estimated
	    memory space, so a setting of 0.5 or 0.6 often provides enough
	    memory for umfpack_*_numeric to factorize the matrix with no
	    subsequent increases in the size of this block.  A value less than
	    zero is treated as zero (in which case, just the bare minimum
	    amount of memory needed to start the factorization is initially
	    allocated).  The bare initial memory required is returned by
	    umfpack_*_*symbolic in Info [UMFPACK_VARIABLE_INIT_ESTIMATE] (which
	    in fact not an estimate, but exact).  If the variable-size part of
	    the Numeric object is found to be too small sometime after numerical
	    factorization has started, the memory is increased in size by a
	    factor of 1.2.   If this fails, the request is reduced by a factor
	    of 0.95 until it succeeds, or until it determines that no increase
	    in size is possible.  Garbage collection then occurs.  These two
	    factors (1.2 and 0.95) are fixed control parameters defined in
	    umf_internal.h and cannot be changed at run-time.  You would need
	    to edit umf_internal.h to modify them.  If you do this, be sure that
	    the two factors are greater than 1 and less than 1, respectively.

	    The strategy of attempting to malloc a working space, and re-trying
	    with a smaller space, may not work under MATLAB, since mxMalloc
	    aborts the mexFunction if it fails.  The built-in umfpack routine
	    uses utMalloc instead, which does cause this problem.  If you are
	    using the umfpack mexFunction, decrease Control [UMFPACK_ALLOC_INIT]
	    if you run out of memory in MATLAB.

	    Default initial allocation size: 0.7.  Thus, with the default
	    control settings, the upper-bound is reached after two reallocations
	    (0.7 * 1.2 * 1.2 = 1.008).

	    Changing this parameter has no effect on fill-in or operation count.
	    It has a small impact on run-time (the extra time required to do
	    the garbage collection and memory reallocation).

    double Info [UMFPACK_INFO] ;	Output argument.

	Contains statistics about the numeric factorization.  If a
	(double *) NULL pointer is passed, then no statistics are returned in
	Info (this is not an error condition).  The following statistics are
	computed in umfpack_*_numeric:

	Info [UMFPACK_STATUS]: status code.  This is also the return value,
	    whether or not Info is present.

	    UMFPACK_OK

		Numeric factorization was successful.  umfpack_*_numeric
		computed a valid numeric factorization.

	    UMFPACK_WARNING_singular_matrix

		Numeric factorization was successful, but the matrix is
		singular.  umfpack_*_numeric computed a valid numeric
		factorization, but you will get a divide by zero in
		umfpack_*_*solve.  For the other cases below, no Numeric object
		is created (*Numeric is (void *) NULL).
		
	    UMFPACK_ERROR_out_of_memory

		Insufficient memory to complete the numeric factorization.

	    UMFPACK_ERROR_argument_missing

		One or more required arguments are missing.

	    UMFPACK_ERROR_invalid_Symbolic_object

		Symbolic object provided as input is invalid.

	    UMFPACK_ERROR_different_pattern

		The pattern (Ap and/or Ai) has changed since the call to
		umfpack_*_*symbolic which produced the Symbolic object.

	Info [UMFPACK_NROW]:  the value of n_row stored in the Symbolic object.

	Info [UMFPACK_NCOL]:  the value of n_col stored in the Symbolic object.

	Info [UMFPACK_NZ]:  the number of entries in the input matrix.
	    This value is obtained from the Symbolic object.

	Info [UMFPACK_SIZE_OF_UNIT]:  the number of bytes in a Unit, for memory
	    usage statistics below.

	Info [UMFPACK_VARIABLE_INIT]: the initial size (in Units) of the
	    variable-sized part of the Numeric object.  If this differs from
	    Info [UMFPACK_VARIABLE_INIT_ESTIMATE], then the pattern (Ap and/or
	    Ai) has changed since the last call to umfpack_*_*symbolic, which is
	    an error condition.

	Info [UMFPACK_VARIABLE_PEAK]: the peak size (in Units) of the
	    variable-sized part of the Numeric object.  This size is the amount
	    of space actually used inside the block of memory, not the space
	    allocated via UMF_malloc.  You can reduce UMFPACK's memory
	    requirements by setting Control [UMFPACK_ALLOC_INIT] to the ratio
	    Info [UMFPACK_VARIABLE_PEAK] / Info[UMFPACK_VARIABLE_PEAK_ESTIMATE].
	    This will ensure that no memory reallocations occur (you may want to
	    add 0.001 to make sure that integer roundoff does not lead to a
	    memory size that is 1 Unit too small; otherwise, garbage collection
	    and reallocation will occur).

	Info [UMFPACK_VARIABLE_FINAL]: the final size (in Units) of the
	    variable-sized part of the Numeric object.  It holds just the
	    sparse LU factors.

	Info [UMFPACK_NUMERIC_SIZE]:  the actual final size (in Units) of the
	    entire Numeric object, including the final size of the variable
	    part of the object.  Info [UMFPACK_NUMERIC_SIZE_ESTIMATE],
	    an estimate, was computed by umfpack_*_*symbolic.  The estimate is
	    normally an upper bound on the actual final size, but this is not
	    guaranteed.

	Info [UMFPACK_PEAK_MEMORY]:  the actual peak memory usage (in Units) of
	    both umfpack_*_*symbolic and umfpack_*_numeric.  An estimate,
	    Info [UMFPACK_PEAK_MEMORY_ESTIMATE], was computed by
	    umfpack_*_*symbolic.  The estimate is normally an upper bound on the
	    actual peak usage, but this is not guaranteed.  With testing on
	    hundreds of matrix arising in real applications, I have never
	    observed a matrix where this estimate or the Numeric size estimate
	    was less than the actual result, but this is theoretically possible.
	    Please send me one if you find such a matrix.

	Info [UMFPACK_FLOPS]:  the actual count of the (useful) floating-point
	    operations performed.  An estimate, Info [UMFPACK_FLOPS_ESTIMATE],
	    was computed by umfpack_*_*symbolic.  The estimate is guaranteed to
	    be an upper bound on this flop count.  The flop count excludes
	    "useless" flops on zero values, flops performed during the pivot
	    search (for tentative updates and assembly of candidate columns),
	    and flops performed to add frontal matrices together.

	    For the real version, only (+ - * /) are counted.  For the complex
	    version, the following counts are used:

		operation	flops
	    	c = 1/b		6
		c = a*b		6
		c -= a*b	8

	Info [UMFPACK_LNZ]: the actual nonzero entries in final factor L,
	    including the diagonal.  This excludes any zero entries in L,
	    although some of these are stored in the Numeric object.  The
	    Info [UMFPACK_LU_ENTRIES] statistic does account for all
	    explicitly stored zeros, however.  Info [UMFPACK_LNZ_ESTIMATE],
	    an estimate, was computed by umfpack_*_*symbolic.  The estimate is
	    guaranteed to be an upper bound on Info [UMFPACK_LNZ].

	Info [UMFPACK_UNZ]: the actual nonzero entries in final factor U,
	    including the diagonal.  This excludes any zero entries in U,
	    although some of these are stored in the Numeric object.  The
	    Info [UMFPACK_LU_ENTRIES] statistic does account for all
	    explicitly stored zeros, however.  Info [UMFPACK_UNZ_ESTIMATE],
	    an estimate, was computed by umfpack_*_*symbolic.  The estimate is
	    guaranteed to be an upper bound on Info [UMFPACK_UNZ].

	Info [UMFPACK_NUMERIC_DEFRAG]:  The number of garbage collections
	    performed during umfpack_*_numeric, to compact the contents of the
	    variable-sized workspace used by umfpack_*_numeric.  No estimate was
	    computed by umfpack_*_*symbolic.  In the current version of UMFPACK,
	    garbage collection is performed and then the memory is reallocated,
	    so this statistic is the same as Info [UMFPACK_NUMERIC_REALLOC],
	    below.  It may differ in future releases.

	Info [UMFPACK_NUMERIC_REALLOC]:  The number of times that the Numeric
	    object was increased in size from its initial size.  A rough upper
	    bound on the peak size of the Numeric object was computed by
	    umfpack_*_*symbolic, so reallocations should be rare.  However, if
	    umfpack_*_numeric is unable to allocate that much storage, it
	    reduces its request until either the allocation succeeds, or until
	    it gets too small to do anything with.  If the memory that it
	    finally got was small, but usable, then the reallocation count
	    could be high.  No estimate of this count was computed by
	    umfpack_*_*symbolic.

	Info [UMFPACK_NUMERIC_COSTLY_REALLOC]:  The number of times that the
	    system realloc library routine (or mxRealloc for the mexFunction)
	    had to move the workspace.  Realloc can sometimes increase the size
	    of a block of memory without moving it, which is much faster.  This
	    statistic will always be <= Info [UMFPACK_NUMERIC_REALLOC].  If your
	    memory space is fragmented, then the number of "costly" realloc's
	    will be equal to Info [UMFPACK_NUMERIC_REALLOC].

	Info [UMFPACK_COMPRESSED_PATTERN]:  The number of integers used to
	    represent the pattern of L and U.

	Info [UMFPACK_LU_ENTRIES]:  The total number of numerical values that
	    are stored for the LU factors.  Some of the values may be explicitly
	    zero in order to save space (allowing for a smaller compressed
	    pattern).

	Info [UMFPACK_NUMERIC_TIME]:  The time taken by umfpack_*_numeric, in
	    seconds.  In the ANSI C version, this may be invalid if the time
	    taken is more than about 36 minutes, because of wrap-around in the
	    ANSI C clock function.  Compile UMFPACK with -DGETRUSAGE if you have
	    the more accurate getrusage function.

	Info [UMFPACK_RCOND]:  A rough estimate of the condition number, equal
	    to min (abs (diag (U))) / max (abs (diag (U))), or zero if the
	    diagonal of U is all zero.

	Info [UMFPACK_UDIAG_NZ]:  The number of numerically nonzero values on
	    the diagonal of U.

	Only the above listed Info [...] entries are accessed.  The remaining
	entries of Info are not accessed or modified by umfpack_*_numeric.
	Future versions might modify different parts of Info.
*/

```

sparse-matrix/umfpack/umf_mem_alloc_element.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_mem_alloc_element
(
    NumericType *Numeric,
    Int nrows,
    Int ncols,
    Int **Rows,
    Int **Cols,
    Entry **C,
    Int *size,
    Element **epout
) ;


```

sparse-matrix/umfpack/README:
```
This directory contains a subset of the files from the
UMFPACK Version 4.0 (Apr 11, 2002) sparse matrix package.
UMFPACK is copyright (c) 2002 by Timothy A. Davis.

See  License  for the UMFPACK license.

See  README.UMFPACK  for the original UMFPACK README file, including
the url where the full package (including documentation) may be optained.

make.code.defn  controls the Cactus compilation of this directory.

umfpack_wsolve.c  is a copy of  umfpack_solve.c  to work around
limitations in the Cactus  make.code.defn  system.

```

sparse-matrix/umfpack/umfpack_free_symbolic.c:
```
/* ========================================================================== */
/* === UMFPACK_free_symbolic ================================================ */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  See umfpack_free_symbolic.h for details.
    All 10 objects comprising the Symbolic object are free'd via UMF_free.
*/

#include "umf_internal.h"
#include "umf_free.h"

GLOBAL void UMFPACK_free_symbolic
(
    void **SymbolicHandle
)
{

    SymbolicType *Symbolic ;
    if (!SymbolicHandle)
    {
	return ;
    }
    Symbolic = *((SymbolicType **) SymbolicHandle) ;
    if (!Symbolic)
    {
	return ;
    }

    (void) UMF_free ((void *) Symbolic->Cperm_init) ;
    (void) UMF_free ((void *) Symbolic->Rperm_init) ;
    (void) UMF_free ((void *) Symbolic->Front_npivcol) ;
    (void) UMF_free ((void *) Symbolic->Front_parent) ;
    (void) UMF_free ((void *) Symbolic->Front_1strow) ;
    (void) UMF_free ((void *) Symbolic->Front_leftmostdesc) ;
    (void) UMF_free ((void *) Symbolic->Chain_start) ;
    (void) UMF_free ((void *) Symbolic->Chain_maxrows) ;
    (void) UMF_free ((void *) Symbolic->Chain_maxcols) ;
    (void) UMF_free ((void *) Symbolic) ;

    *SymbolicHandle = (void *) NULL ;
}


```

sparse-matrix/umfpack/umf_utsolve.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL double UMF_utsolve
(
    NumericType *Numeric,
    Entry X [ ],
    Int Pattern [ ]
) ;


GLOBAL double UMF_uhsolve
(
    NumericType *Numeric,
    Entry X [ ],
    Int Pattern [ ]
) ;


```

sparse-matrix/umfpack/umf_garbage_collection.c:
```
/* ========================================================================== */
/* === UMF_garbage_collection =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Compress the elements at the tail of Numeric->Memory, and delete the tuples.
    Elements are renumbered.  The new numbering space is compressed, and
    in the order of element creation (original elements of A first, followed
    by the new elements in the order that they were formed).
*/

#include "umf_internal.h"

GLOBAL void UMF_garbage_collection
(
    NumericType *Numeric,
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int size, e, n_row, n_col, nrows, ncols, nrowsleft, ncolsleft, prevsize,
	csize, size2, i2, j2, i, j, cdeg, rdeg, *E, row, col, n_inner,
	*Rows, *Cols, *Rows2, *Cols2, nel, e2, *Row_tuples, *Col_tuples,
	*Row_degree, *Col_degree ;
    Entry *C, *C1, *C3, *C2 ;
    Unit *psrc, *pdest, *p, *pnext ;
    Element *epsrc, *epdest ;

#ifndef NDEBUG
    Int nmark ;
#endif

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

    Col_degree = Numeric->Cperm ;	/* for NON_PIVOTAL_COL macro */
    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro */
    Row_tuples = Numeric->Uip ;
    Col_tuples = Numeric->Lip ;
    E = Work->E ;
    n_row = Work->n_row ;
    n_col = Work->n_col ;
    n_inner = MIN (n_row, n_col) ;

    /* note that the tuple lengths (Col_tlen and Row_tlen) are updated, but */
    /* the tuple lists themselves are stale and are about to be destroyed */
    /* and recreated.  Do not attempt to scan them until they are recreated. */

#ifndef NDEBUG
    DEBUG0 (("::::GARBAGE COLLECTION::::\n")) ;
    UMF_dump_memory (Numeric) ;
#endif

    Numeric->ngarbage++ ;

    /* ---------------------------------------------------------------------- */
    /* delete the tuple lists by marking the blocks as free */
    /* ---------------------------------------------------------------------- */

    /* do not modify Row_tlen and Col_tlen */
    /* those are needed for UMF_build_tuples */

    for (row = 0 ; row < n_row ; row++)
    {
	if (NON_PIVOTAL_ROW (row) && Row_tuples [row])
	{
	    DEBUG2 (("row "ID" tuples "ID"\n", row, Row_tuples [row])) ;
	    p = Numeric->Memory + Row_tuples [row] - 1 ;
	    DEBUG2 (("Freeing tuple list row "ID", p-S "ID", size "ID"\n",
		row, p-Numeric->Memory, p->header.size)) ;
	    ASSERT (p->header.size > 0) ;
	    ASSERT (p >= Numeric->Memory + Numeric->itail) ;
	    ASSERT (p < Numeric->Memory + Numeric->size) ;
	    p->header.size = -p->header.size ;
	    Row_tuples [row] = 0 ;
	}
    }

    for (col = 0 ; col < n_col ; col++)
    {
	if (NON_PIVOTAL_COL (col) && Col_tuples [col])
	{
	    DEBUG2 (("col "ID" tuples "ID"\n", col, Col_tuples [col])) ;
	    p = Numeric->Memory + Col_tuples [col] - 1 ;
	    DEBUG2 (("Freeing tuple list col "ID", p-S "ID", size "ID"\n",
		col, p-Numeric->Memory, p->header.size)) ;
	    ASSERT (p->header.size > 0) ;
	    ASSERT (p >= Numeric->Memory + Numeric->itail) ;
	    ASSERT (p < Numeric->Memory + Numeric->size) ;
	    p->header.size = -p->header.size ;
	    Col_tuples [col] = 0 ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* mark the elements, and compress the name space */
    /* ---------------------------------------------------------------------- */

    nel = Work->nel ;

#ifndef NDEBUG
    nmark = 0 ;
#endif

    e2 = 0 ;
    ASSERT (!E [0]) ;	/* current version never uses element zero */

    for (e = 0 ; e <= nel ; e++) /* for all elements in order of creation */
    {
	if (E [e])
	{
	    psrc = Numeric->Memory + E [e] ;
	    psrc-- ;		/* get the header of this block */
	    if (e > 0)
	    {
		e2++ ;	/* do not renumber element zero */
	    }
	    ASSERT (psrc->header.size > 0) ;
	    psrc->header.size = e2  ;	/* store the new name in the header */
#ifndef NDEBUG
	    nmark++ ;
#endif
	    DEBUG7 ((ID":: Mark e "ID" at psrc-S "ID", new e "ID"\n",
		nmark, e, psrc-Numeric->Memory, e2)) ;
	    E [e] = 0 ;

	    if (e == Work->nelorig)
	    {
		/* this is the highest numbered original element */
		Work->nelorig = e2 ;
	    }
	    if (e == Work->prior_element)
	    {
		Work->prior_element = e2 ;
	    }
	}
    }

    /* all 1..e2 are now in use (element zero may or may not be in use) */
    Work->nel = e2 ;
    nel = Work->nel ;

#ifndef NDEBUG
    for (e = 0 ; e <= n_col + n_inner ; e++)
    {
	ASSERT (!E [e]) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* compress the elements */
    /* ---------------------------------------------------------------------- */

    /* point to tail marker block of size 1 + header */
    psrc = Numeric->Memory + Numeric->size - 2 ;
    pdest = psrc ;
    prevsize = psrc->header.prevsize ;
    DEBUG7 (("Starting the compression:\n")) ;

    while (prevsize > 0)
    {

	/* ------------------------------------------------------------------ */
	/* move up to the next element above the current header, and */
	/* get the element name and size */
	/* (if it is an element, the name will be positive) */
	/* ------------------------------------------------------------------ */

	size = prevsize ;
	psrc -= (size + 1) ;
	e = psrc->header.size ;
	prevsize = psrc->header.prevsize ;
	/* top block at tail has prevsize of 0 */

	/* a free block will have a negative size, so skip it */
	/* otherwise, if size > 0, it holds the element name, not the size */

	DEBUG8 (("psrc-S: "ID" prevsize: "ID" size: "ID, psrc-Numeric->Memory,
	prevsize, size)) ;

	if (e >= 0)
	{

	    /* -------------------------------------------------------------- */
	    /* this is an element, compress and move from psrc down to pdest */
	    /* -------------------------------------------------------------- */

#ifndef NDEBUG
	    DEBUG7 (("\n")) ;
	    DEBUG7 ((ID":: Move element "ID": from: "ID" \n",
		nmark, e, psrc-Numeric->Memory)) ;
	    nmark-- ;
	    ASSERT (e <= nel) ;
	    ASSERT (E [e] == 0) ;
#endif

	    /* -------------------------------------------------------------- */
	    /* get the element scalars, and pointers to C, Rows, and Cols: */
	    /* -------------------------------------------------------------- */

	    p = psrc + 1 ;
	    GET_ELEMENT (epsrc, p, Cols, Rows, ncols, nrows, C) ;
	    nrowsleft = epsrc->nrowsleft ;
	    ncolsleft = epsrc->ncolsleft ;
	    cdeg = epsrc->cdeg ;
	    rdeg = epsrc->rdeg ;

#ifndef NDEBUG
	    DEBUG7 ((" nrows "ID" nrowsleft "ID"\n", nrows, nrowsleft)) ;
	    DEBUG7 ((" ncols "ID" ncolsleft "ID"\n", ncols, ncolsleft)) ;
	    DEBUG8 ((" Rows:")) ;
	    for (i = 0 ; i < nrows ; i++) DEBUG8 ((" "ID, Rows [i])) ;
	    DEBUG8 (("\n Cols:")) ;
	    for (j = 0 ; j < ncols ; j++) DEBUG8 ((" "ID, Cols [j])) ;
	    DEBUG8 (("\n")) ;
#endif

	    /* -------------------------------------------------------------- */
	    /* determine the layout of the new element */
	    /* -------------------------------------------------------------- */

	    csize = nrowsleft * ncolsleft ;
	    size2 = UNITS (Element, 1)
		  + UNITS (Int, nrowsleft + ncolsleft)
		  + UNITS (Entry, csize) ;

	    DEBUG7 (("Old size "ID" New size "ID"\n", size, size2)) ;

	    pnext = pdest ;
	    pnext->header.prevsize = size2 ;
	    pdest -= (size2 + 1) ;
	    DEBUG7 (("Move element from psrc-S "ID" to pdest-S "ID"\n",
		psrc-Numeric->Memory, pdest-Numeric->Memory));

	    ASSERT (e > 0 || size == size2) ;
	    ASSERT (size2 <= size) ;
	    ASSERT (psrc + 1 + size <= pnext) ;
	    ASSERT (psrc <= pdest) ;

	    p = pdest + 1 ;
	    epdest = (Element *) p ;
	    p += UNITS (Element, 1) ;
	    Cols2 = (Int *) p ;
	    Rows2 = Cols2 + ncolsleft ;
	    p += UNITS (Int, nrowsleft + ncolsleft) ;
	    C2 = (Entry *) p ;

	    ASSERT (epdest >= epsrc) ;
	    ASSERT (Rows2 >= Rows) ;
	    ASSERT (Cols2 >= Cols) ;
	    ASSERT (C2 >= C) ;
	    ASSERT (p + UNITS (Entry, csize) == pnext) ;

	    /* -------------------------------------------------------------- */
	    /* move the contribution block */
	    /* -------------------------------------------------------------- */

	    /* overlap = psrc + size + 1 > pdest ; */

	    if (nrowsleft < nrows || ncolsleft < ncols)
	    {

		/* ---------------------------------------------------------- */
		/* compress contribution block in place prior to moving it */
		/* ---------------------------------------------------------- */

		DEBUG7 (("Compress C in place prior to move (incl unused):\n"));
#ifndef NDEBUG
		UMF_dump_dense (C, nrows, nrows, ncols) ;
#endif
		C1 = C ;
		C3 = C ;
		for (j = 0 ; j < ncols ; j++)
		{
		    if (Cols [j] >= 0)
		    {
			for (i = 0 ; i < nrows ; i++)
			{
			    if (Rows [i] >= 0)
			    {
				*C3++ = C1 [i] ;
			    }
			}
		    }
		    C1 += nrows ;
		}
		ASSERT (C3-C == csize) ;
		DEBUG8 (("Newly compressed contrib. block (all in use):\n")) ;
#ifndef NDEBUG
		UMF_dump_dense (C, nrowsleft, nrowsleft, ncolsleft) ;
#endif
	    }

	    /* shift the contribution block down */
	    C += csize ;
	    C2 += csize ;
	    for (i = 0 ; i < csize ; i++)
	    {
		*--C2 = *--C ;
	    }

	    /* -------------------------------------------------------------- */
	    /* move the row indices */
	    /* -------------------------------------------------------------- */

	    i2 = nrowsleft ;
	    for (i = nrows - 1 ; i >= 0 ; i--)
	    {
		ASSERT (Rows2+i2 >= Rows+i) ;
		if (Rows [i] >= 0)
		{
		    Rows2 [--i2] = Rows [i] ;
		}
	    }
	    ASSERT (i2 == 0) ;

	    j2 = ncolsleft ;
	    for (j = ncols - 1 ; j >= 0 ; j--)
	    {
		ASSERT (Cols2+j2 >= Cols+j) ;
		if (Cols [j] >= 0)
		{
		    Cols2 [--j2] = Cols [j] ;
		}
	    }
	    ASSERT (j2 == 0) ;

	    /* -------------------------------------------------------------- */
	    /* construct the new header */
	    /* -------------------------------------------------------------- */

	    /* E [0...e] is now valid */
	    E [e] = (pdest + 1) - Numeric->Memory ;
	    epdest = (Element *) (pdest + 1) ;

	    epdest->next = EMPTY ;	/* destroys the son list */
	    epdest->ncols = ncolsleft ;
	    epdest->nrows = nrowsleft ;
	    epdest->ncolsleft = ncolsleft ;
	    epdest->nrowsleft = nrowsleft ;
	    epdest->rdeg = rdeg ;
	    epdest->cdeg = cdeg ;

	    ASSERT (size2 <= size) ;
	    pdest->header.prevsize = 0 ;
	    pdest->header.size = size2 ;

	    DEBUG7 (("After moving it:\n")) ;
#ifndef NDEBUG
	    UMF_dump_element (Numeric, Work, e, FALSE) ;
#endif
	}

#ifndef NDEBUG
	else
	{
	    DEBUG8 ((" free\n")) ;
	}
#endif
	DEBUG7 (("psrc "ID"  tail "ID"\n",
	psrc-Numeric->Memory, Numeric->itail)) ;
    }

    ASSERT (psrc == Numeric->Memory + Numeric->itail) ;
    ASSERT (nmark == 0) ;

    /* ---------------------------------------------------------------------- */
    /* final tail pointer */
    /* ---------------------------------------------------------------------- */

    ASSERT (pdest >= Numeric->Memory + Numeric->itail) ;
    Numeric->itail = pdest - Numeric->Memory ;
    pdest->header.prevsize = 0 ;
    Numeric->ibig = EMPTY ;
    Numeric->tail_usage = Numeric->size - Numeric->itail ;

    /* ---------------------------------------------------------------------- */
    /* clear the unused E [nel+1 .. n_col + n_inner] */
    /* ---------------------------------------------------------------------- */

    for (e = nel+1 ; e <= n_col + n_inner ; e++)
    {
	E [e] = 0 ;
    }

#ifndef NDEBUG
    UMF_dump_packed_memory (Numeric, Work) ;
#endif

    DEBUG8 (("::::GARBAGE COLLECTION DONE::::\n")) ;
}


```

sparse-matrix/umfpack/umf_symbolic_usage.c:
```
/* ========================================================================== */
/* === UMF_symbolic_usage =================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Returns the final size of the Symbolic object, in Units */

#include "umf_internal.h"

GLOBAL double UMF_symbolic_usage
(
    Int n_row,
    Int n_col,
    Int nchains,
    Int nfr
)
{
    double units =
	DUNITS (SymbolicType, 1)	/* Symbolic structure */
	+ DUNITS (Int, n_col+1)		/* Cperm_init */
	+ DUNITS (Int, n_row+1)		/* Rperm_init */
	+ 3*DUNITS (Int, nchains+1)	/* Chain_ */
	+ 4*DUNITS (Int, nfr+1) ;	/* Front_ */

    return (units) ;
}


```

sparse-matrix/umfpack/umf_create_element.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_create_element
(
    NumericType *Numeric,
    WorkType *Work
) ;


```

sparse-matrix/umfpack/umf_build_tuples_usage.c:
```
/* ========================================================================== */
/* === UMF_build_tuples_usage =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Return number of Units needed for UMF_build_tuples */

#include "umf_internal.h"

GLOBAL Int UMF_build_tuples_usage
(
    const Int Col_tlen [ ],
    const Int Col_degree [ ],
    const Int Row_tlen [ ],
    const Int Row_degree [ ],
    Int n_row,
    Int n_col,
    double *dusage		/* input and output argument */
)
{
    Int row, col, usage ;
    double du ;

    /* note: tuple lengths are initialized, but the tuple lists themselves */
    /* may not be. */

    usage = 0 ;
    du = 0 ;
    if (!Col_tlen || !Col_degree)
    {
	/* Col_tlen and Col_degree arrays are missing, so this is the */
	/* initial matrix, with one element per column. */
	usage += n_col * (1 + UNITS (Tuple, 4)) ;
	du += ((double) n_col) * (1 + DUNITS (Tuple, 4)) ;
    }
    else
    {
	for (col = 0 ; col < n_col ; col++)
	{
	    if (NON_PIVOTAL_COL (col))
	    {
		usage += 1 + UNITS (Tuple, MAX (4, Col_tlen [col] + 1)) ;
		du += 1 + DUNITS (Tuple, MAX (4, Col_tlen [col] + 1)) ;
	    }
	}
    }
    ASSERT (Row_tlen && Row_degree) ;
    for (row = 0 ; row < n_row ; row++)
    {
	if (NON_PIVOTAL_ROW (row))
	{
	    usage += 1 + UNITS (Tuple, MAX (4, Row_tlen [row] + 1)) ;
	    du += 1 + DUNITS (Tuple, MAX (4, Row_tlen [row] + 1)) ;
	}
    }

    /* roundoff error in du is at most 3*n*epsilon */
    /* (here, and in UMF_kernel_init_usage) */
    /* Ignore NaN or Inf behavior here. */
    du = MAX (du, (double) usage * (1.0 + MAX_EPSILON)) ;
    du += (((double) n_row) + 2*((double) n_col)) * MAX_EPSILON ;
    du = ceil (du) ;

    DEBUG0 (("UMF_build_tuples_usage "ID" %g\n", usage, *dusage)) ;

    *dusage += du ;
    return (usage) ;
}


```

sparse-matrix/umfpack/umf_blas3_update.c:
```
/* ========================================================================== */
/* === UMF_blas3_update ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

#include "umf_internal.h"

GLOBAL void UMF_blas3_update
(
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Entry *A, *B, *C ;
    Int k, m, n, d ;

    /* ---------------------------------------------------------------------- */
    /* perform the matrix-matrix multiply */
    /* ---------------------------------------------------------------------- */

    DEBUG5 (("In UMF_blas3_update "ID" "ID" "ID"\n",
    	Work->fnpiv, Work->fnrows, Work->fncols)) ;

    k = Work->fnpiv ;
    Work->fnpiv = 0 ;	/* no more pivots in frontal working array */
    Work->fnzeros = 0 ;
    m = Work->fnrows ;
    n = Work->fncols ;
    if (k == 0 || m == 0 || n == 0)
    {
	return ;
    }

    d = Work->fnrows_max ;
    C = Work->Fx ;
    A =	C + (Work->fncols_max - k) * d ;
    B = C + d - k ;

    DEBUG5 (("DO RANK-NB UPDATE of frontal:\n")) ;
    DEBUG5 (("DGEMM : "ID" "ID" "ID"\n", k, m, n)) ;

    /* ---------------------------------------------------------------------- */
    /* C = C - A*B */
    /* ---------------------------------------------------------------------- */

    if (k == 1)
    {

#ifdef USE_NO_BLAS

	/* no BLAS available - use plain C code instead */
	Int i, j ;
	Entry b_sj, *c_ij, *a_is ;
	for (j = 0 ; j < n ; j++)
	{
	    b_sj = B [j*d] ;
	    if (IS_NONZERO (b_sj))
	    {
		c_ij = & C [j*d] ;
		a_is = & A [0] ;
		for (i = 0 ; i < m ; i++)
		{
		    /* (*c_ij++) -= b_sj * (*a_is++) ; */
		    MULT_SUB (*c_ij, b_sj, *a_is) ;
		    c_ij++ ;
		    a_is++ ;
		}
	    }
	}

#else

	BLAS_GER (m, n, A, B, C, d) ;

#endif /* USE_NO_BLAS */

    }
    else
    {

#ifdef USE_NO_BLAS

	/* no BLAS available - use plain C code instead */
	Int i, j, s ;
	Entry b_sj, *c_ij, *a_is ;
	for (j = 0 ; j < n ; j++)
	{
	    for (s = 0 ; s < k ; s++)
	    {
		b_sj = B [s+j*d] ;
		if (IS_NONZERO (b_sj))
		{
		    c_ij = & C [j*d] ;
		    a_is = & A [s*d] ;
		    for (i = 0 ; i < m ; i++)
		    {
			/* (*c_ij++) -= b_sj * (*a_is++) ; */
			MULT_SUB (*c_ij, b_sj, *a_is) ;
			c_ij++ ;
			a_is++ ;
		    }
		}
	    }
	}

#else

	BLAS_GEMM (m, n, k, A, B, C, d) ;

#endif /* USE_NO_BLAS */

    }

    DEBUG2 (("blas3 "ID" "ID" "ID"\n", k, Work->fnrows, Work->fncols)) ;
}


```

sparse-matrix/umfpack/umf_tuple_lengths.c:
```
/* ========================================================================== */
/* === UMF_tuple_lengths ==================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Determine the tuple list lengths, and the amount of memory required for */
/* them.  Return the amount of memory needed to store all the tuples. */
/* This routine assumes that the tuple lists themselves are either already */
/* deallocated, or will be shortly (so Row[ ].tlen and Col[ ].tlen are */
/* overwritten) */

#include "umf_internal.h"
#include "umf_build_tuples_usage.h"

GLOBAL Int UMF_tuple_lengths
(
    NumericType *Numeric,
    WorkType *Work,
    double *dusage
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int e, nrows, ncols, nel, i, *Rows, *Cols, row, col, n_row, n_col, *E,
	*Row_degree, *Row_tlen, *Col_degree, *Col_tlen, usage ;
    Element *ep ;
    Unit *p ;

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

    E = Work->E ;
    Row_degree = Numeric->Rperm ;
    Col_degree = Numeric->Cperm ;
    Row_tlen   = Numeric->Uilen ;
    Col_tlen   = Numeric->Lilen ;
    n_row = Work->n_row ;
    n_col = Work->n_col ;
    nel = Work->nel ;

    DEBUG3 (("TUPLE_LENGTHS: n_row "ID" n_col "ID" nel "ID"\n",
	n_row, n_col, nel)) ;

    /* tuple list lengths already initialized to zero */

    /* ---------------------------------------------------------------------- */
    /* scan each element: count tuple list lengths (include element 0) */
    /* ---------------------------------------------------------------------- */

    for (e = 0 ; e <= nel ; e++)	/* for all elements, in any order */
    {
	if (E [e])
	{
#ifndef NDEBUG
	    UMF_dump_element (Numeric, Work, e, FALSE) ;
#endif
	    p = Numeric->Memory + E [e] ;
	    GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncols) ;
	    nrows = ep->nrows ;
	    ASSERT (e != 0) ;
	    for (i = 0 ; i < nrows ; i++)
	    {
		row = Rows [i] ;
		ASSERT (row == EMPTY || (row >= 0 && row < n_row)) ;
		if (row >= 0)
		{
		    ASSERT (NON_PIVOTAL_ROW (row)) ;
		    Row_tlen [row] ++ ;
		}
	    }
	    for (i = 0 ; i < ncols ; i++)
	    {
		col = Cols [i] ;
		ASSERT (col == EMPTY || (col >= 0 && col < n_col)) ;
		if (col >= 0)
		{
		    ASSERT (NON_PIVOTAL_COL (col)) ;
		    Col_tlen [col] ++ ;
		}
	    }
	}
    }

    /* note: tuple lengths are now modified, but the tuple lists are not */
    /* updated to reflect that fact. */

    /* ---------------------------------------------------------------------- */
    /* determine the required memory to hold all the tuple lists */
    /* ---------------------------------------------------------------------- */

    usage = UMF_build_tuples_usage (Col_tlen, Col_degree,
	Row_tlen, Row_degree, n_row, n_col, dusage) ;
    return (usage) ;
}


```

sparse-matrix/umfpack/umfpack_solve.c:
```
/* ========================================================================== */
/* === UMFPACK_solve ======================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Solves a linear system using the numerical factorization
    computed by UMFPACK_numeric.  See umfpack_solve.h for more details.

    For umfpack_*_solve:
	Dynamic memory usage:  UMFPACK_solve calls UMF_malloc twice, for
	workspace of size c*n*sizeof(double) + n*sizeof(Int), where c is
	defined below.  On return, all of this workspace is free'd via UMF_free.

    For umfpack_*_wsolve:
	Pattern is a workspace of size n Integers.  The double array W must be
	at least of size c*n, where c is defined below.

    If iterative refinement is requested, and Ax=b, A'x=b or A.'x=b is being
    solved, and the matrix A is not singular, then c is 5 for the real version
    and 10 for the complex version.  Otherwise, c is 1 for the real version and
    4 for the complex version.

*/

#include "umf_internal.h"
#include "umf_valid_numeric.h"
#include "umf_solve.h"

#ifndef WSOLVE
#include "umf_malloc.h"
#include "umf_free.h"
#ifndef NDEBUG
PRIVATE Int init_count ;
#endif
#endif

GLOBAL Int
#ifdef WSOLVE
UMFPACK_wsolve
#else
UMFPACK_solve
#endif
(
    Int sys,
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
#ifdef COMPLEX
    const double Az [ ],
#endif
    double Xx [ ],
#ifdef COMPLEX
    double Xz [ ],
#endif
    const double Bx [ ],
#ifdef COMPLEX
    const double Bz [ ],
#endif
    void *NumericHandle,
    const double Control [UMFPACK_CONTROL],
    double User_Info [UMFPACK_INFO]
#ifdef WSOLVE
    , Int Pattern [ ],
    double W [ ]
#endif
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    NumericType *Numeric ;
    Int n, i, irstep, status ;
    double Info2 [UMFPACK_INFO], *Info, tstart, tend ;
#ifndef WSOLVE
    Int *Pattern, wsize ;
    double *W ;
#endif

    /* ---------------------------------------------------------------------- */
    /* get the amount of time used by the process so far */
    /* ---------------------------------------------------------------------- */

    tstart = umfpack_timer ( ) ;

#ifndef WSOLVE
#ifndef NDEBUG
    init_count = UMF_malloc_count ;
#endif
#endif

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

    if (!Control)
    {
	irstep = UMFPACK_DEFAULT_IRSTEP ;
    }
    else if (SCALAR_IS_NAN (Control [UMFPACK_IRSTEP]))
    {
	irstep = UMFPACK_DEFAULT_IRSTEP ;
    }
    else
    {
	irstep = (Int) Control [UMFPACK_IRSTEP] ;
    }

    if (User_Info)
    {
	/* return Info in user's array */
	Info = User_Info ;
	for (i = UMFPACK_IR_TAKEN ; i <= UMFPACK_SOLVE_TIME ; i++)
	{
	    Info [i] = EMPTY ;
	}
    }
    else
    {
	/* no Info array passed - use local one instead */
	Info = Info2 ;
	for (i = 0 ; i < UMFPACK_INFO ; i++)
	{
	    Info [i] = EMPTY ;
	}
    }

    Info [UMFPACK_STATUS] = UMFPACK_OK ;
    Info [UMFPACK_SOLVE_FLOPS] = 0 ;

    Numeric = (NumericType *) NumericHandle ;
    if (!UMF_valid_numeric (Numeric))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Numeric_object ;
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }

    Info [UMFPACK_NROW] = Numeric->n_row ;
    Info [UMFPACK_NCOL] = Numeric->n_col ;

    if (Numeric->n_row != Numeric->n_col)
    {
	/* only square systems can be handled */
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_system ;
	return (UMFPACK_ERROR_invalid_system) ;
    }
    n = Numeric->n_row ;
    if (Numeric->nnzpiv < n
	|| SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond))
    {
	/* turn off iterative refinement if A is singular */
	/* or if U has NaN's on the diagonal. */
	irstep = 0 ;
    }

    if (!Xx || !Bx
#ifdef COMPLEX
    	|| !Xz || !Bz
#endif
    )
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ;
	return (UMFPACK_ERROR_argument_missing) ;
    }

    if (sys >= UMFPACK_Pt_L)
    {
	/* no iterative refinement except for nonsingular Ax=b, A'x=b, A.'x=b */
	irstep = 0 ;
    }

    /* ---------------------------------------------------------------------- */
    /* allocate or check the workspace */
    /* ---------------------------------------------------------------------- */

#ifdef WSOLVE

    if (!W || !Pattern)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ;
	return (UMFPACK_ERROR_argument_missing) ;
    }

#else

#ifdef COMPLEX
    if (irstep > 0)
    {
	wsize = 10*n ;		/* W, X, Z, S, Y, B2 */
    }
    else
    {
	wsize = 4*n ;		/* W, X */
    }
#else
    if (irstep > 0)
    {
	wsize = 5*n ;		/* W, Z, S, Y, B2 */
    }
    else
    {
	wsize = n ;		/* W */
    }
#endif

    Pattern = (Int *) UMF_malloc (n, sizeof (Int)) ;
    W = (double *) UMF_malloc (wsize, sizeof (double)) ;
    if (!W || !Pattern)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	(void) UMF_free ((void *) W) ;
	(void) UMF_free ((void *) Pattern) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }

#endif	/* WSOLVE */

    /* ---------------------------------------------------------------------- */
    /* solve the system */
    /* ---------------------------------------------------------------------- */

    status = UMF_solve (sys, Ap, Ai, Ax, Xx, Bx,
#ifdef COMPLEX
	Az, Xz, Bz,
#endif
	Numeric, irstep, Info, Pattern, W) ;

    /* ---------------------------------------------------------------------- */
    /* free the workspace (if allocated) */
    /* ---------------------------------------------------------------------- */

#ifndef WSOLVE
    (void) UMF_free ((void *) W) ;
    (void) UMF_free ((void *) Pattern) ;
    ASSERT (UMF_malloc_count == init_count) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* get the time used by UMFPACK_*solve */
    /* ---------------------------------------------------------------------- */

    Info [UMFPACK_STATUS] = status ;
    if (status >= 0)
    {
	tend = umfpack_timer ( ) ;
	Info [UMFPACK_SOLVE_TIME] = MAX (0, tend - tstart) ;
    }

    return (status) ;
}


```

sparse-matrix/umfpack/umf_mem_alloc_head_block.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_mem_alloc_head_block
(
    NumericType *Numeric,
    Int nunits
) ;


```

sparse-matrix/umfpack/umf_build_tuples.c:
```
/* ========================================================================== */
/* === UMF_build_tuples ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Construct the tuple lists from a set of packed elements (no holes in
    elements, no internal or external fragmentation, and a packed (0..Work->nel)
    element name space).  Assume no tuple lists are currently allocated, but
    that the tuple lengths have been initialized by UMF_tuple_lengths.

    Returns TRUE if successful, FALSE if not enough memory.
*/

#include "umf_internal.h"
#include "umf_mem_alloc_tail_block.h"

GLOBAL Int UMF_build_tuples
(
    NumericType *Numeric,
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int e, nrows, ncols, nel, *Rows, *Cols, row, col, n_row, n_col, *E,
	*Row_tuples, *Row_degree, *Row_tlen, tlen, tsize,
	*Col_tuples, *Col_degree, *Col_tlen ;
    Element *ep ;
    Unit *p ;
    Tuple tuple, *tp ;

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

    E = Work->E ;
    Col_degree = Numeric->Cperm ;	/* for NON_PIVOTAL_COL macro */
    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro */
    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;
    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;
    n_row = Work->n_row ;
    n_col = Work->n_col ;
    nel = Work->nel ;

    DEBUG3 (("BUILD_TUPLES: n_row "ID" n_col "ID" nel "ID"\n",
	n_row, n_col, nel)) ;

    /* ---------------------------------------------------------------------- */
    /* allocate space for the tuple lists */
    /* ---------------------------------------------------------------------- */

    /* Garbage collection and memory reallocation have already attempted to */
    /* ensure that there is enough memory for all the tuple lists.  If */
    /* memory allocation fails here, then there is nothing more to be done. */

    for (row = 0 ; row < n_row ; row++)
    {
	if (NON_PIVOTAL_ROW (row))
	{
	    tlen = MAX (4, Row_tlen [row] + 1) ;
	    tsize = UNITS (Tuple, tlen) ;
	    Row_tuples [row] = UMF_mem_alloc_tail_block (Numeric, tsize) ;
	    if (!Row_tuples [row])
	    {
		return (FALSE) ;	/* out of memory for row tuples */
	    }
	    Row_tlen [row] = 0 ;
	}
    }

    /* push on stack in reverse order, so column tuples are in the order */
    /* that they will be deleted. */
    for (col = n_col-1 ; col >= 0 ; col--)
    {
	if (NON_PIVOTAL_COL (col))
	{
	    tlen = MAX (4, Col_tlen [col] + 1) ;
	    tsize = UNITS (Tuple, tlen) ;
	    Col_tuples [col] = UMF_mem_alloc_tail_block (Numeric, tsize) ;
	    if (!Col_tuples [col])
	    {
		return (FALSE) ;	/* out of memory for col tuples */
	    }
	    Col_tlen [col] = 0 ;
	}
    }

#ifndef NDEBUG
    UMF_dump_memory (Numeric) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* create the tuple lists (exclude element 0) */
    /* ---------------------------------------------------------------------- */

    /* for all elements, in order of creation */
    for (e = 1 ; e <= nel ; e++)
    {
	DEBUG9 (("Adding tuples for element: "ID" at "ID"\n", e, E [e])) ;
	ASSERT (E [e]) ;	/* no external fragmentation */
	p = Numeric->Memory + E [e] ;
	GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncols) ;
	nrows = ep->nrows ;
	ASSERT (e != 0) ;
	ASSERT (e == 0 || nrows == ep->nrowsleft) ;
	ASSERT (e == 0 || ncols == ep->ncolsleft) ;
	tuple.e = e ;
	for (tuple.f = 0 ; tuple.f < ncols ; tuple.f++)
	{
	    col = Cols [tuple.f] ;
	    ASSERT (col >= 0 && col < n_col) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    ASSERT (Col_tuples [col]) ;
	    tp = ((Tuple *) (Numeric->Memory + Col_tuples [col]))
		+ Col_tlen [col]++ ;
	    *tp = tuple ;
#ifndef NDEBUG
	    UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ;
#endif
	}
	for (tuple.f = 0 ; tuple.f < nrows ; tuple.f++)
	{
	    row = Rows [tuple.f] ;
	    ASSERT (row >= 0 && row < n_row) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    ASSERT (Row_tuples [row]) ;
	    tp = ((Tuple *) (Numeric->Memory + Row_tuples [row]))
		+ Row_tlen [row]++ ;
	    *tp = tuple ;
#ifndef NDEBUG
	    UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ;
#endif
	}
    }

    /* ---------------------------------------------------------------------- */
    /* the tuple lists are now valid, and can be scanned */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    UMF_dump_memory (Numeric) ;
    UMF_dump_matrix (Numeric, Work, FALSE) ;
#endif
    DEBUG3 (("BUILD_TUPLES: done\n")) ;
    return (TRUE) ;
}


```

sparse-matrix/umfpack/umf_kernel.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_kernel
(
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
#ifdef COMPLEX
    const double Az [ ],
#endif
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
) ;


```

sparse-matrix/umfpack/umf_malloc.c:
```
/* ========================================================================== */
/* === UMF_malloc =========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Allocate a block of n objects, each of a given size.  This routine does not
    handle the case when the size is 1 (allocating char's) because of potential
    integer overflow.  UMFPACK never does that.
    Also maintains the UMFPACK malloc count.
*/

#include "umf_internal.h"

#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG)

/*
    UMF_malloc_count is a count of the objects malloc'd by UMFPACK.
    It is increased by 7 by UMFPACK_*symbolic, and by 15 or 16 by
    UMFPACK_*numeric.  It is reduced by the same amount by the corresponding
    UMFPACK_free_* routines.  If you suspect a memory leak in your program
    (caused by not properly destroying the Symbolic and Numeric objects)
    then compile with -DUMF_MALLOC_COUNT and check value of UMF_malloc_count.
    By default, UMF_MALLOC_COUNT is not defined, and thus UMFPACK has
    no global variables.
*/

GLOBAL Int UMF_malloc_count = 0 ;

#endif


GLOBAL void *UMF_malloc
(
    Int n_objects,
    size_t size_of_object
)
{
    size_t size ;
    void *p ;

#ifdef UMF_TCOV_TEST
    /* For exhaustive statement coverage testing only! */
    /* Pretend to fail, to test out-of-memory conditions. */
    umf_fail-- ;
    if (umf_fail <= umf_fail_hi && umf_fail >= umf_fail_lo) { return ((void *) NULL) ; }
#endif

    /* make sure that we allocate something */
    n_objects = MAX (1, n_objects) ;

    size = (size_t) n_objects ;
    ASSERT (size_of_object > 1) ;
    if (size > Int_MAX / size_of_object)
    {
	/* object is too big for integer pointer arithmetic */
	return ((void *) NULL) ;
    }
    size *= size_of_object ;

    /* see umf_config.h for the memory allocator selection */
    p = ALLOCATE (size) ;

#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG)
    if (p)
    {
	/* One more object has been malloc'ed.  Keep track of the count. */
	/* (purely for sanity checks). */
	UMF_malloc_count++ ;
    }
#endif

    return (p) ;
}


```

sparse-matrix/umfpack/umf_mem_init_memoryspace.c:
```
/* ========================================================================== */
/* === UMF_mem_init_memoryspace ============================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* The UMF_mem_* routines manage the Numeric->Memory memory space. */

#include "umf_internal.h"

/* initialize the LU and element workspace (Numeric->Memory) */

GLOBAL void UMF_mem_init_memoryspace
(
    NumericType *Numeric
)
{
    Unit *p ;

    ASSERT (Numeric) ;
    ASSERT (Numeric->size >= 4) ;
    DEBUG0 (("Init memory space, size "ID"\n", Numeric->size)) ;

    Numeric->ngarbage = 0 ;
    Numeric->nrealloc = 0 ;
    Numeric->ncostly = 0 ;
    Numeric->ibig = EMPTY ;
    Numeric->ihead = 0 ;
    Numeric->itail = Numeric->size ;

#ifndef NDEBUG
    UMF_allocfail = FALSE ;
#endif

    /* allocate the 2-unit tail marker block and initialize it */
    Numeric->itail -= 2 ;
    p = Numeric->Memory + Numeric->itail ;
    DEBUG2 (("p "ID" tail "ID"\n", p-Numeric->Memory, Numeric->itail)) ;
    Numeric->tail_usage = 2 ;
    p->header.prevsize = 0 ;
    p->header.size = 1 ;

    /* allocate a 1-unit head marker block at the head of memory */
    /* this is done so that an offset of zero is treated as a NULL pointer */
    Numeric->ihead++ ;

    /* initial usage in Numeric->Memory */
    Numeric->max_usage = 3 ;
    Numeric->init_usage = Numeric->max_usage ;

    /* Note that UMFPACK_*symbolic ensures that Numeric->Memory is of size */
    /* at least 3, so this initialization will always succeed. */

#ifndef NDEBUG
    DEBUG2 (("init_memoryspace, all free (except one unit at head\n")) ;
    UMF_dump_memory (Numeric) ;
#endif

}


```

sparse-matrix/umfpack/make.code.defn:
```
# Cactus specification of things to be compiled in this directory
# $Header$

#
# UMFPACK comes with a Makefile which compiles many of the source files
# multiple times with different -D compiler options, both to generate the
# 4 UMFPACK variants (real/complex, int/long), and for other purposes.
# Alas, Cactus doesn't seem to provide any way to use a Makefile for
# this subdirectory, and still use  make.code.defn  building for the
# rest of this thorn. :( :(
#
# Instead, we [have to] fake things with this  make.code.defn  and the
# accompanying  make.code.deps .  Fortunately, we only need one UMFPACK
# variant:  We know it's real, but we actually want the integer datatype
# to match Fortran's (since ../../elliptic/row_sparse_matrix.{cc,hh}
# potentially use the same data structures for both UMFPACK and ILUCG
# matrices, and ILUCG is Fortran).  This ought to be determined by a
# configure script, but for now (FIXME) we just hard-wire it to  long
# with -D compile flags set in  make.code.deps .
#
# For the other multiple-compilation and special-compilation-flags cases,
# we use wrapper files which #define the appropriate symbols, then #include
# the main UMFPACK source files.
#

# Source files in this directory
SRCS =					\
	umf_analyze.c			\
	umf_apply_order.c		\
	umf_assemble.c			\
	umf_blas3_update.c		\
	umf_build_tuples.c		\
	umf_build_tuples_usage.c	\
	umf_colamd.c			\
	umf_create_element.c		\
	umf_dump.c			\
	umf_extend_front.c		\
	umf_free.c			\
	umf_garbage_collection.c	\
	umf_get_memory.c		\
	umf_init_front.c		\
	umf_is_permutation.c		\
	umf_kernel.c			\
	umf_kernel_init.c		\
	umf_kernel_init_usage.c		\
	umf_kernel_wrapup.c		\
	umf_local_search.c		\
	umf_lsolve.c			\
	umf_ltsolve.c			\
	umf_malloc.c			\
	umf_mem_alloc_element.c		\
	umf_mem_alloc_head_block.c	\
	umf_mem_alloc_tail_block.c	\
	umf_mem_free_tail_block.c	\
	umf_mem_init_memoryspace.c	\
	umf_order_front_tree.c		\
	umf_realloc.c			\
	umf_row_search.c		\
	umf_scale_column.c		\
	umf_set_stats.c			\
	umf_solve.c			\
	umf_symbolic_usage.c		\
	umf_transpose.c			\
	umf_tuple_lengths.c		\
	umf_usolve.c			\
	umf_utsolve.c			\
	umf_valid_numeric.c		\
	umf_valid_symbolic.c		\
	umfpack_defaults.c		\
	umfpack_free_numeric.c		\
	umfpack_free_symbolic.c		\
	umfpack_get_lunz.c		\
	umfpack_get_numeric.c		\
	umfpack_get_symbolic.c		\
	umfpack_numeric.c		\
	umfpack_qsymbolic.c		\
	umfpack_solve.c			\
	umfpack_symbolic.c		\
	umfpack_timer.c			\
	umfpack_transpose.c		\
					\
	wrap_umf_ltsolve.c		\
	wrap_umf_utsolve.c		\
	wrap_umfpack_wsolve.c

# Subdirectories containing source files
SUBDIRS =

```

sparse-matrix/umfpack/umf_set_stats.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL void UMF_set_stats
(
    double Info [ ],
    SymbolicType *Symbolic,
    double max_usage,
    double num_mem_size,
    double flops,
    double lnz,
    double unz,
    double maxfrsize,
    double ulen,
    double npiv,
    Int what
) ;


```

sparse-matrix/umfpack/umf_kernel_wrapup.c:
```
/* ========================================================================== */
/* === UMF_kernel_wrapup ==================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* The matrix is factorized.  Finish the LU data structure. */

#include "umf_internal.h"

GLOBAL void UMF_kernel_wrapup
(
    NumericType *Numeric,
    SymbolicType *Symbolic,
    WorkType *Work
)
{

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

    Int i, k, col, row, llen, ulen, *ip, *Rperm, *Cperm, *Lilen, npiv, lp,
	*Uilen, *Lip, *Uip, *Cperm_init, up, pivrow, pivcol, *Lpos, *Upos, *Wr,
	*Wc, *Wp, *Frpos, *Fcpos, *Row_degree, *Col_degree, *Rperm_init,
	n_row, n_col, n_inner ;
    Entry *D ;

#ifndef NDEBUG
    UMF_dump_matrix (Numeric, Work, FALSE) ;
#endif

    DEBUG0 (("Kernel complete, Starting Kernel wrapup\n")) ;
    n_row = Symbolic->n_row ;
    n_col = Symbolic->n_col ;
    n_inner = MIN (n_row, n_col) ;
    Rperm = Numeric->Rperm ;
    Cperm = Numeric->Cperm ;
    Lilen = Numeric->Lilen ;
    Uilen = Numeric->Uilen ;
    Upos = Numeric->Upos ;
    Lpos = Numeric->Lpos ;
    Lip = Numeric->Lip ;
    Uip = Numeric->Uip ;
    D = Numeric->D ;

    npiv = Work->npiv ;
    Numeric->npiv = npiv ;
    Numeric->ulen = Work->ulen ;

    ASSERT (n_row == Numeric->n_row) ;
    ASSERT (n_col == Symbolic->n_col) ;
    DEBUG0 (("Wrap-up: npiv "ID" ulen "ID"\n", npiv, Numeric->ulen)) ;
    ASSERT (npiv <= n_inner) ;

    /* this will be nonzero only if matrix is singular or rectangular */
    ASSERT (IMPLIES (npiv == n_col, Work->ulen == 0)) ;

    /* ---------------------------------------------------------------------- */
    /* check if matrix is singular or rectangular */
    /* ---------------------------------------------------------------------- */

    Col_degree = Cperm ;	/* for NON_PIVOTAL_COL macro */
    Row_degree = Rperm ;	/* for NON_PIVOTAL_ROW macro */

    if (npiv < n_row)
    {
	/* finalize the row permutation */
	k = npiv ;
	for (row = 0 ; row < n_row ; row++)
	{
	    if (NON_PIVOTAL_ROW (row))
	    {
		Rperm [row] = ONES_COMPLEMENT (k) ;
		ASSERT (!NON_PIVOTAL_ROW (row)) ;
		Lpos [row] = EMPTY ;
		Uip [row] = EMPTY ;
		Uilen [row] = 0 ;
		k++ ;
	    }
	}
	ASSERT (k == n_row) ;
    }

    if (npiv < n_col)
    {
	/* finalize the col permutation */
	k = npiv ;
	for (col = 0 ; col < n_col ; col++)
	{
	    if (NON_PIVOTAL_COL (col))
	    {
		Cperm [col] = ONES_COMPLEMENT (k) ;
		ASSERT (!NON_PIVOTAL_COL (col)) ;
		Upos [col] = EMPTY ;
		Lip [col] = EMPTY ;
		Lilen [col] = 0 ;
		k++ ;
	    }
	}
	ASSERT (k == n_col) ;
    }

    if (npiv < n_inner)
    {
	/* finalize the diagonal of U */
	for (k = npiv ; k < n_inner ; k++)
	{
	    CLEAR (D [k]) ;
	}
    }

    /* save the pattern of the last row of U */
    if (Numeric->ulen > 0)
    {
	Numeric->Upattern = Work->Upattern ;
	Work->Upattern = (Int *) NULL ;
    }

    ASSERT (Numeric->nnzpiv <= npiv) ;
    if (Numeric->nnzpiv < n_inner)
    {
	/* the rest of the diagonal is zero, so min_udiag becomes 0 */
	Numeric->min_udiag = 0.0 ;
    }

    /* ---------------------------------------------------------------------- */
    /* size n_row, n_col workspaces that can be used here: */
    /* ---------------------------------------------------------------------- */

    Frpos = Work->Frpos ;	/* of size n_row+1 */
    Fcpos = Work->Fcpos ;	/* of size n_col+1 */
    Wp = Work->Wp ;		/* of size MAX(n_row,n_col)+1 */
    /* Work->Upattern ;		cannot be used (in Numeric) */
    Wr = Work->Lpattern ;	/* of size n_row+1 */
    Wc = Work->E ;		/* of size n_col+n_inner+1 */

    /* ---------------------------------------------------------------------- */
    /* construct Rperm from inverse permutations */
    /* ---------------------------------------------------------------------- */

    /* use Frpos for temporary copy of inverse row permutation [ */

    for (pivrow = 0 ; pivrow < n_row ; pivrow++)
    {
	k = Rperm [pivrow] ;
	ASSERT (k < 0) ;
	k = ONES_COMPLEMENT (k) ;
	ASSERT (k >= 0 && k < n_row) ;
	Wp [k] = pivrow ;
	Frpos [pivrow] = k ;
    }
    for (k = 0 ; k < n_row ; k++)
    {
	Rperm [k] = Wp [k] ;
    }

    /* ---------------------------------------------------------------------- */
    /* construct Cperm from inverse permutation */
    /* ---------------------------------------------------------------------- */

    /* use Fcpos for temporary copy of inverse column permutation [ */

    for (pivcol = 0 ; pivcol < n_col ; pivcol++)
    {
	k = Cperm [pivcol] ;
	ASSERT (k < 0) ;
	k = ONES_COMPLEMENT (k) ;
	ASSERT (k >= 0 && k < n_col) ;
	Wp [k] = pivcol ;
	/* save a copy of the inverse column permutation in Fcpos */
	Fcpos [pivcol] = k ;
    }
    for (k = 0 ; k < n_col ; k++)
    {
	Cperm [k] = Wp [k] ;
    }

#ifndef NDEBUG
    for (k = 0 ; k < n_col ; k++)
    {
	col = Cperm [k] ;
	ASSERT (col >= 0 && col < n_col) ;
	ASSERT (Fcpos [col] == k) ;		/* col is the kth pivot */
    }
    for (k = 0 ; k < n_row ; k++)
    {
	row = Rperm [k] ;
	ASSERT (row >= 0 && row < n_row) ;
	ASSERT (Frpos [row] == k) ;		/* row is the kth pivot */
    }
#endif

#ifndef NDEBUG
    UMF_dump_lu (Numeric) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* permute Lpos, Upos, Lilen, Lip, Uilen, and Uip */
    /* ---------------------------------------------------------------------- */

    for (k = 0 ; k < npiv ; k++)
    {
	pivrow = Rperm [k] ;
	Wr [k] = Uilen [pivrow] ;
	Wp [k] = Uip [pivrow] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	Uilen [k] = Wr [k] ;
	Uip [k] = Wp [k] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	pivrow = Rperm [k] ;
	Wp [k] = Lpos [pivrow] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	Lpos [k] = Wp [k] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	pivcol = Cperm [k] ;
	Wc [k] = Lilen [pivcol] ;
	Wp [k] = Lip [pivcol] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	Lilen [k] = Wc [k] ;
	Lip [k] = Wp [k] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	pivcol = Cperm [k] ;
	Wp [k] = Upos [pivcol] ;
    }

    for (k = 0 ; k < npiv ; k++)
    {
	Upos [k] = Wp [k] ;
    }

    /* ---------------------------------------------------------------------- */
    /* terminate the last Uchain and last Lchain */
    /* ---------------------------------------------------------------------- */

    Upos [npiv] = EMPTY ;
    Lpos [npiv] = EMPTY ;
    Uip [npiv] = EMPTY ;
    Lip [npiv] = EMPTY ;
    Uilen [npiv] = 0 ;
    Lilen [npiv] = 0 ;

    /* ---------------------------------------------------------------------- */
    /* convert U to the new pivot order */
    /* ---------------------------------------------------------------------- */

    for (k = 0 ; k < npiv ; k++)
    {
	up = Uip [k] ;
	if (up < 0)
	{
	    /* this is the start of a new Uchain (with a pattern) */
	    ulen = Uilen [k] ;
	    DEBUG4 (("K "ID" New U.  ulen "ID" End_Uchain 1\n", k, ulen)) ;
	    if (ulen > 0)
	    {
		up = -up ;
		ip = (Int *) (Numeric->Memory + up) ;
		for (i = 0 ; i < ulen ; i++)
		{
		    col = *ip ;
		    DEBUG4 (("    old col "ID" new col "ID"\n", col, Fcpos [col]));
		    ASSERT (col >= 0 && col < n_col) ;
		    *ip++ = Fcpos [col] ;
		}
	    }
	}
    }

    ulen = Numeric->ulen ;
    if (ulen > 0)
    {
	/* convert last pivot row of U to the new pivot order */
	DEBUG4 (("K "ID" (last)\n")) ;
	for (i = 0 ; i < ulen ; i++)
	{
	    col = Numeric->Upattern [i] ;
	    DEBUG4 (("    old col "ID" new col "ID"\n", col, Fcpos [col])) ;
	    Numeric->Upattern [i] = Fcpos [col] ;
	}
    }

    /* Fcpos no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* convert L to the new pivot order */
    /* ---------------------------------------------------------------------- */

    for (k = 0 ; k < npiv ; k++)
    {
	llen = Lilen [k] ;
	DEBUG4 (("K "ID" New L.  llen "ID" \n", k, llen)) ;
	if (llen > 0)
	{
	    lp = Lip [k] ;
	    if (lp < 0)
	    {
	    	/* this starts a new Lchain */
		lp = -lp ;
	    }
	    ip = (Int *) (Numeric->Memory + lp) ;
	    for (i = 0 ; i < llen ; i++)
	    {
		row = *ip ;
		DEBUG4 (("    old row "ID" new row "ID"\n", row, Frpos [row])) ;
		ASSERT (row >= 0 && row < n_row) ;
		*ip++ = Frpos [row] ;
	    }
	}
    }

    /* Frpos no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* combine symbolic and numeric permutations */
    /* ---------------------------------------------------------------------- */

    Cperm_init = Symbolic->Cperm_init ;
    Rperm_init = Symbolic->Rperm_init ;

    for (k = 0 ; k < n_row ; k++)
    {
	Rperm [k] = Rperm_init [Rperm [k]] ;
    }

    for (k = 0 ; k < n_col ; k++)
    {
        Cperm [k] = Cperm_init [Cperm [k]] ;
    }

    /* Work object will be freed immediately upon return (to UMF_kernel */
    /* and then to UMFPACK_numeric). */
}

```

sparse-matrix/umfpack/umfpack_free_numeric.h:
```
/* ========================================================================== */
/* === umfpack_free_numeric ================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

void umfpack_di_free_numeric
(
    void **Numeric
) ;

void umfpack_dl_free_numeric
(
    void **Numeric
) ;

void umfpack_zi_free_numeric
(
    void **Numeric
) ;

void umfpack_zl_free_numeric
(
    void **Numeric
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    umfpack_di_free_numeric (&Numeric) ;

double long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    umfpack_dl_free_numeric (&Numeric) ;

complex int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    umfpack_zi_free_numeric (&Numeric) ;

complex long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    umfpack_zl_free_numeric (&Numeric) ;

Purpose:

    Deallocates the Numeric object and sets the Numeric handle to NULL.
    This routine is the only valid way of destroying the Numeric object;
    any other action (such as using "free (Numeric) ;" or not freeing Numeric
    at all) will lead to memory leaks.

Arguments:

    void **Numeric ;		Input argument, deallocated and Numeric is
				set to (void *) NULL on output.

	Numeric must point to a valid Numeric object, computed by
	umfpack_*_numeric.  No action is taken if Numeric is a (void *) NULL
	pointer.
*/

```

sparse-matrix/umfpack/umfpack_get_numeric.h:
```
/* ========================================================================== */
/* === umfpack_get_numeric ================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_get_numeric
(
    int Lp [ ],
    int Lj [ ],
    double Lx [ ],
    int Up [ ],
    int Ui [ ],
    double Ux [ ],
    int P [ ],
    int Q [ ],
    double Dx [ ],
    void *Numeric
) ;

long umfpack_dl_get_numeric
(
    long Lp [ ],
    long Lj [ ],
    double Lx [ ],
    long Up [ ],
    long Ui [ ],
    double Ux [ ],
    long P [ ],
    long Q [ ],
    double Dx [ ],
    void *Numeric
) ;

int umfpack_zi_get_numeric
(
    int Lp [ ],
    int Lj [ ],
    double Lx [ ], double Lz [ ],
    int Up [ ],
    int Ui [ ],
    double Ux [ ], double Uz [ ],
    int P [ ],
    int Q [ ],
    double Dx [ ], double Dz [ ],
    void *Numeric
) ;

long umfpack_zl_get_numeric
(
    long Lp [ ],
    long Lj [ ],
    double Lx [ ], double Lz [ ],
    long Up [ ],
    long Ui [ ],
    double Ux [ ], double Uz [ ],
    long P [ ],
    long Q [ ],
    double Dx [ ], double Dz [ ],
    void *Numeric
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    int *Lp, *Lj, *Up, *Ui, *P, *Q, status ;
    double *Lx, *Ux, *Dx ;
    status = umfpack_di_get_numeric (Lp, Lj, Lx, Up, Ui, Ux, P, Q, Dx, Numeric);

double long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    long *Lp, *Lj, *Up, *Ui, *P, *Q, status ;
    double *Lx, *Ux, *Dx ;
    status = umfpack_dl_get_numeric (Lp, Lj, Lx, Up, Ui, Ux, P, Q, Dx, Numeric);

complex int Syntax:

    #include "umfpack.h"
    void *Numeric ;
    int *Lp, *Lj, *Up, *Ui, *P, *Q, status ;
    double *Lx, *Lz, *Ux, *Uz, *Dx, *Dz ;
    status = umfpack_zi_get_numeric (Lp, Lj, Lx, Lz, Up, Ui, Ux, Uz, P, Q,
	Dx, Dz, Numeric) ;

complex long Syntax:

    #include "umfpack.h"
    void *Numeric ;
    long *Lp, *Lj, *Up, *Ui, *P, *Q, status ;
    double *Lx, *Lz, *Ux, *Uz, *Dx, *Dz ;
    status = umfpack_zl_get_numeric (Lp, Lj, Lx, Lz, Up, Ui, Ux, Uz, P, Q,
	Dx, Dz, Numeric) ;

Purpose:

    This routine copies the LU factors and permutation vectors from the Numeric
    object into user-accessible arrays.  This routine is not needed to solve a
    linear system.  Note that the output arrays Lp, Lj, Lx, Up, Ui, Ux, P, Q,
    and Dx are not allocated by umfpack_*_get_numeric; they must exist on input.
    Similarly, Lz, Uz and Dz must exist on input for the complex versions.

Returns:

    Returns UMFPACK_OK if successful.  Returns UMFPACK_ERROR_out_of_memory
    if insufficient memory is available for the 2*max(n_row,n_col) integer
    workspace that umfpack_*_get_numeric allocates to construct L and/or U.
    Returns UMFPACK_ERROR_invalid_Numeric_object if the Numeric object provided
    as input is invalid.

Arguments:

    Int Lp [n_row+1] ;	Output argument.
    Int Lj [lnz] ;	Output argument.
    double Lx [lnz] ;	Output argument.
    double Lz [lnz] ;	Output argument for complex versions.

	The n_row-by-min(n_row,n_col) matrix L is returned in compressed-row
	form.  The column indices of row i and corresponding numerical values
	are in:

	    Lj [Lp [i] ... Lp [i+1]-1]
	    Lx [Lp [i] ... Lp [i+1]-1]	real part
	    Lz [Lp [i] ... Lp [i+1]-1]	imaginary part (complex versions)

	respectively.  Each row is stored in sorted order, from low column
	indices to higher.  The last entry in each row is the diagonal, which
	is numerically equal to one.  The sizes of Lp, Lj, Lx, and Lz are
	returned by umfpack_*_get_lunz.    If Lp, Lj, or Ux (or Uz for the
	complex version) are not present, then the matrix L is not returned.
	This is not an error condition.  The L matrix can be printed if n_row,
	Lp, Lj, Lx (and Lz for the complex versions) are passed to
	umfpack_*_report_matrix (using the "row" form).

	Future complex version:  if Lx is present and Lz is NULL, then both real
	and imaginary parts will be returned in Lx[0..2*lnz-1], with Lx[2*k]
	and Lx[2*k+1] being the real and imaginary part of the kth entry.

    Int Up [n_col+1] ;	Output argument.
    Int Ui [unz] ;	Output argument.
    double Ux [unz] ;	Output argument.
    double Uz [unz] ;	Output argument for complex versions.

	The min(n_row,n_co)-by-n_col matrix U is returned in compressed-column
	form.  The row indices of column j and corresponding numerical values
	are in

	    Ui [Up [j] ... Up [j+1]-1]
	    Ux [Up [j] ... Up [j+1]-1]	real part
	    Uz [Up [j] ... Up [j+1]-1]	imaginary part (complex versions)

	respectively.  Each column is stored in sorted order, from low row
	indices to higher.  The last entry in each column is the diagonal
	(assuming that it is nonzero).  The sizes of Up, Ui, Ux, and Uz are
	returned by umfpack_*_get_lunz.  If Up, Ui, or Ux (or Uz for the complex
	version) are not present, then the matrix U is not returned.  This is
	not an error condition.  The U matrix can be printed if n_col, Up, Ui,
	Ux (and Uz for the complex versions) are passed to
	umfpack_*_report_matrix (using the "column" form).

	Future complex version:  if Ux is present and Uz is NULL, then both real
	and imaginary parts will be returned in Ux[0..2*unz-1], with Ux[2*k]
	and Ux[2*k+1] being the real and imaginary part of the kth entry.

    Int P [n_row] ;		Output argument.

	The permutation vector P is defined as P [k] = i, where the original
	row i of A is the kth pivot row in PAQ.  If you do not want the P vector
	to be returned, simply pass (Int *) NULL for P.  This is not an error
	condition.  You can print P and Q with umfpack_*_report_perm.

    Int Q [n_col] ;		Output argument.

	The permutation vector Q is defined as Q [k] = j, where the original
	column j of A is the kth pivot column in PAQ.  If you not want the Q
	vector to be returned, simply pass (Int *) NULL for Q.  This is not
	an error condition.  Note that Q is not necessarily identical to
	Qtree, the column preordering held in the Symbolic object.  Refer to
	the description of Qtree and Front_npivcol in umfpack_*_get_symbolic for
	details.

    double Dx [min(n_row,n_col)] ;	Output argument.
    double Dz [min(n_row,n_col)] ;	Output argument for complex versions.

	The diagonal of U is also returned in Dx and Dz.  You can extract the
	diagonal of U without getting all of U by passing a non-NULL Dx (and
	Dz for the complex version) and passing Up, Ui, and Ux as NULL.  Dx is
	the real part of the diagonal, and Dz is the imaginary part.

	Future complex version:  if Dx is present and Dz is NULL, then both real
	and imaginary parts will be returned in Dx[0..2*min(n_row,n_col)-1],
	with Dx[2*k] and Dx[2*k+1] being the real and imaginary part of the kth
	entry.

    void *Numeric ;	Input argument, not modified.

	Numeric must point to a valid Numeric object, computed by
	umfpack_*_numeric.
*/

```

sparse-matrix/umfpack/umf_row_search.c:
```
/* ========================================================================== */
/* === UMF_row_search ======================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

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

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

*/

#include "umf_internal.h"

#define IN 0
#define OUT 1

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

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

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

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

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

    Row_degree = Numeric->Rperm ;
    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;
    Wp = Work->Wp ;
    Frpos = Work->Frpos ;
    E = Work->E ;
    Memory = Numeric->Memory ;
    fnrows = Work->fnrows ;

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

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

    maxval = 0.0 ;
    nans_in_col = FALSE ;
    for (i = 0 ; i < cdeg ; i++)
    {
	APPROX_ABS (value, Fcol [i]) ;
	if (SCALAR_IS_NAN (value))
	{
	    nans_in_col = TRUE ;
	    maxval = value ;
	    break ;
	}
	/* This test can now ignore the NaN case: */
	maxval = MAX (maxval, value) ;
    }

    /* if maxval is zero, the matrix is numerically singular */

    toler = Numeric->relpt * maxval ;
    if (SCALAR_IS_ZERO (toler))
    {
	/* guard against underflow, and relpt=0 means relpt=1 */
	toler = maxval ;
    }
    DEBUG5 (("Row_search begins [ maxval %g toler %g\n", maxval, toler)) ;
    if (SCALAR_IS_NAN (toler))
    {
	nans_in_col = TRUE ;
    }

    if (!nans_in_col)
    {
	for (i = 0 ; i < cdeg ; i++)
	{
	    double a ;
	    APPROX_ABS (a, Fcol [i]) ;

	    /* No NaN's exist in this column */
	    ASSERT (!SCALAR_IS_NAN (a)) ;
	    ASSERT (!SCALAR_IS_NAN (toler)) ;

	    if (a >= toler)	/* a double relop, but no NaN's exist here. */
	    {
		row = Pattern [i] ;
		deg = Row_degree [row] ;
#ifndef NDEBUG
		DEBUG6 ((ID" Candidate row "ID" deg "ID" absval %g\n", i, row,
		    deg, a)) ;
		UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
#endif

		if (Frpos [row] >= 0 && Frpos [row] < fnrows)
		{
		    /* row is in the current front */
		    DEBUG4 ((" in front\n")) ;
		    if (deg < rdeg [IN]
		    || (deg == rdeg [IN] && row < pivrow [IN]))
		    {
			/* best row in front, so far */
			pivrow [IN] = row ;
			rdeg [IN] = deg ;
		    }
		}
		else
		{
		    /* row is not in the current front */
		    DEBUG4 ((" NOT in front\n")) ;
		    if (deg < rdeg [OUT]
		    || (deg == rdeg[OUT] && row < pivrow[OUT]))
		    {
			/* best row not in front, so far */
			pivrow [OUT] = row ;
			rdeg [OUT] = deg ;
		    }
		}
	    }
	}
    }

    /* if cdeg > 0 then we must have found a pivot row ... unless NaN's */
    /* exist.  Try with no numerical tests if no pivot found. */ 

    if (cdeg > 0 && pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY)
    {
	/* cleanup for the NaN case */
	DEBUG0 (("Found a NaN in pivot column!\n")) ;

	/* grab the first entry in the pivot column, ignoring degree and */
	/* numerical stability. */
	row = Pattern [0] ;
	deg = Row_degree [row] ;
	if (Frpos [row] >= 0 && Frpos [row] < fnrows)
	{
	    /* row is in the current front */
	    DEBUG4 ((" in front\n")) ;
	    pivrow [IN] = row ;
	    rdeg [IN] = deg ;
	}
	else
	{
	    /* row is not in the current front */
	    DEBUG4 ((" NOT in front\n")) ;
	    pivrow [OUT] = row ;
	    rdeg [OUT] = deg ;
	}

	/* We are now guaranteed to have a pivot, no matter how broken */
	/* (non-IEEE compliant) the underlying numerical operators are. */
	/* This is particularly a problem for Microsoft compilers (they do */
	/* not handle NaN's properly). Now try to find a sparser pivot, if */
	/* possible. */

	for (i = 1 ; i < cdeg ; i++)
        {
	    row = Pattern [i] ;
	    deg = Row_degree [row] ;

	    if (Frpos [row] >= 0 && Frpos [row] < fnrows)
	    {
		/* row is in the current front */
		DEBUG4 ((" in front\n")) ;
		if (deg < rdeg [IN] || (deg == rdeg [IN] && row < pivrow [IN]))
		{
		    /* best row in front, so far */
		    pivrow [IN] = row ;
		    rdeg [IN] = deg ;
		}
	    }
	    else
	    {
		/* row is not in the current front */
		DEBUG4 ((" NOT in front\n")) ;
		if (deg < rdeg [OUT] || (deg == rdeg[OUT] && row < pivrow[OUT]))
		{
		    /* best row not in front, so far */
		    pivrow [OUT] = row ;
		    rdeg [OUT] = deg ;
		}
	    }
	}
    }

    /* We found a pivot if there are entries (even zero ones) in pivot col */
    ASSERT (IMPLIES (cdeg > 0, pivrow [IN] != EMPTY || pivrow [OUT] != EMPTY)) ;

    /* If there are no entries in the pivot column, then no pivot is found */
    ASSERT (IMPLIES (cdeg== 0, pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY)) ;

    /* ---------------------------------------------------------------------- */
    /* check for singular matrix */
    /* ---------------------------------------------------------------------- */

    if (cdeg == 0)
    {
	if (fnrows > 0)
	{
	    /*
	    	Get the pivrow [OUT][IN] from the current front.
	    	The frontal matrix looks like this:

			pivcol[OUT]
			|
			v
		x x x x 0   <- so grab this row as the pivrow [OUT][IN].
		x x x x 0
		x x x x 0 
		0 0 0 0 0

		The current frontal matrix has some rows in it.  The degree
		of the pivcol[OUT] is zero.  The column is empty, and the
		current front does not contribute to it.  

	    */
	    pivrow [IN] = Work->Frows [0] ;
	    DEBUG0 (("Got zero pivrow[OUT][IN] "ID" from current front\n",
		pivrow [IN])) ;
	}
	else
	{

	    /*
	    	Get a pivot row from the row-merge tree, use as
		pivrow [OUT][OUT].   pivrow [IN] remains EMPTY.
		This can only happen if the current front is 0-by-0.
	    */

	    Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1,
		row2, fleftmost, nfr, n_row, frontid ;

	    ASSERT (Work->fncols == 0) ;

	    Front_leftmostdesc = Symbolic->Front_leftmostdesc ;
	    Front_1strow = Symbolic->Front_1strow ;
	    Front_new1strow = Work->Front_new1strow ;
	    nfr = Symbolic->nfr ;
	    n_row = Numeric->n_row ;
	    frontid = Work->frontid ;

	    DEBUG0 (("Warning: pivcol: "ID" is empty front "ID"\n",
	        pivcol, frontid)) ;
#ifndef NDEBUG
	    DEBUG1 (("Calling dump rowmerge\n")) ;
	    UMF_dump_rowmerge (Numeric, Symbolic, Work) ;
#endif

	    /* Row-merge set is the non-pivotal rows in the range */
	    /* Front_new1strow [Front_leftmostdesc [frontid]] to */
	    /* Front_1strow [frontid+1] - 1. */
	    /* If this is empty, then use the empty rows, in the range */
	    /* Front_new1strow [nfr] to n_row-1. */
	    /* If this too is empty, then pivrow [OUT] will be empty. */
	    /* In both cases, update Front_new1strow [...]. */

	    fleftmost = Front_leftmostdesc [frontid] ;
	    row1 = Front_new1strow [fleftmost] ;
	    row2 = Front_1strow [frontid+1] - 1 ;
	    DEBUG1 (("Leftmost: "ID" Rows ["ID" to "ID"] srch ["ID" to "ID"]\n",
	        fleftmost, Front_1strow [frontid], row2, row1, row2)) ;

	    /* look in the range row1 ... row2 */
	    for (row = row1 ; row <= row2 ; row++)
	    {
	        DEBUG2 (("   Row: "ID"\n", row)) ;
	        if (NON_PIVOTAL_ROW (row))
	        {
		    /* found it */
		    DEBUG2 (("   Row: "ID" found\n", row)) ;
		    ASSERT (Frpos [row] == EMPTY) ;
		    pivrow [OUT] = row ;
		    break ;
	        }
	    }
	    Front_new1strow [fleftmost] = row ;

	    if (pivrow [OUT] == EMPTY)
	    {
	        /* not found, look in empty row set in "dummy" front */
	        row1 = Front_new1strow [nfr] ;
	        row2 = n_row-1 ;
	        DEBUG2 (("Empty: "ID" Rows ["ID" to "ID"] srch["ID" to "ID"]\n",
	            nfr, Front_1strow [nfr], row2, row1, row2)) ;

	        /* look in the range row1 ... row2 */
	        for (row = row1 ; row <= row2 ; row++)
	        {
		    DEBUG2 (("   Empty Row: "ID"\n", row)) ;
	    	    if (NON_PIVOTAL_ROW (row))
		    {
		        /* found it */
		        DEBUG2 (("   Empty Row: "ID" found\n", row)) ;
		        ASSERT (Frpos [row] == EMPTY) ;
		        pivrow [OUT] = row ;
		        break ;
		    }
	        }
	        Front_new1strow [nfr] = row ;
	    }

	    if (pivrow [OUT] == EMPTY)
	    {
	        /* Row-merge set is empty.  We can just discard */
	        /* the candidate pivot column. */
		DEBUG0 (("Warning: row-merge set empty\n")) ;
	        return (UMFPACK_WARNING_singular_matrix) ;
	    }
	}
    }

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

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

    if (pivrow [IN] != EMPTY)
    {

	/* the row merge candidate row is not pivrow [IN] */
	freebie [IN] = (pivrow [IN] == prior_pivrow [IN]) && (cdeg > 0) ;
	ASSERT (cdeg >= 0) ;

	if (!freebie [IN])
	{
	    /* include current front in the degree of this row */

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

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

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

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

	    rdeg [IN] = fncols ;

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

	    /* add the pivot column itself */
	    if (Wp [pivcol] > wpflag && Fcpos [pivcol] < 0)
	    {
	        DEBUG0 (("Adding pivot col to pivrow [IN] pattern\n")) ;
	        if (rdeg [IN] >= max_rdeg)
	        {
	    	    return (UMFPACK_ERROR_different_pattern) ;
	        }
	        Wp [pivcol] = wpflag ;
	        W_i [rdeg [IN]++] = pivcol ;
	    }

	    tpi = Row_tuples [pivrow [IN]] ;
	    if (tpi)
	    {
	        tp = (Tuple *) (Memory + tpi) ;
	        tp1 = tp ;
	        tp2 = tp ;
	        tpend = tp + Row_tlen [pivrow [IN]] ;
	        for ( ; tp < tpend ; tp++)
	        {
		    e = tp->e ;
		    ASSERT (e > 0 && e <= Work->nel) ;
		    if (!E [e])
		    {
		        continue ;	/* element already deallocated */
		    }
		    f = tp->f ;
		    p = Memory + E [e] ;
		    ep = (Element *) p ;
		    p += UNITS (Element, 1) ;
		    Cols = (Int *) p ;
		    ncols = ep->ncols ;
		    Rows = Cols + ncols ;
		    if (Rows [f] == EMPTY)
		    {
		        continue ;	/* row already assembled */
		    }
		    ASSERT (pivrow [IN] == Rows [f]) ;

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

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

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

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

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

    }

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

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

    /* If this is a candidate row from the row merge set, force it to be */
    /* scanned (ignore prior_pivrow [OUT]). */

    if (pivrow [OUT] != EMPTY)
    {
    	freebie [OUT] = (pivrow [OUT] == prior_pivrow [OUT]) && cdeg > 0 ;
	ASSERT (cdeg >= 0) ;

	if (!freebie [OUT])
	{

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

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

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

	    rdeg [OUT] = 0 ;

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

	    /* add the pivot column itself */
	    if (Wp [pivcol] > wpflag)
	    {
	        DEBUG0 (("Adding pivot col to pivrow [OUT] pattern\n")) ;
	        if (rdeg [OUT] >= max_rdeg)
	        {
	    	    return (UMFPACK_ERROR_different_pattern) ;
	        }
	        Wp [pivcol] = wpflag ;
	        W_o [rdeg [OUT]++] = pivcol ;
	    }

	    tpi = Row_tuples [pivrow [OUT]] ;
	    if (tpi)
	    {
	        tp = (Tuple *) (Memory + tpi) ;
	        tp1 = tp ;
	        tp2 = tp ;
	        tpend = tp + Row_tlen [pivrow [OUT]] ;
	        for ( ; tp < tpend ; tp++)
	        {
		    e = tp->e ;
		    ASSERT (e > 0 && e <= Work->nel) ;
		    if (!E [e])
		    {
		        continue ;		/* element already deallocated */
		    }
		    f = tp->f ;
		    p = Memory + E [e] ;
		    ep = (Element *) p ;
		    p += UNITS (Element, 1) ;
		    Cols = (Int *) p ;
		    ncols = ep->ncols ;
		    Rows = Cols + ncols ;
		    if (Rows [f] == EMPTY)
		    {
		        continue ;	/* row already assembled */
		    }
		    ASSERT (pivrow [OUT] == Rows [f]) ;

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

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

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

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

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

    }
    DEBUG5 (("Row_search end ] \n")) ;

    return (UMFPACK_OK) ;
}


```

sparse-matrix/umfpack/umf_extend_front.c:
```
/* ========================================================================== */
/* === UMF_extend_front ===================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/* Called by kernel. */

#include "umf_internal.h"

GLOBAL void UMF_extend_front
(
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Entry *Fx, *Flast, *Fd, *Fs, *Fcol, *Frow, temp, *Flrow, *Fu ;
    Int j, i, fnpiv, *Frows, row, col, i2, *Wrow, *Wcol,
	*Frpos, *Fcpos, *Fcols, pivcol, pivrow, fnrows_extended, rrdeg, ccdeg,
	fncols_extended, fnrows_max, fncols_max, fnrows, fncols,
	fspos, fdpos, fs, j2, j3, col2, row2, fncols_orig ;
#ifndef NDEBUG
    Int debug_ok ;
#endif

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

    Frows = Work->Frows ;
    Frpos = Work->Frpos ;
    Fcols = Work->Fcols ;
    Fcpos = Work->Fcpos ;
    Fx = Work->Fx ;
    pivcol = Work->pivcol ;
    pivrow = Work->pivrow ;
    fnrows_max = Work->fnrows_max ;
    fncols_max = Work->fncols_max ;
    fnrows = Work->fnrows ;
    fncols = Work->fncols ;
    fncols_orig = fncols ;
    rrdeg = Work->rrdeg ;
    ccdeg = Work->ccdeg ;

#ifndef NDEBUG
    DEBUG2 (("EXTEND FRONT\n")) ;
    if (fncols == 0 || fnrows == 0)
    {
	DEBUG2 (("Extending empty front "ID" "ID"\n", fnrows,fncols)) ;
    }
    DEBUG6 (("Pivot row, before shift and extension: "ID"\n", fncols)) ;
    for (j = 0 ; j < fncols ; j++)
    {
	DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
	    j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ;
    }
    DEBUG6 (("Pivot col, before shift and extension: "ID"\n", fnrows)) ;
    for (i = 0 ; i < fnrows ; i++)
    {
	DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
	    i, Frows [i], Frpos [Frows [i]], i < fnrows)) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }
    if (Work->pivcol_in_front)
    {
	DEBUG6 (("Extended part of pivot col, before shift/extension: "ID"\n",
	    fnrows)) ;
	for (i = fnrows ; i < fnrows + ccdeg ; i++)
	{
	    DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
		i, Frows [i], Frpos [Frows [i]], i < fnrows)) ;
	    ASSERT (Frpos [Frows [i]] == i) ;
	}
    }
    DEBUG2 (("Work->fnpiv "ID"\n", Work->fnpiv)) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* pivot column already updated */
    /* ---------------------------------------------------------------------- */

    fnpiv = Work->fnpiv ;

    /* ====================================================================== */
    /* === Shift pivot row and column ======================================= */
    /* ====================================================================== */

    /* ---------------------------------------------------------------------- */
    /* move the pivot column into place */
    /* ---------------------------------------------------------------------- */

    fdpos = (fncols_max - fnpiv - 1) * fnrows_max ;
    Fd = Fx + fdpos ;	/* Fd: destination of pivot column */

#ifndef NDEBUG
    /*
    DEBUG7 (("Complete frontal matrix prior to pivcol swap (incl unused):\n")) ;
    UMF_dump_dense (Fx, fnrows_max, fnrows_max, fncols_max) ;
    */
#endif

    if (Work->pivcol_in_front)
    {

	fspos = Fcpos [pivcol] ;
	fs = fspos / fnrows_max ;

	/* Fs: current position of pivot column */
	Fs = Fx + fspos ;

	/* Flast: position of last column in front */
	Flast = Fx + (fncols - 1) * fnrows_max ;

	/* ------------------------------------------------------------------ */
	/* pivot column is in current front - shift into place */
	/* ------------------------------------------------------------------ */

	DEBUG6 (("Swap/shift pivot column in front\n")) ;
	DEBUG6 (("fspos: "ID" flpos: "ID" fdpos: "ID"\n",
	    fspos, (fncols - 1) * fnrows_max, fdpos)) ;

	if (Flast != Fd)
	{
	    if (Fs == Flast)
	    {
		/* ---------------------------------------------------------- */
		/* move Fs => Fd */
		/* ---------------------------------------------------------- */

		DEBUG6 (("col case 1\n")) ;

		/* column of the contribution block: */
		for (i = 0 ; i < fnrows ; i++)
		{
		    Fd [i] = Fs [i] ;
		}

#ifndef NDEBUG
		/* column of the U2 block */
		for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++)
		{
		    ASSERT (IS_ZERO (Fs [i])) ;
		}
#endif

	    }
	    else
	    {
		/* ---------------------------------------------------------- */
		/* move Fs => Fd */
		/* move Flast => Fs */
		/* ---------------------------------------------------------- */

		DEBUG6 (("col case 2\n")) ;

		/* column of the contribution block: */
		for (i = 0 ; i < fnrows ; i++)
		{
		    Fd [i] = Fs [i] ;
		    Fs [i] = Flast [i] ;
		}
		/* column of the U2 block */
		for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++)
		{
		    ASSERT (IS_ZERO (Fs [i])) ;
		    Fs [i] = Flast [i] ;
		}
	    }
	}
	else if (Fs != Fd)
	{
	    /* -------------------------------------------------------------- */
	    /* swap Fs <=> Fd */
	    /* -------------------------------------------------------------- */

	    DEBUG6 (("col case 3\n")) ;

	    /* column of the contribution block: */
	    for (i = 0 ; i < fnrows ; i++)
	    {
		temp = Fd [i] ;
		Fd [i] = Fs [i] ;
		Fs [i] = temp ;
	    }
	    /* column of the U2 block */
	    for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++)
	    {
		ASSERT (IS_ZERO (Fs [i])) ;
		Fs [i] = Fd [i] ;
	    }
	}

	/* move column Flast to Fs in the Fcols pattern */
	col2 = Fcols [fncols - 1] ;
	Fcols [fs] = col2 ;
	Fcpos [col2] = fspos ;

	/* one less column in the contribution block */
	fncols = --(Work->fncols) ;

    }
    else
    {
	/* ------------------------------------------------------------------ */
	/* pivot column is not in front - tack onto L block */
	/* ------------------------------------------------------------------ */

	DEBUG6 (("Pivot column not in front\ncol case 5\n")) ;

	/* column of L */
	for (i = 0 ; i < fnrows ; i++)
	{
	    CLEAR (Fd [i]) ;
	}
	/* column of U2 */
	for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++)
	{
	    CLEAR (Fd [i]) ;
	}

    }

    /* move pivot column to Fd */
    Fcpos [pivcol] = fdpos ;

    /* scan starts at the first new column in Fcols */
    /* also scan the pivot column if it was not in the front */
    Work->fscan_col = fncols ;

#ifndef NDEBUG
    DEBUG6 (("Pivot row, after shift but before extension: "ID"\n", fncols)) ;
    for (j = 0 ; j < fncols ; j++)
    {
	DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
	    j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ;
    }
    /*
    DEBUG7 (("Complete frontal matrix after pivot col swap (incl unused):\n")) ;
    UMF_dump_dense (Fx, fnrows_max, fnrows_max, fncols_max) ;
    */
#endif

    /* ---------------------------------------------------------------------- */
    /* move pivot row into place */
    /* ---------------------------------------------------------------------- */

    fdpos = fnrows_max - fnpiv - 1 ;
    Fd = Fx + fdpos ;	/* Fd: destination of pivot row */

    if (Work->pivrow_in_front)
    {

	fspos = Frpos [pivrow] ;

	/* Fs: current position of pivot column in front */
	Fs = Fx + fspos ;

	/* Flast: position of last row in front */
	Flast = Fx + (fnrows - 1) ;

	/* ------------------------------------------------------------------ */
	/* pivot row is in current front - shift into place */
	/* ------------------------------------------------------------------ */

	DEBUG6 (("Swap/shift pivot row in front:\n")) ;
	DEBUG6 (("fspos: "ID" flpos: "ID" fdpos: "ID"\n",
	    fspos, fnrows-1, fdpos)) ;

	if (Flast != Fd)
	{
	    if (Fs == Flast)
	    {
		/* ---------------------------------------------------------- */
		/* move Fs => Fd */
		/* ---------------------------------------------------------- */

		DEBUG6 (("row case 1\n")) ;

		/* row of the contribution block: */
		j2 = fncols * fnrows_max ;
		for (j = 0 ; j < j2 ; j += fnrows_max)
		{
		    Fd [j] = Fs [j] ;
		}

		/* row of the L2 block: */
		j2 = fncols_max	* fnrows_max ;
		j = (fncols_max - fnpiv	- 1) * fnrows_max ;
		for ( ; j < j2 ; j += fnrows_max)
		{
		    Fd [j] = Fs [j] ;
		}

	    }
	    else
	    {

		/* ---------------------------------------------------------- */
		/* move Fs => Fd */
		/* move Flast => Fs */
		/* ---------------------------------------------------------- */

		DEBUG6 (("row case 2\n")) ;

		/* row of the contribution block: */
		j2 = fncols * fnrows_max ;
		for (j = 0 ; j < j2 ; j += fnrows_max)
		{
		    Fd [j] = Fs [j] ;
		    Fs [j] = Flast [j] ;
		}
		/* row of the L2 block: */
		j2 = fncols_max * fnrows_max ;
		j = (fncols_max - fnpiv - 1) * fnrows_max ;
		for ( ; j < j2 ; j += fnrows_max)
		{
		    Fd [j] = Fs [j] ;
		    Fs [j] = Flast [j] ;
		}
	    }
	}
	else if (Fs != Fd)
	{

	    /* -------------------------------------------------------------- */
	    /* swap Fs <=> Fd */
	    /* -------------------------------------------------------------- */

	    DEBUG6 (("row case 3\n")) ;

	    /* row of the contribution block: */
	    j2 = fncols * fnrows_max ;
	    for (j = 0 ; j < j2 ; j += fnrows_max)
	    {
		temp = Fd [j] ;
		Fd [j] = Fs [j] ;
		Fs [j] = temp ;
	    }
	    /* row of the L2 block: */
	    j2 = fncols_max * fnrows_max ;
	    j = (fncols_max - fnpiv - 1) * fnrows_max ;
	    for ( ; j < j2 ; j += fnrows_max)
	    {
		temp = Fd [j]  ;
		Fd [j] = Fs [j] ;
		Fs [j] = temp ;
	    }
	}

	/* move row Flast to Fs in the Frows pattern */
	row2 = Frows [fnrows-1] ;
	Frows [fspos] = row2 ;
	Frpos [row2] = fspos ;


	if (Work->pivcol_in_front && ccdeg > 0)
	{

	    /* move row Fe to Flast in the extended Frows pattern */
	    row2 = Frows [fnrows + ccdeg - 1] ;
	    Frows [fnrows-1] = row2 ;
	    Frpos [row2] = fnrows-1 ;
	}

	/* one less row in the contribution block */
	fnrows = --(Work->fnrows) ;

	/* ------------------------------------------------------------------ */
	/* update pivot row */
	/* ------------------------------------------------------------------ */

	if (fnpiv > 0 && fncols > 0)
	{
	    DEBUG6 (("Update pivot row (but not pivot entry itself):\n")) ;
	    Fu = Fd + 1 ;
	    Flrow = Fd + (fncols_max - fnpiv) * fnrows_max ;

#ifdef USE_NO_BLAS

	    /* no BLAS available - use plain C code instead */
	    j2 = 0 ;
	    for (j = 0 ; j < fncols ; j++)
	    {
		i2 = 0 ;
		for (i = 0 ; i < fnpiv ; i++)
		{
		    /* Fd [j2] -= Fu [i+j*fnrows_max] * Flrow [i2] ; */
		    MULT_SUB (Fd [j2], Fu [i+j*fnrows_max], Flrow [i2]) ;
		    i2 += fnrows_max ;
		}
		j2 += fnrows_max ;
	    }

#else

	    BLAS_GEMV_ROW (fnpiv, fncols, Fu, Flrow, Fd, fnrows_max) ;

#endif	/* USE_NO_BLAS */

	}

    }
    else
    {
	/* ------------------------------------------------------------------ */
	/* pivot row is not in front - tack onto U block */
	/* ------------------------------------------------------------------ */

	DEBUG6 (("Pivot row not in current front\nrow case 5\n")) ;

	/* row of U */
	j2 = fncols * fnrows_max ;
	for (j = 0 ; j < j2 ; j += fnrows_max)
	{
	    CLEAR (Fd [j]) ;
	}
	/* row of L2 */
	j2 = fncols_max * fnrows_max ;
	j = (fncols_max - fnpiv - 1) * fnrows_max ;
	for ( ; j < j2 ; j += fnrows_max)
	{
	    CLEAR (Fd [j]) ;
	}

    }

    /* move pivot row to Fd */
    Frpos [pivrow] = fdpos ;

    /* scan1 starts at the first new row in Frows */
    /* also scan the pivot row if it was not in the front */
    Work->fscan_row = fnrows ;

#ifndef NDEBUG
    debug_ok = TRUE ;
    DEBUG6 (("Pivot col, before shift but before extension: "ID"\n", fnrows)) ;
    for (i = 0 ; i < fnrows ; i++)
    {
	DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
	    i, Frows [i], Frpos [Frows [i]], i < fnrows)) ;
	debug_ok = debug_ok && (Frpos [Frows [i]] == i) ;
    }
    ASSERT (debug_ok) ;
#endif

    /* ====================================================================== */
    /* === EXTEND PATTERN OF FRONT ========================================== */
    /* ====================================================================== */

    /* ---------------------------------------------------------------------- */
    /* extend row pattern of the front with the new pivot column extension */
    /* ---------------------------------------------------------------------- */

    fnrows_extended = fnrows ;
    fncols_extended = fncols ;

    if (Work->pivcol_in_front)
    {
	/* extended pattern and position already in Frows and Frpos */
	fnrows_extended += ccdeg ;

#ifndef NDEBUG
    debug_ok = TRUE ;
    for (i = fnrows ; i < fnrows + ccdeg ; i++)
    {
	row = Frows [i] ;
	DEBUG2 ((" row:: "ID" (ext)\n", row)) ;
	debug_ok = debug_ok && (Frpos [row] == i) && (row != pivrow) ;
    }
    ASSERT (debug_ok) ;
#endif

    }
    else
    {
	/* extended pattern is in Wcol, not yet in the front */
	Wcol = Work->Wcol ;
	ASSERT (Wcol == Work->Wm) ;
	for (i = 0 ; i < ccdeg ; i++)
	{
	    row = Wcol [i] ;
	    DEBUG2 ((" row:: "ID" (ext)\n", row)) ;
	    ASSERT (Frpos [row] == EMPTY);
	    ASSERT (row != pivrow) ;
	    Frows [fnrows_extended] = row ;
	    Frpos [row] = fnrows_extended ;
	    fnrows_extended++ ;
	}
    }

    ASSERT (fnpiv + fnrows_extended <= fnrows_max) ;

    /* ---------------------------------------------------------------------- */
    /* extend the column pattern of the front with the new pivot row */
    /* ---------------------------------------------------------------------- */

    if (Work->pivrow_in_front)
    {
	if (Work->pivcol_in_front)
	{

	    /* fill in the hole made when the pivot column was removed */
	    if (rrdeg > fncols_orig)
	    {
		Fcols [fncols] = Fcols [--rrdeg] ;
		fncols_extended = rrdeg ;
		for (i = fncols ; i < rrdeg ; i++)
		{
#ifndef NDEBUG
		    col = Fcols [i] ;
		    ASSERT (col != pivcol) ;
		    DEBUG2 ((" col:: "ID" (ext)\n", col)) ;
		    ASSERT (Fcpos [col] < 0) ;
#endif
		    Fcpos [Fcols [i]] = i * fnrows_max ;
		}
	    }
	}
	else
	{
	    Wrow = Work->Wrow ;
	    for (i = fncols_orig ; i < rrdeg ; i++)
	    {
		col = Wrow [i] ;
		if (col != pivcol)
		{
		    DEBUG2 ((" col:: "ID" (ext)\n", col)) ;
		    ASSERT (Fcpos [col] < 0) ;
		    Fcols [fncols_extended] = col ;
		    Fcpos [col] = fncols_extended * fnrows_max ;
		    fncols_extended++ ;
		}
	    }
	}
    }
    else
    {
	Wrow = Work->Wrow ;
	for (i = 0 ; i < rrdeg ; i++)
	{
	    col = Wrow [i] ;
	    if (col != pivcol && Fcpos [col] < 0)
	    {
		DEBUG2 ((" col:: "ID" (ext)\n", col)) ;
		Fcols [fncols_extended] = col ;
		Fcpos [col] = fncols_extended * fnrows_max ;
		fncols_extended++ ;
	    }
	}
    }


#ifndef NDEBUG
    ASSERT (fnpiv + fncols_extended <= fncols_max) ;
    DEBUG6 (("Pivot row, after shift and extension: "ID" "ID"\n",
    fncols,fncols_extended)) ;
    for (j = 0 ; j < fncols_extended ; j++)
    {
	DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
	    j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ;
    }
    DEBUG6 (("Pivot col, after shift and extension: "ID" "ID"\n",
    fnrows,fnrows_extended)) ;
    for (i = 0 ; i < fnrows_extended ; i++)
    {
	DEBUG7 ((" "ID" "ID" "ID"  "ID"\n",
	    i, Frows [i], Frpos [Frows [i]], i < fnrows)) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }
    /*
    DEBUG7 (("Complete frontal matrix after all swaps (incl unused):\n")) ;
    UMF_dump_dense (Fx, fnrows_max, fnrows_max, fncols_max) ;
    */
#endif

    /* ====================================================================== */
    /* Prepare for degree update and next local pivot search */
    /* ====================================================================== */

#ifndef NDEBUG
    DEBUG6 (("JUST BEFORE SCAN3A/4A:\nPivot row pattern:\n")) ;
    for (j = 0 ; j < fncols_extended ; j++)
    {
	DEBUG7 ((ID" "ID" "ID" "ID"\n",
	    j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ;
    }
    DEBUG6 (("Pivot col pattern:\n")) ;
    for (i = 0 ; i < fnrows_extended ; i++)
    {
	DEBUG7 ((ID" "ID" "ID" "ID"\n",
	    i, Frows [i], Frpos [Frows [i]], i < fnrows)) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* Finished with step fnpiv (except for assembly and scale of pivot col) */
    /* ---------------------------------------------------------------------- */

    fnpiv = ++(Work->fnpiv) ;

#ifndef NDEBUG
    DEBUG6 (("EXT: pivot row pattern:  len="ID"\n", fncols_extended)) ;
    for (j = 0 ; j < fncols_extended ; j++) DEBUG7 ((ID"\n", Fcols [j])) ;
    DEBUG6 (("EXT: pivot col pattern:  len="ID"\n", fnrows_extended)) ;
    for (j = 0 ; j < fnrows_extended ; j++) DEBUG7 ((ID"\n", Frows [j])) ;
#endif

    /* ====================================================================== */
    /* === EXTEND NUMERICAL FRONT =========================================== */
    /* ====================================================================== */

    /* ---------------------------------------------------------------------- */
    /* Zero the newly extended frontal matrix */
    /* ---------------------------------------------------------------------- */

    Fcol = Fx + fncols * fnrows_max ;
    i2 = fnrows_max - fnpiv ;
    for (j = fncols ; j < fncols_extended ; j++)
    {
	/* zero the new columns in the contribution block: */
	for (i = 0 ; i < fnrows_extended ; i++)
	{
	    CLEAR (Fcol [i]) ;
	}
	/* zero the new columns in U block: */
	for (i = i2 ; i < fnrows_max ; i++)
	{
	    CLEAR (Fcol [i]) ;
	}
	Fcol += fnrows_max ;
    }

    Frow = Fx + fnrows ;
    j3 = fncols_max - fnpiv ;
    for (i = fnrows ; i < fnrows_extended ; i++)
    {
	/* zero the new rows in the contribution block: */
	for (j = 0 ; j < fncols ; j++)
	{
	    CLEAR (Frow [j * fnrows_max]) ;
	}
	/* zero the new rows in L block: */
	for (j = j3 ; j < fncols_max ; j++)
	{
	    CLEAR (Frow [j * fnrows_max]) ;
	}
	Frow++ ;
    }

    /* ---------------------------------------------------------------------- */
    /* finalize extended row and column pattern of the frontal matrix */
    /* ---------------------------------------------------------------------- */

    Work->fnrows = fnrows_extended ;
    Work->fncols = fncols_extended ;

    Work->scan_pivcol = !Work->pivcol_in_front ;
    Work->scan_pivrow = !Work->pivrow_in_front ;

}

```

sparse-matrix/umfpack/umfpack_qsymbolic.c:
```
/* ========================================================================== */
/* === UMFPACK_qsymbolic ==================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Performs a symbolic factorization.
    See umfpack_qsymbolic.h and umfpack_symbolic.h for details.

    Dynamic memory usage of UMFPACK_qsymbolic:

    1)  calls UMF_malloc 6 times, for workspace of total size (C + 5*n_col)
	integers.  The value of C is determined by the macro
	UMF_COLAMD_RECOMMENDED.  It is roughly
	max (2*nz, 3*n_col) + 8*n_col + 6*n_row + n_col + nz/5, or typically
	about 2.2*nz + 9*n_col + 6*n_row, where nz is the number of entries in
	A.  If A is square, this is 2.2*nz + 15*n.

    2)  three more calls to UMF_malloc are made, for a total space of
	(n_row+1 + n_col+1) integers + sizeof (SymbolicType).  
	sizeof (SymbolicType) is a small constant.  This space is part of the
	Symbolic object and is not freed unless an error occurs.  The analysis
	(UMF_colamd and/or UMF_analyze) is then performed.
	This is about 2n if A is square.

    3)  UMF_malloc is called 7 times, for a total workspace of
	(4*(nfr+1)+3*(nchains+1)) integers, where nfr is the total number of
	frontal matrices and nchains is the total number of frontal matrix
	chains, and where nchains <= nfr <= n_col.  This space is part of the
	Symbolic object and is not free'd unless an error occurs.  This is
	between 7 and about 7n integers when A is square.

	Thus, the peak memory usage of UMFPACK_symbolic occurs at this point.
	For a square matrix, it is at most about (2.2*nz + 24*n) integers for
	temporary workspace, plus between 2*n and 9*n integers for the final
	Symbolic object.

    4)	The 6 workspace objects allocated in step (1) are free'd via
	UMF_free.  The final Symbolic object consists of 10 allocated objects.
	Its final total size is lies roughly between 2*n and 9*n for a square
	matrix.  If an error occurs, all 10 objects are free'd via UMF_free.

    Dynamic memory usage of UMFPACK_free_symbolic:

    1)  All 10 objects comprising the Symbolic object are free'd via UMF_free.

*/

/* ========================================================================== */

#include "umf_internal.h"
#include "umf_symbolic_usage.h"
#include "umf_colamd.h"
#include "umf_set_stats.h"
#include "umf_analyze.h"
#include "umf_transpose.h"
#include "umf_is_permutation.h"
#include "umf_kernel_init_usage.h"
#include "umf_malloc.h"
#include "umf_free.h"

typedef struct	/* SWorkType */
{
    Int *Front_npivcol ;
    Int *Front_nrows ;
    Int *Front_ncols ;
    Int *Front_parent ;
    Int *Front_cols ;		/* in UMF_colamd only */
    Int *Ci ;

} SWorkType ;

PRIVATE void free_work
(
    SWorkType *SWork
) ;

PRIVATE void error
(
    SymbolicType **Symbolic,
    SWorkType *SWork
) ;

#define SYM_WORK_USAGE(n_col,Clen) (DUNITS (Int, Clen) + 5*DUNITS (Int, n_col))

/* required size of Ci for code that calls UMF_transpose and UMF_analyze below*/
#define UMF_ANALYZE_CLEN(nz,n_row,n_col,nn) \
    ((n_col) + MAX ((nz),1) + 3*(nn)+1 + (n_col))

#ifndef NDEBUG
PRIVATE Int init_count ;
#endif

/* ========================================================================== */

GLOBAL Int UMFPACK_qsymbolic
(
    Int n_row,
    Int n_col,
    const Int Ap [ ],
    const Int Ai [ ],
    const Int Q [ ],
    void **SymbolicHandle,
    const double Control [UMFPACK_CONTROL],
    double User_Info [UMFPACK_INFO]
)
{

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

    Int colamd_ok, i, nz, maxfrsize, s, j, newj, status, maxoldpiv, chain_npiv,
	maxnrows, maxncols, nfr, col, nchains, maxrows, maxcols, p, nb, nn,
	*Chain_start, *Chain_maxrows, *Chain_maxcols, *Front_npivcol, *Ci,
	Clen, colamd_stats [COLAMD_STATS], fpiv, frows, fcols, n_inner,
	child, cr, cc, cp, parent, *Link, *Row_degree, row, *Front_parent,
	head_usage, tail_usage, max_usage, analyze_compactions,
	lf, uf, init_tail_usage, k, do_colamd, do_UMF_analyze, too_large,
	fpivcol, fallrows, fallcols, *InFront, *F1,
	*Front_1strow, f1rows, kk, *Cperm_init, *Rperm_init, newrow,
	*Front_leftmostdesc, Clen_analyze ;
    double knobs [COLAMD_KNOBS], flops, f, r, c, tstart, tend,
	*Info, Info2 [UMFPACK_INFO], drow, dcol, dusage, dlf, duf, dmax_usage,
	dhead_usage, dh, dlnz, dunz, dmaxfrsize, dClen, dClen_analyze ;
    Int nempty_row, nempty_col ;
    SymbolicType *Symbolic ;
    SWorkType SWorkSpace, *SWork ;

#ifndef NDEBUG
    double f2 ;
    Int lf2, uf2 ;
    UMF_dump_start ( ) ;
    init_count = UMF_malloc_count ;
#endif

    /* ---------------------------------------------------------------------- */
    /* get the amount of time used by the process so far */
    /* ---------------------------------------------------------------------- */

    tstart = umfpack_timer ( ) ;

    /* ---------------------------------------------------------------------- */
    /* check input parameters */
    /* ---------------------------------------------------------------------- */

    if (Control)
    {
	/* use the Control array passed to us by the caller. */

	if (SCALAR_IS_NAN (Control [UMFPACK_DENSE_ROW]))
	{
	    drow = UMFPACK_DEFAULT_DENSE_ROW ;
	}
	else
	{
	    drow = Control [UMFPACK_DENSE_ROW] ;
	}

	if (SCALAR_IS_NAN (Control [UMFPACK_DENSE_COL]))
	{
	    dcol = UMFPACK_DEFAULT_DENSE_COL ;
	}
	else
	{
	    dcol = Control [UMFPACK_DENSE_COL] ;
	}

	if (SCALAR_IS_NAN (Control [UMFPACK_BLOCK_SIZE]))
	{
	    nb = UMFPACK_DEFAULT_BLOCK_SIZE ;
	}
	else
	{
	    nb = (Int) Control [UMFPACK_BLOCK_SIZE] ;
	}

    }
    else
    {
	/* no Control passed - use defaults instead */
	drow = UMFPACK_DEFAULT_DENSE_ROW ;
	dcol = UMFPACK_DEFAULT_DENSE_COL ;
	nb = UMFPACK_DEFAULT_BLOCK_SIZE ;
    }

    nb = MAX (1, nb) ;
    DEBUG0 (("nb = "ID"\n", nb)) ;

    if (User_Info)
    {
	/* return Info in user's array */
	Info = User_Info ;
    }
    else
    {
	/* no Info array passed - use local one instead */
	Info = Info2 ;
    }
    for (i = 0 ; i < UMFPACK_INFO ; i++)
    {
	Info [i] = EMPTY ;
    }

    nn = MAX (n_row, n_col) ;
    n_inner = MIN (n_row, n_col) ;

    Info [UMFPACK_STATUS] = UMFPACK_OK ;
    Info [UMFPACK_NROW] = n_row ;
    Info [UMFPACK_NCOL] = n_col ;
    Info [UMFPACK_SIZE_OF_UNIT] = (double) (sizeof (Unit)) ;
    Info [UMFPACK_SIZE_OF_INT] = (double) (sizeof (int)) ;
    Info [UMFPACK_SIZE_OF_LONG] = (double) (sizeof (long)) ;
    Info [UMFPACK_SIZE_OF_POINTER] = (double) (sizeof (void *)) ;
    Info [UMFPACK_SIZE_OF_ENTRY] = (double) (sizeof (Entry)) ;
    Info [UMFPACK_SYMBOLIC_DEFRAG] = 0 ;

    if (!Ai || !Ap || !SymbolicHandle)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ;
	return (UMFPACK_ERROR_argument_missing) ;
    }

    *SymbolicHandle = (void *) NULL ;

    if (n_row <= 0 || n_col <= 0)	/* n_row, n_col must be > 0 */
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_n_nonpositive ;
	return (UMFPACK_ERROR_n_nonpositive) ;
    }

    nz = Ap [n_col] ;
    DEBUG0 (("In UMFPACK_symbolic, n_row "ID" n_col "ID" nz "ID"\n",
	n_row, n_col, nz)) ;
    Info [UMFPACK_NZ] = nz ;
    if (nz < 0)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_nz_negative ;
	return (UMFPACK_ERROR_nz_negative) ;
    }

    if (Q)
    {
	do_colamd = FALSE ;
    }
    else
    {
	do_colamd = TRUE ;
    }

    /* ---------------------------------------------------------------------- */
    /* determine amount of memory required for UMFPACK_symbolic */
    /* ---------------------------------------------------------------------- */

    /* The size of Clen required for UMF_colamd is always larger than */
    /* UMF_analyze, but the max is included here in case that changes in */
    /* future versions. */

    dClen = UMF_COLAMD_RECOMMENDED ((double) nz, (double) n_row,
	(double) n_col) ;
    dClen_analyze = UMF_ANALYZE_CLEN ((double) nz, (double) n_row,
	(double) n_col, (double) nn) ;
    dClen = MAX (dClen, dClen_analyze) ;
    too_large = INT_OVERFLOW (dClen * sizeof (Int)) ;

    if (too_large)
    {
	/* Problem is too large for array indexing (Ci [i]) with an Int i. */
	/* Cannot even analyze the problem to determine upper bounds on */
	/* memory usage. Need to use the long integer version, umfpack_*l_*. */
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_problem_too_large ;
	Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] =
	    SYM_WORK_USAGE (n_col, dClen) +
	    UMF_symbolic_usage (n_row, n_col, n_col, n_col) ;
	return (UMFPACK_ERROR_problem_too_large) ;
    }

    Clen = UMF_COLAMD_RECOMMENDED (nz, n_row, n_col) ;
    Clen_analyze = UMF_ANALYZE_CLEN (nz, n_row, n_col, nn) ;
    Clen = MAX (Clen, Clen_analyze) ;

    /* worst case total memory usage for UMFPACK_symbolic (revised below) */
    Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] =
	SYM_WORK_USAGE (n_col, Clen) +
	UMF_symbolic_usage (n_row, n_col, n_col, n_col) ;

    /* ---------------------------------------------------------------------- */
    /* allocate workspace */
    /* ---------------------------------------------------------------------- */

    Symbolic = (SymbolicType *) NULL ;
    SWork = &SWorkSpace ;	/* used for UMFPACK_symbolic only */

    /* Note that SWork->Front_* does not include the dummy placeholder front. */
    /* This space is accounted for by the SYM_WORK_USAGE (n_col, Clen) macro. */
    SWork->Ci = (Int *) UMF_malloc (Clen, sizeof (Int)) ;
    SWork->Front_npivcol = (Int *) UMF_malloc (n_col, sizeof (Int)) ;
    SWork->Front_nrows = (Int *) UMF_malloc (n_col, sizeof (Int)) ;
    SWork->Front_ncols = (Int *) UMF_malloc (n_col, sizeof (Int)) ;
    SWork->Front_parent = (Int *) UMF_malloc (n_col, sizeof (Int)) ;
    SWork->Front_cols = (Int *) UMF_malloc (n_col, sizeof (Int)) ;

    if (!SWork->Ci || !SWork->Front_npivcol || !SWork->Front_nrows ||
	!SWork->Front_ncols || !SWork->Front_parent || !SWork->Front_cols)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Symbolic, SWork) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }

    /* ---------------------------------------------------------------------- */
    /* allocate the first part of the Symbolic object (header and Cperm_init) */
    /* ---------------------------------------------------------------------- */

    Symbolic = (SymbolicType *) UMF_malloc (1, sizeof (SymbolicType)) ;

    if (!Symbolic)
    {
	/* If we fail here, Symbolic is NULL and thus it won't be */
	/* dereferenced by UMFPACK_free_symbolic, as called by error ( ). */
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Symbolic, SWork) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }

    /* We now know that Symbolic has been allocated */
    Symbolic->valid = 0 ;
    Symbolic->Chain_start = (Int *) NULL ;
    Symbolic->Chain_maxrows = (Int *) NULL ;
    Symbolic->Chain_maxcols = (Int *) NULL ;
    Symbolic->Front_npivcol = (Int *) NULL ;
    Symbolic->Front_parent = (Int *) NULL ;
    Symbolic->Front_1strow = (Int *) NULL ;
    Symbolic->Front_leftmostdesc = (Int *) NULL ;

    Symbolic->Cperm_init = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ;
    Symbolic->Rperm_init = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ;
    Cperm_init = Symbolic->Cperm_init ;
    Rperm_init = Symbolic->Rperm_init ;

    if (!Symbolic->Cperm_init || !Symbolic->Rperm_init)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Symbolic, SWork) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }

    DEBUG0 (("(1)Symbolic UMF_malloc_count - init_count = "ID"\n",
	UMF_malloc_count - init_count)) ;
    ASSERT (UMF_malloc_count == init_count + 9) ;

    Symbolic->n_row = n_row ;
    Symbolic->n_col = n_col ;
    Symbolic->nz = nz ;
    Symbolic->drow = drow ;
    Symbolic->dcol = dcol ;
    Symbolic->nb = nb ;

    /* ---------------------------------------------------------------------- */
    /* use colamd or input column ordering Q */
    /* ---------------------------------------------------------------------- */

    if (do_colamd)
    {

	/* ------------------------------------------------------------------ */
	/* copy the matrix into colamd workspace (colamd destroys its input) */
	/* ------------------------------------------------------------------ */

	Ci = SWork->Ci ;
	for (i = 0 ; i < nz ; i++)
	{
	    DEBUG5 (("Ai  "ID": "ID"\n", i, Ai [i])) ;
	    Ci [i] = Ai [i] ;
	}

	/* load the column pointers into Cperm_init [0..n_col] */
	for (col = 0 ; col <= n_col ; col++)
	{
	    DEBUG5 (("Ap  "ID": "ID"\n", col, Ap [col])) ;
	    Cperm_init [col] = Ap [col] ;
	}

	/* ------------------------------------------------------------------ */
	/* set UMF_colamd defaults */
	/* ------------------------------------------------------------------ */

	UMF_colamd_set_defaults (knobs) ;
	knobs [COLAMD_DENSE_ROW] = drow ;
	knobs [COLAMD_DENSE_COL] = dcol ;

	/* ------------------------------------------------------------------ */
	/* check input matrix and find the initial column pre-ordering */
	/* ------------------------------------------------------------------ */

	colamd_ok = UMF_colamd (n_row, n_col, Clen, SWork->Ci,
		Cperm_init, knobs, colamd_stats,
		SWork->Front_npivcol,
		SWork->Front_nrows,
		SWork->Front_ncols,
		SWork->Front_parent,
		/* used in UMF_colamd only: */ SWork->Front_cols,
		&nfr) ;

	/* get number of truly empty rows and columns */
	nempty_row = colamd_stats [COLAMD_EMPTY_ROW] ;
	nempty_col = colamd_stats [COLAMD_EMPTY_COL] ;

	if (!colamd_ok)
	{
	    /* This will be modified below.  It is not (yet) an internal */
	    /* error.  It is only an internal error if is it not modified */
	    /* below (colamd_stats missing). */
	    Info [UMFPACK_STATUS] = UMFPACK_ERROR_internal_error ;
	}

	s = colamd_stats [COLAMD_STATUS] ;

	if (s == COLAMD_OK)
	{
	    status = UMFPACK_OK ;
	}
	else if (s == COLAMD_ERROR_jumbled_matrix)
	{
	    status = UMFPACK_ERROR_jumbled_matrix ;
	}
	else if (s == COLAMD_ERROR_p0_nonzero)
	{
	    status = UMFPACK_ERROR_Ap0_nonzero ;
	}
	else if (s == COLAMD_ERROR_row_index_out_of_bounds)
	{
	    status = UMFPACK_ERROR_row_index_out_of_bounds ;
	}
	else if (s == COLAMD_ERROR_col_length_negative)
	{
	    status = UMFPACK_ERROR_col_length_negative ;
	}
	else
	{
	    /* these errors "cannot" happen */
	    /* COLAMD_ERROR_A_too_small */
	    /* COLAMD_ERROR_A_not_present */
	    /* COLAMD_ERROR_p_not_present */
	    /* COLAMD_ERROR_out_of_memory */
	    /* COLAMD_ERROR_internal_error */
	    /* COLAMD_ERROR_nrow_negative */
	    /* COLAMD_ERROR_ncol_negative */
	    /* COLAMD_ERROR_nnz_negative */
	    status = UMFPACK_ERROR_internal_error ;
	}

	Info [UMFPACK_STATUS] = status ;
	if (status != UMFPACK_OK)
	{
	    error (&Symbolic, SWork) ;
	    return (status) ;
	}

	Info [UMFPACK_NDENSE_ROW] = colamd_stats [COLAMD_DENSE_ROW] ;
	Info [UMFPACK_NEMPTY_ROW] = colamd_stats [COLAMD_EMPTY_ROW]
		+ colamd_stats [COLAMD_NEWLY_EMPTY_ROW] ;
	Info [UMFPACK_NDENSE_COL] = colamd_stats [COLAMD_DENSE_COL] ;
	Info [UMFPACK_NEMPTY_COL] = colamd_stats [COLAMD_EMPTY_COL]
		+ colamd_stats [COLAMD_NEWLY_EMPTY_COL] ;
	Info [UMFPACK_SYMBOLIC_DEFRAG] = colamd_stats [COLAMD_DEFRAG_COUNT];

	/* re-analyze if any "dense" rows or cols ignored by UMF_colamd */
	do_UMF_analyze = 
	    colamd_stats [COLAMD_DENSE_ROW] > 0 ||
	    colamd_stats [COLAMD_DENSE_COL] > 0 ;

#ifndef NDEBUG
	for (col = 0 ; col < n_col ; col++)
	{
	    DEBUG1 (("Cperm_init ["ID"] = "ID"\n", col, Cperm_init[col]));
	}
	/* make sure colamd returned a valid permutation */
	ASSERT (Cperm_init) ;
	ASSERT (UMF_is_permutation (Cperm_init, SWork->Ci, n_col, n_col)) ;
#endif

    }
    else
    {

	/* ------------------------------------------------------------------ */
	/* do not call colamd - use input Q instead */
	/* ------------------------------------------------------------------ */

	Int length ;

	/* use Ci as workspace to check input permutation */
	if (!UMF_is_permutation (Q, SWork->Ci, n_col, n_col))
	{
	    Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_permutation ;
	    error (&Symbolic, SWork) ;
	    return (UMFPACK_ERROR_invalid_permutation) ;
	}

	if (Ap [0] != 0)
	{
	    Info [UMFPACK_STATUS] = UMFPACK_ERROR_Ap0_nonzero ;
	    error (&Symbolic, SWork) ;
	    return (UMFPACK_ERROR_Ap0_nonzero) ;
	}

	nempty_col = 0 ;

	/* check Ap input */
	for (j = 0 ; j < n_col ; j++)
	{
	    length = Ap [j+1] - Ap [j] ;
	    if (length == 0)
	    {
	    	/* this is an empty column */
		DEBUG1 (("Original empty column: "ID"\n", j)) ;
		nempty_col++ ;
	    }
	    if (length < 0)
	    {
		Info [UMFPACK_STATUS] = UMFPACK_ERROR_col_length_negative ;
		error (&Symbolic, SWork) ;
		return (UMFPACK_ERROR_col_length_negative) ;
	    }
	}

	Info [UMFPACK_NDENSE_ROW] = 0 ;
	Info [UMFPACK_NEMPTY_ROW] = 0 ;	/* this is fixed below */
	Info [UMFPACK_NDENSE_COL] = 0 ;
	Info [UMFPACK_NEMPTY_COL] = nempty_col ;

	if (nempty_col == 0)
	{
	    /* copy the user's input permutation */
	    for (k = 0 ; k < n_col ; k++)
	    {
		Cperm_init [k] = Q [k] ;
	    }
	}
	else
	{
	    /* partition the user's input permutation */

	    /* move empty columns last */
	    k = n_col ;
	    for (j = n_col-1 ; j >= 0 ; j--)
	    {
		col = Q [j] ;
		length = Ap [col+1] - Ap [col] ;
		if (length == 0)
		{
		    Cperm_init [--k] = col ;
		    DEBUG1 (("Moving empty col "ID" last: "ID"\n", col, k)) ;
		}
	    }
	    ASSERT (n_col-k == nempty_col) ;

	    /* move remaining columns first */
	    k = 0 ;
	    for (j = 0 ; j < n_col ; j++)
	    {
		col = Q [j] ;
		length = Ap [col+1] - Ap [col] ;
		if (length != 0)
		{
		    DEBUG1 (("Non empty col "ID" at: "ID"\n", col, k)) ;
		    Cperm_init [k++] = col ;
		}
	    }
	}

	do_UMF_analyze = TRUE ;

    }

    Cperm_init [n_col] = EMPTY ;	/* unused in Cperm_init */

    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG3 (("Cperm_init column permutation:\n")) ;
    ASSERT (UMF_is_permutation (Cperm_init, SWork->Ci, n_col, n_col)) ;
    for (k = 0 ; k < n_col ; k++)
    {
	DEBUG3 ((ID"\n", Cperm_init [k])) ;
    }
    /* ensure that empty columns have been placed last in A (:,Cperm_init) */
    for (newj = 0 ; newj < n_col ; newj++)
    {
	/* empty columns will be last in A (:, Cperm_init (1:n_col)) */
	j = Cperm_init [newj] ;
	ASSERT (IMPLIES (newj >= n_col-nempty_col, Ap [j+1] - Ap [j] == 0)) ;
	ASSERT (IMPLIES (newj <  n_col-nempty_col, Ap [j+1] - Ap [j] > 0)) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* analyze, using the given ordering, or using colamd's ordering */
    /* ---------------------------------------------------------------------- */

    if (do_UMF_analyze)
    {

	Int *W, *Bp, *Bi, *Cperm2, ok, ilast, *P, Clen2, bsize, Clen0 ;

	/* ------------------------------------------------------------------ */
	/* Ci [0 .. Clen-1] holds the following work arrays:

		first Clen0 entries	empty space, where Clen0 =
					Clen - (nn+1 + 2*nn + n_col)
					and Clen0 >= nz + n_col
		next nn+1 entries	Bp [0..nn]
		next nn entries		Link [0..nn-1]
		next nn entries		W [0..nn-1]
		last n_col entries	Cperm2 [0..n_col-1]
	*/

	Ci = SWork->Ci ;
	Clen0 = Clen - (nn+1 + 2*nn + n_col) ;
	Bp = Ci + Clen0 ;
	Link = Bp + (nn+1) ;
	W = Link + nn ;
	Cperm2 = W + nn ;
	ASSERT (Cperm2 + n_col == Ci + Clen) ;
	ASSERT (Clen0 >= nz + n_col) ;

	/* ------------------------------------------------------------------ */
	/* P = order that rows will be used in UMF_analyze */
	/* ------------------------------------------------------------------ */

	/* use W to mark rows, and use Link for row permutation P [ [ */
	for (row = 0 ; row < n_row ; row++)
	{
	    W [row] = FALSE ;
	}
	P = Link ;

	k = 0 ;
	for (newj = 0 ; newj < n_col ; newj++)
	{
	    /* empty columns will be last in A (:,Cperm_init) */
	    j = Cperm_init [newj] ;
	    ilast = -1 ;
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		row = Ai [p] ;
		if (row < 0 || row >= n_row)
		{
		    Info [UMFPACK_STATUS] =
			UMFPACK_ERROR_row_index_out_of_bounds ;
		    error (&Symbolic, SWork) ;
		    return (UMFPACK_ERROR_row_index_out_of_bounds) ;
		}
		if (row <= ilast)
		{
		    Info [UMFPACK_STATUS] = UMFPACK_ERROR_jumbled_matrix ;
		    error (&Symbolic, SWork) ;
		    return (UMFPACK_ERROR_jumbled_matrix) ;
		}
		if (!W [row])
		{
		    /* this row has just been see for the first time */
		    W [row] = TRUE ;
		    P [k++] = row ;
		}
		ilast = row ;
	    }
	}

	/* If the matrix has truly empty rows, then P will not be */
	/* complete, and visa versa.  The matrix is structurally singular. */
	ASSERT (IMPLIES (do_colamd, nempty_row == n_row-k)) ;
	nempty_row = n_row - k ;
	Info [UMFPACK_NEMPTY_ROW] = nempty_row ;
	if (k < n_row)
	{
	    /* complete P by putting empty rows last in their natural order, */
	    /* rather than declaring an error (the matrix is singular) */
	    for (row = 0 ; row < n_row ; row++)
	    {
		if (!W [row])
		{
		    /* W [row] = TRUE ;  (not required) */
		    P [k++] = row ;
		}
	    }
	}

	/* contents of W no longer needed ] */

#ifndef NDEBUG
	DEBUG3 (("Induced row permutation:\n")) ;
	ASSERT (k == n_row) ;
	ASSERT (UMF_is_permutation (P, W, n_row, n_row)) ;
	for (k = 0 ; k < n_row ; k++)
	{
	    DEBUG3 ((ID"\n", P [k])) ;
	}
#endif

	/* ------------------------------------------------------------------ */
	/* B = row-form of the pattern of A (P, Cperm_init (1:n_col)) */
	/* ------------------------------------------------------------------ */

	/* Ci [0 .. Clen-1] holds the following work arrays:

		first Clen2 entries	empty space, must be at least >= n_col
		next max (nz,1)		Bi [0..max (nz,1)-1]
		next nn+1 entries	Bp [0..nn]
		next nn entries		Link [0..nn-1]
		next nn entries		W [0..nn-1]
		last n_col entries	Cperm2 [0..n_col-1]

		This memory usage is accounted for by the UMF_ANALYZE_CLEN
		macro.
	*/

	Clen2 = Clen0 ;
	bsize = MAX (nz, 1) ;
	Clen2 -= bsize ;
	Bi = Ci + Clen2 ;
	ASSERT (Clen2 >= n_col) ;

	/* skip error test (already done, above).  Do pattern only. */
	(void) UMF_transpose (n_row, n_col, Ap, Ai, (double *) NULL, P,
	    Cperm_init, n_col - nempty_col, Bp, Bi, (double *) NULL, W, FALSE
#ifdef COMPLEX
	    , (double *) NULL, (double *) NULL, FALSE
#endif
	    ) ;

	/* contents of P (same as Link) and W not needed */
	/* still need Link and W as work arrays, though ] */

	ASSERT (Bp [0] == 0) ;
	ASSERT (Bp [n_row] == nz) ;

	/* increment Bp to point into Ci, not Bi */
	for (i = 0 ; i <= n_row ; i++)
	{
	    Bp [i] += Clen2 ;
	}
	ASSERT (Bp [0] == Clen0 - bsize) ;
	ASSERT (Bp [n_row] <= Clen0) ;

	/* Ci [0 .. Clen-1] holds the following work arrays:

		first Clen0 entries	Ci [0 .. Clen0-1], where the col indices
					of B are at the tail end of this part,
					and Bp [0] = Clen2 >= n_col.  Note
					that Clen0 = Clen2 + max (nz,1).
		next nn+1 entries	Bp [0..nn]
		next nn entries		Link [0..nn-1]
		next nn entries		W [0..nn-1]
		last n_col entries	Cperm2 [0..n_col-1]
	*/

	/* ------------------------------------------------------------------ */
	/* analyze */
	/* ------------------------------------------------------------------ */

	/* only analyze the non-empty part of the matrix */
	ok = UMF_analyze (n_row - nempty_row, n_col - nempty_col,
		Ci, Bp, Cperm2, W, Link,
		SWork->Front_ncols, SWork->Front_nrows, SWork->Front_npivcol,
		SWork->Front_parent, &nfr, &analyze_compactions) ;
	if (!ok)
	{
	    Info [UMFPACK_STATUS] = UMFPACK_ERROR_internal_error ;
	    error (&Symbolic, SWork) ;
	    return (UMFPACK_ERROR_internal_error) ;
	}
	Info [UMFPACK_SYMBOLIC_DEFRAG] += analyze_compactions ;

	/* ------------------------------------------------------------------ */
	/* combine the input permutation and UMF_analyze's permutation */
	/* ------------------------------------------------------------------ */

	/* Cperm2 is the column etree post-ordering */
	ASSERT (UMF_is_permutation (Cperm2, W,
	    n_col-nempty_col, n_col-nempty_col)) ;

	/* Note that the empty columns remain at the end of Cperm_init */
	for (k = 0 ; k < n_col - nempty_col ; k++)
	{
	    W [k] = Cperm_init [Cperm2 [k]] ;
	}

	for (k = 0 ; k < n_col - nempty_col ; k++)
	{
	    Cperm_init [k] = W [k] ;
	}

	ASSERT (UMF_is_permutation (Cperm_init, W, n_col, n_col)) ;

    }

    /* ---------------------------------------------------------------------- */
    /* determine the size of the Symbolic object */
    /* ---------------------------------------------------------------------- */

    nchains = 0 ;
    for (i = 0 ; i < nfr ; i++)
    {
	if (SWork->Front_parent [i] != i+1)
	{
	    nchains++ ;
	}
    }

    Symbolic->nchains = nchains ;
    Symbolic->nfr = nfr ;

    /* final size of Symbolic object */
    Info [UMFPACK_SYMBOLIC_SIZE] =
	UMF_symbolic_usage (n_row, n_col, nchains, nfr) ;

    /* actual peak memory usage for UMFPACK_symbolic (actual nfr, nchains) */
    Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] =
	SYM_WORK_USAGE (n_col, Clen) + Info [UMFPACK_SYMBOLIC_SIZE] ;
    Symbolic->peak_sym_usage = Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] ;

    DEBUG0 (("Number of fronts: "ID"\n", nfr)) ;

    /* ---------------------------------------------------------------------- */
    /* allocate the second part of the Symbolic object (Front_*, Chain_*) */
    /* ---------------------------------------------------------------------- */

    /* Note that Symbolic->Front_* does include the dummy placeholder front */
    Symbolic->Front_npivcol = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ;
    Symbolic->Front_parent = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ;
    Symbolic->Front_1strow = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ;
    Symbolic->Front_leftmostdesc = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ;

    Symbolic->Chain_start = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ;
    Symbolic->Chain_maxrows = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ;
    Symbolic->Chain_maxcols = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ;

    if (!Symbolic->Front_npivcol || !Symbolic->Front_parent ||
	!Symbolic->Front_1strow || !Symbolic->Front_leftmostdesc ||
	!Symbolic->Chain_start || !Symbolic->Chain_maxrows ||
	!Symbolic->Chain_maxcols)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Symbolic, SWork) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }
    DEBUG0 (("(2)Symbolic UMF_malloc_count - init_count = "ID"\n",
	UMF_malloc_count - init_count)) ;
    ASSERT (UMF_malloc_count == init_count + 16) ;

    Front_npivcol = Symbolic->Front_npivcol ;
    Front_parent = Symbolic->Front_parent ;
    Front_1strow = Symbolic->Front_1strow ;
    Front_leftmostdesc = Symbolic->Front_leftmostdesc ;

    Chain_start = Symbolic->Chain_start ;
    Chain_maxrows = Symbolic->Chain_maxrows ;
    Chain_maxcols = Symbolic->Chain_maxcols ;

    /* ---------------------------------------------------------------------- */
    /* find row degrees and assign rows to fronts */
    /* ---------------------------------------------------------------------- */

    /* Use SWork->Ci as temporary workspace for Row_degree, InFront, and F1 */
    Row_degree = SWork->Ci ;		/* [ of size n_row */
    InFront = Row_degree + n_row ;	/* [ of size n_row */
    F1 = InFront + n_row ;		/* [ of size nfr+1 */
    ASSERT (Clen >= 2*n_row + nfr+1) ;
    for (row = 0 ; row < n_row ; row++)
    {
	Row_degree [row] = 0 ;
	InFront [row] = nfr ;	/* empty rows go to dummy front nfr */
    }

    newj = 0 ;
    k = 0 ;
    for (i = 0 ; i < nfr ; i++)
    {
	fpivcol = SWork->Front_npivcol [i] ;
	DEBUG1 (("Front "ID" k "ID" npivcol "ID" nrows "ID" ncols "ID"\n",
	    i, k, fpivcol, SWork->Front_nrows [i], SWork->Front_ncols [i])) ;
	k += fpivcol ;

	/* copy Front info into Symbolic object from SWork */
	Front_npivcol [i] = fpivcol ;
	Front_parent [i] = SWork->Front_parent [i] ;

	f1rows = 0 ;

	/* for all pivot columns in front i */
        for (kk = 0 ; kk < fpivcol ; kk++, newj++)
        {
	    j = Cperm_init [newj] ;
	    ASSERT (IMPLIES (newj >= n_col-nempty_col, Ap [j+1] - Ap [j] == 0));
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		row = Ai [p] ;
		if (Row_degree [row] == 0)
		{
		    /* this row belongs to front i */
		    DEBUG1 (("    Row "ID" in Front "ID"\n", row, i)) ;
		    InFront [row] = i ;
		    f1rows++ ;
		}
	        Row_degree [row]++ ;
	    }
    	}
	Front_1strow [i] = f1rows ;
    }

    /* assign empty columns to dummy placehold front nfr */
    DEBUG1 (("Dummy Cols in Front "ID" :: "ID"\n", nfr, n_col-k)) ;
    Front_npivcol [nfr] = n_col - k ;
    Front_parent [nfr] = EMPTY ;

    /* ---------------------------------------------------------------------- */
    /* find initial row permutation */
    /* ---------------------------------------------------------------------- */

    /* determine the first row in each front (in the new row ordering) */
    k = 0 ;
    for (i = 0 ; i < nfr ; i++)
    {
	f1rows = Front_1strow [i] ;
	DEBUG1 (("Front "ID" :: npivcol "ID" parent "ID,
	    i, Front_npivcol [i], Front_parent [i])) ;
	DEBUG1 (("    1st rows in Front "ID" : "ID"\n", i, f1rows)) ;
	Front_1strow [i] = k ;
    	k += f1rows ;
    }

    /* assign empty rows to dummy placehold front nfr */
    DEBUG1 (("Rows in Front "ID" (dummy): "ID"\n", nfr, n_row-k)) ;
    Front_1strow [nfr] = k ;
    DEBUG1 (("nfr "ID" 1strow[nfr] "ID" nrow "ID"\n", nfr, k, n_row)) ;

    for (i = 0 ; i <= nfr ; i++)
    {
	F1 [i] = Front_1strow [i] ;
    }

    for (row = 0 ; row < n_row ; row++)
    {
	i = InFront [row] ;
	newrow = F1 [i]++ ;
	Rperm_init [newrow] = row ;
    }
    Rperm_init [n_row] = EMPTY ;	/* unused */

#ifndef NDEBUG
    for (k = 0 ; k < n_row ; k++)
    {
    	DEBUG2 (("Rperm_init ["ID"] = "ID"\n", k, Rperm_init [k])) ;
    }
#endif

    /* ] done using F1 */
    /* ] done using InFront */

    /* ---------------------------------------------------------------------- */
    /* find the leftmost descendant of each front */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i <= nfr ; i++)
    {
	Front_leftmostdesc [i] = EMPTY ;
    }

    for (i = 0 ; i < nfr ; i++)
    {
	/* start at i and walk up the tree */
	DEBUG2 (("Walk up front tree from "ID"\n", i)) ;
	j = i ;
	while (j != EMPTY && Front_leftmostdesc [j] == EMPTY)
	{
	    DEBUG3 (("  Leftmost desc of "ID" is "ID"\n", j, i)) ;
	    Front_leftmostdesc [j] = i ;
	    j = Front_parent [j] ;
	    DEBUG3 (("  go to j = "ID"\n", j)) ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* compute memory and flop estimates */
    /* ---------------------------------------------------------------------- */

    nchains = 0 ;			/* number of chains */
    Chain_start [0] = 0 ;		/* front 0 starts a new chain */
    maxrows = 1 ;
    maxcols = 1 ;
    maxfrsize = 1 ;
    dmaxfrsize = 1 ;
    chain_npiv = 0 ;			/* number of pivots in current chain */

    /* ---------------------------------------------------------------------- */
    /* simulate UMF_kernel_init */
    /* ---------------------------------------------------------------------- */

    /* Numeric->Memory usage estimate, in Units */
    head_usage = 1 ;		/* head marker (see UMF_mem_init_memoryspace) */
    init_tail_usage = 2 ;	/* tail marker (see UMF_mem_init_memoryspace) */
    dusage = 3 ;		/* head and tail markers */
    dhead_usage = 1 ;

    /* elements and tuples at tail*/
    init_tail_usage += UMF_kernel_init_usage (Ap, Row_degree, n_row, n_col,
	&dusage) ;

    /* ] done using Row_degree */

    ASSERT (UMF_is_permutation (Rperm_init, SWork->Ci, n_row, n_row)) ;

    tail_usage = init_tail_usage ;
    DEBUG2 (("tail_usage: "ID" (initial)\n", tail_usage)) ;
    max_usage = head_usage + init_tail_usage ;
    dmax_usage = dusage ;

    Symbolic->num_mem_init_usage = max_usage ;

    too_large = INT_OVERFLOW (dusage * sizeof (Unit)) ;
    if (too_large)
    {
	/* Initial memory usage, for input matrix only, */
	/* has encountered integer overflow. This is an error */
	/* condition, but keep going to compute other statistics. */
	Info [UMFPACK_VARIABLE_INIT_ESTIMATE] = dusage ;	/* too large */
    }
    else
    {
	Info [UMFPACK_VARIABLE_INIT_ESTIMATE] = (double) max_usage ;
    }

    /* ---------------------------------------------------------------------- */
    /* simulate UMF_kernel */
    /* ---------------------------------------------------------------------- */

    /* Use SWork->Ci as temporary workspace for link lists */
    Link = SWork->Ci ;
    for (i = 0 ; i < nfr ; i++)
    {
	Link [i] = EMPTY ;
    }

    dlnz = MIN (n_row, n_col) ;	/* upper limit of nz in L (incl diag) */
    dunz = dlnz ;		/* upper limit of nz in U (incl diag) */
    flops = 0 ;			/* flop count upper bound */

    DEBUG1 (("Umfpack symbolic, fronts:  nfr = "ID"\n", nfr)) ;

    for (i = 0 ; i < nfr ; i++)
    {

	fpivcol  = Front_npivcol [i] ; /* # candidate pivot columns */
	fallrows = SWork->Front_nrows [i] ;   /* all rows (not just Schur comp*/
	fallcols = SWork->Front_ncols [i] ;   /* all cols (not just Schur comp*/
	parent = Front_parent [i] ; /* parent in column etree */

	DEBUG1 (("\nFront "ID" fpivcol "ID" fallrows "ID" fallcols "ID"\n",
	    i, fpivcol, fallrows, fallcols)) ;

	/* determine the max size of the contribution block */
	fpiv = MIN (fpivcol, fallrows) ;	/* # pivot rows and cols */
	frows = fallrows - fpiv ;		/* max # rows in Schur comp. */
	fcols = fallcols - fpiv ;		/* max # cols in Schur comp. */

	DEBUG1 (("    "ID" : fpiv "ID" frows "ID" fcols "ID" parent "ID"\n",
		i, fpiv, frows, fcols, parent)) ;

	/* UMF_analyze can generate 0-by-c sized frontal matrices */
	ASSERT (fpiv >= 0 && fcols >= 0 && frows >= 0) ;

	/* assemble all children of front i in column etree */
	for (child = Link [i] ; child != EMPTY ; child = Link [child])
	{
	    ASSERT (child >= 0 && child < i) ;
	    ASSERT (Front_parent [child] == i) ;
	    /* free the child element */
	    cp = MIN (Front_npivcol [child], SWork->Front_nrows [child]);
	    cr = SWork->Front_nrows [child] - cp ;
	    cc = SWork->Front_ncols [child] - cp ;
	    ASSERT (cp >= 0 && cr >= 0 && cc >= 0) ;
	    tail_usage -= GET_ELEMENT_SIZE (cr, cc) + 1 ;
	    dusage -= DGET_ELEMENT_SIZE (cr, cc) + 1 ;
	    /* remove it from tuple lists */
	    tail_usage -= (cr + cc) * UNITS (Tuple, 1) ;
	    dusage -= ((double) cr + (double) cc) * UNITS (Tuple, 1) ;
	    DEBUG2 (("tail_usage: "ID" (assembled "ID" of size "ID")\n",
		tail_usage, child,
		GET_ELEMENT_SIZE (cr, cc) + 1 + (cr + cc) * UNITS (Tuple, 1))) ;
	}

	/* the flop count excludes the BLAS2 calls during pivot search */
	/* the assembly required to search a column, and assembly between */
	/* frontal matrices.  Those are "mushy" flops. */
	/* The flop count computed here is "canonical". */

	/* factorize the frontal matrix */
	f = (double) fpiv ;			/* # of pivots */
	r = (double) frows ;			/* # rows in Schur complement */
	c = (double) fcols ;			/* # cols in Schur complement */
	flops += DIV_FLOPS * (f*r + (f-1)*f/2)	/* scale pivot columns */
	    /* f outer products: */
	    + MULTSUB_FLOPS * (f*r*c + (r+c)*(f-1)*f/2 + (f-1)*f*(2*f-1)/6) ;

	/* count nonzeros in L and U */
	lf = (fpiv*fpiv-fpiv)/2 + fpiv*frows ;	/* nz in L below diagonal */
	uf = (fpiv*fpiv-fpiv)/2 + fpiv*fcols ;	/* nz in U above diagonal */

	/* store the f columns of L and f rows of U */
	head_usage +=
	    UNITS (Entry, lf + uf)	 /* numerical values (excl diagonal) */
	    + UNITS (Int, frows + fcols + fpiv) ; /* indices (compressed) */

	/* count nonzeros and memory usage in double precision */
	dlf = (f*f-f)/2 + f*r ;	/* nz in L below diagonal */
	duf = (f*f-f)/2 + f*c ;	/* nz in U above diagonal */
	dlnz += dlf ;
	dunz += duf ;
	dh =
	    DUNITS (Entry, dlf + duf)    /* numerical values (excl diagonal) */
	    + DUNITS (Int, r + c + f) ; /* indices (compressed) */
	dusage += dh ;
	dhead_usage += dh ;

	if (parent != EMPTY)
	{
	    /* create new element */
	    tail_usage += GET_ELEMENT_SIZE (frows, fcols) + 1 ;
	    dusage += DGET_ELEMENT_SIZE (frows, fcols) + 1 ;

	    /* place new element in tuple lists */
	    tail_usage += (frows + fcols) * UNITS (Tuple, 1) ;
	    dusage += (r + c) * UNITS (Tuple, 1) ;
	    DEBUG2 (("tail_usage: "ID" (create    "ID" of size "ID")\n",
		tail_usage, i,
		GET_ELEMENT_SIZE (frows, fcols) + 1
		+ (frows + fcols) * UNITS (Tuple, 1))) ;

	    /* place in link list of parent */
	    Link [i] = Link [parent] ;
	    Link [parent] = i ;
	}

	/* keep track of peak Numeric->Memory usage */
	max_usage = MAX (max_usage, head_usage + tail_usage) ;

	/* max_usage may encounter integer overflow, so dmax_usage also kept. */
	/* account for possible roundoff errors in dusage. */
	/* Ignore NaN case. */
	dusage *= (1.0 + MAX_EPSILON) ;
	dusage = MAX (dusage,
	    (double) (head_usage + tail_usage) * (1.0 + MAX_EPSILON)) ;
	dmax_usage = MAX (dmax_usage, dusage) ;
	dhead_usage *= (1.0 + MAX_EPSILON) ;
	dhead_usage = MAX (dhead_usage,
	    (double) head_usage * (1.0 + MAX_EPSILON)) ;

	/* at most nb or chain_npiv pending pivots from old fronts */
	maxoldpiv = MIN (nb, chain_npiv) ;
	maxrows = MAX (maxrows, maxoldpiv + fallrows) ;
	maxcols = MAX (maxcols, maxoldpiv + fallcols) ;

	DEBUG1 (("    ID: chain_npiv "ID" fpiv "ID" fallrows "ID,
		i, chain_npiv, fpiv, fallrows)) ;
	DEBUG1 ((" fallcols "ID"   maxoldpiv "ID" maxrows "ID" maxcols "ID"\n",
		fallcols, maxoldpiv, maxrows, maxcols)) ;

	chain_npiv += fpiv ;
	if (parent != i+1)
	{
	    /* this is the end of a chain */
	    Chain_maxrows [nchains] = maxrows ;
	    Chain_maxcols [nchains] = maxcols ;
	    /* Ignore NaN case. */
	    dmaxfrsize = MAX (dmaxfrsize, (double) maxrows * (double) maxcols) ;
	    maxfrsize = MAX (maxfrsize, maxrows * maxcols) ;
	    nchains++ ;
	    Chain_start [nchains] = i+1 ;
	    maxrows = 1 ;
	    maxcols = 1 ;
	    chain_npiv = 0 ;
	}
    }

    dhead_usage = ceil (dhead_usage) ;
    dmax_usage = ceil (dmax_usage) ;

    tail_usage -= init_tail_usage ;

    /* all tuples and elements are now deallocated */
    DEBUG0 (("final tail_usage: "ID"\n", tail_usage)) ;
    ASSERT (tail_usage == 0) ;

    DEBUG1 (("dmaxfrsize %30.20g Int_MAX %30d\n", dmaxfrsize, Int_MAX)) ;

    /* check if the frontal matrix is too big */
    too_large = too_large || INT_OVERFLOW (dmaxfrsize * sizeof (Entry)) ;

    /* ---------------------------------------------------------------------- */
    /* find the biggest frontal matrix, for all chains */
    /* ---------------------------------------------------------------------- */

    maxnrows = 1 ;
    maxncols = 1 ;
    for (i = 0 ; i < nchains ; i++)
    {
	maxnrows = MAX (maxnrows, Chain_maxrows [i]) ;
	maxncols = MAX (maxncols, Chain_maxcols [i]) ;
    }

    /* information to keep for numeric factorization */
    Symbolic->maxfrsize = maxfrsize ;
    Symbolic->maxnrows = maxnrows ;
    Symbolic->maxncols = maxncols ;
    Symbolic->num_mem_usage_est = dmax_usage ;
    Symbolic->num_mem_size_est = dhead_usage ;

    /* ---------------------------------------------------------------------- */
    /* estimate total memory usage in UMFPACK_numeric */
    /* ---------------------------------------------------------------------- */

    UMF_set_stats (
	Info,
	Symbolic,
	dmax_usage,		/* estimated peak size of Numeric->Memory */
	dhead_usage,		/* estimated final size of Numeric->Memory */
	flops,			/* estimated "true flops" */
	dlnz,			/* estimated nz in L */
	dunz,			/* estimated nz in U */
	dmaxfrsize,		/* estimated largest front size */
	(double) n_col,		/* worst case Numeric->Upattern size */
	(double) n_inner,	/* max possible pivots to be found */
	ESTIMATE) ;

    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    for (i = 0 ; i < nchains ; i++)
    {
	DEBUG2 (("Chain "ID" start "ID" end "ID" maxrows "ID" maxcols "ID"\n",
		i, Chain_start [i], Chain_start [i+1] - 1,
		Chain_maxrows [i], Chain_maxcols [i])) ;
	UMF_dump_chain (Chain_start [i],
	    SWork->Front_parent,
	    SWork->Front_npivcol,
	    SWork->Front_nrows,
	    SWork->Front_ncols,
	    nfr) ;
    }
    fpivcol = 0 ;
    for (i = 0 ; i < nfr ; i++)
    {
	fpivcol = MAX (fpivcol, Front_npivcol [i]) ;
    }
    DEBUG0 (("Max pivot cols in any front: "ID"\n", fpivcol)) ;
    DEBUG1 (("Largest front: maxnrows "ID" maxncols "ID" maxfrsize "ID"\n",
	maxnrows, maxncols, maxfrsize)) ;
    DEBUG1 (("(savings "ID") nchains "ID"\n",
	(maxnrows*maxncols) - maxfrsize, nchains)) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* is the problem too large? */
    /* ---------------------------------------------------------------------- */

    if (too_large)
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_problem_too_large ;
	error (&Symbolic, SWork) ;
	return (UMFPACK_ERROR_problem_too_large) ;
    }

    /* ---------------------------------------------------------------------- */
    /* UMFPACK_symbolic was successful, return the object handle */
    /* ---------------------------------------------------------------------- */

    Symbolic->valid = SYMBOLIC_VALID ;
    *SymbolicHandle = (void *) Symbolic ;

    /* ---------------------------------------------------------------------- */
    /* free workspace */
    /* ---------------------------------------------------------------------- */

    free_work (SWork) ;
    /* Symbolic contains 10 objects */
    DEBUG0 (("(3)Symbolic UMF_malloc_count - init_count = "ID"\n",
	UMF_malloc_count - init_count)) ;
    ASSERT (UMF_malloc_count == init_count + 10) ;

    /* ---------------------------------------------------------------------- */
    /* get the time used by UMFPACK_*symbolic */
    /* ---------------------------------------------------------------------- */

    tend = umfpack_timer ( ) ;
    Info [UMFPACK_SYMBOLIC_TIME] = MAX (0, tend - tstart) ;

    return (UMFPACK_OK) ;
}


/* ========================================================================== */
/* === free_work ============================================================ */
/* ========================================================================== */

PRIVATE void free_work
(
    SWorkType *SWork
)
{
    ASSERT (SWork) ;

    SWork->Ci = (Int *) UMF_free ((void *) SWork->Ci) ;
    SWork->Front_npivcol = (Int *) UMF_free ((void *) SWork->Front_npivcol) ;
    SWork->Front_nrows = (Int *) UMF_free ((void *) SWork->Front_nrows) ;
    SWork->Front_ncols = (Int *) UMF_free ((void *) SWork->Front_ncols) ;
    SWork->Front_parent = (Int *) UMF_free ((void *) SWork->Front_parent) ;
    SWork->Front_cols = (Int *) UMF_free ((void *) SWork->Front_cols) ;

}


/* ========================================================================== */
/* === error ================================================================ */
/* ========================================================================== */

/* Error return from UMFPACK_symbolic.  Free all allocated memory. */

PRIVATE void error
(
    SymbolicType **Symbolic,
    SWorkType *SWork
)
{

    free_work (SWork) ;
    UMFPACK_free_symbolic ((void **) Symbolic) ;
    ASSERT (UMF_malloc_count == init_count) ;
}


```

sparse-matrix/umfpack/umf_colamd.h:
```
/* ========================================================================== */
/* === umf_colamd.h ========================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*

Authors:

    The authors of the COLAMD code itself are Stefan I. Larimore and Timothy A.
    Davis, University of Florida.  The algorithm was developed in collaboration
    with John Gilbert, Xerox PARC, and Esmond Ng, Oak Ridge National Laboratory.

Date:

    UMFPACK Version: see above.
    COLAMD Version 2.0 was released on January 31, 2000.

Acknowledgements:

    This work was supported by the National Science Foundation, under
    grants DMS-9504974 and DMS-9803599.

UMFPACK:  Copyright (c) 2002 by Timothy A. Davis.  All Rights Reserved.

See the UMFPACK README file for the License for your use of this code.

Availability:

    Both UMFPACK and the original unmodified colamd/symamd library are
    available at http://www.cise.ufl.edu/research/sparse.

*/

#ifndef COLAMD_H
#define COLAMD_H

/* ========================================================================== */
/* === Include files ======================================================== */
/* ========================================================================== */

#include <stdlib.h>

/* ========================================================================== */
/* === Knob and statistics definitions ====================================== */
/* ========================================================================== */

/* size of the knobs [ ] array.  Only knobs [0..1] are currently used. */
#define COLAMD_KNOBS 20

/* number of output statistics.  Only stats [0..8] are currently used. */
#define COLAMD_STATS 20

/* knobs [0] and stats [0]: dense row knob and output statistic. */
#define COLAMD_DENSE_ROW 0

/* knobs [1] and stats [1]: dense column knob and output statistic. */
#define COLAMD_DENSE_COL 1

/* stats [2]: memory defragmentation count output statistic */
#define COLAMD_DEFRAG_COUNT 2

/* stats [3]: colamd status:  zero OK, > 0 warning or notice, < 0 error */
#define COLAMD_STATUS 3

/* stats [4..6]: error info, or info on jumbled columns */
#define COLAMD_INFO1 4
#define COLAMD_INFO2 5
#define COLAMD_INFO3 6

/* ------------------ */
/* added for UMFPACK: */
/* stats [7]: number of originally empty rows */
#define COLAMD_EMPTY_ROW 7
/* stats [8]: number of originally empty cols */
#define COLAMD_EMPTY_COL 8
/* stats [9]: number of rows with entries only in dense cols */
#define COLAMD_NEWLY_EMPTY_ROW 9
/* stats [10]: number of cols with entries only in dense rows */
#define COLAMD_NEWLY_EMPTY_COL 10
/* ------------------ */

/* error codes returned in stats [3]: */
#define COLAMD_OK				(0)
#define COLAMD_ERROR_jumbled_matrix		(-11)
#define COLAMD_ERROR_A_not_present		(-1)
#define COLAMD_ERROR_p_not_present		(-2)
#define COLAMD_ERROR_nrow_negative		(-3)
#define COLAMD_ERROR_ncol_negative		(-4)
#define COLAMD_ERROR_nnz_negative		(-5)
#define COLAMD_ERROR_p0_nonzero			(-6)
#define COLAMD_ERROR_A_too_small		(-7)
#define COLAMD_ERROR_col_length_negative	(-8)
#define COLAMD_ERROR_row_index_out_of_bounds	(-9)
#define COLAMD_ERROR_out_of_memory		(-10)
#define COLAMD_ERROR_internal_error		(-999)

/* ========================================================================== */
/* === 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 the COLAMD_RECOMMENDED */
/* macro. */

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 ;

    /* ------------------ */
    /* added for UMFPACK: */
    Int nextcol ;	/* next column in this supercolumn */
    Int lastcol ;	/* last column in this supercolumn */
    /* ------------------ */

} 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 ;

    /* ------------------ */
    /* added for UMFPACK: */
    Int thickness ;	/* number of original rows represented by this row */
			/* that are not yet pivotal */
    Int front ;		/* -1 if an original row */
			/* k if this row represents the kth frontal matrix */
			/* where k goes from 0 to at most n_col-1 */
    /* ------------------ */

} Colamd_Row ;



/* ========================================================================== */
/* === Colamd recommended memory size ======================================= */
/* ========================================================================== */

/*
    The recommended length Alen of the array A passed to colamd is given by
    the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro.  It returns -1 if any
    argument is negative.  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.  An additional n_col space is the minimal amount of "elbow room",
    and nnz/5 more space is recommended for run time efficiency.

    This macro is not needed when using symamd.
*/

#define UMF_COLAMD_C(n_col) ((n_col + 1) * sizeof (Colamd_Col) / sizeof (Int))
#define UMF_COLAMD_R(n_row) ((n_row + 1) * sizeof (Colamd_Row) / sizeof (Int))

/* UMFPACK:  make sure Clen is > 3*n_col + Col and Row structures */
#define UMF_COLAMD_RECOMMENDED(nnz, n_row, n_col)                             \
(                                                                             \
((nnz) < 0 || (n_row) < 0 || (n_col) < 0)                                     \
?                                                                             \
    (-1)                                                                      \
:                                                                             \
    (MAX (2 * (nnz), 3 * (n_col)) +					      \
    (Int) UMF_COLAMD_C (n_col) +					      \
    (Int) UMF_COLAMD_R (n_row) + (n_col) + ((nnz) / 5))			      \
)

/* ========================================================================== */
/* === Prototypes of user-callable routines ================================= */
/* ========================================================================== */

/* colamd_recommended removed for UMFPACK */

void UMF_colamd_set_defaults	/* sets default parameters */
(				/* knobs argument is modified on output */
    double knobs [COLAMD_KNOBS]	/* parameter settings for colamd */
) ;

Int UMF_colamd			/* returns (1) if successful, (0) otherwise*/
(				/* A and p arguments are modified on output */
    Int n_row,			/* number of rows in A */
    Int n_col,			/* number of columns in A */
    Int Alen,			/* size of the array A */
    Int A [],			/* row indices of A, of size Alen */
    Int p [],			/* column pointers of A, of size n_col+1 */
    double knobs [COLAMD_KNOBS],/* parameter settings for colamd */
    Int stats [COLAMD_STATS]	/* colamd output statistics and error codes */
    /* ------------------ */
    /* added for UMFPACK: */
    , Int Front_npivcol [ ]
    , Int Front_nrows [ ]
    , Int Front_ncols [ ]
    , Int Front_parent [ ]
    , Int Front_cols [ ]
    , Int *p_nfr
    /* ------------------ */
) ;

/* symamd deleted for UMFPACK */

/* colamd_report deleted for UMFPACK */

/* symamd_report deleted for UMFPACK */

#endif /* COLAMD_H */

```

sparse-matrix/umfpack/umf_valid_numeric.h:
```
/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_valid_numeric
(
    NumericType *Numeric
) ;


```

sparse-matrix/umfpack/umf_realloc.c:
```
/* ========================================================================== */
/* === UMF_realloc ========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

/*
    Realloc a block previously allocated by UMF_malloc.
    Return NULL on failure (in which case the block is still allocated, and will
    be kept at is present size).  This routine is only used for Numeric->Memory.
*/

#include "umf_internal.h"



GLOBAL void *UMF_realloc
(
    void *p,
    Int n_objects,
    size_t size_of_object
)
{
    size_t size ;

#ifdef UMF_TCOV_TEST
    /* For exhaustive statement coverage testing only! */
    /* Pretend to fail, to test out-of-memory conditions. */
    umf_realloc_fail-- ;
    if (umf_realloc_fail <= umf_realloc_hi && umf_realloc_fail >= umf_realloc_lo) { return ((void *) NULL) ; }
#endif

    /* make sure that we allocate something */
    n_objects = MAX (1, n_objects) ;

    size = (size_t) n_objects ;
    ASSERT (size_of_object > 1) ;
    if (size > Int_MAX / size_of_object)
    {
	/* object is too big for integer pointer arithmetic */
	return ((void *) NULL) ;
    }
    size *= size_of_object ;

    DEBUG0 (("UMF_realloc n_objects "ID"  size_of_object "ID"\n",
	n_objects, (Int) size_of_object)) ;

    /* see umf_config.h for the memory allocator selection */
    return (REALLOCATE (p, size)) ;
}


```

sparse-matrix/umfpack/umfpack_timer.h:
```
/* ========================================================================== */
/* === umfpack_timer ======================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

double umfpack_timer ( void ) ;

/*
Syntax (for all versions: di, dl, zi, and zl):

    #include "umfpack.h"
    double t ;
    t = umfpack_timer ( ) ;

Purpose:

    Returns the CPU time used by the process.  Includes both "user" and "system"
    time (the latter is time spent by the system on behalf of the process, and
    is thus charged to the process).  It does not return the wall clock time.

    This routine uses the Unix getrusage routine, if available.  It is less
    subject to overflow than the ANSI C clock routine.  If getrusage is not
    available, the portable ANSI C clock routine is used instead.
    Unfortunately, clock ( ) overflows if the CPU time exceeds 2147 seconds
    (about 36 minutes) when sizeof (clock_t) is 4 bytes.  If you have getrusage,
    be sure to compile UMFPACK with the -DGETRUSAGE flag set; see umf_config.h
    and the User Guide for details.  Even the getrusage routine can overlow.

Arguments:

    None.
*/

```

sparse-matrix/umfpack/umfpack_triplet_to_col.h:
```
/* ========================================================================== */
/* === umfpack_triplet_to_col =============================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

int umfpack_di_triplet_to_col
(
    int n_row,
    int n_col,
    int nz,
    const int Ti [ ],
    const int Tj [ ],
    const double Tx [ ],
    int Ap [ ],
    int Ai [ ],
    double Ax [ ],
    int Map [ ]
) ;

long umfpack_dl_triplet_to_col
(
    long n_row,
    long n_col,
    long nz,
    const long Ti [ ],
    const long Tj [ ],
    const double Tx [ ],
    long Ap [ ],
    long Ai [ ],
    double Ax [ ],
    long Map [ ]
) ;

int umfpack_zi_triplet_to_col
(
    int n_row,
    int n_col,
    int nz,
    const int Ti [ ],
    const int Tj [ ],
    const double Tx [ ], const double Tz [ ],
    int Ap [ ],
    int Ai [ ],
    double Ax [ ], double Az [ ],
    int Map [ ]
) ;

long umfpack_zl_triplet_to_col
(
    long n_row,
    long n_col,
    long nz,
    const long Ti [ ],
    const long Tj [ ],
    const double Tx [ ], const double Tz [ ],
    long Ap [ ],
    long Ai [ ],
    double Ax [ ], double Az [ ],
    long Map [ ]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ;
    double *Tx, *Ax ;
    status = umfpack_di_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx,
	Ap, Ai, Ax, Map) ;

double long Syntax:

    #include "umfpack.h"
    long n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ;
    double *Tx, *Ax ;
    status = umfpack_dl_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx,
	Ap, Ai, Ax, Map) ;

complex int Syntax:

    #include "umfpack.h"
    int n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ;
    double *Tx, *Tz, *Ax, *Az ;
    status = umfpack_zi_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx, Tz,
	Ap, Ai, Ax, Az, Map) ;

long Syntax:

    #include "umfpack.h"
    long n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ;
    double *Tx, *Tz, *Ax, *Az ;
    status = umfpack_zl_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx, Tz,
	Ap, Ai, Ax, Az, Map) ;

Purpose:

    Converts a sparse matrix from "triplet" form to compressed-column form.
    Analogous to A = spconvert (Ti, Tj, Tx + Tx*1i) in MATLAB, except that
    zero entries present in the triplet form are present in A.

    The triplet form of a matrix is a very simple data structure for basic
    sparse matrix operations.  For example, suppose you wish to factorize a
    matrix A coming from a finite element method, in which A is a sum of
    dense submatrices, A = E1 + E2 + E3 + ... .  The entries in each element
    matrix Ei can be concatenated together in the three triplet arrays, and
    any overlap between the elements will be correctly summed by
    umfpack_*_triplet_to_col.

    Transposing a matrix in triplet form is simple; just interchange the
    use of Ti and Tj.  You can construct the complex conjugate transpose by
    negating Tz, for the complex versions.

    Permuting a matrix in triplet form is also simple.  If you want the matrix
    PAQ, or A (P,Q) in MATLAB notation, where P [k] = i means that row i of
    A is the kth row of PAQ and Q [k] = j means that column j of A is the kth
    column of PAQ, then do the following.  First, create inverse permutations
    Pinv and Qinv such that Pinv [i] = k if P [k] = i and Qinv [j] = k if
    Q [k] = j.  Next, for the mth triplet (Ti [m], Tj [m], Tx [m], Tz [m]),
    replace Ti [m] with Pinv [Ti [m]] and replace Tj [m] with Qinv [Tj [m]].

    If you have a column-form matrix with duplicate entries or unsorted
    columns, you can sort it and sum up the duplicates by first converting it
    to triplet form with umfpack_*_col_to_triplet, and then coverting it back
    with umfpack_*_triplet_to_col.

    Constructing a submatrix is also easy.  Just scan the triplets and remove
    those entries outside the desired subset of 0...n_row-1 and 0...n_col-1,
    and renumber the indices according to their position in the subset.

    You can do all these operations on a column-form matrix by first
    converting it to triplet form with umfpack_*_col_to_triplet, doing the
    operation on the triplet form, and then converting it back with
    umfpack_*_triplet_to_col.

    The only operation not supported easily in the triplet form is the
    multiplication of two sparse matrices (UMFPACK does not provide this
    operation).

    You can print the input triplet form with umfpack_*_report_triplet, and
    the output matrix with umfpack_*_report_matrix.

    The matrix may be singular (nz can be zero, and empty rows and/or columns
    may exist).  It may also be rectangular and/or complex.

Returns:

    UMFPACK_OK if successful.
    UMFPACK_ERROR_argument_missing if Ap, Ai, Ti, and/or Tj are missing.
    UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0.
    UMFPACK_ERROR_nz_negative if nz < 0.
    UMFPACK_ERROR_invalid_triplet if for any k, Ti [k] and/or Tj [k] are not in
	the range 0 to n_row-1 or 0 to n_col-1, respectively.
    UMFPACK_ERROR_out_of_memory if unable to allocate sufficient workspace.

Arguments:

    Int n_row ;		Input argument, not modified.
    Int n_col ;		Input argument, not modified.

	A is an n_row-by-n_col matrix.  Restriction: n_row > 0 and n_col > 0.
	All row and column indices in the triplet form must be in the range
	0 to n_row-1 and 0 to n_col-1, respectively.

    Int nz ;		Input argument, not modified.

	The number of entries in the triplet form of the matrix.  Restriction:
	nz >= 0.

    Int Ti [nz] ;	Input argument, not modified.
    Int Tj [nz] ;	Input argument, not modified.
    double Tx [nz] ;	Input argument, not modified.
    double Tz [nz] ;	Input argument, not modified, for complex versions.

	Ti, Tj, Tx, and Tz hold the "triplet" form of a sparse matrix.  The kth
	nonzero entry is in row i = Ti [k], column j = Tj [k], and the real part
	of a_ij is Tx [k].  The imaginary part of a_ij is Tz [k], for complex
	versions.  The row and column indices i and j must be in the range 0 to
	n_row-1 and 0 to n_col-1, respectively.  Duplicate entries may be
	present; they are summed in the output matrix.  This is not an error
	condition.  The "triplets" may be in any order.  Tx, Tz, Ax, and Az
	are optional.  For the real version, Ax is computed only if both Ax
	and Tx are present (not (double *) NULL).  For the complex version, Ax
	and Az are computed only if Tx, Tz, Ax, and Az are all present.  These
	are not error conditions; the routine can create just the pattern of
	the output matrix from the pattern of the triplets.

	Future complex version:  if Tx is present and Tz is NULL, then both real
	and imaginary parts will be contained in Tx[0..2*nz-1], with Tx[2*k]
	and Tx[2*k+1] being the real and imaginary part of the kth entry.

    Int Ap [n_col+1] ;	Output argument.

	Ap is an integer array of size n_col+1 on input.  On output, Ap holds
	the "pointers" for the column form of the sparse matrix A.  Column j of
	the matrix A is held in Ai [(Ap [j]) ... (Ap [j+1]-1)].  The first
	entry, Ap [0], is zero, and Ap [j] <= Ap [j+1] holds for all j in the
	range 0 to n_col-1.  The value nz2 = Ap [n_col] is thus the total
	number of entries in the pattern of the matrix A.  Equivalently, the
	number of duplicate triplets is nz - Ap [n_col].

    Int Ai [nz] ;	Output argument.

	Ai is an integer array of size nz on input.  Note that only the first
	Ap [n_col] entries are used.

	The nonzero pattern (row indices) for column j is stored in
	Ai [(Ap [j]) ... (Ap [j+1]-1)].  The row indices in a given column j
	are in ascending order, and no duplicate row indices are present.
	Row indices are in the range 0 to n_col-1 (the matrix is 0-based).

    double Ax [nz] ;	Output argument.
    double Az [nz] ;	Output argument for complex versions.

	Ax and Az (for the complex versions) are double arrays of size nz on
	input.  Note that only the first Ap [n_col] entries are used
	in both arrays.

	Ax is optional; if Tx and/or Ax are not present (a (double *) NULL
	pointer), then Ax is not computed.  Az is also optional; if Tz and/or
	Az are not present, then Az is not computed.  If present, Ax holds the
	numerical values of the the real part of the sparse matrix A and Az
	holds the imaginary parts.  The nonzero pattern (row indices) for
	column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and the
	corresponding numerical values are stored in
	Ax [(Ap [j]) ... (Ap [j+1]-1)].  The imaginary parts are stored in
	Az [(Ap [j]) ... (Ap [j+1]-1)], for the complex versions.

	Future complex version:  if Ax is present and Az is NULL, then both real
	and imaginary parts will be returned in Ax[0..2*nz2-1], with Ax[2*k]
	and Ax[2*k+1] being the real and imaginary part of the kth entry.

    int Map [nz] ;	Optional output argument.

	If Map is present (a non-NULL pointer to an Int array of size nz), then
	on output it holds the position of the triplets in the column-form
	matrix.  That is, suppose p = Map [k], and the k-th triplet is i=Ti[k],
	j=Tj[k], and aij=Tx[k].  Then i=Ai[p], and aij will have been summed
	into Ax[p] (or simply aij=Ax[p] if there were no duplicate entries also
	in row i and column j).  Also, Ap[j] <= p < Ap[j+1].  The Map array is
	not computed if it is (Int *) NULL.  The Map array is useful for
	converting a subsequent triplet form matrix with the same pattern as the
	first one, without calling this routine.  If Ti and Tj do not change,
	then Ap, and Ai can be reused from the prior call to
	umfpack_*_triplet_to_col.  You only need to recompute Ax (and Az for the
	complex version).  This code excerpt properly sums up all duplicate
	values (for the real version):

	    for (p = 0 ; p < Ap [n_col] ; p++) Ax [p] = 0 ;
	    for (k = 0 ; k < nz ; k++) Ax [Map [k]] += Tx [k] ;

	This feature is useful (along with the reuse of the Symbolic object) if
	you need to factorize a sequence of triplet matrices with identical
	nonzero pattern (the order of the triplets in the Ti,Tj,Tx arrays must
	also remain unchanged).  It is faster than calling this routine for
	each matrix, and requires no workspace.
*/

```


combined_doc_context: ## README (README):
```
Cactus Code Thorn AHFinderDirect
Authors    : Jonathan Thornburg <jthorn@aei.mpg.de>
Maintainer : Jonathan Thornburg <jthorn@aei.mpg.de>
CVS info   : $Header$
--------------------------------------------------------------------------

Purpose of This Thorn
=====================

This thorn finds an apparent horizon (or more generally, a closed
2-surface with S^2 topology having any desired constant expansion)
given the 3D xyz-grid metric and extrinsic curvature (and optionally
the StaticConformal conformal factor).  It uses a direct method,
writing the apparent horizon equation as an elliptic PDE on
angular-coordinate space.  This is very fast and accurate, but
it does require an intitial guess for the apparent horizon position.


Documentation
=============

The doc/ directory contains detailed documentation for this thorn,
including a discussion of those parameters which you'll likely want
to set (or at least know about) in practice.  For full information
on all the parameters, see the comments in the  param.ccl  file.

The par/ directory contains some sample par files.

src/CODESTYLE documents the general programming conventions used in
this thorn.

src/PORTING.OUTSIDE.CACTUS has some general remarks on what it would
take to port this thorn to a non-Cactus numerical relativity code.

See below in this file for notes on compiling this thorn.


Publications
============
The algorithms used in this thorn are described in the papers
@article{
	Thornburg2003:AH-finding,
	author = "Jonathan Thornburg",
	title  = "A Fast Apparent-Horizon Finder
		  for 3-Dimensional {C}artesian Grids in Numerical Relativity",
	journal = "Classical and Quantum Gravity",
	volume = 21, number = 2,
	year   = "2004", month = "January 21",
	pages  = "743--766",
	doi    = "10.1088/0264-9381/21/2/026",
	url    = "http://stacks.iop.org/0264-9381/21/743",
	eprint = "gr-qc/0306056",
	note   = "gr-qc/0306056",
	}
and
@article{
	Thornburg-1996-horizon-finding,
	author = "Jonathan Thornburg",
	title = "Finding Apparent Horizons in Numerical Relativity",
	journal = "Physical Review D",
	pages = "4899--4918",
	volume = 54, number = 8,
	year = 1996, month = "October 15",
	eprint = "gr-qc/9508014",
	}
As a courtesy, I ask that these papers be cited in any published research
which uses this thorn, or which uses code from this thorn.


Copyright
=========

This thorn is copyright (C) 2001-2004
by Jonathan Thornburg <jthorn@aei.mpg.de>.

This thorn is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This thorn 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this thorn (see the file COPYING in this directory);
if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA  02111-1307  USA


Other Software Used (Already Contained within AHFinderDirect)
=============================================================

AHFinderDirect's  src/sparse-matrix/  directory contains various
sparse-matrix libraries, which have their own copyrights and licensing terms:

The src/sparse-matrix/umfpack/ directory contains a subset of the files
in UMFPACK version 4.0 (11.Apr.2002).  This code is copyright (C) 2002 by
Timothy A. Davis, and is subect to the UMFPACK License:

    Your use or distribution of UMFPACK or any modified version of
    UMFPACK implies that you agree to this License.

    THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
    EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.

    Permission is hereby granted to use or copy this program, 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 UMFPACK or any modified version of UMFPACK 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.  This
    software was developed with support from the National Science Foundation,
    and is provided to you free of charge.


Other Software Required
=======================

This thorn inherits from Grid, ADMBase, StaticConformal, SpaceMask, IO
(all in the CactusBase or CactusEinstein arrangements), and
SphericalSurface (in the AEIThorns arrangement; you can get AEIThorns
by anonymous CVS checkout from cvs_anon@cvs.aei.mpg.de:/numrelcvs, CVS
path AEIThorns/SphericalSurface).

By default this thorn uses various Cactus APIs which are supplied by
other thorns:
* This thorn uses CCTK_InterpGridArrays() for interpolating grid arrays.
  This is supplied by a driver-specific thorn, such as PUGHInterp or
  CarpetInterp.
* This thorn uses CCTK_ReduceLocArrayToArray1D() for interprocessor
  communication in the multiprocessor Newton solver.
	[see src/driver/README.parallel for details]
  This is supplied by a driver-specific thorn; at present PUGHReduce
  it this for the PUGH driver.
* This thorn uses CCTK_InterpLocalUniform() for interpatch and surface
  interpolation, and it needs various interpolation options which at
  present are (only) supported by the AEIThorns/AEILocalInterp local
  interpolator.

This thorn is written in C++, so you'll need a C++ compiler -- in fact
a fairly modern one -- to compile this thorn.  See the "Compilation Notes"
section below for details.

Most of this thorn's relativity code is machine-generated using Maple
(version 7), but you don't need Maple unless you want to modify the
relativity code.

By default, this thorn doesn't use any external libraries.  However,
if HAVE_DENSE_JACOBIAN__LAPACK is defined in src/include/config.h,
then this thorn uses the LAPACK library (which in turn uses the BLAS
library), so you will need to configure your Cactus to use LAPACK.
Instructions on doing this are in the file README.library in this
directory.


Code Notes
==========

This thorn is written in C++, and requires a fairly modern C++ compiler.
In particular:
* Templates are used, but only rather simple ones for containers or other
  low-level stuff templated on the floating-point or integer datatype.
  These templates are always instantiated explicitly.
* bool, mutable, typename are used.
* The new-style casts static_cast<...> and const_cast<...> are used.
  reinterpret_cast<...> and dynamic_cast<...> are *not* used.
* The code will compile ok under either the archaic or the modern
  for-loop declaration scope rules.
* C header files are used in the pre-namespaces form (eg <stdio.h>).
  (In ISO-standard C++ this is deprecated (but still supported), but
  alas none of the more modern forms (<cstdio> and namespace std::)
  seem to be supported on as wide a range of systems as the pre-namespaces
  form.)
* <assert.h> is used fairly heavily for sanity checks.
* To avoid various portability problems, none of the C++ standard
  template library (STL) is used.


Compiler Notes
==============

This thorn should be fully portable across architectures and operating
systems, but C++ compilers may be a problem:

This thorn has been compiled and run successfully using

x86
===
* gcc version 2.95.3 20010125 (prerelease)
* g++ (GCC) 3.2 for x86
* g++ (GCC) 3.2.2 for x86
* g++ (GCC) 3.3 for x86
* Intel(R) C++ Compiler for 32-bit applications, Version 5.0.1   Build 010730D0
* Intel C++ Compiler for Linux, version 6.0
* Intel(R) C++ Compiler for 32-bit applications, Version 7.0   Build 20021021Z
  On platinum.ncsa.uiuc.edu, Denis Pollney reports success configuring Cactus
  with
    LIBS="crypt F90 CEPCF90 intrins mkl mkl_lapack guide"
    LIBDIRS=/usr/local/intel/mkl/lib/32
* Portland Group pgCC 3.3-2
  On platinum.ncsa.uiuc.edu, Denis Pollney reports success configuring Cactus
  with
    LAPACK=yes
    LAPACK_DIR=/usr/local/pgi/linux86

ia64, a.k.a. Itanium
====================
* Intel(R) C++ Itanium(R) Compiler for Itanium(R)-based applications
  Version 7.0 Beta, Build 20020911
  On titan.ncsa.uiuc.edu, Denis Pollney reports success configuring Cactus
  with
    LIBS="crypt F90 CEPCF90 intrins mkl_lapack mkl_itp guide"
    LIBDIRS=/usr/local/intel/mkl/lib/64

Alpha
=====
* g++ (GCC) 3.0
  On lemieux.psc.edu, Denis Pollney reports success configuring Cactus
  with
    LIBS="m c_r dxml for"
* Using the DEC/Compaq/HP/whatever-their-corporate-name-is-this-week
  compiler, you need to specify the  -nopt  compiler option to disable
  automatic template instantiation.

SGI
===
* On modi4.ncsa.uiuc.edu (an old SGI Origin), using
  MIPSpro Compilers: Version 7.3.1.3m
  you need to specify the  -no_auto_include  compiler option to disable
  automatic template instantiation.  In fact, you should probably specify
  this option on any SGI system...

IBM Power4 (AIX)
================
* C for AIX Compiler, Version 6
  On psi19.rzg.mpg.de, Denis Pollney reports success configuring Cactus
  with
    LIBS="f m lapack-essl essl"
    LIBDIRS="/afs/rzg/@sys/lib"

Please let me (Jonathan) know of any other compilers which are ok.

This thorn compiles, but does *NOT* work, using the folloing
systems/compilers; the cure is to switch to a newer compiler version:
x86
===
* gcc version 2.96 20000731 (Red Hat Linux 7.1 2.96-98)
  (This is the system gcc on Red Hat GNU/Linux 7.2 systems)

The code will almost certainly *not* compile using
* any "egcs" version of gcc, or more generally, any version of gcc
  earlier than 2.95
* any version of Microsoft Visual C++ up to and including mid-2002

```

## Documentation (.tex files):
Files: documentation.tex

```tex
=== documentation.tex ===
% *======================================================================*
%  Cactus Thorn template for ThornGuide documentation
%  Author: Ian Kelley
%  Date: Sun Jun 02, 2002
%  $Header$
%
%  Thorn documentation in the latex file doc/documentation.tex
%  will be included in ThornGuides built with the Cactus make system.
%  The scripts employed by the make system automatically include
%  pages about variables, parameters and scheduling parsed from the
%  relevent thorn CCL files.
%
%  This template contains guidelines which help to assure that your
%  documentation will be correctly added to ThornGuides. More
%  information is available in the Cactus UsersGuide.
%
%  Guidelines:
%   - Do not change anything before the line
%       % BEGIN CACTUS THORNGUIDE",
%     except for filling in the title, author, date etc. fields.
%   - You can define your own macros are OK, but they must appear after
%     the BEGIN CACTUS THORNGUIDE line, and do not redefine standard
%     latex commands.
%   - To avoid name clashes with other thorns, 'labels', 'citations',
%     'references', and 'image' names should conform to the following
%     convention:
%       ARRANGEMENT_THORN_LABEL
%     For example, an image wave.eps in the arrangement CactusWave and
%     thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps
%   - Graphics should only be included using the graphix package.
%     More specifically, with the "includegraphics" command. Do
%     not specify any graphic file extensions in your .tex file. This
%     will allow us (later) to create a PDF version of the ThornGuide
%     via pdflatex. |
%   - References should be included with the latex "bibitem" command.
%   - For the benefit of our Perl scripts, and for future extensions,
%     please use simple latex.
%
% *======================================================================*
%
% Example of including a graphic image:
%    \begin{figure}[ht]
% 	\begin{center}
%    	   \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure}
% 	\end{center}
% 	\caption{Illustration of this and that}
% 	\label{MyArrangement_MyThorn_MyLabel}
%    \end{figure}
%
% Example of using a label:
%   \label{MyArrangement_MyThorn_MyLabel}
%
% Example of a citation:
%    \cite{MyArrangement_MyThorn_Author99}
%
% Example of including a reference
%   \bibitem{MyArrangement_MyThorn_Author99}
%   {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999),
%   1--16. {\tt http://www.nowhere.com/}}
%
% *======================================================================*

% If you are using CVS use this line to give version information
% $Header$

\documentclass{article}
\bibliographystyle{alpha}

% Use the Cactus ThornGuide style file
% (Automatically used from Cactus distribution, if you have a
%  thorn without the Cactus Flesh download this from the Cactus
%  homepage at www.cactuscode.org)
\usepackage{../../../../doc/latex/cactus}

\begin{document}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% The author of the documentation
\author{Jonathan Thornburg \quad {\tt <jthorn@aei.mpg.de>}}

% The title of the document (not necessarily the name of the Thorn)
\title{{\bf AHFinderDirect} -- A Fast Apparent Horizon Finder}

% the date your document was last changed, if your document is in CVS,
% please us:
%    \date{$ $Date$ $}
\date{$ $Date$ $}

\maketitle

% Do not delete next line
% START CACTUS THORNGUIDE

% Add all definitions used in this documentation here
%   \def\mydef etc

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% misc text stuff
\def\code#1{{\tt #1}}			% variable names, parameter values, etc
\def\defn#1{``#1''}			% definition of a term
\def\arrangement#1{{\bf #1}}		% name of an arrangement
\def\thorn#1{{\bf #1}}			% name of a thorn
\def\program#1{{\bf #1}}		% name of computer program
\def\cvsplace#1{{\bf #1}}		% name of a CVS repository/directory/tag
\def\cf{\hbox{cf.\hbox{}}}
\def\eg{\hbox{eg.\hbox{}}}
\def\ie{\hbox{i.e.\hbox{}}}
\def\eqref#1{$(\ref{#1})$}

% get size/spacing of "++" right, cf online C++ FAQ question 35.1
\def\Cplusplus{\hbox{C\raise.25ex\hbox{\footnotesize ++}}}

% misc math mode stuff
\def\assign{\leftarrow}			% for pseudocode algorithms
\def\ltsim{\lesssim}
\def\gtsim{\gtrsim}
\def\tfrac#1#2{{\textstyle    \frac{#1}{#2}}}
\def\dfrac#1#2{{\displaystyle \frac{#1}{#2}}}
\def\thalf{\tfrac{1}{2}}
\def\const{{\rm const}}
\def\ij{{ij}}
\def\uv{{uv}}
\def\del{\nabla}

\def\I{{\text{\scriptsize I}}}                  % grid-point index
\def\J{{\text{\scriptsize J}}}                  % grid-point index
\def\K{{\text{\scriptsize K}}}                  % grid-point index
\def\M{{\text{\scriptsize M}}}                  % molecule index

\def\Jac[#1]{{\bf J} \bigl[ #1 \bigr]}          % discrete Jacobian for fn #1

% Add an abstract for this thorn's documentation
\begin{abstract}
Thorn \thorn{AHFinderDirect} locates apparent horizons (or more
generally, closed 2-surfaces with $S^2$ topology having any desired
constant expansion) in a numerically computed slice using a direct
method, posing the apparent horizon equation as an elliptic PDE on
angular-coordinate space.  This is very fast and accurate, but
requires a ``reasonable'' initial guess.  This thorn guide describes
how to use the thorn.
\end{abstract}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Introduction}

A \defn{marginally trapped surface} is a closed 2-surface in a slice whose
congruence of future-pointing outgoing null geodesics has zero expansion.
There may be several such surfaces, some nested inside others; an
\defn{apparent horizon} is an outermost marginally trapped surface.
In terms of the usual $3+1$ variables, an apparent horizon satisfies
the equation
\begin{equation}
\Theta \equiv \del_i n^i + K_\ij n^i n^j - K = 0
					\label{AHFinderDirect/eqn-horizon}
\end{equation}
where $n^i$ is the outward-pointing unit normal to the apparent horizon,
and $\del_i$ is the covariant derivative operator associated with the
3-metric $g_\ij$ in the slice.  (See \cite{AHFinderDirect/York89}
for a derivation of equation~\eqref{AHFinderDirect/eqn-horizon}.)
(Optionally, you can replace the right hand side
of~\eqref{AHFinderDirect/eqn-horizon} by any specified nonzero
constant, \ie{} you can find a surface of constant
(in general nonzero) expansion.; this is dicsussed in
section~\ref{AHFinderDirect/sect-parameters/other-parameters}.)

Thorn~\thorn{AHFinderDirect} finds an apparent horizon by numerically
solving equation~\eqref{AHFinderDirect/eqn-horizon}.  It requires
as input the usual Cactus 3-metric $g_\ij$ and extrinsic curvature
$K_\ij$, (and optionally the conformal factor $\psi$ if the
\thorn{StaticConformal} metric semantics are used),
and produces as output the Cactus $(x,y,z)$ coordinates of a large
number of points on the apparent horizon, together with some auxiliary
information like the apparent horizon area and centroid position, and
the irreducable mass associated with the area.

Besides this thorn guide, the other main sources of information on
\thorn{AHFinderDirect} are the comments in the \verb|param.ccl| file,
the paper \cite{AHFinderDirect/Thornburg2003:AH-finding},
and to a lesser extent the paper \cite{AHFinderDirect/Thornburg95}.
As a courtesy, I ask that both these papers be cited in any published
research which uses this thorn, or which uses code from this thorn.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{What \thorn{AHFinderDirect} Needs}
\label{AHFinderDirect/sect-what-AHFinderDirect-needs}

There are some restrictions on the spacetime, or more precisely
on each slice where you want to find apparent horizons, which are
necessary in order for \thorn{AHFinderDirect} to work:
\begin{itemize}
\item	\thorn{AHFinderDirect} requires that the Cactus geometry
	($g_{ij}$, $K_{ij}$, and optionally $\psi$) be nonsingular
	in a neighborhood of the apparent horizon.  In particular,
	this means that it quite certainly will {\em not\/} work for
	spacetimes/slicings which have a singular geometry on the
	horizon, such as Schwarzschild/Schwarzschild and
	Kerr/Boyer-Lindquist.%%%
\footnote{%%%
	 Nonsingular slicings of those same spacetimes,
	 such as Schwarzschild/Eddington-Finkelstein,
	 Kerr/Kerr, and Kerr/Kerr-Schild, are fine, and
	 are in fact nice test cases.
	 }%%%
\item	Less obviously, this also means that if there is a
	singularity in the geometry somewhere near the apparent
	horizon, then you need to have a high enough Cactus 3-D grid
	resolution that the geometry interpolation doesn't ``see''
	the singularity.  (If \thorn{AHFinderDirect} ``sees'' the
	singularity, it may ``just'' fail to find the horizon,
	and/or it may report that the interpolated $g_{ij}$ fails
	to be positive definite or even contains NaNs.)
\item	At the moment \thorn{AHFinderDirect} and the Cactus interpolators
	don't know how to avoid an excised region, so if the apparent horizon
	(or any trial horizon surface as the algorithm is iterating
	towards the apparent horizon) gets too close to an excised
	region, you'll get garbage results as the interpolator tries
	to interpolate data from the excised region.  I plan to fix
	this sometime soon.
\item	\thorn{AHFinderDirect} requires that any apparent horizon
	it's going to (try to) find must be a \defn{Strahlk\"{o}rper}
	(literally ``ray body'', or more commonly ``star-shaped region'')
	relative to some local coordinate origin (which you must specify).
	A Strahlk\"{o}rper is defined by Minkowski
	(\cite[p.~108]{AHFinderDirect/Schroeder86})
	as
	   \begin{quote}
	   a region in $n$-dimensional Euclidean space containing the
	   origin and whose surface, as seen from the origin, exhibits
	   only one point in any direction.
	   \end{quote}
	In other words, using polar spherical coordinates relative
	to the local coordinate origin, the apparent horizon's shape
	must be parameterizable as $r = h(\text{angle})$ for some
	single-valued function $h: S^2 \to \Re^+$.  (\thorn{AHFinderDirect}
	uses precisely this parameterization.)
\end{itemize}

There are also some restrictions on your Cactus configuration and run;
here's what works and what doesn't:
\begin{itemize}
\item	I {\em strongly\/} recommend using a current-CVS checkout of
	the Cactus flesh and of all relevant thorns.  I haven't tested
	\thorn{AHFinderDirect} at all with older versions of the flesh
	or other thorns.
\item	\thorn{AHFinderDirect} works fine with the \thorn{PUGH} unigrid
	driver and with the \thorn{Carpet} mesh-refinement driver.  So
	far as I know it's never been tested with any other driver.
\item	\thorn{AHFinderDirect} works fine in single- or multi-processor
	Cactus runs.
\item	Obviously, your Cactus configuration must include
	\thorn{AHFinderDirect}, and your \code{ActiveThorns} parameter(s)
	must activate it.
\item	\thorn{AHFinderDirect} inherits from the other thorns
	(strictly speaking, implementations) listed in
	table~\ref{AEIThorns/AHFinderDirect/tab-inherits-from},
	so you'll need them (or more precisely some thorns providing them)
	in your configuration and activated, too.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{table}[tbp]
\begin{center}
\begin{tabular}{l@{\qquad}l}
\thorn{Implementation}		& Typically provided by \thorn{Thorn}	\\
\hline %---------------------------------------------------------------
\thorn{Grid}			& \thorn{CactusBase/CartGrid3d}		\\
\thorn{IO}			& \thorn{CactusBase/IOUtil}		\\
\thorn{ADMBase}			& \thorn{CactusEinstein/ADMBase}	\\
\thorn{StaticConformal}		& \thorn{CactusEinstein/StaticConformal}\\
\thorn{SpaceMask}		& \thorn{CactusEinstein/SpaceMask}	\\
\thorn{SphericalSurface}	& \thorn{AEIThorns/SphericalSurface}	%%%\\
\end{tabular}
\end{center}
\caption[Other Thorns from which \thorn{AHFinderDirect} Inherits]
	{
	This table lists all the other implementations from which
	\thorn{AHFinderDirect} inherits, and the thorns which typically
	provide these implementations.
	}
\label{AEIThorns/AHFinderDirect/tab-inherits-from}
\end{table}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\item	\verb|Grid::domain = "full"|, \verb|"bitant"|,
	\verb|"quadrant"|, and \verb|"octant"| are supported.
	Alas, at present rotating (or more precisely nonlocal)
	symmetry boundary conditions aren't supported.
\item	The \verb|ADMBase::metric_type| values \verb|"physical"| and
	\verb|"static conformal"| are supported; for the latter you must
	have storage turned on for at least the conformal factor
	\verb|StaticConformal::psi|.  (The Cactus 3-D grid functions
	for 1st and 2nd derivatives of \verb|psi| aren't used.)
\item	\thorn{AHFinderDirect} uses the \verb|CCTK_InterpGridArrays()|
	Cactus global (multi-processor grid array) interpolator API;
	this is provided by \thorn{PUGHInterp} or \thorn{CarpetInterp}
	(so you must have the appropriate one of these thorns compiled
	in and activated).  \verb|CCTK_InterpGridArrays()| in turn uses
	the new \verb|CCTK_InterpLocalUniform()| processor-local
	interpolator API; \thorn{AHFinderDirect} uses various options
	in this API which at present are only supported by thorn
	\thorn{AEILocalInterp} (so you must have this thorn compiled in
	and activated)
\item	\thorn{AHFinderDirect} uses various Cactus reduction APIs to
	coordinate multi-processor horizon finding, so (even if you're
	only going to run on a single processor) you must have a reduction
	thorn like \thorn{PUGHReduce} or \thorn{CarpetReduce} compiled in
	and activated.
\item	At present only a few of \thorn{AHFinderDirect}'s parameters
	are steerable.  (This is actually quite easy to fix; I just
	haven't gotten around to it yet.)
\item	I think \thorn{AHFinderDirect} will ``work'' with checkpoint/restart,
	but I haven't tested this yet.  Here ``work'' means the restart
	will be like starting a new run, in that \thorn{AHFinderDirect}
	will set the initial guess for each horizon in a start-of-a-new-run
	manner.  Alas, it will also write a new \verb|BH_diagnostics| file
	for each horizon found, overwriting any existing \verb|BH_diagnostics|
	files.  This is a bug, which I plan to fix soon (the right behavior
	is/will be to {\em append\/} to the existing \verb|BH_diagnostics|
	file).
\end{itemize}

\thorn{AHFinderDirect} can pass information to the rest of Cactus in
several ways; these are described in detail in
section~\ref{AHFinderDirect/sect-parameters/communicating-with-other-thorns}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Obtaining and Compiling \thorn{AHFinderDirect}}

You should be able to obtain the source code for this thorn via the
usual procedures for anonymous git checkout; at present it lives in
the \arrangement{EinsteinAnalysis} arrangement.

This thorn is written primarily in \Cplusplus{}, calling C and
Fortran~77 numerical libraries.%%%
\footnote{%%%
	 The (C) code for the relativity computations
	 (\ie{}~the apparent horizon equation itself)
	 is mainly machine-generated from Maple code,
	 which in turn uses a Perl preprocessor.
	 But the machine-generated C code is already
	 in CVS along with the rest of this thorn's
	 source code, so you don't need to have Maple
	 installed unless you want to modify the
	 relativity computations.%%%
	 }%%%
{}  In theory the code should be quite portable to modern \Cplusplus{}
compilers, but in practice I've had a number of portability problems
with various compilers.  See the ``Code Notes'' and ``Compiler Notes''
sections in the top-level \code{README} file for details and lists of
compilers currently known to be ok or not.

By default \thorn{AHFinderDirect} doesn't use any external libraries.
However, if \code{HAVE\_DENSE\_JACOBIAN\_\_LAPACK} is defined in
\code{src/include/config.h}, then \thorn{AHFinderDirect} uses the
LAPACK library for solving linear equations.  In this case you need
to configure Cactus with \code{LAPACK=yes}.  See the top-level
\code{README} and \code{README.library} files for details on this.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{\thorn{AHFinderDirect} Parameters}

This thorn has lots of parameters, but most of them have reasonable
default values which you probably won't need to change.  Here I describe
the parameters which you are likely to want to at least look at, and
possibly set explicitly.

Note that all of the ``\code{[}$n$\code{]}'' parameters are Cactus
array parameters, which you need to specify separately in your parameter
file for each apparent horizon.  {\bf IMPORTANT: Apparent horizons are
numbered starting at 1, not 0!}  The example in
section~\ref{AHFinderDirect/sect-examples} should make this clear.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Overall Parameters}
\label{AHFinderDirect/sect-parameters/overall-parameters}

\begin{description}
\item[\code{find\_every}]
\mbox{}\\
	This is an integer parameter specifying how often
	\thorn{AHFinderDirect} should try to find apparent horizons:
	If $\verb|find_every| = 0$, \thorn{AHFinderDirect} is a no-op.
	If $\verb|find_every| \ne 0$, \thorn{AHFinderDirect} tries to
	find apparent horizons each \verb|find_every| time steps.%%%
\footnote{%%%
	 More precisely, if ${\tt find\_every} \ne 0$,
	 \thorn{AHFinderDirect} tries to find apparent
	 horizons at each time step where {\tt cctk\_iteration}
	 is evenly divisible by {\tt find\_every}.
	 }%%%
{}	The default value is~1, \ie{} \thorn{AHFinderDirect} tries
	to find apparent horizons at every time step.
\item[\code{N\_horizons}]
\mbox{}\\
	How many apparent horizons do you want to find in each slice?
	Typical values are 1 (the default), 2, or 3.%%%
\footnote{%%%
	 Larger values are also possible.  The present upper
	 limit is 100, but it would be trivial to raise this
	 further if desired -- see the comments in \code{param.ccl}
	 for details.
	 }%%%
{}	{\bf This thorn numbers the apparent horizons from 1 to
	\code{N\_horizons} inclusive.}
	There are a number of other parameters (described below)
	which you need to set for of these each apparent horizons.

	Note that \code{N\_horizons} sets the number of apparent horizons
	you want to find {\em in the Cactus 3-D numerical grid\/}, not in
	the whole spacetime.  For example, if you are simulating (say)
	Misner data with \verb|Grid::domain = "bitant"|, with the two
	throats at (say) roughly $z = \pm 1$, then you should set
	\verb|N_horizons = 1| to find those two apparent horizons,
	since you're only finding one apparent horizon within the
	numerical grid.  If you also want to search for a common
	apparent horizon surrounding both black holes, then you should
	set \verb|N_horizons = 2|, since you're finding at most 2
	apparent horizons within the numerical grid.

\item[\code{verbose\_level}]
\mbox{}\\
	This controls how verbose this thorn is in printing
	informational (non-error) messages describing what it's
	doing.  In order from tersest to most verbose, the allowable
	values are
	\begin{description}
	\item[\code{"no output"}]
	\mbox{}\\
		Don't print anything.
	\item[\code{"physics highlights"}]
	\mbox{}\\
		Print only a single line each time
		\thorn{AHFinderDirect} runs, giving which
		horizons were found.
	\item[\code{"physics details"}]
	\mbox{}\\
		Print two lines for each horizon found, giving
		the horizon area, centroid position, and irreducible mass.
		This is the default.
	\item[\code{"algorithm highlights"}]
	\mbox{}\\
		Also print a single line for each Newton iteration giving
		the 2-norm and $\infty$-norm of the $\Theta(h)$ function
		defined by equation~\eqref{AHFinderDirect/eqn-horizon}.
	\item[\code{"algorithm details"}]
	\mbox{}\\
		Print lots of detailed messages tracing what the code
		is doing.
	\item[\code{"algorithm debug"}]
	\mbox{}\\
		Print even more detailed messages tracing what the code
		is doing, mainly useful for debugging purposes.
	\end{description}
\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Choosing the Local Coordinate Origin for each Apparent Horizon}
\label{AHFinderDirect/sect-parameters/local-coordinate-origin}

For each apparent horizon you want to find, you need to specify
the Cactus $(x,y,z)$ coordinates of a local coordinate system origin.
As described in
section~\ref{AHFinderDirect/sect-what-AHFinderDirect-needs},
each apparent horizon must be a Strahlk\"{o}rper with respect to its
local coordinate system origin.

You specify the local coordinate system origin for each horizon with
the (Cactus array) parameters
\begin{description}
\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{origin\_x[}$n$\code{]}	\\
     \code{origin\_y[}$n$\code{]}	\\
     \code{origin\_z[}$n$\code{]}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	These all default to 0.0.
	In practice, you should set these parameters to be somewhere
	reasonably close to your best guess for the center of each apparent
	horizon.  These aren't {\em too\/} critical: being off by 1/4
	of the horizon radius is no problem, and -- assuming the algorithm
	still converges -- even 1/2 of the horizon radius only slows
	the convergence by an extra iteration or two.  But poor values
	of these parameters do make the algorithm more likely to fail
	to converge.
\end{description}

At present the local coordinate origin is fixed once you set it;
there's no provision for it to move to track a moving black hole.
I hope to add such a provision soon.%%%
\footnote{%%%
	 This would be along the lines of the
	 \thorn{AHFinder} apparent horizon finder's
	 \code{AHFinder::ahf\_wander = true}
	 option.
	 }%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Specifying the Initial Guess}

\thorn{AHFinderDirect} requires an initial guess
for the apparent horizon's coordinate position and shape
(that is, for the $h(\text{angle})$ function defined in
section~\ref{AHFinderDirect/sect-what-AHFinderDirect-needs}),
for each apparent horizon you want to find.  Unlike some other apparent
horizon finders (\eg{}~the curvature flow method in \thorn{AHFinder}),
for \thorn{AHFinderDirect} there's no restriction on whether the initial
guess is inside, outside, or crossing the actual apparent horizon: the
only important thing is that it should be ``close''.  Just how close
the initial guess needs to be for \thorn{AHFinderDirect} to find the
(a) apparent horizon depends on the slice and the coordinates, but as
a general rule of thumb any initial guess that's within 1/3 to 1/2 of
the mean horizon radius will probably work.

The ``initial guess'' specification is used the first time we try to
find any given apparent horizon, and also any succeeding time when the
most recent attempt to find this apparent horizon failed.  If we succeed
in finding a given apparent horizon, than that apparent horizon position
is automatically reused as the initial guess the next time we try to
find the same apparent horizon; in this case the explicit ``initial guess''
specification is ignored.%%%
\footnote{%%%
	 This is similar to the \thorn{AHFinder} apparent
	 horizon finder's behavior with the
	 \code{AHFinder::ahf\_guessold = true}
	 option.
	 }%%%

There are a number of parameters for specifying the initial guess:
\begin{description}
\item[\code{initial\_guess\_method[}$n$\code{]}]
\mbox{}\\
	This sets what type of the initial guess is used for each
	apparent horizon position.
	There are several possibilities, most with their own sets
	of subparameters:%%%
\footnote{%%%
	 The naming scheme for these is similar to
	 that used in the \thorn{Exact} thorn.
	 }%%%
$^,$%%%
\footnote{%%%
	 For the Kerr parameters, note that this thorn
	 always uses the convention that the spin parameter
	 $a \equiv J/m^2$ is dimensionless.
	 }%%%
	\begin{description}
	\item[\code{"read from file"}]
	\mbox{}\\
		This reads the initial-guess $h(\text{angle})$ function
		from a data file.  The file format is currently hard-wired
		to be that written with \verb|file_format = "ASCII (gnuplot)"|
		(see below).  The subparameter
		\verb|initial_guess__read_from_named_file__file_name|
		specifies the file name.
	\item[\code{"Kerr/Kerr"}]
	\mbox{}\\
		This sets the initial guess to the analytically-known
		apparent horizon position in a Kerr spacetime
		in Kerr coordinates.  This is a coordinate sphere
		of radius $(1 + \sqrt{1 - a^2}) m$.
		There are subparameters
		\begin{description}
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_Kerr\_Kerr\_\_x\_posn[}$n$\code{]} \\
		     \code{initial\_guess\_\_Kerr\_Kerr\_\_y\_posn[}$n$\code{]} \\
		     \code{initial\_guess\_\_Kerr\_Kerr\_\_z\_posn[}$n$\code{]} %%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			to set the position of the Kerr black hole
			(note this position is in {\em global\/} Cactus
			coordinates, not relative to the local coordinate
			origin), and
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_Kerr\_Kerr\_\_mass[}$n$\code{]} \\
		     \code{initial\_guess\_\_Kerr\_Kerr\_\_spin[}$n$\code{]} %%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			to set its mass and spin.
		\end{description}
	\item[\code{"Kerr/Kerr-Schild"}]
	\mbox{}\\
		This sets the initial guess to the analytically-known
		apparent horizon position in a Kerr spacetime in
		Kerr-Schild coordinates.  This is a coordinate ellipsoid
		with radia (semi-major axes)
		\begin{equation}
		\renewcommand{\arraystretch}{2.0}
		\begin{array}{lclcl}
		r_z	& = &	(1 + \sqrt{1 - a^2}) m			\\
		r_x = r_y
			& = &	r_z \sqrt{1 + \left(\dfrac{am}{r_z}\right)^2}
			& = &	\sqrt{\dfrac{2 r_z}{m}} \, m
									%%%\\
		\end{array}
		\end{equation}
		\begin{description}
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_Kerr\_KerrSchild\_\_x\_posn[}$n$\code{]} \\
		     \code{initial\_guess\_\_Kerr\_KerrSchild\_\_y\_posn[}$n$\code{]} \\
		     \code{initial\_guess\_\_Kerr\_KerrSchild\_\_z\_posn[}$n$\code{]} %%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			(note this position is in {\em global\/} Cactus
			coordinates, not relative to the local coordinate
			origin), and
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_Kerr\_KerrSchild\_\_mass[}$n$\code{]} \\
		     \code{initial\_guess\_\_Kerr\_KerrSchild\_\_spin[}$n$\code{]} %%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			to set its mass and spin.
		\end{description}
	\item[\code{"coordinate sphere"}]
	\mbox{}\\
		This sets the initial guess to a coordinate sphere;
		there are subparameters
		\begin{description}
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_coord\_sphere\_\_x\_center[}$n$\code{]} \\
		     \code{initial\_guess\_\_coord\_sphere\_\_y\_center[}$n$\code{]} \\
		     \code{initial\_guess\_\_coord\_sphere\_\_z\_center[}$n$\code{]} %%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			(note this position is in {\em global\/} Cactus
			coordinates, not relative to the local coordinate
			origin), and
		\item[\code{initial\_guess\_\_coord\_sphere\_\_radius[}$n$\code{]}]
		\mbox{}\\
			to set the radius.
		\end{description}
	\item[\code{"coordinate ellipsoid"}]
	\mbox{}\\
		This sets the initial guess to a coordinate ellipsoid;
		there are subparameters
		\begin{description}
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_coord\_ellipsoid\_\_x\_center[}$n$\code{]} \\
		     \code{initial\_guess\_\_coord\_ellipsoid\_\_y\_center[}$n$\code{]} \\
		     \code{initial\_guess\_\_coord\_ellipsoid\_\_z\_center[}$n$\code{]} %%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			(note this position is in {\em global\/} Cactus
			coordinates, not relative to the local coordinate
			origin), and
		\item[%%%
		     \begin{tabular}{@{}l@{}}
		     \code{initial\_guess\_\_coord\_ellipsoid\_\_x\_radius[}$n$\code{]}	\\
		     \code{initial\_guess\_\_coord\_ellipsoid\_\_y\_radius[}$n$\code{]}	\\
		     \code{initial\_guess\_\_coord\_ellipsoid\_\_z\_radius[}$n$\code{]}	%%%\\
		     \end{tabular}%%%
		     ]
		\mbox{}\\
			to set the radia (semimajor axes).
		\end{description}
	\end{description}
\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{I/O Parameters for the Apparent Horizon Shape(s)}

The main output of this thorn is the computed horizon shape function
$h({\rm angle})$, and correspondingly the $(x,y,z)$ coordinate positions
of the apparent-horizon-surface (angular) grid points.  There are several
parameters controlling if, how often, and how these should be written
to data files:

\begin{description}
\item[\code{output\_h\_every}]
\mbox{}\\
	As described in
	section~\ref{AHFinderDirect/sect-parameters/overall-parameters},
	\thorn{AHFinderDirect} will try to find the apparent horizon(s)
	every \verb|find_every| time steps.  However, you can control
	how often (if at all) the apparent horizon shape(s) are written
	to data files: this is only done if \verb|output_h_every|
	is nonzero, and the Cactus time step number \verb|cctk_iteration|
	is an integral multiple of this parameter (\verb|output_h_every|).

\item[\code{file\_format}]
\mbox{}\\
	This specifies the file format for horizon-shape (and other
	angular-grid-function) data files.  Unfortunately, at the
	moment only a single format is implemented,
	\begin{description}
	\item[\code{"ASCII (gnuplot)"}]
	\mbox{}\\
		This is a simple ASCII format designed for easy
		plotting with gnuplot:
		\begin{itemize}
		\item	\thorn{AHFinderDirect} writes a separate data file
			for each Cactus time step and for each apparent
			horizon found.  By default these are all written
			in to the \verb|IOUtil::out_dir| directory;
			see \verb|h_directory| (below) to change this.
		\item	The time step number and the apparent horizon number
			are both encoded in the file name; the actual
			file name is given by a \verb|printf()| format
			\verb|"%s/%s.t%d.ah%d.%s"|, where
			\begin{itemize}
			\item	the first \verb|%s| is the directory
				set by the \verb|IO::out_dir| and/or
				\verb|h_directory| parameters (see below)
			\item	the second \verb|%s| is the base file name
				set by the \verb|h_base_file_name|
				parameter (see below)
			\item	the first \verb|%d| is the
				Cactus time step number
			\item	the second \verb|%d| is the
				apparent horizon number
			\item	the third \verb|%s| is the
				file name extension, set by the
				\verb|ASCII_gnuplot_file_name_extension|
				parameter; this defaults to \verb|"gp"|
			\end{itemize}
		\item	Comment lines begin with \verb|#|.
		\item	Patches are separated by 2 blank lines;
			rows of apparent-horizon points within a patch
			are separated by single blank lines.
		\item	Each apparent-horizon-surface point
			is described by a single line, containing
			the whitespace-separated fields
			\begin{itemize}
			\item	Two ``unwrapped'' angular coordinates
				in degrees, representing a point on $S^2$.
			\item	The $h$ value, giving the radius
				of the apparent horizon surface
				at that angle.
			\item	The corresponding Cactus $(x,y,z)$
				coordinates.
			\end{itemize}
		\end{itemize}
		See section~\ref{AHFinderDirect/sect-visualization}
		for a discussion of visualization for these and other
		\thorn{AHFinderDirect} output files.
	\end{description}

\item[\code{h\_directory}]
\mbox{}\\
	This specifies the directory in which the $h$ data files
	are to be written.  If it doesn't already exist, this directory
	is created before writing the data files.  This parameter
	defaults to the value of the \verb|IO::out_dir| parameter.

\item[\code{h\_base\_file\_name}]
\mbox{}\\
	This specifies the base file name for $h$ data files, as
	described above.  This defaults to "h".

\item[\code{ASCII\_gnuplot\_file\_name\_extension}]
\mbox{}\\
	This specifies the file name extension for $h$ data files, as
	described above.  This defaults to "gp".
\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Parameters for the ``BH\_diagnostics'' Files}
\label{AHFinderDirect/sect-parameters/BH-diagnostics-parameters}

As well as the apparent horizon shape files, this thorn can also
write files giving time series of various diagnostics.  These are
controlled by the following parameters:

\begin{description}
\item[\code{output\_BH\_diagnostics}]
\mbox{}\\
	If this Boolean parameter is set to true,
	\thorn{AHFinderDirect} will write a ``black hole diagnostics''
	file for each distinct apparent horizon found
	(up to \verb|N_horizons| files in all).  Each such file
	contains a time series of various diagnostics for all time
	steps when the corresponding apparent horizon was found.
	The file format is again a simple ASCII format designed
	for easy plotting with gnuplot:
	\begin{itemize}
	\item	The apparent horizon number is encoded in the file name;
		the actual file name is given by a \verb|printf()| format
		\verb|"%s/%s.ah%d.%s"|, where
		\begin{itemize}
		\item	the first \verb|%s| is the directory
			set by the \verb|IO::out_dir| and/or
			\verb|BH_diagnostics_directory| parameters (see below)
		\item	the second \verb|%s| is the base file name
			set by the \verb|BH_diagnostics_base_file_name|
			parameter (see below); this defaults to
			\verb|"BH_diagnostics"|
		\item	the \verb|%d| is the apparent horizon number
		\item	the third \verb|%s| is the file name extension,
			set by the \verb|BH_diagnostics_file_name_extension|
			parameter; this defaults to \verb|"gp"|
		\end{itemize}
	\item	The file begins with a block of header comments
		(lines begining with \verb|#|) describing the data
		fields.
	\item	Each time this apparent horizon is found, a single
		line is appended to the data file, containing various
		whitespace-separated fields.  The the precise list of
		fields, see the header comments, or see the function
		\verb|output()| in \verb|src/driver/BH_diagnostics.cc|.
		As of this writing the fields are:
		\begin{itemize}
		\item	the Cactus iteration number \verb|cctk_iteration|
		\item	the Cactus time coordinate \verb|cctk_time|
		\item	the Cactus $(x,y,z)$ coordinates of the apparent
			horizon centroid
		\item	the minimum, maximum, and mean coordinate radia
			of the apparent horizon about the local
			coordinate origin
		\item	the minimum and maximum Cactus $x$, $y$, and $z$
			coordinates of the apparent horizon surface
		\item	the proper circumferences of the apparent horizon
			in the $xy$, $xz$, and $yz$ local-coordinate planes%%%
\footnote{%%%
	 Note that if the apparent horizon is not
	 symmetric across one of these coordinate planes,
	 then the circumference in this plane is not
	 very meaningful.  In particular, in this case
	 this circumference is probably different from
	 the apparent horizon's maximum ``hoop'' circumference.
	 }%%%
		\item	the $xz/xy$ and $yz/xy$ ratios of the proper
			circumferences
		\item	the proper area of the apparent horizon, $A$
		\item	the irreducible mass of the apparent horizon,
			$\sqrt{A/16\pi}$
		\item	the areal radius of the apparent horizon,
			$\sqrt{A/4\pi}$
		\end{itemize}
	\end{itemize}

\item[\code{BH\_diagnostics\_directory}]
\mbox{}\\
	This specifies the directory in which the black hole diagnostics
	data files are to be written.  If it doesn't already exist, this
	directory is created before writing the data files.  This parameter
	defaults to the value of the \verb|IO::out_dir| parameter.

\item[\code{BH\_diagnostics\_base\_file\_name}]
\mbox{}\\
	This specifies the base file name for black hole diagnostics
	data files, as described above.  This defaults to
	\verb|"BH_diagnostics"|.

\item[\code{BH\_diagnostics\_file\_name\_extension}]
\mbox{}\\
	This specifies the file name extension for black hole diagnostics
	data files, as described above.  This defaults to \verb|"gp"|.
\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{(Excision) Mask Parameters}
\label{AHFinderDirect/sect-parameters/mask-parameters}

This thorn can optionally set a mask grid function (or functions)
at each point of the Cactus grid, to indicate where that point is
with respect to the apparent horizon(s).  This is usually used for
excision.

\begin{description}
\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{set\_mask\_for\_all\_horizons}			\\
     \code{set\_mask\_for\_individual\_horizon[}$n$\code{]}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	These Boolean parameters control whether \thorn{AHFinderDirect}
	should set a mask grid function(s): If the (C) expression
	\verb:set_mask_for_all_horizons || set_mask_for_individual_horizon[i]:
	is true, then \thorn{AHFinderDirect} will set a mask grid
	function(s) for apparent horizon $i$.
	All these parameters default to false (don't set a mask).

	If any of these parameters is set to true, you almost certainly
	also need to set the Boolean parameter \verb|SpaceMask::use_mask|
	to true to to turn on storage for the mask grid function(s)!
\end{description}

If it's setting a mask(s), \thorn{AHFinderDirect} partitions the
Cactus grid into 3~regions: an \defn{inside}, a \defn{buffer},
and an \defn{outside}.  Typically the inner region is excised, but
\thorn{AHFinderDirect} doesn't itself do this:  It just sets the mask(s);
you need to use some other thorn(s) to do the actual excision.

The 3~regions are defined as follows:  For a grid point a distance $r[i]$
from horizon~$i$'s local coordinate origin, with horizon~$i$'s radius
in this same direction (again, measured from its local coordinate origin)
being $r_{\text horizon}[i]$,%%%
\footnote{%%%
	 Note that these are flat-space distances
	 $[(x-x_0)^2 + (y - y_0)^2 + (z-z_0)^2]^{1/2}$
	 -- the spacetime metric is {\em not\/} used here.
	 }%%%
{} the regions are defined by
\begin{equation}
\renewcommand{\arraystretch}{1.333}
\begin{tabular}{l@{~}lll@{~}lll}
$r[i] \le r{	ext{r}}[i]$	& \text{for some $i$}
	&
	&				&
	& $\Rightarrow$	& \text{inner}					\\
$r[i] >   r_{\text{r}}[i]$	& \text{for all $i$}
	& \text{and}	&
	$r[i] \le r_{\text{r}}[i]$	& \text{for some $i$}
	& $\Rightarrow$	& \text{buffer}					\\
$r[i] >   r_{\text{r}}[i]$	& \text{for all $i$}
	&
	&				&
	& $\Rightarrow$	& \text{outer}				%%%\\
\end{tabular}
\end{equation}
where
\begin{equation}
\renewcommand{\arraystretch}{1.333}
\begin{array}{lcl}
r_{\text{r}}
	& = &	\verb|mask_radius_multiplier| \times r_{\text{n}}
		\,\,+\,\,
		\verb|mask_radius_offset| \times {\Delta x}_{\text{e}}
									\\
r_{\text{r}}
	& = &	r_{\text{r}}
		\,\,+\,\,
		\verb|mask_buffer_thickness| \times {\Delta x}_{\text{e}}
									%%%\\
\end{array}
				\label{AHFinderDirect/eqn-r-inside-outside}
\end{equation}
and where ${\Delta x}_{\text{e}}$ is the base-grid Cactus grid spacing
(more precisely, the geometric mean of the base grid's $x$, $y$,
and $z$ Cactus grid spacings)%%%
\footnote{%%%
	 The ``base grid'' part here only matters if
	 you're doing mesh refinement, in which case
	 it's necessary to keep excision consistent
	 between the different refinement levels.
	 }%%%
.

\begin{description}
\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{mask\_radius\_multiplier}	\\
     \code{mask\_radius\_offset}	\\
     \code{mask\_buffer\_thickness}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	These parameters are used to define the radia $r_{\text{r}}$
	and $r_{\text{r}}$ in
	equation~\eqref{AHFinderDirect/eqn-r-inside-outside} above.

	Note that the sign convention here is that
	\verb|mask_radius_multiplier| is {\em multiplied\/}
	by the horizon radius, then \verb|mask_radius_offset|
	(scaled by the Cactus grid spacing) is {\em added\/}.
	Thus for use with excision (where the inner region --
	which will be excised -- must be somewhat {\em inside\/}
	the horizon), \verb|mask_radius_multiplier| should be a
	positive real number slightly less than~1.0, and/or
	\verb|mask_radius_offset| a negative real number.

	The default values for these parameters are
	\begin{verbatim}
	mask_radius_multiplier =  0.8
	mask_radius_offset     = -5.0
	mask_buffer_thickness  =  5.0
	\end{verbatim}

\item[\code{mask\_is\_noshrink}]
\mbox{}\\
	This Boolean parameter specifies whether the inside and
	buffer regions should be prevented from ever shrinking
	during a time evolution (this is the default), or whether
	they should be set independently from one time step to the
	next (and thus allowed to either grow or shrink).  More
	precisely, once a given grid point has been classified as
	inside, buffer, or outside, \thorn{AHFinderDirect} executes
	the following algorithm:
	\begin{description}
	\item[inside]\mbox{}\\[-\baselineskip]
		\begin{tabbing}
		$\verb|mask| \assign \verb|inside_value|$		  %%%\\
		\end{tabbing}
	\item[buffer]\mbox{}\\[-\baselineskip]
		\begin{tabbing}
		if \=(\verb|mask_is_noshrink|
		      \verb|&&|
		      ($\verb|mask| = \verb|inside_value|$))		\+\\
		   then \=\{						\+\\
			\verb|#|
			this point was previously {\bf inside}
			$\Rightarrow$ no-op here			  \\
			\}						\-\\
		   else \>$\verb|mask| \assign \verb|buffer_value|$	\-%%%\\
		\end{tabbing}
	\item[outside]\mbox{}\\[-\baselineskip]
		\begin{tabbing}
		if \=(\verb|mask_is_noshrink|
		      \verb|&&|
		      (($\verb|mask| = \verb|inside_value|$)
		       \verb.||. ($\verb|mask| = \verb|buffer_value|$))	\+\\
		   then \=\{						\+\\
			\verb|#|
			this point was previously {\bf inside}
			or {\bf buffer} $\Rightarrow$ no-op here	  \\
			\}						\-\\
		   else \>$\verb|mask| \assign \verb|outside_value|$	\-%%%\\
		\end{tabbing}
	\end{description}

\item[\code{min\_horizon\_radius\_points\_for\_mask}]
\mbox{}\\
	By default, \thorn{AHFinderDirect} sets the mask for each
	apparent horizon found.  If we're using mesh refinement, it's
	possible for an apparent horizon to be found on a coarse grid,
	and the masked region to be only a few grid points across on
	a fine grid.  This causes some other Cactus thorns
	(\eg{} \thorn{LegoExcision}) to crash. :(

	This parameter can be used to avoid this problem:
	For each apparent horizon that it finds, \thorn{AHFinderDirect}
	only sets the mask on a given grid if
	\begin{equation}
	r_{\text{inner},\min} \ge \verb|min_horizon_radius_points_for_mask|
		     * {\Delta x}_{\text{current},\max}
	\end{equation}
	where $r_{\text{inner},\min}$ is the minimum over all angles
	of $r_{\text{r}}$ as defined by
	equation~\eqref{AHFinderDirect/eqn-r-inside-outside},
	and ${\Delta x}_{\text{current},\max}$ is the maximum of the
	Cactus $x$, $y$, and~$z$ grid spacings on the current Cactus
	grid.%%%
\footnote{%%%
	 Note that this is the {\bf current\/} Cactus
	 grid spacing, not the {\bf base\/} Cactus grid
	 spacing that's used when calculating
	 $r_{\text{r}}$ and $r_{\text{r}}$ by
	 equation~\eqref{AHFinderDirect/eqn-r-inside-outside}.
	 }%%%
{}	If this condition isn't satisfied, then \thorn{AHFinderDirect}
	skips setting the mask for this apparent horizon, just as if
	this apparent horizon wasn't found.  (Note that all other
	processing for an apparent horizon being found is still done,
	including writing output files, using the apparent horizon shape
	as the initial guess for the next time step's apparent-horizon
	finding, etc.; it's only the mask processing for this horizon
	(at this time step) that's skipped.)
\end{description}

\thorn{AHFinderDirect} supports two types of mask grid functions;
the following two Boolean parameters choose which of them you want
to set; you can set either or even both of these:
\begin{description}
\item[\code{set\_old\_style\_mask}]
\mbox{}\\
	This parameter (default \verb|true|) specifies an
	old-style excision mask, one stored in a \verb|CCTK_REAL|
	Cactus grid function.  (The \thorn{AHFinder} apparent horizon
	finder uses this type of mask.)

\item[\code{set\_new\_style\_mask}]
\mbox{}\\
	This parameter (default \verb|false|) specifies a
	new-style excision mask, one stored in a specified bit field
	of a \verb|CCTK_INT| Cactus grid function.
	The bit field is specified by its name,
	as registered with the \thorn{SpaceMask} thorn.
	We plan to eventually convert all Cactus excision
	(and other uses of mask grid functions) to this scheme,
	but at the moment not much code supports it.
	Note that \thorn{AHFinderDirect} doesn't itself create/register
	any bit fields or state names with \thorn{SpaceMask} --
	you must arrange for some other thorn(s) to do this.
\end{description}

For an old-style mask, the following parameters specify the mask grid
function and how it should be set:
\begin{description}
\item[\code{old\_style\_mask\_gridfn\_name}]
\mbox{}\\
	This parameter specifies the mask grid function's name.

\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{old\_style\_mask\_inside\_value}	\\
     \code{old\_style\_mask\_buffer\_value}	\\
     \code{old\_style\_mask\_outside\_value}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	If an old-style mask is to be set in the corresponding
	regions, these parameters specify the values to which
	it should be set.  These are all \verb|CCTK_REAL| values.
\end{description}

For an new-style mask, the following parameters specify the mask grid
function and how it should be set:
\begin{description}
\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{new\_style\_mask\_gridfn\_name}	\\
     \code{new\_style\_mask\_bitfield\_name}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	These parameters specify the mask grid function's name
	and the bitfield name within it.

\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{new\_style\_mask\_inside\_value}	\\
     \code{new\_style\_mask\_buffer\_value}	\\
     \code{new\_style\_mask\_outside\_value}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	If an new-style mask is to be set in the corresponding
	regions, these parameters specify the values to which
	it should be set.  These are all character-string state
	names, as registered with the \thorn{SpaceMask} thorn.
\end{description}
Note that \thorn{AHFinderDirect} doesn't itself register any bitfields
or states with \thorn{SpaceMask} -- you must arrange for some other thorn(s)
to do this before \thorn{AHFinderDirect} tries to find the horizon(s).

If \thorn{AHFinderDirect} sets a mask or masks, this happens in the
same schedule bin(s) as the horizon finding.  More precisely,
\thorn{AHFinderDirect} creates two schedule groups for this purpose:
\begin{itemize}
\item	The schedule group \verb|group_for_mask_stuff| is
	scheduled to run just after the horizon is found.
\item	The schedule group \verb|group_where_mask_is_set|
	is scheduled inside the schedule group \verb|group_for_mask_stuff|.
\item	The actual setting of the mask is scheduled inside
	the schedule group \verb|group_where_mask_is_set|.
\end{itemize}
Thorn \thorn{PreviousMask} uses these schedule groups to keep a
``previous'' as well as a ``current'' mask.  See that thorn's thorn
guide for further details.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Communicating with Other Thorns}
\label{AHFinderDirect/sect-parameters/communicating-with-other-thorns}

Besides the data files it writes, \thorn{AHFinderDirect} currently
has three ways to communicate with other Cactus thorns:
\begin{itemize}
\item	\thorn{AHFinderDirect} can set a mask grid function(s) based
	on the (a) horizon's shape; other thorns may then use this for
	excision or other purposes.  \thorn{AHFinderDirect} supports
	both the old-style (\verb|CCTK_REAL|) mask
	(compatible with \thorn{AHFinder}) or
	the new-style (\verb|CCTK_INT|) mask bit-fields defined by
	\thorn{SpaceMask}; you can even use both styles simultaneously.
\item	\thorn{AHFinderDirect} can announce a selected horizon's centroid
	position to another thorn (typically \thorn{DriftCorrect}, which
	uses this to adjust its corotating shift vector).
	This uses the new function-aliasing version of \thorn{DriftCorrect},
	not the old version which worked with an auxiliary thorn
	\thorn{AHFSetDCCentroid}.
\item	\thorn{AHFinderDirect} provides a set of aliased functions
	which any other thorn(s) can call to find out the shape of
	a specified horizon.
\item	\thorn{AHFinderDirect} can store information about the
	horizon(s) it finds in the \thorn{SphericalSurface} variables
	for other thorns to use.
\end{itemize}

\thorn{AHFinderDirect}'s mask features are described in
section~\ref{AHFinderDirect/sect-parameters/mask-parameters};
the other communication mechanisms are described in the following
subsections.

%%%%%%%%%%%%%%%%%%%%

\subsubsection{Parameters for Announcing a Horizon Centroid to Other Thorns}

This thorn can optionally announce the centroid of a specified
apparent horizon to another thorn (typically \thorn{DriftCorrect})
each time that apparent horizon is found.  This is controlled by the
following parameter:

\begin{description}
\item[\code{which\_horizon\_to\_announce\_centroid}]
\mbox{}\\
	This is an integer parameter which defaults to
	0 (which means not to announce any centroid).
	If it's set to a nonzero integer, that specifies the
	horizon number to have its centroid announced.
\end{description}

%%%%%%%%%%%%%%%%%%%%

\subsubsection{Aliased Functions to Provide Horizon-Shape Information}

\thorn{AHFinderDirect} provides the following aliased functions
to allow other thorns to find out about the horizons.  Each function
returns a status code which is $\ge 0$~for ok, or negative for an error.
\begin{verbatim}
#
# This function returns the local coordinate origin for a given horizon.
#
CCTK_INT FUNCTION HorizonLocalCoordinateOrigin                          \
   (CCTK_INT IN horizon_number,                                         \
    CCTK_REAL OUT origin_x, CCTK_REAL OUT origin_y, CCTK_REAL OUT origin_z)

#
# The following function queries whether or not the specified horizon
# was found the most recent time AHFinderDirect searched for it.
# The return value is:
#  1 if the horizon was found
#  0 if the horizon was not found
#  negative for an error
#
CCTK_INT FUNCTION HorizonWasFound(CCTK_INT IN horizon_number)

#
# The following function computes the horizon radius in the direction
# of each (x,y,z) point, or -1.0 if this horizon wasn't found the most
# recent time AHFinderDirect searched for it.  More precisely, for each
# (x,y,z), consider the ray from the local coordinate origin through
# (x,y,z).  This function computes the Euclidean distance between the
# local coordinate origin and this ray's intersection with the horizon,
# or -1.0 if this horizon wasn't found the most recent time AHFinderDirect
# searched for it.  
#
# If this function is to be used in a multiprocessor run on a horizon
# which was found on some other processor, the parameter
#   AHFinderDirect::always_broadcast_horizon_shape
# must be set to true to get the correct answer.
#
CCTK_INT FUNCTION HorizonRadiusInDirection                              \
   (CCTK_INT IN horizon_number,                                         \
    CCTK_INT IN N_points,                                               \
    CCTK_REAL IN ARRAY x, CCTK_REAL IN ARRAY y, CCTK_REAL IN ARRAY z,   \
    CCTK_REAL OUT ARRAY radius)
\end{verbatim}

%%%%%%%%%%%%%%%%%%%%

\subsubsection{Storing Horizon-Shape Information in the \thorn{SphericalSurface} Variables}

\thorn{SphericalSurface} (in the \thorn{AEIThorns} arrangement)
defines a set of generic grid arrays which describe ``spherical surfaces''.
\thorn{AHFinderDirect} can optionally store information about the
horizons it finds in the \thorn{SphericalSurface} variables.
This is controlled by the following parameters:
\begin{description}
\item[\code{which\_surface\_to\_store\_info[}$n$\code{]}]
\mbox{}\\
	This parameter should be set to the \thorn{SphericalSurface}
	surface number into which information on a given \thorn{AHFinderDirect}
	horizon should be stored, or to -1 to skip storing the information.
	It defaults to -1 for each horizon.

	{\bf Note that \thorn{SphericalSurface} numbers surfaces
	starting from 0, whereas \thorn{AHFinderDirect} numbers
	horizons starting from 1!}

	At present, if multiple \thorn{AHFinderDirect} horizons
	specify the same \thorn{SphericalSurface} surface, the
	highest-numbered horizon will ``win'', \ie{} it will overwrite
	the data from any lower-numbered horizons.
\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Other Parameters}
\label{AHFinderDirect/sect-parameters/other-parameters}

\begin{description}
\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{run\_at\_CCTK\_ANALYSIS}			\\
     \code{run\_at\_CCTK\_POSTSTEP}			\\
     \code{run\_at\_CCTK\_POSTINITIAL}			\\
     \code{run\_at\_CCTK\_POST\_RECOVER\_VARIABLES}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	These parameters (which default to \verb|true|, \verb|false|,
	\verb|false|, and \verb|true| respectively) control which
	schedule bins \thorn{AHFinderDirect} runs in.  Historically,
	\thorn{AHFinderDirect} ran in CCTK\_ANALYSIS, and that's
	still the default, but these parameters allow you to change this
	so it runs in CCTK\_POSTSTEP and/or CCTK\_POSTINITIAL
	instead.  (You can even run in all three bins if you want!)

	In general we need to run at CCTK\_POST\_RECOVER\_VARIABLES, since
	\begin{itemize}
	\item	parameters may have been steered at recovery, so we may need
		to find a new horizon or horizons, and
	\item	we need to set the mask again to make sure it's correct
		right away (since our next regular horizon-finding may not
		be until some time steps later)
	\end{itemize}
	Therefore the \verb|run_at_CCTK_POST_RECOVER_VARIABLES| parameter
	should probably be left at its default setting of \verb|true|.

\item[%%%
     \begin{tabular}{@{}l@{}}
     \code{geometry\_interpolator\_name}	\\
     \code{geometry\_interpolator\_pars}	%%%\\
     \end{tabular}
     ]
\mbox{}\\
	These parameters control the (3-D) \defn{geometry interpolation}
	of the spacetime geometry ($g_{ij}$ and $K_{ij}$, or their
	\thorn{StaticConformal} equivalents) to the apparent horizon
	position.  The defaults are set to use a quadratic Hermite
	interpolator.  This works fairly well, but because of the interpolator
	molecule size you must use $\verb|driver::ghost_size| \ge 2$.

	If you want to get very high accuracy from \thorn{AHFinderDirect},
	then you should use a cubic Hermite geometry interpolator, by
	setting
\begin{verbatim}
AHFinderDirect::geometry_interpolator_pars = "
  order=3
  boundary_off_centering_tolerance={1.0e-10 1.0e-10 1.0e-10 1.0e-10 1.0e-10 1.0e-10}
  boundary_extrapolation_tolerance={0.0 0.0 0.0 0.0 0.0 0.0}
                                             "
\end{verbatim}
	Assuming perfectly accurate geometry variables in the 3-D Cactus grid,
	this will make \thorn{AHFinderDirect} (very) roughly an order
	of magnitude more accurate.  However, the larger molecule size
	will make it about a factor of 2--3~slower, and will also require
	that you set $\verb|driver::ghost_size| \ge 3$.
	The sample parameter file \verb|par/Kerr-order3.par|
	shows an example of this.

\item[\code{N\_zones\_per\_right\_angle[}$n$\code{]}]
\mbox{}\\
	This parameter sets the angular resolution used to compute
	each patch.  The units are the number of angular grid zones
	per right angle.  The default is 18, \ie{} a 5~degree angular
	resolution.  There's no problem with this parameter varying
	from one horizon to another, but for simplicity it should be
	even.%%%
\footnote{%%%
	 More precisely, it {\em must\/} be even for
	 any horizon whose patch system type is anything
	 other than {\tt "full sphere"}.  See the comments
	 in {\tt param.ccl} for further information.%%%
	 }%%%

{}	For any horizon which is close to spherical about its
	local coordinate origin, you can lower this parameter to make
	\thorn{AHFinderDirect} run faster (typical run-times scale
	roughly as the cube of this parameter); 6~is about the
	minimum reasonable value.

	For any horizon which is highly non-spherical about its
	local coordinate origin, you can raise this parameter to
	get better resolution; 30~should be enough for even a
	highly non-spherical horizon.

\item[\code{max\_allowable\_horizon\_radius[}$n$\code{]}]
\mbox{}\\
	This parameter gives the maximum mean-coordinate-radius
	which any given trial surface may have in the course of
	trying to solve the apparent horizon equation.%%%
\footnote{%%%
	 Note that this is an unweighted arithmetic
	 mean over the horizon-surface grid points,
	 the same value which is printed as \code{r\_grid}
	 during the horizon-finding iterations.
	 }%%%
{}	In particular, if any trial surface has a mean coordinate radius
	which exceeds this parameter, \thorn{AHFinderDirect} gives up
	and deems this apparent horizon to be ``not found''.

	This parameter defaults to $10^{10}$ (effectively $+\infty$)
	for each apparent horizon.  You can set it to a smaller value
	to make \thorn{AHFinderDirect} a bit more efficient, or
	(probably more important in practice) to stop
	\thorn{AHFinderDirect} from iterating off the edge of the grid
	if this causes problems with interpolation or boundary conditions.

\item[\code{max\_allowable\_Theta}]
\mbox{}\\
	This parameter gives the maximum $\|\Theta\|_\infty$
	which any given trial surface may have in the course of
	trying to solve the apparent horizon equation.
	In particular, if any trial surface has $\|\Theta\|_\infty$
	exceeding this parameter, \thorn{AHFinderDirect} gives up
	and deems this apparent horizon to be ``not found''.
	This parameter defaults to $10^{10}$ (effectively $+\infty$)
	for each apparent horizon.

\item[\code{surface\_expansion[}$n$\code{]}]
\mbox{}\\
	This parameter (which defaults to 0.0) sets the expansion
	of each surface.  With the default setting this thorn
	solves~\eqref{AHFinderDirect/eqn-horizon} to find apparent
	horizons.  With other settings of this parameter this thorn
	can be used to find \defn{surfaces of constant expansion};
	these may be useful for excision, wave extraction, or
	other purposes (\cite{AHFinderDirect/Schnetter03a}).

	To help in choosing the value(s) of the
	\verb|surface_expansion[|$n$\verb|]| parameter,
	figure~\ref{AHFinderDirect/fig-Schwarzschild-EF-Theta(r)}
	(from \cite{AHFinderDirect/Thornburg95}),
	shows the expansion of $r = \text{constant}$ surfaces
	in an Eddington-Finkelsteon slice of the unit-mass
	Schwarzschild spacetime.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{figure}[htbp]
\begin{center}
\includegraphics{AEIThorns_AHFinderDirect_Schw_EF_Theta_of_r}
\end{center}
\caption[Expansion $\Theta$ for $r = \text{constant}$ Surfaces
	 in an Eddington-Finkelstein slice
	 of the Unit-Mass Schwarzschild Spacetime]
	{
	This figure shows the expansion $\Theta$ for $r = \text{constant}$
	surfaces in an Eddington-Finkelstein slice of the unit-Mass
	Schwarzschild Spacetime.
	}
\label{AHFinderDirect/fig-Schwarzschild-EF-Theta(r)}
\end{figure}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Monitoring \thorn{AHFinderDirect}'s Status}

There are two primary ways of monitoring what \thorn{AHFinderDirect}
is doing during a Cactus run: the \verb|BH_diagnostics| files and the
\verb|CCTK_INFO| messages written to the Cactus standard output:

The \verb|BH_diagnostics| files are described in detail in
section~\ref{AHFinderDirect/sect-parameters/BH-diagnostics-parameters}.
These files are written and ``flushed'' at each time step, so they're
always up-to-date.

During the apparent-horizon--finding process, \thorn{AHFinderDirect}
writes various \verb|CCTK_INFO| messages describing the convergence
of the iterative solution of the apparent horizon
equation~\ref{AHFinderDirect/eqn-horizon} on each processor.
In particular, if \verb|verbose_level| is set to
\verb|"algorithm highlights"| or a more verbose setting
(\cf{}~section~\ref{AHFinderDirect/sect-parameters/overall-parameters}),
then \thorn{AHFinderDirect} writes \verb|CCTK_INFO| messages like
these:
\begin{verbatim}
INFO (AHFinderDirect):    proc 0/horizon 1:it 1 r_grid=0.595 ||Theta||=1.1e-01
INFO (AHFinderDirect):    proc 0/horizon 1:it 2 r_grid=0.614 ||Theta||=7.2e-02
INFO (AHFinderDirect):    proc 0/horizon 1:it 3 r_grid=0.632 ||Theta||=2.9e-02
INFO (AHFinderDirect):    proc 0/horizon 1:it 4 r_grid=0.642 ||Theta||=9.9e-04
INFO (AHFinderDirect):    proc 0/horizon 1:it 5 r_grid=0.642 ||Theta||=7.9e-07
INFO (AHFinderDirect):    proc 0/horizon 1:it 6 r_grid=0.642 ||Theta||=7.2e-13
INFO (AHFinderDirect): AH 1/2: r=0.660716 at (0.000000,0.000000,1.127434)
INFO (AHFinderDirect): AH 1/2: area=338.0473838 m_irreducible=2.59330658
INFO (AHFinderDirect): writing h to "misner.h.t0.ah1.gp"
\end{verbatim}
Here \verb|r_grid| is a rough estimate of the mean radius of
the trial surface at each iteration, and \verb:||Theta||: is the
infinity-norm of $\Theta$, the left hand side of the apparent
horizon equation~\ref{AHFinderDirect/eqn-horizon} over the surface.
Once the apparent horizon has been found
(\verb:||Theta||: is sufficiently small), then \thorn{AHFinderDirect}
prints its mean radius,%%%
\footnote{%%%
	 Note that this may differ significantly from
	 the last iterations' estimated radius.  This
	 is because the mean radius printed during the
	 iterations is only a rough estimate (it's the
	 unweighted arithmetic mean of the radius at
	 all the horizon-surface grid points), while
	 the radius printed after the horizon is found
	 is a more accurate value (it's computed via
	 numerical integrals over the surface, taking
	 into account the induced metric).
	 }%%%
{} centroid position, area, and irreducible mass.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Visualization}
\label{AHFinderDirect/sect-visualization}

There are several ways to visualize \thorn{AHFinderDirect}'s output:

The simplest is to plot various quantities from the
\verb|BH_diagnostics| files (described in detail in
section~\ref{AHFinderDirect/sect-parameters/BH-diagnostics-parameters}).
For example, using gnuplot (\verb|http://www.gnuplot.info|), you can
plot a graph of the surface area of horizon \#4 as a function of
coordinate time, with the command
\begin{verbatim}
plot 'BH_diagnostics.ah4.gp' using 2:26 with points
\end{verbatim}
ygraph (\verb|http://www.aei.mpg.de/~pollney/ygraph/|) may also be
able to directly plot the \verb|BH_diagnostics| files.

Given a horizon-shape data file \verb|h.t105.h4.gp|, the gnuplot
command
\begin{verbatim}
splot 'h.t105.h4.gp' with lines
\end{verbatim}
will plot the $h(\text{angle})$ function, with the $x$ and $y$~axes
of the plot being the two ``unwrapped'' angular coordinates on $S^2$,
in degrees, and the $z$~axis being $h(\text{angle})$.  However,
in practice this usually isn't very informative.  Instead, you
probably want the gnuplot command
\begin{verbatim}
splot 'h.t105.h4.gp' using 4:5:6 with lines
\end{verbatim}
which will plot the 3-D shape of the apparent horizon surface.

The \verb|src/misc| directory of the \thorn{AHFinderDirect} source
code contains several perl scripts which are useful in visualizing
\thorn{AHFinderDirect} output.  In particular, the script \verb|select.plane|
selects a particular 2-D plane.  If you put this in your Unix path,
it can be used with a gnuplot command like
\begin{verbatim}
splot '<select.plane xy <h.t105.h4.gp' using 4:5 with lines
\end{verbatim}
to show the shape of an apparent horizon in the xy plane.%%%
\footnote{%%%
	 Note that this script isn't very smart: it doesn't
	 know about the \thorn{AHFinderDirect} local coordinate
	 origin.  That is, for example, if you select the $xy$
	 plane, you're selecting the \emph{global} $xy$ plane
	 ($z=0$).
	 }%%%

Another Visualization option is OpenDX (\verb|http://www.opendx.org/|).
Thomas Radke's has written some OpenDX macros
\program{ImportAHFinderDirectGnuplot.net}
and \program{ImportAHFinderDirectGnuplotPatch.net}
to import \thorn{AHFinderDirect} horizon-shape data files.  
These macros use a set of ``control files'' named \verb|*.dx|,
one per horizon, which \thorn{AHFinderDirect} (by default) writes
into the same directory as the main horizon--shape output files.
You can get these macros by anonymous CVS with the command
\begin{verbatim}
cvs -d :pserver:cvs_anon@cvs.aei.mpg.de:/numrelcvs \
     checkout AEIPhysics/Visualization/OpenDX
\end{verbatim}

Another Visualization option is to produce files in the standard
Xdmf~\cite{AHFinderDirect/Xdmf:web} file format which can be visualized for
example using VisIt~\cite{AHFinderDirect/VisIt:web}. Frank L\"offler has
written a python script \program{AH2xdmf.py}, which is included in this
thorn's code repository, which generates such files from
\thorn{AHFinderDirect}'s output from horizon shape files
\verb|h.t%d.ah%d.gp\|. Please see its help text for details.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Accuracy}

The apparent horizon positions are typically computed very accurately;
tests on Kerr spacetimes give typical errors of $10^{-4}m$ to $10^{-5}m$.

The various diagnostics printed to standard output and written to the
black hole diagnostics file(s), are typically computed to accuracies
on the order of a part per million or so.

Note, however, that the irreducible mass $m_{\text{e}}$
may differ considerably from the black hole's local mass or its
contribution to the slice's ADM mass.  For example, for Kerr spacetime
in Kerr-Schild coordinates,
$m_{\text{e}}/m_{\text{M}} = 0.949$, $0.894$, and $0.723$
for spin parameters $a \equiv J/m^2 = 0.6$, $0.8$, and $0.999$, respectively.
It would be better to (also) use the ``isolated horizons'' formalism of
\cite{AHFinderDirect/Dreyer-etal-2002-isolated-horizons};
at some point this thorn may be enhanced to do this.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Examples}
\label{AHFinderDirect/sect-examples}

There are a few example parameter files in the \code{par/} directory,
including Kerr initial data, Misner initial data, and Misner time-evolution
tests.  The \verb|Kerr-tiny.par| parameter file is close to a minimal
\thorn{AHFinderDirect} example:

\begin{verbatim}
# This parameter file sets up Kerr/Kerr-Schild initial data, then
# finds the apparent horizon in it.  The local coordinate system origin
# and the initial guess are both deliberately de-centered with respect
# to the black hole, to make this a non-trivial test for the apparent
# horizon finder.
#
# This parameter file is "tiny" in the sense that it sets only a
# small number of AHFinderDirect parameters.

# flesh
cactus::cctk_itlast = 0

ActiveThorns = "PUGH"
driver::ghost_size = 2
driver::global_nx = 31
driver::global_ny = 31
driver::global_nz = 19

ActiveThorns = "CoordBase CartGrid3D"
grid::domain = "bitant"
grid::avoid_origin = false
grid::type = "byspacing"
grid::dxyz = 0.2

ActiveThorns = "ADMBase ADMCoupling StaticConformal Spacemask CoordGauge Exact"
ADMBase::initial_lapse = "exact"
ADMBase::initial_shift = "exact"
ADMBase::initial_data = "exact"
ADMBase::lapse_evolution_method = "static"
ADMBase::shift_evolution_method = "static"
ADMBase::metric_type = "physical"
Exact::exact_model = "Kerr/Kerr-Schild"
Exact::Kerr_KerrSchild__mass = 1.0
Exact::Kerr_KerrSchild__spin = 0.6

########################################

ActiveThorns = "IOUtil"
IOUtil::parfile_write = "no"

########################################

ActiveThorns = "SphericalSurface"
ActiveThorns = "AEILocalInterp PUGHInterp PUGHReduce AHFinderDirect"
AHFinderDirect::h_base_file_name     = "Kerr-tiny.h"

AHFinderDirect::N_horizons = 1
AHFinderDirect::origin_x[1] = 0.5
AHFinderDirect::origin_y[1] = 0.7
AHFinderDirect::origin_z[1] = 0.0

AHFinderDirect::initial_guess_method[1] = "coordinate sphere"
AHFinderDirect::initial_guess__coord_sphere__x_center[1] = -0.2
AHFinderDirect::initial_guess__coord_sphere__y_center[1] =  0.3
AHFinderDirect::initial_guess__coord_sphere__z_center[1] =  0.0
AHFinderDirect::initial_guess__coord_sphere__radius[1] = 2.0
\end{verbatim}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Surfaces of Constant Expansion}

Surfaces of Constant Expansion (CE surfaces) are introduced in
\cite{AHFinderDirect/Schnetter03a} as a generalisation of
apparent horizons (AH).  On an AH surface, the expansion is zero
everywhere.  On a CE surfaces, the expansion is still everywhere the
same, but it need not be zero.  CE surfaces are also a generalisation
of Constant Mean Curvature surfaces (CMC surfaces); both are identical
when the extrinsic curvature vanishes.  As described in
\cite{AHFinderDirect/Schnetter03a}, it is likely that CE
surfaces foliate the spacelike hypersurface outside of some interior
region.  This interior region is inside the common apparent horizon,
if it exists.

CE surfaces can give some insight into the spacetime, because they can
be used to analyse the part of the spacelike hypersurface ``between
the horizons and infinity''.  Most notably, they can be used to look
at the region where a common horizon is about to (or believed to)
form.  Similarly, one can use them for collapsing stars where an
apparent horizon has not yet formed.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Pretracking}

Apparent horizon pretracking is introduced in
\cite{AHFinderDirect/Schnetter03a}.  This is an application
of CE surfaces.  Even when there is no common horizon, there are still
common CE surfaces surrounding multiple black holes.  Pretracking
consists of tracking in time the smallest common CE surface that can
be found.  It is reasonable to believe that this surface will evolve
into the common horizon at the time where this common horizon begins
to exist.  The expansion of this smallest CE surface is also an
indication of how close the spacelike hypersurface is to having a
common apparent horizon.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{How \thorn{AHFinderDirect} Works}
\label{AHFinderDirect/sect-how-ahfinderdirect-works}

\thorn{AHFinderDirect} uses the apparent horizon
(henceforth \defn{horizon}) finding algorithm of
\cite{AHFinderDirect/Thornburg95},
modified slightly to work with $g_\ij$ and $K_\ij$ on a Cartesian ($xyz$)
grid.  The algorithm is described in detail in
\cite{AHFinderDirect/Thornburg2003:AH-finding}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{General Description of the Algorithm}

As described above, I parameterizes the horizon shape by
$r = h(\text{angle})$ for some single-value function $h: S^2 \to \Re^+$.
The apparent horizon equation~\eqref{AHFinderDirect/eqn-horizon}
then becomes a 2-D elliptic PDE on $S^2$ for the function $h$.  I finite
difference this in angle to obtain a system of simultaneous nonlinear
algebraic equations for $h$ at the angular grid points, and solve this
system of equations by a global Newton's method (or a variant with
improved convergence).

Computationally, this algorithm has 3 main parts:
\begin{itemize}
\item	Computation of the ``horizon function'' $\Theta(h)$ given a trial
	surface defined by a trial horizon shape function $h$.  This is
	done by interpolating the Cactus geometry fields $g_\ij$ and
	$K_\ij$ (and optionally $\psi$) from the 3-D $xyz$ grid to the
	(2-D set of) trial-horizon-surface grid points (also computing
	$\partial_k g_\ij$ in the interpolation process), then doing
	all further computations with angular grid functions defined
	solely on $S^2$ (\ie{} at the horizon-surface grid points).
\item	Computation of the Jacobian matrix $\Jac[\Theta(h)]$ of $\Theta(h)$.
	This thorn incorporates the \defn{symbolic differentiation} technique
	described in \cite{AHFinderDirect/Thornburg95},
	so this computation is quite fast.  The Jacobian is a highly
	sparse matrix; \thorn{AHFinderDirect} has code to store it
	as either a dense matrix (for debugging purposes), or a sparse
	matrix (the default).  Which option is used is determined by
	a compile-time configuration in \verb|src/include/config.h|.
\item	Solving the nonlinear equations $\Theta(h) = 0$ by a global
	Newton's method or a variant.  How this is done depends on how
	the Jacobian is stored.  At present,
	\begin{itemize}
	\item	If \thorn{AHFinderDirect} is configured to store the
		Jacobian as a dense matrix, then LAPACK is used to solve
		the linear equations.
	\item	If \thorn{AHFinderDirect} is configured to store the
		Jacobian as a sparse matrix, then an
		incomplete-$\sf LU$-decomposition--conjugate-gradient
		solver is used.
	\end{itemize}
	By default only the sparse-matrix code is configured, so LAPACK
	isn't used and there's no need to link with the LAPACK library.
\end{itemize}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{The Multipatch System}
\label{AHFinderDirect/sect-multipatch-system}

Perhaps the most unusual feature of \thorn{AHFinderDirect} is the
``multipatch'' system used to cover $S^2$ without coordinate singularities.
In general there are 6~patches, one each covering a neighborhood of
the $\pm z$, $\pm x$, and $\pm y$ axes, but this may be reduced in
the presence of suitable symmetries.  For example,
figure~\ref{AHFinderDirect/fig-3patch}
on page~\pageref{AHFinderDirect/fig-3patch} shows a system
of 3~patches covering the $+xyz$~octant of $S^2$.  This would be
suitable for finding an apparent horizon with mirror symmetry about
the (local) $z=0$~plane, and either 90~degree periodic rotation symmetry
about the (local) $z$~axis, or mirror symmetry about each of the (local)
$x$~and $y$~axes.

\begin{figure}[htbp]
\begin{center}
\includegraphics{AEIThorns_AHFinderDirect_3patch}
\end{center}
\caption[Illustration of the Multipatch System]
	{
	This figure shows a multipatch system covering the
	$(+,+,+)$~octant of the unit sphere~$S^2$ with 3~patches.
	The angular resolution is 5~degrees.  Notice that the
	patches overlap by several ``ghost zone'' grid points.
	}
\label{AHFinderDirect/fig-3patch}
\end{figure}

To allow easy angular finite differencing within the patch system,
each patch is extended beyond its nominal extent by a ``ghost zone''%%%
\footnote{%%%
	 Note that this terminology differs somewhat
	 from that used by Cactus in general; Cactus
	 would call these ``patch zones'' or ``symmetry
	 zones''.
	 }%%%
{} (2~grid points wide in figure~\ref{AHFinderDirect/fig-3patch}).
Angular grid function values in the ghost zone can be obtained by
interpatch interpolation%%%
\footnote{%%%
	 Due to the way the patch coordinates are defined,
	 adjacent patches always share a common ``perpendicular''
	 angular coordinate, so only 1-D interpolation
	 is needed here.
	 }%%%
{} or by applying symmetry operations.  Once this is done, then angular
finite differencing within the nominal extent of each patch can proceed
normally, ignoring the patch boundaries.  \thorn{AHFinderDirect} can
be configured at compile time to use either 2nd~order or 4th~order
angular finite differencing (3~point or 5~point angular molecules);
the default is 4th~order (5~point).  This is configured at compile time
in \verb|src/include/config.h|.

By default \thorn{AHFinderDirect} will automagically choose a patch
system type for each apparent horizon searched for, based on the local
coordinate origin and the symmetries implicit in the Cactus grid type.
This generally works well, but if desired you can instead manually
specify the patch system type, the angular resolution, the width of
the ghost zones, etc.  See the \verb|param.ccl| file for details.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Other Software Used}

\thorn{AHFinderDirect}'s \verb|src/sparse-matrix/| directory contains
various sparse-matrix libraries, which have their own copyrights and
licensing terms:

The \verb|src/sparse-matrix/umfpack/| directory contains a subset of the
files in UMFPACK version 4.0 (11.Apr.2002).  This code is
copyright~(\copyright)~2002 by Timothy A. Davis, and is subect to the
UMFPACK License:

\begin{verbatim}
Your use or distribution of UMFPACK or any modified version of
UMFPACK implies that you agree to this License.

THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.

Permission is hereby granted to use or copy this program, 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 UMFPACK or any modified version of UMFPACK 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.  This
software was developed with support from the National Science Foundation,
and is provided to you free of charge.
\end{verbatim}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Other Related Thorns}

If you're interested in \thorn{AHFinderDirect}, you might also be
interested in some other related thorns:

\begin{description}
\item[\thorn{EHFinder}] (in the \arrangement{AEIDevelopment} arrangement)
	was written by Peter Diener, and finds the {\em event\/} horizon(s)
	in a numerically computed spacetime.  It's described in detail in
	the paper~\cite{AHFinderDirect/Diener03a}.
\item[\thorn{AHFinder}] (in the \arrangement{CactusEinstein} arrangement)
	was written by Miguel Alcubierre, and includes two different
	algorithms for finding apparent horizons, a minimization method
	and a ``fast flow'' method based on \cite{AHFinderDirect/Gundlach97a}.
	Unfortunately, both methods are very slow in practice.
\item[\thorn{TGRapparentHorizon2D}] (in the \arrangement{TAT} arrangement)
	was written by Erik Schnetter, and is another apparent horizon
	finder.  It uses methods very similar to this thorn, and (like
	this thorn) is very fast and accurate.  However, it's no longer
	under active development.  It's described in detail in the
	papers~\cite{AHFinderDirect/Schnetter02a}
	and~\cite{AHFinderDirect/Schnetter03a}.
\item[\thorn{AHFinderDirect} (\cvsplace{Erik} branch)]
      (in the \arrangement{AEIThorns} arrangement)\\
	Erik Schnetter has added a number of new features to
	\thorn{AHFinderDirect} on a CVS branch with the tag \cvsplace{Erik},
	including horizon pretracking (to locate places where horizons
	are about to form), and the ability to find constant-expansion
	and constant-mean-curvature surfaces specified by their areal radius.
	We hope to integrate these into the main \thorn{AHFinderDirect}
	branch during the summer of 2004.
\end{description}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\section{Acknowledgments}

I thank Peter Diener, Ian Hawke, and Erik Schnetter for many valuable
conversations.  I think Thomas Radke for his work on the new interpolators.
I thank the whole Cactus crew for a great infrastructure!

Erik Schnetter originally implemented a number of improvements to
this thorn, notably the \thorn{SphericalSurface} interface and the
new features in the \cvsplace{Erik} branch.

I thank the Alexander von Humboldt foundation and the AEI visitors'
and postdoctoral fellowships programs for financial support.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% make LaTeX read in AHFinderDirect.bbl produced by bibtex
\bibliography{AHFinderDirect}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Do not delete next line
% END CACTUS THORNGUIDE

\end{document}

```



Reference answer:
src_code: /* -------------------------------------------------------------------------- */
/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis.  All Rights Reserved.  See README for License.                      */
/* email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.            */
/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
/* -------------------------------------------------------------------------- */

GLOBAL Int UMF_get_memory
(
    NumericType *Numeric,
    WorkType *Work,
    Int needunits
) ;