Task: Implement sparse-matrix/umfpack/umf_lsolve.c for EinsteinAnalysis/AHFinderDirect thorn

thorn_name: EinsteinAnalysis/AHFinderDirect
src_filename: sparse-matrix/umfpack/umf_lsolve.c
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/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.
*/

```

sparse-matrix/umfpack/umfpack_defaults.h:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

void umfpack_di_defaults
(
    double Control [UMFPACK_CONTROL]
) ;

void umfpack_dl_defaults
(
    double Control [UMFPACK_CONTROL]
) ;

void umfpack_zi_defaults
(
    double Control [UMFPACK_CONTROL]
) ;

void umfpack_zl_defaults
(
    double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

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

double long Syntax:

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

complex int Syntax:

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

complex long Syntax:

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

Purpose:

    Sets the default control parameter settings.

Arguments:

    double Control [UMFPACK_CONTROL] ;	Output argument.

	Control is set to the default control parameter settings.  You can
	then modify individual settings by changing specific entries in the
	Control array.  If Control is a (double *) NULL pointer, then
	umfpack_*_defaults returns silently (no error is generated, since
	passing a NULL pointer for Control to any UMFPACK routine is valid).
*/

```

sparse-matrix/umfpack/umf_assemble.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_assemble
(
    NumericType *Numeric,
    WorkType *Work
) ;


```

sparse-matrix/umfpack/umf_transpose.c:
```
/* ========================================================================== */
/* === UMF_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                       */
/* -------------------------------------------------------------------------- */

/*  Not user-callable.  Computes a permuted transpose, R = (A (P,Q(1:nq)))' in
	MATLAB notation, where R is in column-form.  A is n_row-by-n_col, the
	row-form matrix R is n_row-by-nq, where nq <= n_col.  A may be singular.
	The complex version can do transpose (') or array transpose (.').

	Uses Gustavson's method (Two Fast Algorithms for Sparse Matrices:
	Multiplication and Permuted Transposition, ACM Trans. on Math. Softw.,
	vol 4, no 3, pp. 250-269).
*/

#include "umf_internal.h"
#include "umf_is_permutation.h"

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

GLOBAL Int UMF_transpose
(
    Int n_row,			/* A is n_row-by-n_col */
    Int n_col,
    const Int Ap [ ],		/* size n_col+1 */
    const Int Ai [ ],		/* size nz = Ap [n_col] */
    const double Ax [ ],	/* size nz if present */

    const Int P [ ],	/* P [k] = i means original row i is kth row in A(P,Q)*/
			/* P is identity if not present */
			/* size n_row, if present */

    const Int Q [ ],	/* Q [k] = j means original col j is kth col in A(P,Q)*/
			/* Q is identity if not present */
			/* size nq, if present */
    Int nq,		/* size of Q, ignored if Q is (Int *) NULL */

			/* output matrix: Rp, Ri, Rx, and Rz: */
    Int Rp [ ],		/* size n_row+1 */
    Int Ri [ ],		/* size nz */
    double Rx [ ],	/* size nz, if present */

    Int W [ ],		/* size max (n_row,n_col) workspace */

    Int check		/* if true, then check inputs */
#ifdef COMPLEX
    , const double Az [ ]	/* size nz */
    , double Rz [ ]		/* size nz */
    , Int do_conjugate		/* if true, then do conjugate transpose */
				/* otherwise, do array transpose */
#endif
)
{

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

    Int i, j, k, p, bp, nz, newj, ilast, do_values ;

    /* ---------------------------------------------------------------------- */
    /* check inputs */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    ASSERT (n_col >= 0) ;
    nz = Ap ? Ap [n_col] : 0 ;
    DEBUG2 (("UMF_transpose:  nz "ID"\n", nz)) ;
    DEBUG2 (("n_row "ID"  & "ID"\n", n_row, &n_row)) ;
    DEBUG2 (("n_col "ID"  & "ID"\n", n_col, &n_col)) ;
    DEBUG2 (("Ap   & "ID" to & "ID"\n", Ap, Ap + (n_col+1) - 1)) ;
    DEBUG2 (("Ai   & "ID" to & "ID"\n", Ai, Ai + (nz) - 1)) ;
    if (Ax) DEBUG2 (("Ax   & "ID" to & "ID"\n", Ax, Ax + (nz) - 1)) ;
    if (P)  DEBUG2 (("P    & "ID" to & "ID"\n", P , P  + (n_row) - 1)) ;
    if (Q)  DEBUG2 (("Q    & "ID" to & "ID"\n", Q , Q  + (n_col) - 1)) ;
    DEBUG2 (("Rp   & "ID" to & "ID"\n", Rp, Rp + (n_row+1) - 1)) ;
    DEBUG2 (("Ri   & "ID" to & "ID"\n", Ri, Ri + (nz) - 1)) ;
    if (Rx) DEBUG2 (("Rx   & "ID" to & "ID"\n", Rx, Rx + (nz) - 1)) ;
    DEBUG2 (("W    & "ID" to & "ID"\n", W, W + MAX(n_row,n_col) - 1)) ;
#ifdef COMPLEX
    if (Az) DEBUG2 (("Az   & "ID" to & "ID"\n", Az, Az + (nz) - 1)) ;
    if (Rz) DEBUG2 (("Rz   & "ID" to & "ID"\n", Rz, Rz + (nz) - 1)) ;
    DEBUG2 (("do_conjugate "ID"  & "ID"\n", do_conjugate, &do_conjugate)) ;
#endif
#endif

    if (check)
    {
	/* UMFPACK_symbolic skips this check */
	/* UMFPACK_transpose always does this check */

	if (!Ai || !Ap || !Ri || !Rp || !W)
	{
	    return (UMFPACK_ERROR_argument_missing) ;
	}

	if (n_row <= 0 || n_col <= 0)		/* n_row,n_col must be > 0 */
	{
	    return (UMFPACK_ERROR_n_nonpositive) ;
	}

	nz = Ap [n_col] ;
	if (nz < 0)		/* nz must be >= 0 */
	{
	    return (UMFPACK_ERROR_nz_negative) ;
	}

	if (!UMF_is_permutation (P, W, n_row, n_row) ||
	    !UMF_is_permutation (Q, W, nq, nq))
	{
	    return (UMFPACK_ERROR_invalid_permutation) ;
	}

	if (Ap [0] != 0)
	{
	    return (UMFPACK_ERROR_Ap0_nonzero) ;
	}

	for (j = 0 ; j < n_col ; j++)
	{
	    if (Ap [j] > Ap [j+1])
	    {
		return (UMFPACK_ERROR_col_length_negative) ;
	    }
	}

	for (j = 0 ; j < n_col ; j++)
	{
	    ilast = -1 ;
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		i = Ai [p] ;
		if (i < 0 || i >= n_row)
		{
		    return (UMFPACK_ERROR_row_index_out_of_bounds) ;
		}
		if (i <= ilast)
		{
		    return (UMFPACK_ERROR_jumbled_matrix) ;
		}
		ilast = i ;
	    }
	}
    }

#ifndef NDEBUG
    DEBUG2 (("UMF_transpose, input matrix:\n")) ;
    UMF_dump_col_matrix (Ax,
#ifdef COMPLEX
	Az,
#endif
	Ai, Ap, n_row, n_col, nz) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* count the entries in each row of A */
    /* ---------------------------------------------------------------------- */

    /* use W as workspace for RowCount */

    for (i = 0 ; i < n_row ; i++)
    {
	W [i] = 0 ;
	Rp [i] = 0 ;
    }

    if (Q)
    {
        for (newj = 0 ; newj < nq ; newj++)
        {
	    j = Q [newj] ;
	    ASSERT (j >= 0 && j < n_col) ;
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		i = Ai [p] ;
		ASSERT (i >= 0 && i < n_row) ;
	        W [i]++ ;
	    }
        }
    }
    else
    {
        for (j = 0 ; j < n_col ; j++)
        {
	    for (p = Ap [j] ; p < Ap [j+1] ; p++)
	    {
		i = Ai [p] ;
		ASSERT (i >= 0 && i < n_row) ;
	        W [i]++ ;
	    }
        }
    }

    /* ---------------------------------------------------------------------- */
    /* compute the row pointers for R = A (P,Q) */
    /* ---------------------------------------------------------------------- */

    if (P)
    {
	Rp [0] = 0 ;
	for (k = 0 ; k < n_row ; k++)
	{
	    i = P [k] ;
	    ASSERT (i >= 0 && i < n_row) ;
	    Rp [k+1] = Rp [k] + W [i] ;
	}
	for (k = 0 ; k < n_row ; k++)
	{
	    i = P [k] ;
	    ASSERT (i >= 0 && i < n_row) ;
	    W [i] = Rp [k] ;
	}
    }
    else
    {
	Rp [0] = 0 ;
	for (i = 0 ; i < n_row ; i++)
	{
	    Rp [i+1] = Rp [i] + W [i] ;
	}
	for (i = 0 ; i < n_row ; i++)
	{
	    W [i] = Rp [i] ;
	}
    }
    ASSERT (Rp [n_row] <= Ap [n_col]) ;

    /* at this point, W holds the permuted row pointers */

    /* ---------------------------------------------------------------------- */
    /* construct the row form of B */
    /* ---------------------------------------------------------------------- */

    do_values = Ax && Rx ;
#ifdef COMPLEX
    do_values = do_values && Az && Rz ;
#endif

#ifdef COMPLEX
    if (do_conjugate && do_values)
    {
	if (Q)
	{

		/* R = A (P,Q)' */
		for (newj = 0 ; newj < nq ; newj++)
		{
		    j = Q [newj] ;
		    ASSERT (j >= 0 && j < n_col) ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = newj ;
			Rx [bp] = Ax [p] ;
			Rz [bp] = -Az [p] ;
		    }
		}

	}
	else
	{

		/* R = A (P,:)' */
		for (j = 0 ; j < n_col ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = j ;
			Rx [bp] = Ax [p] ;
			Rz [bp] = -Az [p] ;
		    }
		}

	}
    }
    else
#endif
    {
	if (Q)
	{
	    if (do_values)
	    {

		/* R = A (P,Q).' */
		for (newj = 0 ; newj < nq ; newj++)
		{
		    j = Q [newj] ;
		    ASSERT (j >= 0 && j < n_col) ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = newj ;
			Rx [bp] = Ax [p] ;
#ifdef COMPLEX
			Rz [bp] = Az [p] ;
#endif
		    }
		}

	    }
	    else
	    {

		/* R = pattern of A (P,Q).' */
		for (newj = 0 ; newj < nq ; newj++)
		{
		    j = Q [newj] ;
		    ASSERT (j >= 0 && j < n_col) ;
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			Ri [W [Ai [p]]++] = newj ;
		    }
		}

	    }
	}
	else
	{
	    if (do_values)
	    {

		/* R = A (P,:).' */
		for (j = 0 ; j < n_col ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			bp = W [Ai [p]]++ ;
			Ri [bp] = j ;
			Rx [bp] = Ax [p] ;
#ifdef COMPLEX
			Rz [bp] = Az [p] ;
#endif
		    }
		}

	    }
	    else
	    {

		/* R = pattern of A (P,:).' */
		for (j = 0 ; j < n_col ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			Ri [W [Ai [p]]++] = j ;
		    }
		}

	    }
	}

    }

#ifndef NDEBUG
    for (k = 0 ; k < n_row ; k++)
    {
	if (P)
	{
	    i = P [k] ;
	}
	else
	{
	    i = k ;
	}
	DEBUG3 ((ID":  W[i] "ID" Rp[k+1] "ID"\n", i, W [i], Rp [k+1])) ;
	ASSERT (W [i] == Rp [k+1]) ;
    }
    DEBUG2 (("UMF_transpose, output matrix:\n")) ;
    UMF_dump_col_matrix (Rx,
#ifdef COMPLEX
	Rz,
#endif
	Ri, Rp, n_col, n_row, Rp [n_row]) ;
#endif

    return (UMFPACK_OK) ;
}


```

sparse-matrix/umfpack/umf_kernel_init_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_kernel_init_usage
(
    const Int Ap [ ],
    const Int Row_degree [ ],
    Int n_row,
    Int n_col,
    double *dusage
) ;


```

sparse-matrix/umfpack/umf_internal.h:
```
/* ========================================================================== */
/* === umf_internal.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                       */
/* -------------------------------------------------------------------------- */

/*
    This file is for internal use in UMFPACK itself, and should not be included
    in user code.  Use umfpack.h instead.  User-accessible file names and
    routine names all start with the letters "umfpack_".  Non-user-accessible
    file names and routine names all start with "umf_".
*/

/* -------------------------------------------------------------------------- */
/* ANSI standard include files */
/* -------------------------------------------------------------------------- */

/* from stdlib.h:  malloc, free, realloc */
/* and when in debug mode:  rand, RAND_MAX */
#include <stdlib.h>

/* from limits.h:  INT_MAX and LONG_MAX */
#include <limits.h>

/* from float.h:  DBL_EPSILON */
#include <float.h>

/* from stdio.h:  printf, NULL */
/* and when in debug mode:  fopen, fscanf */
#include <stdio.h>

/* from string.h: strcmp */
#include <string.h>

/* from math.h:  sqrt, ceil */
#include <math.h>

/* when debugging, assert.h and the assert macro are used (see umf_dump.h) */

/* -------------------------------------------------------------------------- */
/* MATLAB include files */
/* -------------------------------------------------------------------------- */

#ifdef MATHWORKS
#include "util.h"
#endif

#ifdef MATLAB_MEX_FILE
#include "matrix.h"
#include "mex.h"
#endif

/* -------------------------------------------------------------------------- */
/* Architecture */
/* -------------------------------------------------------------------------- */

#if defined (__sun) || defined (MSOL2) || defined (ARCH_SOL2)
#define UMF_SOL2
#define UMFPACK_ARCHITECTURE "Sun Solaris"

#elif defined (__sgi) || defined (MSGI) || defined (ARCH_SGI)
#define UMF_SGI
#define UMFPACK_ARCHITECTURE "SGI Irix"

#elif defined (__linux) || defined (MGLNX86) || defined (ARCH_GLNX86)
#define UMF_LINUX
#define UMFPACK_ARCHITECTURE "Linux"

#elif defined (_AIX) || defined (MIBM_RS) || defined (ARCH_IBM_RS)
#define UMF_AIX
#define UMFPACK_ARCHITECTURE "IBM AIX"

#elif defined (__alpha) || defined (MALPHA) || defined (ARCH_ALPHA)
#define UMF_ALPHA
#define UMFPACK_ARCHITECTURE "Compaq Alpha"

#elif defined (__WIN32) || defined (_WIN32) || defined (_win32) || defined (__win32) || defined (WIN32)
#define UMF_WINDOWS
#define UMFPACK_ARCHITECTURE "Microsoft Windows"

#elif defined (__hppa) || defined (__hpux) || defined (MHPUX) || defined (ARCH_HPUX)
#define UMF_HP
#define UMFPACK_ARCHITECTURE "HP Unix"

#elif defined (__hp700) || defined (MHP700) || defined (ARCH_HP700)
#define UMF_HP
#define UMFPACK_ARCHITECTURE "HP 700 Unix"

#else
/* If the architecture is unknown, and you call the BLAS, you may need to */
/* define BLAS_BY_VALUE, BLAS_NO_UNDERSCORE, and/or BLAS_CHAR_ARG yourself. */
#define UMFPACK_ARCHITECTURE "unknown"
#endif


/* -------------------------------------------------------------------------- */
/* basic definitions */
/* -------------------------------------------------------------------------- */

#ifdef MAX
#undef MAX
#endif
#ifdef MIN
#undef MIN
#endif

/* for integer MAX/MIN, or for doubles when we don't care how NaN's behave: */
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))

#define ONES_COMPLEMENT(r) (-(r)-1)

/* largest allowable double precision epsilon */
#define MAX_EPSILON 1e-8

/* logical expression of p implies q: */
#define IMPLIES(p,q) (!(p) || (q))

/* Note that the IBM RS 6000 xlc predefines TRUE and FALSE in <types.h>. */
/* The Compaq Alpha also predefines TRUE and FALSE. */
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif

#define TRUE (1)
#define FALSE (0)
#define PRIVATE static
#define GLOBAL
#define EMPTY (-1)

/* Note that Linux's gcc 2.96 defines NULL as ((void *) 0), but other */
/* compilers (even gcc 2.95.2 on Solaris) define NULL as 0 or (0). */
#ifdef NULL
#undef NULL
#endif

#define NULL 0

/* -------------------------------------------------------------------------- */
/* Real/complex and int/long definitions, double relops */
/* -------------------------------------------------------------------------- */

#include "umf_version.h"

/* -------------------------------------------------------------------------- */
/* Compile-time configurations */
/* -------------------------------------------------------------------------- */

#include "umf_config.h"

/* -------------------------------------------------------------------------- */
/* umfpack include file */
/* -------------------------------------------------------------------------- */

#include "umfpack.h"

/* -------------------------------------------------------------------------- */
/* for contents of Info.  This must correlate with umfpack.h */
/* -------------------------------------------------------------------------- */

#define ESTIMATE (UMFPACK_NUMERIC_SIZE_ESTIMATE - UMFPACK_NUMERIC_SIZE)
#define ACTUAL 0

/* -------------------------------------------------------------------------- */
/* for clearing the external degree counters */
/* -------------------------------------------------------------------------- */

#define MAX_MARK(n) Int_MAX - (2*(n)+1)

/* -------------------------------------------------------------------------- */
/* dense row/column macro */
/* -------------------------------------------------------------------------- */

/* In order for a row or column to be treated as "dense", it must have more */
/* entries than the value returned by this macro.  n is the dimension of the */
/* matrix, and alpha is the dense row/column control parameter. */

/* Note: this is not defined in alpha is NaN or Inf: */
#define UMFPACK_DENSE_DEGREE_THRESHOLD(alpha,n) \
    ((Int) MAX (16.0, (alpha) * 16.0 * sqrt ((double) (n))))

/* -------------------------------------------------------------------------- */
/* PRINTF */
/* -------------------------------------------------------------------------- */

#define PRINTFk(k,params) { if (prl >= (k)) { PRINTF (params) ; } }
#define PRINTF1(params) PRINTFk (1, params)
#define PRINTF2(params) PRINTFk (2, params)
#define PRINTF3(params) PRINTFk (3, params)
#define PRINTF4(params) PRINTFk (4, params)
#define PRINTF5(params) PRINTFk (5, params)
#define PRINTF6(params) PRINTFk (6, params)

/* -------------------------------------------------------------------------- */
/* Fixed control parameters */
/* -------------------------------------------------------------------------- */

/* maximum number of columns to consider at one time, in a single front */
#define MAX_CANDIDATES 128

/* reduce Numeric->Memory request by this ratio, if allocation fails */
#define UMF_REALLOC_REDUCTION (0.95)

/* increase Numeric->Memory request by this ratio, if we need more */
#define UMF_REALLOC_INCREASE (1.2)

/* -------------------------------------------------------------------------- */
/* Memory space definitions */
/* -------------------------------------------------------------------------- */

/* for memory alignment - assume double has worst case alignment */
typedef double Align ;

/* get number of bytes required to hold n items of a type: */
/* note that this will not overflow, because sizeof (type) is always */
/* greater than or equal to sizeof (Int) >= 2 */
#define BYTES(type,n) (sizeof (type) * (n))

/* ceiling of (b/u).  Assumes b >= 0 and u > 0 */
#define CEILING(b,u) (((b) + (u) - 1) / (u))

/* get number of Units required to hold n items of a type: */
#define UNITS(type,n) (CEILING (BYTES (type, n), sizeof (Unit)))

/* same as DUNITS, but use double instead of int to avoid overflow */
#define DUNITS(type,n) (ceil (BYTES (type, (double) n) / sizeof (Unit)))

union Unit_union
{	/* memory is allocated in multiples of Unit */
    struct
    {
	Int
	    size,	/* size, in Units, of the block, excl. header block */
			/* size >= 0: block is in use */
			/* size < 0: block is free, of |size| Units */
	    prevsize ;	/* size, in Units, of preceding block in S->Memory */
			/* during garbage_collection, prevsize is set to -e-1 */
			/* for element e, or positive (and thus a free block) */
			/* otherwise */
    } header ;		/* block header */
    Align  xxxxxx ;	/* force alignment of blocks (xxxxxx is never used) */
} ;

typedef union Unit_union Unit ;

/* get the size of an allocated block */
#define GET_BLOCK_SIZE(p) (((p)-1)->header.size)

/* -------------------------------------------------------------------------- */
/* Numeric */
/* -------------------------------------------------------------------------- */

/*
    NUMERIC_VALID and SYMBOLIC_VALID:
    The different values of SYBOLIC_VALID and NUMERIC_VALID are chosen as a
    first defense against corrupted *Symbolic or *Numeric pointers passed to an
    UMFPACK routine.  They also ensure that the objects are used only by the
    same version that created them (umfpack_di_*, umfpack_dl_*, umfpack_zi_*,
    or umfpack_zl_*).  The values have also been changed since prior releases of
    the code to ensure that all routines that operate on the objects are of the 
    same release.  The values themselves are purely arbitrary.  The are less
    than the ANSI C required minimums of INT_MAX and LONG_MAX, respectively.
*/

#ifdef DINT
#define NUMERIC_VALID  15674
#define SYMBOLIC_VALID 41234
#endif
#ifdef DLONG
#define NUMERIC_VALID  456789120
#define SYMBOLIC_VALID 432192913
#endif
#ifdef ZINT
#define NUMERIC_VALID  17654
#define SYMBOLIC_VALID 40123
#endif
#ifdef ZLONG
#define NUMERIC_VALID  123987654
#define SYMBOLIC_VALID 119291234
#endif

typedef struct	/* NumericType */
{
    double
	flops,		/* "true" flop count */
	relpt,		/* relative pivot tolerance used */
	relax,		/* relaxed amalgamation parameter */
	relax2,		/* relax2 amalgamation parameter */
	relax3,		/* relax3 amalgamation parameter */
	alloc_init ;	/* initial allocation of Numeric->memory */

    Int valid ;		/* set to NUMERIC_VALID, for validity check */

    /* Memory space for A and LU factors */
    Unit
	*Memory ;	/* working memory for A and LU factors */
    Int
	ihead,		/* pointer to tail of LU factors, in Numeric->Memory */
	itail,		/* pointer to top of elements & tuples,  */
			/* in Numeric->Memory */
	ibig,		/* pointer to largest free block seen in tail */
	size ;		/* size of Memory, in Units */

    Int
	*Rperm,		/* pointer to row perm array, size: n+1 */
			/* after UMF_kernel:  Rperm [new] = old */
			/* during UMF_kernel: Rperm [old] = new */
	*Cperm,		/* pointer to col perm array, size: n+1 */
			/* after UMF_kernel:  Cperm [new] = old */
			/* during UMF_kernel: Cperm [old] = new */

	*Upos,		/* see UMFPACK_get_numeric for a description */
	*Lpos,
	*Lip,
	*Lilen,
	*Uip,
	*Uilen,
	*Upattern ;	/* pattern of last row of U (if singular) */

    Int
	ulen,		/* length of Upattern */
	npiv,		/* number of structural pivots found (sprank approx) */
	nnzpiv ;	/* number of numerical (nonzero) pivots found */

    Entry
	*D ;		/* D [i] is the diagonal entry of U */

    double
	min_udiag,	/* smallest abs value on diagonal of D */
	max_udiag,	/* smallest abs value on diagonal of D */
	rcond ;		/* min (D) / max (D) */

    Int
	n_row, n_col ;	/* A is n_row-by-n_row */

    /* for information only: */
    Int
	tail_usage,	/* amount of memory allocated in tail */
			/* head_usage is Numeric->ihead */
	init_usage,	/* memory usage just after UMF_kernel_init */
	max_usage,	/* peak memory usage (excludes internal and external */
			/* fragmentation in the tail) */
	ngarbage,	/* number of garbage collections performed */
	nrealloc,	/* number of reallocations performed */
	ncostly,	/* number of costly reallocations performed */
	isize,		/* size of integer pattern of L and U */
	nLentries,	/* number of entries in L, excluding diagonal */
	nUentries,	/* number of entries in U, including diagonal */
			/* Some entries may be numerically zero. */
	lnz,		/* number of nonzero entries in L, excl. diagonal */
	unz,		/* number of nonzero entries in U, excl. diagonal */
	maxfrsize ;	/* largest actual front size */

} NumericType ;



/* -------------------------------------------------------------------------- */
/* Element tuples for connecting elements together in a matrix */
/* -------------------------------------------------------------------------- */

typedef struct	/* Tuple */
{
    /* The (e,f) tuples for the element lists */
    Int
	e,		/* element */
	f ;		/* contribution to the row/col appears at this offset */

} Tuple ;

/* Col_degree is aliased with Cperm, and Row_degree with Rperm */
#define NON_PIVOTAL_COL(col) (Col_degree [col] >= 0)
#define NON_PIVOTAL_ROW(row) (Row_degree [row] >= 0)

/* -------------------------------------------------------------------------- */
/* An element */
/* -------------------------------------------------------------------------- */

typedef struct	/* Element */
{
    Int

	cdeg,		/* external column degree + cdeg0 offset */
	rdeg,		/* external row degree    + rdeg0 offset */
	nrowsleft,	/* number of rows remaining */
	ncolsleft,	/* number of columns remaining */
	nrows,		/* number of rows */
	ncols,		/* number of columns */
	next ;		/* for list link of sons, used during assembly only */

    /* followed in memory by:
    Int
	col [0..ncols-1],	column indices of this element
	row [0..nrows-1] ;	row indices of this element
    Entry			(suitably aligned, see macro below)
	C [0...nrows-1, 0...ncols-1] ;
	size of C is nrows*ncols Entry's
    */

} Element ;

/* macros for computing pointers to row/col indices, and contribution block: */

#define GET_ELEMENT_SIZE(nr,nc) \
(UNITS (Element, 1) + UNITS (Int, (nc) + (nr)) + UNITS (Entry, (nc) * (nr)))

#define DGET_ELEMENT_SIZE(nr,nc) \
(DUNITS (Element, 1) + DUNITS (Int, (nc) + (nr)) + DUNITS (Entry, (nc) * (nr)))

#define GET_ELEMENT_COLS(ep,p,Cols) { \
    ASSERT (p) ; \
    ASSERT (p >= Numeric->Memory + Numeric->itail) ; \
    ASSERT (p <= Numeric->Memory + Numeric->size) ; \
    ep = (Element *) p ; \
    p += UNITS (Element, 1) ; \
    Cols = (Int *) p ; \
}

#define GET_ELEMENT_PATTERN(ep,p,Cols,Rows,ncm) { \
    GET_ELEMENT_COLS (ep, p, Cols) ; \
    ncm = ep->ncols ; \
    Rows = Cols + ncm ; \
}

#define GET_ELEMENT(ep,p,Cols,Rows,ncm,nrm,C) { \
    GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncm) ; \
    nrm = ep->nrows ; \
    p += UNITS (Int, ncm + nrm) ; \
    C = (Entry *) p ; \
}

/* -------------------------------------------------------------------------- */
/* Work data structure */
/* -------------------------------------------------------------------------- */

/*
    This data structure holds items needed only during factorization.
    All of this is freed when UMFPACK_numeric completes.  Note that some of
    it is stored in the tail end of Numeric->S (namely, the Tuples and the
    Elements).
*/

typedef struct	/* WorkType */
{

    /* ---------------------------------------------------------------------- */
    /* information about each row and col of A */
    /* ---------------------------------------------------------------------- */

    /*
	Row_tuples:	pointer to tuple list (alias with Numeric->Uip)
	Row_tlen:	number of tuples (alias with Numeric->Uilen)
	Col_tuples:	pointer to tuple list (alias with Numeric->Lip)
	Col_tlen:	number of tuples (alias with Numeric->Lilen)
	Row_degree:	degree of the row or column (alias Numeric->Rperm)
	Col_degree:	degree of the row or column (alias Numeric->Cperm)

	The Row_degree and Col_degree are MATLAB-style colmmd approximations,
	are equal to the sum of the sizes of the elements (contribution blocks)
	in each row and column.  They are maintained when elements are created
	and assembled.  They are used only during the pivot row and column
	search.  They are not needed to represent the pattern of the remaining
	matrix.
    */

    /* ---------------------------------------------------------------------- */
    /* information about each element */
    /* ---------------------------------------------------------------------- */

    Int	*E ;		/* E [0 .. n_col + n_inner] element "pointers" */
			/* (offsets in Numeric->Memory) */

    /* ---------------------------------------------------------------------- */
    /* generic workspace */
    /* ---------------------------------------------------------------------- */

    Int			/* Sizes:  nn = MAX (n_row, n_col) */
	*Wp,		/* nn+1 */
	*Wm,		/* maxnrows+1 */
	*Wio,		/* maxncols+1 */
	*Woi,		/* maxncols+1 */
	*Woo,		/* MAX (maxnrows,maxncols)+1 */
	*Wcol,		/* pointer to Wm */
	*Wrow ;		/* pointer to Fcols, Wio, or Woi */

    /* ---------------------------------------------------------------------- */
    Int
	*Lpattern,	/* pattern of column of L, for one Lchain */
	*Upattern,	/* pattern of row of U, for one Uchain */
	ulen, llen ;	/* length of Upattern and Lpattern */

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

    Int
	n_row, n_col,	/* matrix is n_row-by-n_col */
	nz,		/* nonzeros in the elements for this matrix */
	npiv,		/* number of pivot rows and columns so far */
	Wpflag,
	nel,		/* elements in use are in the range 1..nel */
	nelorig,	/* elements 1..nelorig are original elements */
	prior_element,
	overlap,
	rdeg0, cdeg0,
	rrdeg, ccdeg,
	Candidates [MAX_CANDIDATES],	 /* current candidate pivot columns */
	ncand,		/* number of candidates (some not in Candidates[ ]) */
	nextcand,	/* next candidate to place in Candidate search set */
	pivrow,		/* current pivot row */
	pivcol,		/* current pivot column */
	scan_pivrow, scan_pivcol,
	do_extend,	/* true if the next pivot extends the current front */
	do_update,	/* true if update should be applied */
	do_scan2row,
	do_scan2col,
	pivot_case,
	frontid,	/* id of current frontal matrix */
	nfr,		/* number of frontal matrices */
	maxfrsize,
	maxnrows,	/* largest number of rows in any front */
	maxncols ;	/* largest number of columns in any front */

    /* ---------------------------------------------------------------------- */
    /* For row-merge tree */
    /* ---------------------------------------------------------------------- */

    Int
	*Front_new1strow ;

    /* ---------------------------------------------------------------------- */
    /* current frontal matrix */
    /* ---------------------------------------------------------------------- */

    Entry
	*Fx ;		/* Fx [0..fnrows_max-1, 0..fncols_max-1] */
			/* working array */
    Int
	*Frows,		/* Frows [0.. ]: row indices of F */

	*Fcols,		/* Fcols [0.. ]: column indices of F */

	*Frpos,		/* position of row indices in F, or -1 if not present */
			/* if Frows[i] == row, then Frpos[row] == i */

	*Fcpos,		/* position of col indices in F, or -1 if not present */
			/* if Fcols[j] == col, then */
			/* Fcpos[col] == j*Front->fnrows_max */

	fnrows,		/* number of rows in contribution block in F */
	fncols,		/* number of columns in contribution block in F */
	fnrows_max,	/* column-dimension (max # of rows) of F */
	fncols_max,	/* row-dimension (max # of columns) of F */
	fnpiv,		/* number of pivots in F */
	fcolpos,	/* offset of candidate pivot column in Fcols [...] */
	frowpos,	/* offset of candidate pivot row in Frows [...] */
	fnzeros,	/* number of explicit zero entries in LU block */
	fscan_row,
	fscan_col,
	pivrow_in_front,	/* true if current pivot row in Frows */
	pivcol_in_front ;	/* true if current pivot column in Fcols */

    /* ---------------------------------------------------------------------- */
    /* Current frontal matrix                                                 */
    /* ---------------------------------------------------------------------- */
    /*  Fx points to current frontal matrix (contribution block and LU        */
    /*  factors).  For example, if Front->fnrows = 4, Front->fncols = 6, and  */
    /*  Front->fnpiv = 3, then "x" is a term in the contribution block, "l"   */
    /*  in L1, "u" in U1, "L" in L2, "U" in U2, and "." is unused.  Fx [0] is */
    /*  "X". The first 3 pivot values (diagonal entries in U1) are 1,2, and 3.*/
    /*  For this frontal matrix, Front->fncols_max = 12 (the number of        */
    /*  columns), and Front->fnrows_max = 8 (the number of rows).  The        */
    /*  frontal matrix is (Front->fnrows_max)-by-(Front->fncols_max)          */
    /*									      */
    /*                             |----------- col 1 of L1 and L2, etc.      */
    /*                             V                                          */
    /*       X x x x x x . . . L L L                                          */
    /*       x x x x x x . . . L L L                                          */
    /*       x x x x x x . . . L L L                                          */
    /*       x x x x x x . . . L L L                                          */
    /*       . . . . . . . . . . . .                                          */
    /*       U U U U U U . . . 3 l l         <- row 3 of U1 and U2            */
    /*       U U U U U U . . . u 2 l         <- row 2 of U1 and U2            */
    /*       U U U U U U . . . u u 1         <- row 1 of U1 and U2            */
    /*									      */
    /* ---------------------------------------------------------------------- */

} WorkType ;


/* -------------------------------------------------------------------------- */
/* Symbolic */
/* -------------------------------------------------------------------------- */

/*
    This is is constructed by UMFPACK_symbolic, and is needed by UMFPACK_numeric
    to factor the matrix.
*/

typedef struct	/* SymbolicType */
{

    double
	drow,		/* dense row control parameter used */
	dcol,		/* dense column control parameter used */
	num_mem_usage_est,	/* estimated max Numeric->Memory size */
	num_mem_size_est,	/* estimated final Numeric->Memory size */
	peak_sym_usage ;	/* peak Symbolic and SymbolicWork usage */

    Int valid,		/* set to SYMBOLIC_VALID, for validity check */
	nchains,
	*Chain_start,
	*Chain_maxrows,
	*Chain_maxcols,
	maxfrsize,
	maxnrows,		/* largest number of rows in any front */
	maxncols,		/* largest number of columns in any front */
	*Front_npivcol,		/* Front_npivcol [j] = size of jth supercolumn*/
	*Front_1strow,		/* first row index in front j */
	*Front_leftmostdesc,	/* leftmost desc of front j */
	*Front_parent,		/* super-column elimination tree */
	*Cperm_init,		/* initial column ordering */
	*Rperm_init,		/* initial row ordering */
	nfr,
	n_row, n_col,		/* matrix A is n_row-by-n_col */
	nz,			/* nz of original matrix */
	nb,			/* block size for BLAS 3 */
	num_mem_init_usage ;	/* min Numeric->Memory for UMF_kernel_init */

} SymbolicType ;


/* -------------------------------------------------------------------------- */
/* for debugging only: */
/* -------------------------------------------------------------------------- */

#include "umf_dump.h"


```

sparse-matrix/umfpack/umf_usolve.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_usolve
(
    NumericType *Numeric,
    Entry X [ ],
    Int Pattern [ ]
) ;


```

sparse-matrix/umfpack/make.code.deps:
```
# extra Make rules for Cactus compilation of things in this directory
# $Header$

#
# See the header comments in  make.code.defn  for general comments.
#
CPPFLAGS += -DNBLAS -DNDEBUG -DINT

```

sparse-matrix/umfpack/umfpack.h:
```
/* ========================================================================== */
/* === umfpack.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                       */
/* -------------------------------------------------------------------------- */

/*
    This is the umfpack.h include file, and should be included in all user code
    that uses UMFPACK.  Do not include any of the umf_* header files in user
    code.  All routines in UMFPACK starting with "umfpack_" are user-callable.
    All other routines are prefixed "umfXY_", (where X is d or z, and Y is
    i or l) and are not user-callable.
*/

#ifndef UMFPACK_H
#define UMFPACK_H

/* -------------------------------------------------------------------------- */
/* size of Info and Control arrays */
/* -------------------------------------------------------------------------- */

#define UMFPACK_INFO 90
#define UMFPACK_CONTROL 20

/* -------------------------------------------------------------------------- */
/* User-callable routines */
/* -------------------------------------------------------------------------- */

/* Primary routines: */
#include "umfpack_symbolic.h"
#include "umfpack_numeric.h"
#include "umfpack_solve.h"
#include "umfpack_free_symbolic.h"
#include "umfpack_free_numeric.h"

/* Alternative routines: */
#include "umfpack_defaults.h"
#include "umfpack_qsymbolic.h"
#include "umfpack_wsolve.h"

/* Matrix manipulation routines: */
#include "umfpack_triplet_to_col.h"
#include "umfpack_col_to_triplet.h"
#include "umfpack_transpose.h"

/* Getting the contents of the Symbolic and Numeric opaque objects: */
#include "umfpack_get_lunz.h"
#include "umfpack_get_numeric.h"
#include "umfpack_get_symbolic.h"

/* Reporting routines (the above 14 routines print nothing): */
#include "umfpack_report_status.h"
#include "umfpack_report_info.h"
#include "umfpack_report_control.h"
#include "umfpack_report_matrix.h"
#include "umfpack_report_triplet.h"
#include "umfpack_report_vector.h"
#include "umfpack_report_symbolic.h"
#include "umfpack_report_numeric.h"
#include "umfpack_report_perm.h"

/* Utility routines: */
#include "umfpack_timer.h"


/* -------------------------------------------------------------------------- */
/* Version, copyright, and license */
/* -------------------------------------------------------------------------- */

#define UMFPACK_VERSION "UMFPACK V4.0 (Apr 11, 2002)"

#define UMFPACK_COPYRIGHT \
"UMFPACK:  Copyright (c) 2002 by Timothy A. Davis.  All Rights Reserved.\n"

#define UMFPACK_LICENSE_PART1 \
"\nUMFPACK License:\n" \
"\n" \
"   Your use or distribution of UMFPACK or any modified version of\n" \
"   UMFPACK implies that you agree to this License.\n" \
"\n" \
"   THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY\n" \
"   EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.\n"
#define UMFPACK_LICENSE_PART2 \
"\n" \
"   Permission is hereby granted to use or copy this program, provided\n" \
"   that the Copyright, this License, and the Availability of the original\n" \
"   version is retained on all copies.  User documentation of any code that\n" \
"   uses UMFPACK or any modified version of UMFPACK code must cite the\n" \
"   Copyright, this License, the Availability note, and \"Used by permission.\"\n"
#define UMFPACK_LICENSE_PART3 \
"   Permission to modify the code and to distribute modified code is granted,\n" \
"   provided the Copyright, this License, and the Availability note are\n" \
"   retained, and a notice that the code was modified is included.  This\n" \
"   software was developed with support from the National Science Foundation,\n" \
"   and is provided to you free of charge.\n" \
"\n" \
"Availability: http://www.cise.ufl.edu/research/sparse/umfpack\n" \
"\n"

/* -------------------------------------------------------------------------- */
/* contents of Info */
/* -------------------------------------------------------------------------- */

/* Note that umfpack_report.m must coincide with these definitions. */

/* returned by all routines that use Info: */
#define UMFPACK_STATUS 0
#define UMFPACK_NROW 1
#define UMFPACK_NCOL 16
#define UMFPACK_NZ 2

/* computed in UMFPACK_*symbolic and UMFPACK_numeric: */
#define UMFPACK_SIZE_OF_UNIT 3

/* computed in UMFPACK_*symbolic: */
#define UMFPACK_SIZE_OF_INT 4
#define UMFPACK_SIZE_OF_LONG 5
#define UMFPACK_SIZE_OF_POINTER 6
#define UMFPACK_SIZE_OF_ENTRY 7
#define UMFPACK_NDENSE_ROW 8
#define UMFPACK_NEMPTY_ROW 9
#define UMFPACK_NDENSE_COL 10
#define UMFPACK_NEMPTY_COL 11
#define UMFPACK_SYMBOLIC_DEFRAG 12
#define UMFPACK_SYMBOLIC_PEAK_MEMORY 13
#define UMFPACK_SYMBOLIC_SIZE 14
#define UMFPACK_SYMBOLIC_TIME 15

/* Info [17..19] unused */

/* estimates computed in UMFPACK_*symbolic: */
#define UMFPACK_NUMERIC_SIZE_ESTIMATE 20
#define UMFPACK_PEAK_MEMORY_ESTIMATE 21
#define UMFPACK_FLOPS_ESTIMATE 22
#define UMFPACK_LNZ_ESTIMATE 23
#define UMFPACK_UNZ_ESTIMATE 24
#define UMFPACK_VARIABLE_INIT_ESTIMATE 25
#define UMFPACK_VARIABLE_PEAK_ESTIMATE 26
#define UMFPACK_VARIABLE_FINAL_ESTIMATE 27
#define UMFPACK_MAX_FRONT_SIZE_ESTIMATE 28

/* Info [29..39] unused */

/* exact values, (estimates shown above) computed in UMFPACK_numeric: */
#define UMFPACK_NUMERIC_SIZE 40
#define UMFPACK_PEAK_MEMORY 41
#define UMFPACK_FLOPS 42
#define UMFPACK_LNZ 43
#define UMFPACK_UNZ 44
#define UMFPACK_VARIABLE_INIT 45
#define UMFPACK_VARIABLE_PEAK 46
#define UMFPACK_VARIABLE_FINAL 47
#define UMFPACK_MAX_FRONT_SIZE 48

/* Info [49..59] unused */

/* computed in UMFPACK_numeric: */
#define UMFPACK_NUMERIC_DEFRAG 60
#define UMFPACK_NUMERIC_REALLOC 61
#define UMFPACK_NUMERIC_COSTLY_REALLOC 62
#define UMFPACK_COMPRESSED_PATTERN 63
#define UMFPACK_LU_ENTRIES 64
#define UMFPACK_NUMERIC_TIME 65
#define UMFPACK_UDIAG_NZ 66
#define UMFPACK_RCOND 67

/* Info [68..79] unused */

/* computed in UMFPACK_solve: */
#define UMFPACK_IR_TAKEN 80
#define UMFPACK_IR_ATTEMPTED 81
#define UMFPACK_OMEGA1 82
#define UMFPACK_OMEGA2 83
#define UMFPACK_SOLVE_FLOPS 84
#define UMFPACK_SOLVE_TIME 85

/* Info [86..89] unused */

/* Unused parts of Info may be used in future versions of UMFPACK. */


/* -------------------------------------------------------------------------- */
/* contents of Control */
/* -------------------------------------------------------------------------- */

/* used in all UMFPACK_report_* routines: */
#define UMFPACK_PRL 0

/* used in UMFPACK_*symbolic only: */
#define UMFPACK_DENSE_ROW 1
#define UMFPACK_DENSE_COL 2

/* used in UMFPACK_numeric only: */
#define UMFPACK_PIVOT_TOLERANCE 3
#define UMFPACK_BLOCK_SIZE 4
#define UMFPACK_RELAXED_AMALGAMATION 5
#define UMFPACK_ALLOC_INIT 6
/* #define UMFPACK_PIVOT_OPTION 12: obsolete */
#define UMFPACK_RELAXED2_AMALGAMATION 13
#define UMFPACK_RELAXED3_AMALGAMATION 14

/* used in UMFPACK_*solve only: */
#define UMFPACK_IRSTEP 7

/* compile-time settings - Control [8..11] cannot be changed at run time: */
#define UMFPACK_COMPILED_WITH_BLAS 8
#define UMFPACK_COMPILED_FOR_MATLAB 9
#define UMFPACK_COMPILED_WITH_GETRUSAGE 10
#define UMFPACK_COMPILED_IN_DEBUG_MODE 11

/* Control [12, 15...19] unused */

/* Unused parts of Control may be used in future versions of UMFPACK. */


/* -------------------------------------------------------------------------- */
/* default values of Control [0..7,13..14]: */
/* -------------------------------------------------------------------------- */

/* Note that the default block sized changed for Version 3.1 and following. */

#define UMFPACK_DEFAULT_PRL 1
#define UMFPACK_DEFAULT_DENSE_ROW 0.2
#define UMFPACK_DEFAULT_DENSE_COL 0.2
#define UMFPACK_DEFAULT_PIVOT_TOLERANCE 0.1
#define UMFPACK_DEFAULT_BLOCK_SIZE 24
#define UMFPACK_DEFAULT_RELAXED_AMALGAMATION 0.25
#define UMFPACK_DEFAULT_RELAXED2_AMALGAMATION 0.1
#define UMFPACK_DEFAULT_RELAXED3_AMALGAMATION 0.125
#define UMFPACK_DEFAULT_ALLOC_INIT 0.7
#define UMFPACK_DEFAULT_IRSTEP 2
/* #define UMFPACK_DEFAULT_PIVOT_OPTION 0: obsolete */

/* default values of Control [0..7,13..14] may change in future versions */
/* of UMFPACK. */

/* -------------------------------------------------------------------------- */
/* status codes */
/* -------------------------------------------------------------------------- */

#define UMFPACK_OK (0)

/* status > 0 means a warning, but the method was successful anyway. */
/* A Symbolic or Numeric object was still created. */
#define UMFPACK_WARNING_singular_matrix (1)

/* status < 0 means an error, and the method was not successful. */
/* No Symbolic of Numeric object was created. */
#define UMFPACK_ERROR_out_of_memory (-1)
#define UMFPACK_ERROR_invalid_Numeric_object (-3)
#define UMFPACK_ERROR_invalid_Symbolic_object (-4)
#define UMFPACK_ERROR_argument_missing (-5)
#define UMFPACK_ERROR_n_nonpositive (-6)
#define UMFPACK_ERROR_nz_negative (-7)
#define UMFPACK_ERROR_jumbled_matrix (-8)
#define UMFPACK_ERROR_Ap0_nonzero (-9)
#define UMFPACK_ERROR_row_index_out_of_bounds (-10)
#define UMFPACK_ERROR_different_pattern (-11)
#define UMFPACK_ERROR_col_length_negative (-12)
#define UMFPACK_ERROR_invalid_system (-13)
#define UMFPACK_ERROR_invalid_triplet (-14)
#define UMFPACK_ERROR_invalid_permutation (-15)
#define UMFPACK_ERROR_problem_too_large (-16)
#define UMFPACK_ERROR_internal_error (-911)

/* -------------------------------------------------------------------------- */
/* solve codes */
/* -------------------------------------------------------------------------- */

/* Solve the system ( )x=b, where ( ) is defined below.  "t" refers to the */
/* linear algebraic transpose (complex conjugate if A is complex), or the (') */
/* operator in MATLAB.  "at" refers to the array transpose, or the (.') */
/* operator in MATLAB. */

#define UMFPACK_A	(0)	/* Ax=b		*/
#define UMFPACK_At	(1)	/* A'x=b	*/
#define UMFPACK_Aat	(2)	/* A.'x=b	*/

#define UMFPACK_Pt_L	(3)	/* P'Lx=b	*/
#define UMFPACK_L	(4)	/* Lx=b		*/
#define UMFPACK_Lt_P	(5)	/* L'Px=b	*/
#define UMFPACK_Lat_P	(6)	/* L.'Px=b	*/
#define UMFPACK_Lt	(7)	/* L'x=b	*/
#define UMFPACK_Lat	(8)	/* L.'x=b	*/

#define UMFPACK_U_Qt	(9)	/* UQ'x=b	*/
#define UMFPACK_U	(10)	/* Ux=b		*/
#define UMFPACK_Q_Ut	(11)	/* QU'x=b	*/
#define UMFPACK_Q_Uat	(12)	/* QU.'x=b	*/
#define UMFPACK_Ut	(13)	/* U'x=b	*/
#define UMFPACK_Uat	(14)	/* U.'x=b	*/

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

/* Integer constants are used for status and solve codes instead of enum */
/* to make it easier for a Fortran code to call UMFPACK. */

#endif /* UMFPACK_H */

```

sparse-matrix/umfpack/umf_order_front_tree.c:
```
/* ========================================================================== */
/* === UMF_order_front_tree ================================================= */
/* ========================================================================== */

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

/*
    Post-ordering of supernodal column elimination tree.
*/

#include "umf_internal.h"

GLOBAL Int UMF_order_front_tree
(
    Int root,
    Int k,
    Int Front_child [ ],		/* input argument, destroyed */
    const Int Front_sibling [ ],
    Int Front_order [ ],
    Int Stack [ ]
)
{
    Int f, head, h, i ;

/* recursive version (Stack [ ] is not used):
    i = root ;
    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
    {
	k = UMF_order_front_tree (f, k, Front_child, Front_sibling,
	    Front_order) ;
    }
    Front_order [i] = k++ ;
    return (k) ;
*/

    /* push root on the stack */
    head = 0 ;
    Stack [0] = root ;

    while (head >= 0)
    {
	/* get head of stack */
	i = Stack [head] ;
	DEBUG1 (("head of stack "ID" \n", i)) ;
	ASSERT (i >= 0 && i < UMF_nbug && head <= UMF_fbug) ;

	if (Front_child [i] != EMPTY)
	{
	    /* the children of i are not yet ordered */
	    /* push each child onto the stack in reverse order */
	    /* so that small ones at the head of the list get popped first */
	    /* and the biggest one at the end of the list gets popped last */
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		head++ ;
	    }
	    h = head ;
	    ASSERT (head <= UMF_fbug) ;
	    for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f])
	    {
		Stack [h--] = f ;
		DEBUG1 (("push "ID" on stack\n", f)) ;
		ASSERT (f >= 0 && f < UMF_nbug) ;
	    }
	    ASSERT (Stack [h] == i) ;

	    /* delete child list so that i gets ordered next time we see it */
	    Front_child [i] = EMPTY ;
	}
	else
	{
	    /* the children of i (if there were any) are already ordered */
	    /* remove i from the stack and order it.  Front i is kth front */
	    head-- ;
	    DEBUG1 (("pop "ID" order "ID"\n", i, k)) ;
	    Front_order [i] = k++ ;
	    ASSERT (k <= UMF_fbug) ;
	}

#ifndef NDEBUG
	DEBUG1 (("\nStack:")) ;
	for (h = head ; h >= 0 ; h--)
	{
	    Int j = Stack [h] ;
	    DEBUG1 ((" "ID, j)) ;
	    ASSERT (j >= 0 && j < UMF_nbug) ;
	}
	DEBUG1 (("\n\n")) ;
	ASSERT (head < UMF_fbug) ;
#endif

    }
    return (k) ;
}


```

sparse-matrix/umfpack/umf_init_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_init_front
(
    WorkType *Work
) ;


```

sparse-matrix/umfpack/umf_report_vector.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_vector
(
    Int n,
    const double Xx [ ],
    const double Xz [ ],
    Int prl,
    Int user
) ;


```

sparse-matrix/umfpack/umf_is_permutation.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_is_permutation
(
    const Int P [ ],
    Int W [ ],
    Int n,
    Int r
) ;


```

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

/* -------------------------------------------------------------------------- */
/* 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_info
(
    const double Control [UMFPACK_CONTROL],
    const double Info [UMFPACK_INFO]
) ;

void umfpack_dl_report_info
(
    const double Control [UMFPACK_CONTROL],
    const double Info [UMFPACK_INFO]
) ;

void umfpack_zi_report_info
(
    const double Control [UMFPACK_CONTROL],
    const double Info [UMFPACK_INFO]
) ;

void umfpack_zl_report_info
(
    const double Control [UMFPACK_CONTROL],
    const double Info [UMFPACK_INFO]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    umfpack_di_report_info (Control, Info) ;

double long Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    umfpack_dl_report_info (Control, Info) ;

complex int Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    umfpack_zi_report_info (Control, Info) ;

complex long Syntax:

    #include "umfpack.h"
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    umfpack_zl_report_info (Control, Info) ;

Purpose:

    Reports statistics from the umfpack_*_*symbolic, umfpack_*_numeric, and
    umfpack_*_*solve routines.

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.

	    0 or less: no output, even when an error occurs
	    1: error messages only
	    2 or more: error messages, and print all of Info
	    Default: 1

    double Info [UMFPACK_INFO] ;		Input argument, not modified.

	Info is an output argument of several UMFPACK routines.
	The contents of Info are printed on standard output.
*/

```

sparse-matrix/umfpack/umfpack_get_lunz.c:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Determines the number of nonzeros in L and U, and the size
    of L and U.
*/

#include "umf_internal.h"
#include "umf_valid_numeric.h"

GLOBAL Int UMFPACK_get_lunz
(
    Int *lnz,
    Int *unz,
    Int *n_row,
    Int *n_col,
    Int *nz_udiag,
    void *NumericHandle
)
{
    NumericType *Numeric ;

    Numeric = (NumericType *) NumericHandle ;

    if (!UMF_valid_numeric (Numeric))
    {
	return (UMFPACK_ERROR_invalid_Numeric_object) ;
    }
    if (!lnz || !unz || !n_row || !n_col || !nz_udiag)
    {
	return (UMFPACK_ERROR_argument_missing) ;
    }

    *n_row = Numeric->n_row ;
    *n_col = Numeric->n_col ;

    /* number of nz's in L below diagonal, plus the unit diagonal of L */
    *lnz = Numeric->lnz + MIN (Numeric->n_row, Numeric->n_col) ;

    /* number of nz's in U above diagonal, plus nz's on diagaonal of U */
    *unz = Numeric->unz + Numeric->nnzpiv ;

    /* number of nz's on the diagonal */
    *nz_udiag = Numeric->nnzpiv ;

    return (UMFPACK_OK) ;
}


```

sparse-matrix/umfpack/umf_scale_column.c:
```
/* ========================================================================== */
/* === UMF_scale_column ===================================================== */
/* ========================================================================== */

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

/*
    Scale the current pivot column, and log the permutation.
    Store the LU factors.  Called by the kernel.

    Returns TRUE if successful, FALSE if out of memory.
*/

#include "umf_internal.h"
#include "umf_mem_alloc_head_block.h"
#include "umf_mem_free_tail_block.h"
#include "umf_get_memory.h"

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

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

    Int i, k, k1, fnrows_max, fnrows, fncols, *Frpos, *Fcpos, pos, row, col,
	pivrow, pivcol, *Frows, *Fcols, *Lpattern, *Upattern, *Lpos, *Upos,
	llen, ulen, fncols_max, fnpiv, uilen, lnz, unz, *Row_tuples,
	*Col_tuples, *Rperm, *Cperm, *Lilen, *Uilen, *Lip, *Uip, *Li, *Ui,
	pivcol_position, newLchain, newUchain, pivrow_position, p, size, lip,
	uip, lnzi, lnzx, unzx, lnz2i, lnz2x, unz2i, unz2x, zero_pivot,
	is_nonzero, nan_pivot ;
    Entry *D, x, pivot_value, *Fx, *Fcol, *Frow, *Lval, *Uval ;
    double d ;

#ifndef NDEBUG
    Int *Col_degree, *Row_degree ;
    UMF_allocfail = FALSE ;
    if (UMF_gprob > 0)			    /* a double relop, but ignore NaN case */
    {
	double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ;
	DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ;
	UMF_allocfail = rrr < UMF_gprob ;   /* a double relop, but ignore NaN case */
	if (UMF_allocfail)
	{
	    DEBUG1 (("Random garbage collection (scale_column)\n")) ;
	}
    }
#endif

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

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

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

    Rperm = Numeric->Rperm ;
    Cperm = Numeric->Cperm ;
    Lpos = Numeric->Lpos ;
    Upos = Numeric->Upos ;
    Lilen = Numeric->Lilen ;
    Uilen = Numeric->Uilen ;

    Lip = Numeric->Lip ;
    Uip = Numeric->Uip ;
    D = Numeric->D ;

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

    k = Work->npiv++ ;

    Fx = Work->Fx ;
    fnrows_max = Work->fnrows_max ;
    fncols_max = Work->fncols_max ;
    fnpiv = Work->fnpiv ;
    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;
    Frows = Work->Frows ;
    Fcols = Work->Fcols ;
    pivrow = Work->pivrow ;
    pivcol = Work->pivcol ;

    ASSERT (pivrow >= 0 && pivrow < Work->n_row) ;
    ASSERT (pivcol >= 0 && pivcol < Work->n_col) ;
    ASSERT (k < MIN (Work->n_row, Work->n_col)) ;

#ifndef NDEBUG
    Col_degree = Numeric->Cperm ;	/* for NON_PIVOTAL_COL macro */
    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro */
    if (k % 1000 == 0) DEBUG0 (("step "ID"\n", k))  ;
#endif

    Row_tuples = Numeric->Uip ;
    Col_tuples = Numeric->Lip ;

    Lpattern = Work->Lpattern ;
    llen = Work->llen ;
    Upattern = Work->Upattern ;
    ulen = Work->ulen ;

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

    /* Frpos [row] >= 0 for each row in pivot column pattern.   */
    /* offset into pattern is given by:			   	*/
    /* Frpos [row] == offset - 1				*/
    /* Frpos [pivrow] is the offset of the latest pivot row	*/

    /* Fcpos [col] >= 0 for each col in pivot row pattern.	*/
    /* Fcpos [col] == (offset - 1) * fnrows_max		 	*/
    /* Fcpos [pivcol] is the offset of the latest pivot column  */

    /* Fcols [0..fncols-1] is the pivot row pattern (excl pivot cols) */
    /* Frows [0..fnrows-1] is the pivot col pattern (excl pivot rows) */

#ifndef NDEBUG
    DEBUG7 (("Current frontal matrix: (prior to pivcol scale)\n")) ;
    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif

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

#ifndef NDEBUG
    {
	Int row, col, i, lcnt, ucnt ;

	DEBUG2 (("Store column of L, k = "ID", llen "ID"\n", k, llen)) ;
	for (i = 0 ; i < llen ; i++)
	{
	    row = Lpattern [i] ;
	    ASSERT (row >= 0 && row < Work->n_row) ;
	    DEBUG2 (("    Lpattern["ID"] "ID" Lpos "ID, i, row, Lpos [row])) ;
	    if (row == pivrow) DEBUG2 ((" <- pivot row")) ;
	    DEBUG2 (("\n")) ;
	    ASSERT (NON_PIVOTAL_ROW (row)) ;
	    ASSERT (i == Lpos [row]) ;
	}

	DEBUG2 (("Store row of U, k = "ID", ulen "ID"\n", k, ulen)) ;
	for (i = 0 ; i < ulen ; i++)
	{
	    col = Upattern [i] ;
	    DEBUG2 (("    Upattern["ID"] "ID, i, col)) ;
	    if (col == pivcol) DEBUG2 ((" <- pivot col")) ;
	    DEBUG2 (("\n")) ;
	    ASSERT (col >= 0 && col < Work->n_col) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    ASSERT (i == Upos [col]) ;
	}

	lcnt = 0 ;
	ucnt = 0 ;
	if (Work->n_row < 1000)
	{
	    for (row = 0 ; row < Work->n_row ; row++)
	    {
		if (NON_PIVOTAL_ROW (row) && Lpos [row] != EMPTY) lcnt++ ;
	    }
	    ASSERT (lcnt == llen) ;
	}
	if (Work->n_col < 1000)
	{
	    for (col = 0 ; col < Work->n_col ; col++)
	    {
		if (NON_PIVOTAL_COL (col) && Upos [col] != EMPTY) ucnt++ ;
	    }
	    ASSERT (ucnt == ulen) ;
	}
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* remove pivot row from L */
    /* ---------------------------------------------------------------------- */

    /* remove pivot row index from current column of L */
    /* if a new Lchain starts, then all entries are removed later */
    DEBUG2 (("Removing pivrow from Lpattern, k = "ID"\n", k)) ;
    ASSERT (NON_PIVOTAL_ROW (pivrow)) ;
    pivrow_position = Lpos [pivrow] ;
    if (pivrow_position != EMPTY)
    {
	/* place the last entry in the column in the */
	/* position of the pivot row index */
	ASSERT (pivrow == Lpattern [pivrow_position]) ;
	row = Lpattern [--llen] ;
	ASSERT (NON_PIVOTAL_ROW (row)) ;
	Lpattern [pivrow_position] = row ;
	Lpos [row] = pivrow_position ;
	Lpos [pivrow] = EMPTY ;
    }

    /* ---------------------------------------------------------------------- */
    /* store the pivot value, for the diagonal matrix D */
    /* ---------------------------------------------------------------------- */

    /* fnpiv-th pivot in frontal matrix located in */
    /* Fx (fnrows_max-fnpiv, fncols_max-fnpiv) */

    Fcol = Fx + (fncols_max - fnpiv) * fnrows_max ;
    pivot_value = Fcol [fnrows_max - fnpiv] ;
    D [k] = pivot_value ;

    ABS (d, pivot_value) ;
    zero_pivot = SCALAR_IS_ZERO (d) ;
    nan_pivot = SCALAR_IS_NAN (d) ;

    if (k == 0)
    {
	Numeric->min_udiag = d ;
	Numeric->max_udiag = d ;
    }
    else
    {
	/* min (abs (diag (U))) behaves as follows:  If any entry is zero,
	   then the result is zero (regardless of the presence of NaN's).
	   Otherwise, if any entry is NaN, then the result is NaN.  Otherwise,
	   the result is the smallest absolute value on the diagonal of U.
	*/

	if (SCALAR_IS_NONZERO (Numeric->min_udiag))
	{
	    if (zero_pivot || nan_pivot)
	    {
		Numeric->min_udiag = d ;
	    }
	    else if (!SCALAR_IS_NAN (Numeric->min_udiag))
	    {
		/* d and min_udiag are both non-NaN */
		Numeric->min_udiag = MIN (Numeric->min_udiag, d) ;
	    }
	}

	/*
	   max (abs (diag (U))) behaves as follows:  If any entry is NaN
	   then the result is NaN.  Otherise, the result is the largest
	   absolute value on the diagonal of U.
	*/

	if (nan_pivot)
	{
	    Numeric->max_udiag = d ;
	}
	else if (!SCALAR_IS_NAN (Numeric->max_udiag))
	{
	    /* d and max_udiag are both non-NaN */
	    Numeric->max_udiag = MAX (Numeric->max_udiag, d) ;
	}
    }

    if (!zero_pivot)
    {
	/* the pivot is nonzero, but might be Inf or NaN */
	Numeric->nnzpiv++ ;
    }
    DEBUG4 (("Pivot abs value: %g nnzpiv: "ID" D["ID"]=", d, Numeric->nnzpiv, k)) ;
    EDEBUG4 (pivot_value) ;
    DEBUG4 (("\n")) ;

    /* ---------------------------------------------------------------------- */
    /* scale pivot column and count nonzeros in kth column of L */
    /* ---------------------------------------------------------------------- */

    lnz = 0 ;
    lnz2i = 0 ;
    lnz2x = llen ;
    for (i = 0 ; i < fnrows ; i++)
    {
	EDEBUG4 (Fcol [i]) ;
	EDEBUG4 (pivot_value) ;
	if (IS_NONZERO (Fcol [i]))
	{
	    /* Fcol [i] is nonzero, NaN, or Inf */
	    /* Fcol [i] = Fcol [i] / pivot_value ; */
	    DIV (x, Fcol [i], pivot_value) ;
	    Fcol [i] = x ;
	    /* underflow may have occured, so check if result is zero */
	    is_nonzero = IS_NONZERO (x) ;
	}
	else
	{
	    /* Fcol [i] is zero.  Do not divide by pivot value. */
	    is_nonzero = FALSE ;
	}
	DEBUG4 (("pivot column: "ID" is_nonzero "ID, i, is_nonzero)) ;
	EDEBUG4 (Fcol [i]) ;
	DEBUG4 (("\n")) ;

	/* if we start a new Lchain: */
	if (is_nonzero)
	{
	    /* one new integer and one new Entry */
	    DEBUG4 ((" got an entry \n")) ;
	    lnz++ ;
	}

	/* if we continue the prior Lchain: */
	row = Frows [i] ;
	ASSERT (row != pivrow) ;
	ASSERT (NON_PIVOTAL_ROW (row)) ;
	pos = Lpos [row] ;
	if (pos == EMPTY && is_nonzero)
	{
	    /* row is not in the Lpattern, add it if Fcol [i] is nonzero */
	    lnz2i++ ;
	    lnz2x++ ;
	}
	DEBUG3 (("Scale L col, row "ID" pos "ID" scaled value", row, pos)) ;
	EDEBUG3 (Fcol [i]) ;
	DEBUG3 ((" ::: lnz "ID" lnz2i "ID" lnz2x "ID"\n", lnz, lnz2i, lnz2x)) ;
    }

    /* determine if we start a new Lchain or continue the old one */
    if (llen == 0 || zero_pivot)
    {
	/* llen == 0 means there is no prior Lchain */
	/* D [k] == 0 means the pivot column is empty */
	newLchain = TRUE ;
    }
    else
    {
	newLchain =
		/* storage for starting a new Lchain */
		UNITS (Entry, lnz) + UNITS (Int, lnz)
	    <=
		/* storage for continuing a prior Lchain */
		UNITS (Entry, lnz2x) + UNITS (Int, lnz2i) ;
    }

    if (newLchain)
    {
	/* start a new chain for column k of L */
	DEBUG2 (("Start new Lchain, k = "ID"\n", k)) ;

	pivrow_position = EMPTY ;

	/* clear the prior Lpattern */
	for (i = 0 ; i < llen ; i++)
	{
	    row = Lpattern [i] ;
	    ASSERT (NON_PIVOTAL_ROW (row)) ;
	    Lpos [row] = EMPTY ;
	}
	llen = 0 ;

	lnzi = lnz ;
	lnzx = lnz ;
    }
    else
    {
	/* continue the prior Lchain */
	DEBUG2 (("Continue  Lchain, k = "ID"\n", k)) ;
	lnzi = lnz2i ;
	lnzx = lnz2x ;
    }

    /* ---------------------------------------------------------------------- */
    /* count the nonzeros in the row of U */
    /* ---------------------------------------------------------------------- */

    /* store the numerical entries and find new nonzeros */
    Frow = Fx + (fnrows_max - fnpiv) ;

    unz = 0 ;
    unz2i = 0 ;
    unz2x = ulen ;
    DEBUG2 (("unz2x is "ID"\n", unz2x)) ;

    /* if row k does not end a Uchain, pivcol will not be included in ulen */

    ASSERT (NON_PIVOTAL_COL (pivcol)) ;
    pivcol_position = Upos [pivcol] ;
    if (pivcol_position != EMPTY)
    {
	unz2x-- ;
	DEBUG2 (("(exclude pivcol) unz2x is now "ID"\n", unz2x)) ;
    }

    ASSERT (unz2x >= 0) ;

    for (i = 0 ; i < fncols ; i++)
    {
	x = Frow [i * fnrows_max] ;
	is_nonzero = IS_NONZERO (x) ;

	/* if we start a new Uchain */
	if (is_nonzero)
	{
	    unz++ ;
	    DEBUG2 (("If Unew: "ID, unz)) ;
	    EDEBUG2 (x) ;
	    DEBUG2 (("\n")) ;
	}

	/* if we continue the prior Uchain */
	col = Fcols [i] ;
	ASSERT (col != pivcol) ;
	ASSERT (NON_PIVOTAL_COL (col)) ;
	pos = Upos [col] ;
	if (pos == EMPTY && is_nonzero)
	{
	    /* add this new nonzero entry to the U pattern, if nonzero */
	    unz2i++ ;
	    unz2x++ ;
	    DEBUG2 (("If old:                      "ID" :", unz2x)) ;
	    EDEBUG2 (x) ;
	    DEBUG2 (("\n")) ;
	}
    }

    ASSERT (IMPLIES (k == 0, ulen == 0)) ;

    /* determine if we start a new Uchain or continue the old one */
    if (ulen == 0 || zero_pivot)
    {
	/* ulen == 0 means there is no prior Uchain */
	/* D [k] == 0 means the matrix is singular (pivot row might */
	/* not be empty, however, but start a new Uchain to prune zero */
	/* entries for the deg > 0 test in UMF_u*solve) */
	newUchain = TRUE ;
    }
    else
    {
	newUchain =
		/* approximate storage for starting a new Uchain */
		UNITS (Entry, unz) + UNITS (Int, unz)
	    <=
		/* approximate storage for continuing a prior Uchain */
		UNITS (Entry, unz2x) + UNITS (Int, unz2i) ;

	/* this would be exact, except for the Int to Unit rounding, */
	/* because the Upattern is stored only at the end of the Uchain */
    }

    /* ---------------------------------------------------------------------- */
    /* allocate space for the column of L and the row of U */
    /* ---------------------------------------------------------------------- */

    size = UNITS (Int, lnzi) + UNITS (Entry, lnzx) ;
    if (newUchain)
    {
	/* store the pattern of the last row in the prior Uchain */
	size += UNITS (Int, ulen) ;
	unzx = unz ;
    }
    else
    {
	unzx = unz2x ;
    }
    size += UNITS (Entry, unzx) ;

    p = UMF_mem_alloc_head_block (Numeric, size) ;
    if (!p)
    {
	/* do garbage collection, realloc, and try again */
	if (!UMF_get_memory (Numeric, Work, size))
	{
	    return (FALSE) ;	/* out of memory */
	}
	p = UMF_mem_alloc_head_block (Numeric, size) ;
    }
    if (!p)
    {
	return (FALSE) ;	/* out of memory */
    }

    /* ---------------------------------------------------------------------- */
    /* store the column of L */
    /* ---------------------------------------------------------------------- */

    lip = p ;

    Li = (Int *) (Numeric->Memory + p) ;
    p += UNITS (Int, lnzi) ;
    Lval = (Entry *) (Numeric->Memory + p) ;
    p += UNITS (Entry, lnzx) ;

    for (i = 0 ; i < lnzx ; i++)
    {
	CLEAR (Lval [i]) ;
    }

    /* store the numerical entries */

    if (newLchain)
    {
	/* flag the first column in the Lchain by negating Lip [k] */
	lip = -lip ;

	ASSERT (llen == 0) ;
	for (i = 0 ; i < fnrows ; i++)
	{
	    x = Fcol [i] ;
	    DEBUG4 (("Store column, i "ID" is_nonzero(x) "ID"\n", i,
		IS_NONZERO (x))) ;
	    EDEBUG4 (x) ;
	    EDEBUG4 (Fcol [i]) ;
	    DEBUG4 (("\n")) ;

	    if (IS_NONZERO (x))
	    {
		DEBUG4 (("Store column, i "ID" is_nonzero(x) "ID"\n", i,
		IS_NONZERO (x))) ;

		row = Frows [i] ;
		ASSERT (NON_PIVOTAL_ROW (row)) ;
		pos = llen++ ;
		Lpattern [pos] = row ;
		Lpos [row] = pos ;
		Li [pos] = row ;
		Lval [pos] = x ;
		DEBUG2 (("(newLchain) New entry in Lpattern: row "ID" pos "ID
		    "\n", row, pos)) ;
		DEBUG2 (("(newLchain) Store Lval row "ID" pos "ID" value",
		    row, pos)) ;
		EDEBUG2 (x) ;
		DEBUG2 (("\n")) ;
	    }
	}
    }
    else
    {
	ASSERT (llen > 0) ;
	for (i = 0 ; i < fnrows ; i++)
	{
	    x = Fcol [i] ;
	    if (IS_NONZERO (x))
	    {
		row = Frows [i] ;
		ASSERT (NON_PIVOTAL_ROW (row)) ;
		pos = Lpos [row] ;
		if (pos == EMPTY)
		{
		    /* add this new nonzero entry to the L pattern */
		    pos = llen++ ;
		    DEBUG2 (("New entry in Lpattern: row "ID" pos "ID"\n",
			row, pos)) ;
		    ASSERT (llen <= lnzx) ;
		    Lpattern [pos] = row ;
		    Lpos [row] = pos ;
		    *Li++ = row ;
		}
		DEBUG2 (("Store Lval row "ID" pos "ID" value", row, pos)) ;
		EDEBUG2 (x) ;
		DEBUG2 (("\n")) ;
		ASSERT (row == Lpattern [pos]) ;
		ASSERT (pos < lnzx) ;
		Lval [pos] = x ;
	    }
	}
    }
    DEBUG4 (("llen "ID" lnzx "ID"\n", llen, lnzx)) ;
    ASSERT (llen == lnzx) ;
    ASSERT (lnz <= llen) ;

    Numeric->lnz += lnz ;
    Numeric->nLentries += lnzx ;
    Work->llen = llen ;
    Numeric->isize += lnzi ;

    /* ---------------------------------------------------------------------- */
    /* store the row of U */
    /* ---------------------------------------------------------------------- */

    uip = p ;

    if (newUchain)
    {
	/* starting a new Uchain - flag this by negating Uip [k] */
	uip = -uip ;
	DEBUG2 (("Start new Uchain, k = "ID"\n", k)) ;

	pivcol_position = EMPTY ;

	/* end the prior Uchain */
	/* save the current Upattern, and then */
	/* clear it and start a new Upattern */
	DEBUG2 (("Ending prior chain, k-1 = "ID"\n", k-1)) ;
	uilen = ulen ;
	Ui = (Int *) (Numeric->Memory + p) ;
	Numeric->isize += ulen ;
	p += UNITS (Int, ulen) ;
	for (i = 0 ; i < ulen ; i++)
	{
	    col = Upattern [i] ;
	    ASSERT (col >= 0 && col < Work->n_col) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    Upos [col] = EMPTY ;
	    Ui [i] = col ;
	}

	ulen = 0 ;

    }
    else
    {
	/* continue the prior Uchain */
	DEBUG2 (("Continue  Uchain, k = "ID"\n", k)) ;
	ASSERT (k > 0) ;

	/* remove pivot col index from current row of U */
	/* if a new Uchain starts, then all entries are removed later */
	DEBUG2 (("Removing pivcol from Upattern, k = "ID"\n", k)) ;

	if (pivcol_position != EMPTY)
	{
	    /* place the last entry in the row in the */
	    /* position of the pivot col index */
	    ASSERT (pivcol == Upattern [pivcol_position]) ;
	    col = Upattern [--ulen] ;
	    ASSERT (col >= 0 && col < Work->n_col) ;
	    ASSERT (NON_PIVOTAL_COL (col)) ;
	    Upattern [pivcol_position] = col ;
	    Upos [col] = pivcol_position ;
	    Upos [pivcol] = EMPTY ;
	}

	/* this row continues the Uchain.  Keep track of how much */
	/* to trim from the k-th length to get the length of the */
	/* (k-1)st row of U */
	uilen = unz2i ;

    }

    Uval = (Entry *) (Numeric->Memory + p) ;
    /* p += UNITS (Entry, unzx), no need to increment p */

    for (i = 0 ; i < unzx ; i++)
    {
	CLEAR (Uval [i]) ;
    }

    if (newUchain)
    {
	ASSERT (ulen == 0) ;
	for (i = 0 ; i < fncols ; i++)
	{
	    x = Frow [i * fnrows_max] ;
	    if (IS_NONZERO (x))
	    {
		/* add this new nonzero entry to the U pattern */
		col = Fcols [i] ;
		ASSERT (col >= 0 && col < Work->n_col) ;
		ASSERT (NON_PIVOTAL_COL (col)) ;
		pos = ulen++ ;
		Upattern [pos] = col ;
		Upos [col] = pos ;
		Uval [pos] = x ;
		DEBUG2 (("(newUchain) New entry in Upattern: col "ID" pos "ID
		    "\n", col, pos)) ;
		DEBUG2 (("(newUchain) Store Uval col "ID" pos "ID" value",
		    col, pos)) ;
		EDEBUG2 (x) ;
		DEBUG2 (("\n")) ;
	    }
	}
    }
    else
    {

	ASSERT (ulen > 0) ;

	/* store the numerical entries and find new nonzeros */

	for (i = 0 ; i < fncols ; i++)
	{
	    x = Frow [i * fnrows_max] ;
	    if (IS_NONZERO (x))
	    {
		col = Fcols [i] ;
		ASSERT (col >= 0 && col < Work->n_col) ;
		ASSERT (NON_PIVOTAL_COL (col)) ;
		pos = Upos [col] ;
		if (pos == EMPTY)
		{
		    /* add this new nonzero entry to the U pattern */
		    ASSERT (ulen < unzx) ;
		    pos = ulen++ ;
		    Upattern [pos] = col ;
		    Upos [col] = pos ;
		    DEBUG2 (("New entry in Upattern: col "ID" pos "ID"\n",
			col, pos)) ;
		}
		DEBUG2 (("Store Uval col "ID" pos "ID" value", col, pos)) ;
		EDEBUG2 (x) ;
		DEBUG2 (("\n")) ;
		ASSERT (col == Upattern [pos]) ;
		ASSERT (pos < unzx) ;
		Uval [pos] = x ;
	    }
	}
    }

    ASSERT (ulen == unzx) ;
    ASSERT (unz <= ulen) ;
    Numeric->unz += unz ;
    Numeric->nUentries += unzx ;
    Work->ulen = ulen ;
    DEBUG1 (("Work->ulen = "ID" at end of pivot step, k: "ID"\n", ulen, k)) ;

    /* ---------------------------------------------------------------------- */
    /* count the "true" flops, based on LU pattern only */
    /* ---------------------------------------------------------------------- */

    /* the outer product is not done here, but in the BLAS */
    Numeric->flops += DIV_FLOPS * lnz		/* scale pivot column */
	+ MULTSUB_FLOPS * (lnz*unz) ;		/* outer product */

    /* ====================================================================== */
    /* A pivot step is complete */
    /* ====================================================================== */

#ifndef NDEBUG
    DEBUG7 (("Current frontal matrix: (after pivcol scale)\n")) ;
    UMF_dump_current_front (Numeric, Work, TRUE) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* remove pivot row and column from frontal pattern */
    /* ---------------------------------------------------------------------- */

    Frpos [pivrow] = EMPTY ;
    Fcpos [pivcol] = EMPTY ;

    /* ---------------------------------------------------------------------- */
    /* deallocate the pivot row and pivot column tuples */
    /* ---------------------------------------------------------------------- */

    UMF_mem_free_tail_block (Numeric, Row_tuples [pivrow]) ;
    UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol]) ;

    /* ---------------------------------------------------------------------- */
    /* the pivot column is fully assembled and scaled, and is now the */
    /* k-th column of L. The pivot row is the k-th row of U. */
    /* ---------------------------------------------------------------------- */

    DEBUG5 (("number of pivots prior to this one: "ID"\n", k)) ;
    ASSERT (NON_PIVOTAL_ROW (pivrow)) ;
    ASSERT (NON_PIVOTAL_COL (pivcol)) ;

    /* save row and column inverse permutation */
    k1 = ONES_COMPLEMENT (k) ;
    Rperm [pivrow] = k1 ;			/* aliased with Row_degree */
    Cperm [pivcol] = k1 ;			/* aliased with Col_degree */

    ASSERT (!NON_PIVOTAL_ROW (pivrow)) ;
    ASSERT (!NON_PIVOTAL_COL (pivcol)) ;

    Lpos [pivrow] = pivrow_position ;
    Upos [pivcol] = pivcol_position ;

    Lip [pivcol] = lip ;			/* aliased with Col_tuples */
    Lilen [pivcol] = lnzi ;			/* aliased with Col_tlen */

    Uip [pivrow] = uip ;			/* aliased with Row_tuples */
    Uilen [pivrow] = uilen ;			/* aliased with Row_tlen */

    return (TRUE) ;

}


```

sparse-matrix/umfpack/umf_kernel_init.c:
```
/* ========================================================================== */
/* === UMF_kernel_init ====================================================== */
/* ========================================================================== */

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

/*
    Initialize the kernel, build tuple lists.  Assumes elements are packed.
    Returns TRUE if successful, FALSE if out of memory or if the pattern has
    changed since UMFPACK_*symbolic.  UMFPACK_numeric allocates at least enough
    space for UMF_kernel_init to succeed; otherwise it does not call
    UMF_kernel_init.  So an out-of-memory condition means that the pattern must
    have gotten larger.
*/

#include "umf_internal.h"
#include "umf_tuple_lengths.h"
#include "umf_build_tuples.h"
#include "umf_mem_init_memoryspace.h"
#include "umf_mem_alloc_element.h"

GLOBAL Int UMF_kernel_init
(
    const Int Ap [ ],		/* user's input matrix (not modified) */
    const Int Ai [ ],
    const double Ax [ ],
#ifdef COMPLEX
    const double Az [ ],
#endif
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int row, k, oldcol, size, e, p1, p2, p, *ip, nz, *Rows, *Cols, *E, i, *Upos,
	*Lpos, n_row, n_col, *Wp, *Cperm_init, *Frpos, *Fcpos, *Row_degree, nn,
	*Row_tlen, *Col_degree, *Col_tlen, deg, oldrow, newrow, ilast,
	*Rperm_init, col, n_inner ;
    double unused = 0 ;
    Entry *xp, *C ;
    Element *ep ;

#ifndef NDEBUG
    double gprob_save = UMF_gprob ;
    UMF_gprob = 0 ;
#endif

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

    DEBUG0 (("KERNEL INIT\n")) ;

    n_row = Symbolic->n_row ;
    n_col = Symbolic->n_col ;
    nn = MAX (n_row, n_col) ;
    n_inner = MIN (n_row, n_col) ;
    ASSERT (n_row > 0 && n_col > 0) ;
    Cperm_init = Symbolic->Cperm_init ;
    Rperm_init = Symbolic->Rperm_init ;
    nz = Ap [n_col] ;
    if (nz < 0 || Ap [0] != 0 || nz != Symbolic->nz)
    {
	return (FALSE) ;	/* pattern changed */
    }

    /* ---------------------------------------------------------------------- */
    /* initialize the Numeric->Memory space for LU, elements, and tuples */
    /* ---------------------------------------------------------------------- */

    UMF_mem_init_memoryspace (Numeric) ;

    /* ---------------------------------------------------------------------- */
    /* initialize the Work and Numeric objects */
    /* ---------------------------------------------------------------------- */

    /* current front is empty */
    Work->fnpiv = 0 ;
    Work->fncols = 0 ;
    Work->fnrows = 0 ;
    Work->fnzeros = 0 ;

    Work->overlap = 0 ;
    Work->nz = nz ;
    Work->prior_element = EMPTY ;
    Work->ulen = 0 ;
    Work->llen = 0 ;
    Work->npiv = 0 ;
    Work->frontid = 0 ;

    Row_degree = Numeric->Rperm ;
    Col_degree = Numeric->Cperm ;
    /* Row_tuples = Numeric->Uip ; */
    Row_tlen   = Numeric->Uilen ;
    /* Col_tuples = Numeric->Lip ; */
    Col_tlen   = Numeric->Lilen ;

    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;
    Wp = Work->Wp ;

    /* D = Numeric->D ; */
    Upos = Numeric->Upos ;
    Lpos = Numeric->Lpos ;
    /* D [0..min(n_row,n_col)] = 0 ; no need to initialize this */

    for (row = 0 ; row <= n_row ; row++)
    {
	Lpos [row] = EMPTY ;
	/* Row_tuples [row] = 0 ; set in UMF_build_tuples */
	Row_degree [row] = 0 ;
	Row_tlen [row] = 0 ;
	/* Frpos [row] = EMPTY ;  do this later */
    }

    for (col = 0 ; col <= n_col ; col++)
    {
	Upos [col] = EMPTY ;
	/* Col_tuples [col] = 0 ; set in UMF_build_tuples */
	/* Col_degree [col] = 0 ; initialized below */
	Col_tlen [col] = 0 ;
	Fcpos [col] = EMPTY ;
    }

    for (i = 0 ; i <= nn ; i++)
    {
	Wp [i] = EMPTY ;
    }
    Work->Wpflag = -2 ;

    /* When cleared, Wp [0..nn] is < 0 and > wpflag. */
    /* In row search, Wp [col] is set to wpflag, which is negative. */
    /* In col search, Wp [row] is set to a position, which is >= 0. */
    /* Wp is cleared immediately after the row and col searches. */

    /* no need to initialize Wm, Wio, Woi, and Woo */

    /* clear the external degree counters */
    Work->cdeg0 = 1 ;
    Work->rdeg0 = 1 ;

    E = Work->E ;

    Numeric->n_row = n_row ;
    Numeric->n_col = n_col ;
    Numeric->npiv = 0 ;
    Numeric->nnzpiv = 0 ;
    Numeric->min_udiag = 0.0 ;
    Numeric->max_udiag = 0.0 ;
    Numeric->rcond = 0.0 ;
    Numeric->isize = 0 ;
    Numeric->nLentries = 0 ;
    Numeric->nUentries = 0 ;
    Numeric->lnz = 0 ;
    Numeric->unz = 0 ;
    Numeric->maxfrsize = 0 ;
    Numeric->flops = 0. ;

    /* ---------------------------------------------------------------------- */
    /* allocate the elements, copy the columns of A, and initialize degrees */
    /* ---------------------------------------------------------------------- */

    /* also apply the row and column pre-ordering.  */

    DEBUG3 (("LOAD_MATRIX:\n")) ;

    /* construct the inverse row permutation (use Frpos as temp) */
    for (newrow = 0 ; newrow < n_row ; newrow++)
    {
	oldrow = Rperm_init [newrow] ;
	ASSERT (oldrow >= 0 && oldrow < n_row) ;
	Frpos [oldrow] = newrow ;
    }

    e = 0 ;
    E [e] = 0 ;
    for (k = 0 ; k < n_col ; k++)
    {
	oldcol = Cperm_init [k] ;
	ASSERT (oldcol >= 0 && oldcol < n_col) ;
	p1 = Ap [oldcol] ;
	p2 = Ap [oldcol+1] ;
	deg = p2 - p1 ;
	Col_degree [k] = deg ;
	if (deg < 0)
	{
	    return (FALSE) ;		/* pattern changed */
	}
	else if (deg > 0)
	{
	    e++ ;
	    E [e] = UMF_mem_alloc_element (Numeric, deg, 1, &Rows, &Cols, &C,
	        &size, &ep) ;
	    if (E [e] <= 0)
	    {
	        /* We ran out of memory, which can only mean that */
	        /* the pattern (Ap and or Ai) has changed (gotten larger). */
	        DEBUG0 (("Pattern has gotten larger - alloc el. failed\n")) ;
	        DEBUG0 (("column k = "ID" size "ID"\n", k, size)) ;
	        return (FALSE) ;	/* pattern changed */
	    }
	    Cols [0] = k ;
	    ip = Rows ;
	    xp = C ;
	    ilast = -1 ;
	    for (p = p1 ; p < p2 ; p++)
	    {
		oldrow = Ai [p] ;
		if (oldrow <= ilast || oldrow >= n_row)
		{
		    return (FALSE) ;	/* pattern changed */
		}
		ilast = oldrow ;
		newrow = Frpos [oldrow] ;
		*ip++ = newrow ;
		ASSIGN (*xp, Ax [p], Az [p]) ;
		xp++ ;
		Row_degree [newrow]++ ;
	    }
	}
    }

    Work->nel = e ;
    Work->nelorig = e ;

    Col_degree [n_col] = 0 ;

    for (e++ ; e <= n_col + n_inner ; e++)
    {
	E [e] = 0 ;
    }

    /* Frpos no longer needed */
    for (row = 0 ; row <= n_row ; row++)
    {
	Frpos [row] = EMPTY ;
    }

    /* ---------------------------------------------------------------------- */
    /* build the tuple lists */
    /* ---------------------------------------------------------------------- */

    /* if the memory usage changes, then the pattern has changed */

    (void) UMF_tuple_lengths (Numeric, Work, &unused) ;
    if (!UMF_build_tuples (Numeric, Work))
    {
	/* We ran out of memory, which can only mean that */
	/* the pattern (Ap and or Ai) has changed (gotten larger). */
	DEBUG0 (("Pattern has gotten larger - build tuples failed\n")) ;
	return (FALSE) ;	/* pattern changed */
    }

    Numeric->init_usage = Numeric->max_usage ;
    if (Symbolic->num_mem_init_usage != Numeric->init_usage)
    {
	/* the pattern (Ap and or Ai) has changed */
	DEBUG0 (("Pattern has changed in size\n")) ;
	return (FALSE) ;	/* pattern changed */
    }

    /* NOTE:  this test does not detect all changes to Ap and/or Ai since the */
    /* last call to UMFPACK_*symbolic.  The pattern can change with the memory*/
    /* usage remaining the same. */

    /* ---------------------------------------------------------------------- */
    /* construct the row merge sets */
    /* ---------------------------------------------------------------------- */

    for (i = 0 ; i <= Symbolic->nfr ; i++)
    {
	Work->Front_new1strow [i] = Symbolic->Front_1strow [i] ;
    }

#ifndef NDEBUG
    UMF_dump_rowmerge (Numeric, Symbolic, Work) ;
    DEBUG6 (("Column form of original matrix:\n")) ;
    UMF_dump_col_matrix (Ax,
#ifdef COMPLEX
    	Az,
#endif
	Ai, Ap, n_row, n_col, nz) ;
    UMF_gprob = gprob_save ;
    UMF_dump_memory (Numeric) ;
    UMF_dump_matrix (Numeric, Work, FALSE) ;
    DEBUG0 (("kernel init done...\n")) ;
#endif

    return (TRUE) ;

}


```

sparse-matrix/umfpack/umfpack_symbolic.h:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

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

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

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

long umfpack_zl_symbolic
(
    long n_row,
    long n_col,
    const long Ap [ ],
    const long Ai [ ],
    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, status ;
    double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ;
    status = umfpack_di_symbolic (n_row, n_col, Ap, Ai, &Symbolic, Control,
	Info) ;

double long Syntax:

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

complex int Syntax:

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

complex long Syntax:

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

Purpose:

    Given nonzero pattern of a sparse matrix A in column-oriented form,
    umfpack_*_symbolic performs a column pre-ordering to reduce fill-in
    (using UMF_colamd, modified from colamd V2.0 for UMFPACK), and a symbolic
    factorization.  This is required before the matrix can be numerically
    factorized with umfpack_*_numeric.  If you wish to bypass the UMF_colamd
    pre-ordering and provide your own ordering, use umfpack_*_qsymbolic instead.

Returns:

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

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.

	Ap is an integer array of size n_col+1.  On input, it 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], must be zero, and Ap [j] <= Ap [j+1] must hold for all
	j in the range 0 to n_col-1.  The value nz = Ap [n_col] is thus the
	total number of entries in the pattern of the matrix A.  nz must be
	greater than or equal to zero.

    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).
	See umfpack_*_triplet_to_col for how to sort the columns of a matrix
	and sum up the duplicate entries.  See umfpack_*_report_matrix for how
	to print the matrix A.

    void **Symbolic ;	Output argument.

	**Symbolic 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 Symbolic 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_DENSE_COL]:  columns with more than
	    max (16, Control [UMFPACK_DENSE_COL] * 16 * sqrt (n_row))
	    entries are placed placed last in the column pre-ordering by
	    UMF_colamd.  Default: 0.2.

	Control [UMFPACK_DENSE_ROW]:  rows with more than
	    max (16, Control [UMFPACK_DENSE_ROW] * 16 * sqrt (n_col))
	    entries (after "dense" columns are removed) are ignored in the
	    column pre-ordering, UMF_colamd.  Default: 0.2.

	Control [UMFPACK_BLOCK_SIZE]:  the block size to use for Level-3 BLAS
	    in the subsequent numerical factorization (umfpack_*_numeric).
	    A value less than 1 is treated as 1.  Default: 24.  Modifying this
	    parameter affects when updates are applied to the working frontal
	    matrix, and can indirectly affect fill-in and operation count.
	    As long as the block size is large enough (8 or so), this parameter
	    has modest effect on performance.  In Version 3.0, this parameter
	    was an input to umfpack_*_numeric, and had a default value of 16.
	    On a Sun UltraSparc, a block size of 24 is better for larger
	    matrices (16 is better for smaller ones, but not by much).  In the
	    current version, it is required in the symbolic analysis phase, and
	    is thus an input to this phase.

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

	Contains statistics about the symbolic analysis.  If a (double *) NULL
	pointer is passed, then no statistics are returned in Info (this is not
	an error condition).  The entire Info array is cleared (all entries set
	to -1) and then the following statistics are computed:

	Info [UMFPACK_STATUS]: status code.  This is also the return value,
	    whether or not Info is present.

	    UMFPACK_OK

		Each column of the input matrix contained row indices
		in increasing order, with no duplicates.  Only in this case
		does umfpack_*_symbolic compute a valid symbolic factorization.
		For the other cases below, no Symbolic object is created
		(*Symbolic is (void *) NULL).

	    UMFPACK_ERROR_jumbled_matrix

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

	    UMFPACK_ERROR_n_nonpositive

		n is less than or equal to zero.

	    UMFPACK_ERROR_nz_negative

		Number of entries in the matrix is negative.

	    UMFPACK_ERROR_Ap0_nonzero

		Ap [0] is nonzero.

	    UMFPACK_ERROR_col_length_negative

		A column has a negative number of entries.

	    UMFPACK_ERROR_row_index_out_of_bounds

		A row index is out of bounds.

	    UMFPACK_ERROR_out_of_memory

		Insufficient memory to perform the symbolic analysis.

	    UMFPACK_ERROR_argument_missing

		One or more required arguments is missing.

	    UMFPACK_ERROR_problem_too_large

		Problem is too large; memory usage estimate causes an integer
		overlow.  If you are using umfpack_*i_symbolic, try using
		the long versions instead, umfpack_*l_symbolic.

	    UMFPACK_ERROR_internal_error

		Something very serious went wrong.  This is a bug.
		Please contact the author (davis@cise.ufl.edu).

	Info [UMFPACK_NROW]:  the value of the input argument n_row.

	Info [UMFPACK_NCOL]:  the value of the input argument n_col.

	Info [UMFPACK_NZ]:  the number of entries in the input matrix
	    (Ap [n_col]).

	Info [UMFPACK_SIZE_OF_UNIT]:  the number of bytes in a Unit,
	    for memory usage statistics below.

	Info [UMFPACK_SIZE_OF_INT]:  the number of bytes in an int.

	Info [UMFPACK_SIZE_OF_LONG]:  the number of bytes in a long.

	Info [UMFPACK_SIZE_OF_POINTER]:  the number of bytes in a void *
	    pointer.

	Info [UMFPACK_SIZE_OF_ENTRY]:  the number of bytes in a numerical entry.

	Info [UMFPACK_NDENSE_ROW]:  number of "dense" rows in A.  These rows are
	    ignored when the column pre-ordering is computed in UMF_colamd.
	    If > 0, then the matrix had to be re-analyzed by UMF_analyze, which
	    does not ignore these rows.

	Info [UMFPACK_NEMPTY_ROW]:  number of "empty" rows in A.  These are
	    rows that either have no entries, or whose entries are all in
	    "dense" columns.  Any given row is classified as either "dense"
	    or "empty" or "sparse".

	Info [UMFPACK_NDENSE_COL]:  number of "dense" columns in A.  These
	    columns are ordered last in the factorization, but before "empty"
	    columns.  Any given column is classified as either "dense" or
	    "empty" or "sparse".

	Info [UMFPACK_NEMPTY_COL]:  number of "empty" columns in A.  These are
	    columns that either have no entries, or whose entries are all in
	    "dense" rows.  These columns are ordered last in the factorization,
	    to the right of "dense" columns.

	Info [UMFPACK_SYMBOLIC_DEFRAG]:  number of garbage collections
	    performed in UMF_colamd, the column pre-ordering routine, and in
	    UMF_analyze, which is called if UMF_colamd isn't, or if UMF_colamd
	    ignores one or more "dense" rows.

	Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]:  the amount of memory (in Units)
	    required for umfpack_*_symbolic to complete.  This is roughly
	    2.2*nz + (26 to 31)*n integers for a square matrix, depending on the
	    matrix.  This count includes the size of the Symbolic object itself,
	    which is reported in Info [UMFPACK_SYMBOLIC_SIZE].

	Info [UMFPACK_SYMBOLIC_SIZE]: the final size of the Symbolic object (in
	    Units).  This is fairly small, roughly 2*n to 9*n integers,
	    depending on the matrix.

	Info [UMFPACK_VARIABLE_INIT_ESTIMATE]: the Numeric object contains two
	    parts.  The first is fixed in size (O (n_row+n_col)).  The
	    second part holds the sparse LU factors and the contribution blocks
	    from factorized frontal matrices.  This part changes in size during
	    factorization.  Info [UMFPACK_VARIABLE_INIT_ESTIMATE] is the exact
	    size (in Units) required for this second variable-sized part in
	    order for the numerical factorization to start.

	Info [UMFPACK_VARIABLE_PEAK_ESTIMATE]: the estimated peak size (in
	    Units) of the variable-sized part of the Numeric object.  This is
	    usually an upper bound, but that is not guaranteed.

	Info [UMFPACK_VARIABLE_FINAL_ESTIMATE]: the estimated final size (in
	    Units) of the variable-sized part of the Numeric object.  This is
	    usually an upper bound, but that is not guaranteed.  It holds just
	    the sparse LU factors.

	Info [UMFPACK_NUMERIC_SIZE_ESTIMATE]:  an estimate of the final size (in
	    Units) of the entire Numeric object (both fixed-size and variable-
	    sized parts), which holds the LU factorization (including the L, U,
	    P and Q matrices).

	Info [UMFPACK_PEAK_MEMORY_ESTIMATE]:  an estimate of the total amount of
	    memory (in Units) required by umfpack_*_symbolic and
	    umfpack_*_numeric to perform both the symbolic and numeric
	    factorization.  This is the larger of the amount of memory needed
	    in umfpack_*_numeric itself, and the amount of memory needed in
	    umfpack_*_symbolic (Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]).  The count
	    includes the size of both the Symbolic and Numeric objects
	    themselves.

	Info [UMFPACK_FLOPS_ESTIMATE]:  an estimate of the total floating-point
	    operations required to factorize the matrix.  This is a "true"
	    theoretical estimate of the number of flops that would be performed
	    by a flop-parsimonious sparse LU algorithm.  It assumes that no
	    extra flops are performed except for what is strictly required to
	    compute the LU factorization.  It ignores, for example, the flops
	    performed by umfpack_*_numeric to add contribution blocks of frontal
	    matrices together.  If L and U are the upper bound on the pattern
	    of the factors, then this flop count estimate can be represented in
	    MATLAB (for real matrices, not complex) as:

		Lnz = full (sum (spones (L))) - 1 ;	% nz in each col of L
		Unz = full (sum (spones (U')))' - 1 ;	% nz in each row of U
		flops = 2*Lnz*Unz + sum (Lnz) ;

	    The actual "true flop" count found by umfpack_*_numeric will be less
	    than this estimate.

	    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_ESTIMATE]:  an estimate of the number of nonzeros in
	    L, including the diagonal.  Since L is unit-diagonal, the diagonal
	    of L is not stored.  This estimate is a strict upper bound on the
	    actual nonzeros in L to be computed by umfpack_*_numeric.

	Info [UMFPACK_UNZ_ESTIMATE]:  an estimate of the number of nonzeros in
	    U, including the diagonal.  This estimate is a strict upper bound on
	    the actual nonzeros in U to be computed by umfpack_*_numeric.

	Info [UMFPACK_SYMBOLIC_TIME]:  The time taken by umfpack_*_symbolic, 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.

	At the start of umfpack_*_symbolic, all of Info is set of -1, and then
	after that only the above listed Info [...] entries are accessed.
	Future versions might modify different parts of Info.
*/

```

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

This is the UMFPACK Version 4.0 README file.  It is a terse overview of UMFPACK.
Refer to the User Guide (UserGuide.ps (postscript) or UserGuide.pdf (PDF))
for how to install and use UMFPACK.

Description:

    UMFPACK is a set of routines for solving unsymmetric sparse linear systems,
    Ax=b, using the Unsymmetric MultiFrontal method.  Written in ANSI/ISO C,
    with a MATLAB (Version 6.0 and 6.1) interface.

Authors:

    Timothy A. Davis (davis@cise.ufl.edu), University of Florida.

    UMFPACK Version 2.2.1 (MA38 in the Harwell Subroutine Library) is
    co-authored with Iain Duff, Rutherford Appleton Laboratory.

    Includes a modified version of COLAMD V2.0, by Stefan I. Larimore and
    Timothy A. Davis, University of Florida.  The COLAMD algorithm was developed
    in collaboration with John Gilbert, Xerox Palo Alto Research Center, and
    Esmond Ng, Lawrence Berkeley National Laboratory.

Acknowledgements:

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

    I would also like to thank the many researchers who provided sparse
    matrices from a wide range of domains and used earlier versions of UMFPACK/
    MA38 in their applications, and thus assisted in the practical development
    of the algorithm (see http://www.cise.ufl.edu/research/sparse, future
    contributions of matrices are always welcome).

    The MathWorks, Inc., provided a pre-release of MATLAB V6 which allowed me
    to release the umfpack mexFunction (v3.0) about 6 months earlier than I had
    originally planned.

    Penny Anderson (The MathWorks, Inc.), Anshul Gupta (IBM), and Friedrich
    Grund (WAIS) assisted in porting UMFPACK to different platforms.  Penny
    Anderson also incorporated UMFPACK into MATLAB, for lu, backslash (\),
    and forward slash (/).

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:

    UMFPACK (including versions 2.2.1 and earlier, in Fortran) is available at
    http://www.cise.ufl.edu/research/sparse.  MA38 is available in the Harwell
    Subroutine Library.  This version of UMFPACK includes a modified form of
    COLAMD Version 2.0, originally released on Jan. 31, 2001, also available at
    http://www.cise.ufl.edu/research/sparse.  COLAMD V2.0 is also incorporated
    as a built-in function in MATLAB V6, by The MathWorks, Inc.
    (http://www.mathworks.com).  COLAMD V1.0 appears as a column-preordering
    in SuperLU (SuperLU is available at http://www.netlib.org).

--------------------------------------------------------------------------------
Files in this V4.0 distribution:
--------------------------------------------------------------------------------

    ----------------------------------------------------------------------------
    Compiling umfpack.a and the umfpack mexFunction:
    ----------------------------------------------------------------------------

    GNUmakefile			a nice Makefile, for GNU make
    Makefile			an ugly Unix Makefile (for older make's)
    Make.alpha			Makefile additions for Compaq Alpha
    Make.generic		Generic Makefile additions
    Make.linux			Makefile additions for Linux
    Make.rs6000			Makefile additions for RS 6000
    Make.sgi			Makefile additions for SGI
    Make.solaris		Makefile additions for Solaris

    ----------------------------------------------------------------------------
    Documentation:
    ----------------------------------------------------------------------------

    ChangeLog			change log
    License			the UMFPACK License
    README			this file
    UserGuide.bib		references for user guide
    UserGuide.stex		user guide source
    UserGuide.sed1		sed script for processing UserGuide.stex
    UserGuide.sed2		sed script for processing UserGuide.stex
    UserGuide.tex		user guide in Latex (from UserGuide.stex)
    UserGuide.pdf		user guide in PDF
    UserGuide.ps		user guide in postscript
    UserGuide.dvi		user guide in DVI

    ----------------------------------------------------------------------------
    MATLAB m-functions:
    ----------------------------------------------------------------------------

    Contents.m			for "help umfpack" listing of toolbox contents
    umfpack.m			for "help umfpack" only
    umfpack_demo.m		a full umfpack demo
    umfpack_demo.m.out		output of umfpack_demo.m
    umfpack_details.m		the details of how to use umfpack
    umfpack_make.m		compile the umfpack mexFunction
    umfpack_report.m		report statistics
    umfpack_simple.m		a simple umfpack demo
    umfpack_simple.m.out	output of umfpack_simple
    umfpack_btf.m		solve Ax=b using umfpack and dmperm
    lu_normest.m		1-norm estimate of A-L*U (by Hager & Davis).

    ----------------------------------------------------------------------------
    User-callable C routines:
    ----------------------------------------------------------------------------

    umfpack.h			include file for user programs

    umfpack_col_to_triplet.[ch]		convert col form to triplet
    umfpack_defaults.[ch]		set Control defaults
    umfpack_free_numeric.[ch]		free Numeric object
    umfpack_free_symbolic.[ch]		free Symbolic object
    umfpack_get_lunz.[ch]		get nz's in L and U
    umfpack_get_numeric.[ch]		get Numeric object
    umfpack_get_symbolic.[ch]		get Symbolic object
    umfpack_numeric.[ch]		numeric factorization
    umfpack_qsymbolic.[ch]		symbolic factorization, user Q
    umfpack_report_control.[ch]		print Control settings
    umfpack_report_info.[ch]		print Info statistics
    umfpack_report_matrix.[ch]		print col or row-form sparse matrix
    umfpack_report_numeric.[ch]		print Numeric object
    umfpack_report_perm.[ch]		print permutation
    umfpack_report_status.[ch]		print return status
    umfpack_report_symbolic.[ch]	print Symbolic object
    umfpack_report_triplet.[ch]		print triplet matrix
    umfpack_report_vector.[ch]		print dense vector
    umfpack_solve.[ch]			solve a linear system
    umfpack_symbolic.[ch]		symbolic factorization
    umfpack_timer.[ch]			timer
    umfpack_transpose.[ch]		transpose a matrix
    umfpack_triplet_to_col.[ch]		convert triplet to col form
    umfpack_wsolve.h			solve a linear system
					(note that there is no umfpack_wsolve.c;
					it is generated from umfpack_solve.c)

    ----------------------------------------------------------------------------
    MATLAB mexFunction
    ----------------------------------------------------------------------------

    umfpackmex.c

    ----------------------------------------------------------------------------
    C demo programs
    ----------------------------------------------------------------------------

    umfpack_simple.c		a simple demo
    umpack_xx_demo.c		template to create the demo codes below
    umfpack_di_demo.sed		for creating umfpack_di_demo.c
    umfpack_dl_demo.sed		for creating umfpack_dl_demo.c
    umfpack_zi_demo.sed		for creating umfpack_zi_demo.c
    umfpack_zl_demo.sed		for creating umfpack_zl_demo.c

    umfpack_di_demo.c		a full demo (real/int version)
    umfpack_di_demo.out		output of umfpack_di_demo with Make.generic
    umfpack_di_demo.out64	output of umfpack_di_demo with Make.solaris,
				in LP64 mode and using the Sun Performance
				Library BLAS (option 4: in Make.solaris).

    umfpack_dl_demo.c		a full demo (real/long version)
    umfpack_dl_demo.out		output of umfpack_dl_demo with Make.generic
    umfpack_dl_demo.out64	output of umfpack_dl_demo with Make.solaris,
				in LP64 mode and using the Sun Performance
				Library BLAS (option 4 in Make.solaris).

    umfpack_zi_demo.c		a full demo (complex/int version)
    umfpack_zi_demo.out		output of umfpack_zi_demo with Make.generic
    umfpack_zi_demo.out64	output of umfpack_zi_demo with Make.solaris,
				in LP64 mode and using the Sun Performance
				Library BLAS (option 4 in Make.solaris).

    umfpack_zl_demo.c		a full demo (complex/long version)
    umfpack_zl_demo.out		output of umfpack_zl_demo with Make.generic
    umfpack_zl_demo.out64	output of umfpack_zl_demo with Make.solaris,
				in LP64 mode and using the Sun Performance
				Library BLAS (option 4 in Make.solaris).

    ----------------------------------------------------------------------------
    Non-user-callable C code:
    ----------------------------------------------------------------------------

    umf_analyze.[ch]		symbolic factorization of A'*A
    umf_apply_order.[ch]	apply column etree postorder
    umf_assemble.[ch]		assemble elements into current front
    umf_blas3_update.[ch]	rank-k update
    umf_build_tuples.[ch]	construct tuples for elements
    umf_build_tuples_usage.[ch]	determine memory usage for umf_build_tuples
    umf_colamd.[ch]		COLAMD pre-ordering, modified for UMFPACK
    umf_create_element.[ch]	create a new element
    umf_dump.[ch]		debugging routines
    umf_extend_front.[ch]	extend the current frontal matrix
    umf_free.[ch]		free memory
    umf_garbage_collection.[ch]	compact Numeric->Memory
    umf_get_memory.[ch]		make Numeric->Memory bigger
    umf_init_front.[ch]		initialize a new frontal matrix
    umf_is_permutation.[ch]	checks the validity of a permutation vector
    umf_kernel.[ch]		the main numeric factorization kernel
    umf_kernel_init.[ch]	initializations for umf_kernel
    umf_kernel_init_usage.[ch]	determine memory usage for umf_kernel_init
    umf_kernel_wrapup.[ch]	wrapup for umf_kernel
    umf_local_search.[ch]	local row and column pivot search
    umf_lsolve.[ch]		solve Lx=b
    umf_ltsolve.[ch]		solve L'x=b and L.'x=b
    umf_malloc.[ch]		malloc some memory
    umf_mem_alloc_element.[ch]		allocate element in Numeric->Memory
    umf_mem_alloc_head_block.[ch]	alloc. block at head of Numeric->Memory
    umf_mem_alloc_tail_block.[ch]	alloc. block at tail of Numeric->Memory
    umf_mem_free_tail_block.[ch]	free block at tail of Numeric->Memory
    umf_mem_init_memoryspace.[ch]	initialize Numeric->Memory
    umf_order_front_tree.[ch]	column etree post-ordering
    umf_realloc.[ch]		realloc memory
    umf_report_perm.[ch]	print a permutation vector
    umf_report_vector.[ch]	print a double vector
    umf_row_search.[ch]		look for a pivot row
    umf_scale_column.[ch]	divide the pivot column by the pivot and log LU
    umf_set_stats.[ch]		set statistics (final or estimates)
    umf_solve.[ch]		solve a linear system
    umf_symbolic_usage.[ch]	determine memory usage for Symbolic object
    umf_transpose.[ch]		transpose a matrix in row or col form
    umf_tuple_lengths.[ch]	determine the tuple list lengths
    umf_usolve.[ch]		solve Ux=b
    umf_utsolve.[ch]		solve U'x=b and U.'x=b
    umf_valid_numeric.[ch]	checks the validity of a Numeric object
    umf_valid_symbolic.[ch]	check the validity of a Symbolic object

    ----------------------------------------------------------------------------
    Non-user-accessible C include files:
    ----------------------------------------------------------------------------

    umf_config.h		configuration file
    umf_internal.h		definitions internal to UMFPACK
    umf_version.h		version definitions (int/long, real/complex)

    ----------------------------------------------------------------------------
    BLAS library for MATLAB's lcc compiler 
    ----------------------------------------------------------------------------

    lcc_lib/libmwlapack.lib	for mex, needed for Windows version only
    lcc_lib/lapacksyms.def	used to created libmwlapack.lib

    ----------------------------------------------------------------------------
    For Windows (these are not tested)
    ----------------------------------------------------------------------------

    Make.windows		include file for Cygnus make on Windows
    libumfpack.def		routines exported by libumfpack.dll


```

sparse-matrix/umfpack/umfpack_numeric.c:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Factorizes A into its LU factors, given a symbolic
    pre-analysis computed by UMFPACK_symbolic.  See umfpack_numeric.h for a
    description.

    Dynamic memory usage: UMFPACK_numeric makes the heaviest usage of memory
    space of all UMFPACK routines.  Here is an outline of how it uses memory:

	1) calls UMF_malloc 14 times, to obtain temporary workspace of size f
	   Entry's and 2*(n_row+1) + 2*(n_col+1) + (n_col+n_inner+1) +
	   (nn+1) + 3*(c+1) + 2*(r+1) + max(r,c) + (nfr+1) integers
	   where f is the size of the working array used for the frontal
	   matrices, r is the maximum number of rows in the working array,
	   c is the maximum number of columns in the working array (f is
	   less than or equal to r*c, and typically f=r*c), n_inner is
	   min (n_row,n_col), nn is max (n_row,n_col), and nfr is the number
	   of frontal matrices.  For a square matrix, this is f Entry's and
	   about 7n + 3c + 2r + max(r,c) + nfr integers.

	2) calls UMF_malloc 10 times, for a total space of sizeof (NumericType)
	   bytes, 4*(n_row+1) + 4*(n_row+1) integers, and (n_inner+1) Entry's.
	   sizeof (NumericType) is a small constant.  This space is part of the
	   permanent Numeric object (which holds the LU factors) and is not
	   freed on return via UMF_free unless an error occurs.

	3) calls UMF_malloc once, to allocate the variable-sized part of the
	   Numeric object, whose size in Units is the larger of:
	   (Control [UMFPACK_ALLOC_INIT]) *  (the approximate upper bound
	   computed by UMFPACK_symbolic), and the minimum required to start the
	   numerical factorization.  The default value of
	   Control [UMFPACK_ALLOC_INIT] is 1.0.  This request is reduced if it
	   fails.  This object is not freed on return via UMF_free unless
	   an error occurs.

	4) During numerical factorization (inside UMF_kernel), the variable-size
	   block of memory is increased in size via a call to UMF_realloc if
	   it is found to be too small.  This is rare with the default control
	   setting of 1.0 (I've never observed it to happen, but it
	   theoretically could happen).  During factorization, this block holds
	   the pattern and values of L and U at the top end, and the elements
	   (contibution blocks) at the bottom end.

	   For a square nonsingular matrix, the peak memory usage at this point
	   is roughly:

		r*c + n Entry's
		15n + 3c + 2r + max(r,c) + nfr integers
		plus peak size of the variable-sized part of the Numeric object
		plus the size of the Symbolic object (2n to 9n integers)

	   where r is the maximum number of rows in the working array used
	   to hold the current frontal matrix, and c is the maximum number of
	   columns in the working array.

	   The peak value of the variable-sized object is estimated in
	   UMFPACK_*symbolic (Info [UMFPACK_VARIABLE_PEAK_ESTIMATE]).
	   The size of the Symbolic object is in Info [UMFPACK_SYMBOLIC_SIZE],
	   and is between 2*n and 9*n integers.

	5) After numerical factorization all of the objects allocated in step
	   (1) are freed via UMF_free, except that one object of size n_col+1
	   is kept if there are nonzeros in the last pivot row.

	6) The variable-sized block is reduced to hold just L and U, via a call
	   to UMF_realloc, since the frontal matrices are no longer needed.

	7) This leaves a total of 11 or 12 objects allocated by UMF_malloc that
	   form the LU factorization.  These remain if UMFPACK_numeric was
	   successful.  Otherwise, they are all freed via UMF_free.
	   The final size of the Numeric object for a square nonsingular matrix
	   is roughly:

		n Entry's
		8n integers
		plus final size of the variable-sized part of the Numeric object

    Dynamic memory usage of UMFPACK_free_numeric:

	1) It frees, via UMF_free, all 11 or 12 objects allocated for the
	   Numeric object, in a prior call to UMFPACK_numeric.

*/

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

#include "umf_internal.h"
#include "umf_valid_symbolic.h"
#include "umf_set_stats.h"
#include "umf_kernel.h"
#include "umf_malloc.h"
#include "umf_free.h"
#include "umf_realloc.h"

#ifndef NDEBUG
PRIVATE Int init_count ;
#endif

PRIVATE Int work_alloc
(
    WorkType *Work
) ;

PRIVATE void free_work
(
    WorkType *Work
) ;

PRIVATE Int numeric_alloc
(
    NumericType **NumericHandle,
    SymbolicType *Symbolic,
    double alloc_init
) ;

PRIVATE void error
(
    NumericType **Numeric,
    WorkType *Work
) ;


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

GLOBAL Int UMFPACK_numeric
(
    const Int Ap [ ],
    const Int Ai [ ],
    const double Ax [ ],
#ifdef COMPLEX
    const double Az [ ],
#endif
    void *SymbolicHandle,
    void **NumericHandle,
    const double Control [UMFPACK_CONTROL],
    double User_Info [UMFPACK_INFO]
)
{

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

    NumericType *Numeric ;
    SymbolicType *Symbolic ;
    WorkType WorkSpace, *Work ;
    Int n_row, n_col, n_inner, newsize, i, status, *inew, npiv, ulen ;
    Unit *mnew ;
    double Info2 [UMFPACK_INFO], *Info, alloc_init, relax, relpt, tstart, tend,
	relax2, relax3 ;

    /* ---------------------------------------------------------------------- */
    /* get the amount of time used by the process so far */
    /* ---------------------------------------------------------------------- */

    tstart = umfpack_timer ( ) ;

    /* ---------------------------------------------------------------------- */
    /* initialize and check inputs */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    init_count = UMF_malloc_count ;
#endif

    if (Control)
    {
	/* use the Control array passed to us by the caller; look for NaN's */

	if (SCALAR_IS_NAN (Control [UMFPACK_PIVOT_TOLERANCE]))
	{
	    relpt = UMFPACK_DEFAULT_PIVOT_TOLERANCE ;
	}
	else
	{
	    relpt = Control [UMFPACK_PIVOT_TOLERANCE] ;
	}

	if (SCALAR_IS_NAN (Control [UMFPACK_RELAXED_AMALGAMATION]))
	{
	    relax = UMFPACK_DEFAULT_RELAXED_AMALGAMATION ;
	}
	else
	{
	    relax = Control [UMFPACK_RELAXED_AMALGAMATION] ;
	}

	if (SCALAR_IS_NAN (Control [UMFPACK_RELAXED2_AMALGAMATION]))
	{
	    relax2 = UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ;
	}
	else
	{
	    relax2 = Control [UMFPACK_RELAXED2_AMALGAMATION] ;
	}

	if (SCALAR_IS_NAN (Control [UMFPACK_RELAXED3_AMALGAMATION]))
	{
	    relax3 = UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ;
	}
	else
	{
	    relax3 = Control [UMFPACK_RELAXED3_AMALGAMATION] ;
	}

	if (SCALAR_IS_NAN (Control [UMFPACK_ALLOC_INIT]))
	{
	    alloc_init = UMFPACK_DEFAULT_ALLOC_INIT ;
	}
	else
	{
	    alloc_init = Control [UMFPACK_ALLOC_INIT] ;
	}

    }
    else
    {
	/* no Control passed - use defaults instead */
	relpt = UMFPACK_DEFAULT_PIVOT_TOLERANCE ;
	relax = UMFPACK_DEFAULT_RELAXED_AMALGAMATION ;
	relax2 = UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ;
	relax3 = UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ;
	alloc_init = UMFPACK_DEFAULT_ALLOC_INIT ;
    }

    relpt = MAX (0.0, MIN (relpt, 1.0)) ;
    relax = MAX (0.0, relax) ;
    relax2 = MAX (0.0, relax2) ;
    relax3 = MAX (0.0, relax3) ;
    alloc_init = MAX (0.0, alloc_init) ;

    if (User_Info)
    {
	/* return Info in user's array */
	Info = User_Info ;
	for (i = UMFPACK_NUMERIC_SIZE ; i <= UMFPACK_NUMERIC_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 ;
	}
    }

    Symbolic = (SymbolicType *) SymbolicHandle ;
    Numeric = (NumericType *) NULL ;
    if (!UMF_valid_symbolic (Symbolic))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Symbolic_object ;
	return (UMFPACK_ERROR_invalid_Symbolic_object) ;
    }

    n_row = Symbolic->n_row ;
    n_col = Symbolic->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_NUMERIC_DEFRAG] = 0 ;
    Info [UMFPACK_NUMERIC_REALLOC] = 0 ;
    Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = 0 ;

    if (!Ap || !Ai || !Ax || !NumericHandle
#ifdef COMPLEX
	|| !Az
#endif
    )
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ;
	return (UMFPACK_ERROR_argument_missing) ;
    }

    Info [UMFPACK_NZ] = Ap [n_col] ;
    *NumericHandle = (void *) NULL ;

    /* ---------------------------------------------------------------------- */
    /* allocate the Work object */
    /* ---------------------------------------------------------------------- */

    Work = &WorkSpace ;
    Work->n_row = n_row ;
    Work->n_col = n_col ;
    Work->nfr = Symbolic->nfr ;
    Work->maxfrsize = Symbolic->maxfrsize ;
    Work->maxnrows = Symbolic->maxnrows ;
    Work->maxncols = Symbolic->maxncols ;

    if (!work_alloc (Work))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Numeric, Work) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }
    ASSERT (UMF_malloc_count == init_count + 14) ;

    /* ---------------------------------------------------------------------- */
    /* allocate Numeric object */
    /* ---------------------------------------------------------------------- */

    if (!numeric_alloc (&Numeric, Symbolic, alloc_init))
    {
	Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ;
	error (&Numeric, Work) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }
    DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n",
	init_count, UMF_malloc_count)) ;
    ASSERT (UMF_malloc_count == init_count + 14 + 11) ;

    /* set control parameters */
    Numeric->relpt = relpt ;
    Numeric->relax = relax ;
    Numeric->relax2 = relax2 ;
    Numeric->relax3 = relax3 ;
    Numeric->alloc_init = alloc_init ;

    DEBUG0 (("umf control: relpt %g relax [%g %g %g] init %g inc %g red %g\n",
	relpt, relax, relax2, relax3, alloc_init,
	UMF_REALLOC_INCREASE, UMF_REALLOC_REDUCTION)) ;

    /* ---------------------------------------------------------------------- */
    /* factorize */
    /* ---------------------------------------------------------------------- */

    status = UMF_kernel (Ap, Ai, Ax,
#ifdef COMPLEX
	Az,
#endif
	Numeric, Work, Symbolic) ;
    Info [UMFPACK_STATUS] = status ;
    Info [UMFPACK_VARIABLE_INIT] = Numeric->init_usage ;
    if (status < 0)
    {
	/* out of memory, or pattern has changed */
	error (&Numeric, Work) ;
	return (status) ;
    }
    DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n",
	init_count, UMF_malloc_count)) ;

    npiv = Numeric->npiv ;	/* = n_inner for nonsingular matrices */
    ulen = Numeric->ulen ;	/* = 0 for square nonsingular matrices */

    /* ---------------------------------------------------------------------- */
    /* free Work object */
    /* ---------------------------------------------------------------------- */

    DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n",
	init_count, UMF_malloc_count)) ;
    free_work (Work) ;
    DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n",
	init_count, UMF_malloc_count)) ;
    DEBUG0 (("Numeric->ulen: "ID"\n", ulen)) ;
    ASSERT (UMF_malloc_count == init_count + 11 + (ulen > 0)) ;

    /* ---------------------------------------------------------------------- */
    /* reduce Lpos, Lilen, Lip, Upos, Uilen and Uip to size npiv+1 */
    /* ---------------------------------------------------------------------- */

    /* This is only needed for rectangular or singular matrices. */

    if (npiv < n_row)
    {
	/* reduce Lpos, Uilen, and Uip from size n_row+1 to size npiv */
	inew = (Int *) UMF_realloc (Numeric->Lpos, npiv+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Lpos = inew ;
	}
	inew = (Int *) UMF_realloc (Numeric->Uilen, npiv+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Uilen = inew ;
	}
	inew = (Int *) UMF_realloc (Numeric->Uip, npiv+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Uip = inew ;
	}
    }

    if (npiv < n_col)
    {
	/* reduce Upos, Lilen, and Lip from size n_col+1 to size npiv */
	inew = (Int *) UMF_realloc (Numeric->Upos, npiv+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Upos = inew ;
	}
	inew = (Int *) UMF_realloc (Numeric->Lilen, npiv+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Lilen = inew ;
	}
	inew = (Int *) UMF_realloc (Numeric->Lip, npiv+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Lip = inew ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* reduce Numeric->Upattern from size n_col+1 to size ulen+1 */
    /* ---------------------------------------------------------------------- */

    /* This is only needed for singular matrices, or if n_row < n_col. */
    /* If ulen is zero, the object does not exist. */

    DEBUG4 (("ulen: "ID" Upattern "ID"\n", ulen, (Int) Numeric->Upattern)) ;
    ASSERT (IMPLIES (ulen == 0, Numeric->Upattern == (Int *) NULL)) ;
    if (ulen > 0 && ulen < n_col)
    {
	inew = (Int *) UMF_realloc (Numeric->Upattern, ulen+1, sizeof (Int)) ;
	if (inew)
	{
	    Numeric->Upattern = inew ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* reduce Numeric->Memory to hold just the LU factors at the head */
    /* ---------------------------------------------------------------------- */

    newsize = Numeric->ihead ;
    if (newsize < Numeric->size)
    {
	mnew = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ;
	if (mnew)
	{
	    /* realloc succeeded (how can it fail since the size is reduced?) */
	    Numeric->Memory = mnew ;
	    Numeric->size = newsize ;
	}
    }
    Numeric->ihead = Numeric->size ;
    Numeric->itail = Numeric->ihead ;
    Numeric->tail_usage = 0 ;
    Numeric->ibig = EMPTY ;
    /* UMF_mem_alloc_tail_block can no longer be called (no tail marker) */

    /* ---------------------------------------------------------------------- */
    /* report the results and return the Numeric object */
    /* ---------------------------------------------------------------------- */

    UMF_set_stats (
	Info,
	Symbolic,
	(double) Numeric->max_usage,	/* actual peak Numeric->Memory */
	(double) Numeric->size,		/* actual final Numeric->Memory */
	Numeric->flops,			/* actual "true flops" */
	(double) Numeric->lnz + n_inner,		/* actual nz in L */
	(double) Numeric->unz + Numeric->nnzpiv,	/* actual nz in U */
	(double) Numeric->maxfrsize,	/* actual largest front size */
	(double) ulen,			/* actual Numeric->Upattern size */
	(double) npiv,			/* actual # pivots found */
	ACTUAL) ;

    Info [UMFPACK_NUMERIC_DEFRAG] = Numeric->ngarbage ;
    Info [UMFPACK_NUMERIC_REALLOC] = Numeric->nrealloc ;
    Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = Numeric->ncostly ;
    Info [UMFPACK_COMPRESSED_PATTERN] = Numeric->isize ;
    Info [UMFPACK_LU_ENTRIES] = Numeric->nLentries + Numeric->nUentries +
    	Numeric->npiv ;
    Info [UMFPACK_UDIAG_NZ] = Numeric->nnzpiv ;

    /* estimate of the recipricol of the condition number. */
    if (SCALAR_IS_ZERO (Numeric->min_udiag)
     || SCALAR_IS_ZERO (Numeric->max_udiag))
    {
	/* rcond is zero if there is any zero on the diagonal, */
	/* even if NaN's are also present. */
	Numeric->rcond = 0.0 ;
    }
    else
    {
	/* estimate of the recipricol of the condition number. */
	/* This is NaN if diagonal is zero-free, but has one or more NaN's. */
	Numeric->rcond = Numeric->min_udiag / Numeric->max_udiag ;
    }
    Info [UMFPACK_RCOND] = Numeric->rcond ;

    if (Numeric->nnzpiv < n_inner
    || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond))
    {
	/* there are zeros and/or NaN's on the diagonal of U */
	DEBUG0 (("Warning, matrix is singular in umfpack_numeric\n")) ;
	DEBUG0 (("nnzpiv "ID" n_inner "ID" rcond %g\n", Numeric->nnzpiv,
	    n_inner, Numeric->rcond)) ;
	status = UMFPACK_WARNING_singular_matrix ;
	Info [UMFPACK_STATUS] = status ;
    }

    Numeric->valid = NUMERIC_VALID ;
    *NumericHandle = (void *) Numeric ;

    /* Numeric has 11 or 12 objects */
    ASSERT (UMF_malloc_count == init_count + 11 + (ulen > 0)) ;

    /* ---------------------------------------------------------------------- */
    /* get the time used by UMFPACK_numeric */
    /* ---------------------------------------------------------------------- */

    tend = umfpack_timer ( ) ;
    Info [UMFPACK_NUMERIC_TIME] = MAX (0, tend - tstart) ;

    /* return UMFPACK_OK or UMFPACK_WARNING_singular_matrix */
    return (status) ;

}


/* ========================================================================== */
/* === numeric_alloc ======================================================== */
/* ========================================================================== */

/* Allocate the Numeric object */

PRIVATE Int numeric_alloc
(
    NumericType **NumericHandle,
    SymbolicType *Symbolic,
    double alloc_init
)
{
    Int n_row, n_col, n_inner, min_usage, trying ;
    NumericType *Numeric ;
    double nsize, bsize ;

    ASSERT (Symbolic) ;

    n_row = Symbolic->n_row ;
    n_col = Symbolic->n_col ;
    n_inner = MIN (n_row, n_col) ;
    *NumericHandle = (NumericType *) NULL ;

    /* 1 allocation:  accounted for in UMF_set_stats (num_fixed_size) */
    Numeric = (NumericType *) UMF_malloc (1, sizeof (NumericType)) ;

    if (!Numeric)
    {
	return (FALSE) ;	/* out of memory */
    }
    Numeric->valid = 0 ;
    *NumericHandle = Numeric ;

    /* 9 allocations:  accounted for in UMF_set_stats (num_fixed_size) */
    Numeric->D = (Entry *) UMF_malloc (n_inner+1, sizeof (Entry)) ;
    Numeric->Rperm = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ;
    Numeric->Cperm = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ;
    Numeric->Lpos = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ;
    Numeric->Lilen = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ;
    Numeric->Lip = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ;
    Numeric->Upos = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ;
    Numeric->Uilen = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ;
    Numeric->Uip = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ;

    Numeric->Memory = (Unit *) NULL ;
    Numeric->Upattern = (Int *) NULL ;	/* used for singular matrices only */

    if (!Numeric->D || !Numeric->Rperm || !Numeric->Cperm || !Numeric->Upos ||
	!Numeric->Lpos || !Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip ||
	!Numeric->Uip)
    {
	return (FALSE) ;	/* out of memory */
    }

    /* ---------------------------------------------------------------------- */
    /* allocate initial Numeric->Memory for LU factors and elements */
    /* ---------------------------------------------------------------------- */

    nsize = (alloc_init * Symbolic->num_mem_usage_est) * (1.0 + MAX_EPSILON) ;
    nsize = ceil (nsize) ;
    min_usage = Symbolic->num_mem_init_usage ;

    /* Numeric->Memory must be large enough for UMF_kernel_init */
    /* double relop, but ignore NaN case. */
    nsize = MAX (ceil ((double) min_usage * (1.0 + MAX_EPSILON)), nsize) ;

    /* Numeric->Memory cannot be larger in size than Int_MAX / sizeof(Unit) */
    /* For ILP32 mode:  2GB (nsize cannot be bigger than 256 Mwords) */

    bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ;
    DEBUG0 (("bsize %g\n", bsize)) ;

    nsize = MIN (nsize, bsize) ;    /* double relop, but ignore NaN case. */

    Numeric->size = (Int) nsize ;

    DEBUG0 (("Num init %g usage_est %g numsize "ID" minusage "ID"\n",
	alloc_init, Symbolic->num_mem_usage_est, Numeric->size, min_usage)) ;

    /* allocates 1 object: */
    /* keep trying until successful, or memory request is too small */
    trying = TRUE ;
    while (trying)
    {
	Numeric->Memory = (Unit *) UMF_malloc (Numeric->size, sizeof (Unit)) ;
	if (Numeric->Memory)
	{
	    DEBUG0 (("Successful Numeric->size: "ID"\n", Numeric->size)) ;
	    return (TRUE) ;
	}
	/* too much, reduce the request (but not below the minimum) */
	/* and try again */
	trying = Numeric->size > min_usage ;
	Numeric->size = (Int)
	    (UMF_REALLOC_REDUCTION * ((double) Numeric->size)) ;
	Numeric->size = MAX (min_usage, Numeric->size) ;
    }

    return (FALSE) ;	/* we failed to allocate Numeric->Memory */
}


/* ========================================================================== */
/* === work_alloc =========================================================== */
/* ========================================================================== */

/* Allocate the Work object.  Return TRUE if successful. */

PRIVATE Int work_alloc
(
    WorkType *Work
)
{
    Int n_row, n_col, nn, n_inner, maxfrsize, maxnrows, maxncols, nfr ;

    n_row = Work->n_row ;
    n_col = Work->n_col ;
    nn = MAX (n_row, n_col) ;
    n_inner = MIN (n_row, n_col) ;
    nfr = Work->nfr ;
    maxfrsize = Work->maxfrsize ;
    maxnrows = Work->maxnrows ;
    maxncols = Work->maxncols ;

    /* largest front will be maxnrows-by-maxncols, at most */
    DEBUG1 (("Allocating frontal matrix, size "ID" ("ID"-by-"ID") saved: "ID
	"\n", maxfrsize, maxnrows, maxncols,
	(maxnrows * maxncols) - maxfrsize)) ;

    /* 13 allocations, freed in free_work: */
    /* accounted for in UMF_set_stats (work_usage) */
    Work->Fx = (Entry *) UMF_malloc (maxfrsize, sizeof (Entry)) ;
    Work->Frpos = (Int *) UMF_malloc (n_row + 1, sizeof (Int)) ;
    Work->Fcpos = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ;
    Work->Lpattern = (Int *) UMF_malloc (n_row + 1, sizeof (Int)) ;
    Work->Wp = (Int *) UMF_malloc (nn + 1, sizeof (Int)) ;
    Work->Frows = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ;
    Work->Fcols = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ;
    Work->Wio = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ;
    Work->Woi = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ;
    Work->Woo = (Int *) UMF_malloc (MAX (maxnrows, maxncols) + 1, sizeof (Int));
    Work->Wm = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ;
    Work->E = (Int *) UMF_malloc (n_col + n_inner + 1, sizeof (Int)) ;
    Work->Front_new1strow = (Int *) UMF_malloc (nfr + 1, sizeof (Int)) ;

    /* 1 allocation, may become part of Numeric (if singular or rectangular): */
    Work->Upattern = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ;

    return (Work->Fx && Work->Frpos
	&& Work->Fcpos && Work->Lpattern && Work->Upattern
	&& Work->E && Work->Frows && Work->Fcols && Work->Wio
	&& Work->Woi && Work->Woo && Work->Wm && Work->Wp
	&& Work->Front_new1strow) ;
}


/* ========================================================================== */
/* === free_work ============================================================ */
/* ========================================================================== */

PRIVATE void free_work
(
    WorkType *Work
)
{
    if (Work)
    {

	/* free 13 or 14 objects (Upattern may already be gone) */
	Work->Fx = (Entry *) UMF_free ((void *) Work->Fx) ;
	Work->Frpos = (Int *) UMF_free ((void *) Work->Frpos) ;
	Work->Fcpos = (Int *) UMF_free ((void *) Work->Fcpos) ;
	Work->Lpattern = (Int *) UMF_free ((void *) Work->Lpattern) ;
	Work->Upattern = (Int *) UMF_free ((void *) Work->Upattern) ;
	Work->Wp = (Int *) UMF_free ((void *) Work->Wp) ;
	Work->Frows = (Int *) UMF_free ((void *) Work->Frows) ;
	Work->Fcols = (Int *) UMF_free ((void *) Work->Fcols) ;
	Work->Wio = (Int *) UMF_free ((void *) Work->Wio) ;
	Work->Woi = (Int *) UMF_free ((void *) Work->Woi) ;
	Work->Woo = (Int *) UMF_free ((void *) Work->Woo) ;
	Work->Wm = (Int *) UMF_free ((void *) Work->Wm) ;
	Work->E = (Int *) UMF_free ((void *) Work->E) ;
	Work->Front_new1strow =
	    (Int *) UMF_free ((void *) Work->Front_new1strow) ;

    }
}


/* ========================================================================== */
/* === error ================================================================ */
/* ========================================================================== */

/* Error return from UMFPACK_numeric.  Free all allocated memory. */

PRIVATE void error
(
    NumericType **Numeric,
    WorkType *Work
)
{
    free_work (Work) ;
    UMFPACK_free_numeric ((void **) Numeric) ;
    ASSERT (UMF_malloc_count == init_count) ;
}


```

sparse-matrix/umfpack/umfpack_free_symbolic.h:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

void umfpack_di_free_symbolic
(
    void **Symbolic
) ;

void umfpack_dl_free_symbolic
(
    void **Symbolic
) ;

void umfpack_zi_free_symbolic
(
    void **Symbolic
) ;

void umfpack_zl_free_symbolic
(
    void **Symbolic
) ;

/*
double int Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    umfpack_di_free_symbolic (&Symbolic) ;

double long Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    umfpack_dl_free_symbolic (&Symbolic) ;

complex int Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    umfpack_zi_free_symbolic (&Symbolic) ;

complex long Syntax:

    #include "umfpack.h"
    void *Symbolic ;
    umfpack_zl_free_symbolic (&Symbolic) ;

Purpose:

    Deallocates the Symbolic object and sets the Symbolic handle to NULL.
    This routine is the only valid way of destroying the Symbolic object;
    any other action (such as using "free (Symbolic) ;" or not freeing Symbolic
    at all) will lead to memory leaks.

Arguments:

    void **Symbolic ;		Input argument, deallocated and Symbolic is
				set to (void *) NULL on output.

	Symbolic must point to a valid Symbolic object, computed by
	umfpack_*_symbolic.  No action is taken if Symbolic is a (void *) NULL
	pointer.
*/

```

sparse-matrix/umfpack/umf_mem_alloc_element.c:
```
/* ========================================================================== */
/* === UMF_mem_alloc_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                       */
/* -------------------------------------------------------------------------- */

/* The UMF_mem_* routines manage the Numeric->Memory memory space. */

/* Allocate a nrows-by-ncols element, and initialize it. */
/* Returns the index into Numeric->Memory if successful, or 0 on failure. */

#include "umf_internal.h"
#include "umf_mem_alloc_tail_block.h"

GLOBAL Int UMF_mem_alloc_element
(
    NumericType *Numeric,
    Int nrows,
    Int ncols,
    Int **Rows,
    Int **Cols,
    Entry **C,
    Int *size,
    Element **epout
)
{

    Element *ep ;
    Unit *p ;
    Int i ;

    ASSERT (Numeric) ;
    ASSERT (Numeric->Memory) ;

#ifndef NDEBUG
    UMF_allocfail = FALSE ;
    if (UMF_gprob > 0)
    {
	double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ;
	DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ;
	UMF_allocfail = rrr < UMF_gprob ;   /* a double relop, but ignore NaN case */
	if (UMF_allocfail)
	{
	    DEBUG0 (("Random garbage collection (alloc_element)\n"));
	}
    }
#endif

    *size = GET_ELEMENT_SIZE (nrows, ncols) ;
    if (INT_OVERFLOW (DGET_ELEMENT_SIZE (nrows, ncols) + 1))
    {
	DEBUG0 (("alloc element failed - problem too large\n")) ;
	return (0) ;	/* problem is too large */
    }

    i = UMF_mem_alloc_tail_block (Numeric, *size) ;
    (*size)++ ;
    if (!i)
    {
	DEBUG0 (("alloc element failed - out of memory\n")) ;
	return (0) ;	/* out of memory */
    }
    p = Numeric->Memory + i ;

    ep = (Element *) p ;

    DEBUG2 (("alloc_element done ("ID" x "ID"): p: "ID" i "ID"\n",
	nrows, ncols, p-Numeric->Memory, i)) ;

    /* Element data structure, in order: */
    p += UNITS (Element, 1) ;		/* (1) Element header */
    *Cols = (Int *) p ;			/* (2) col [0..ncols-1] indices */
    *Rows = *Cols + ncols ;		/* (3) row [0..nrows-1] indices */
    p += UNITS (Int, ncols + nrows) ;
    *C = (Entry *) p ;			/* (4) C [0..nrows-1, 0..ncols-1] */

    ep->nrows = nrows ;		/* initialize the header information */
    ep->ncols = ncols ;
    ep->nrowsleft = nrows ;
    ep->ncolsleft = ncols ;
    ep->cdeg = 0 ;
    ep->rdeg = 0 ;
    ep->next = EMPTY ;

    DEBUG2 (("new block size: "ID" ", GET_BLOCK_SIZE (Numeric->Memory + i))) ;
    DEBUG2 (("Element size needed "ID"\n", GET_ELEMENT_SIZE (nrows, ncols))) ;

    *epout = ep ;

    /* return the offset into Numeric->Memory */
    return (i) ;
}


```

sparse-matrix/umfpack/umf_mem_alloc_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 Int UMF_mem_alloc_tail_block
(
    NumericType *Numeric,
    Int nunits
) ;


```

sparse-matrix/umfpack/umf_mem_free_tail_block.c:
```
/* ========================================================================== */
/* === UMF_mem_free_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. */

/* free a block from the tail of Numeric->memory */

#include "umf_internal.h"

GLOBAL void UMF_mem_free_tail_block
(
    NumericType *Numeric,
    Int i
)
{
    Unit *pprev, *pnext, *p, *pbig ;
    Int sprev ;

    ASSERT (Numeric) ;
    ASSERT (Numeric->Memory) ;
    if (i == EMPTY || i == 0) return ;	/* already deallocated */

    /* ---------------------------------------------------------------------- */
    /* get the block */
    /* ---------------------------------------------------------------------- */

    p = Numeric->Memory + i ;

    p-- ;	/* get the corresponding header */
    DEBUG2 (("free block: p: "ID, p-Numeric->Memory)) ;
    ASSERT (p >= Numeric->Memory + Numeric->itail) ;
    ASSERT (p < Numeric->Memory + Numeric->size) ;
    ASSERT (p->header.size > 0) ;		/* block not already free */
    ASSERT (p->header.prevsize >= 0) ;

    Numeric->tail_usage -= p->header.size + 1 ;

    /* ---------------------------------------------------------------------- */
    /* merge with next free block, if any */
    /* ---------------------------------------------------------------------- */

    pnext = p + 1 + p->header.size ;
    DEBUG2 (("size: "ID" next: "ID" ", p->header.size, pnext-Numeric->Memory)) ;
    ASSERT (pnext < Numeric->Memory + Numeric->size) ;
    ASSERT (pnext->header.prevsize == p->header.size) ;
    ASSERT (pnext->header.size != 0) ;

    if (pnext->header.size < 0)
    {
	/* next block is also free - merge with current block */
	p->header.size += (-pnext->header.size) + 1 ;
	DEBUG2 ((" NEXT FREE ")) ;
    }

    /* ---------------------------------------------------------------------- */
    /* merge with previous free block, if any */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    if (p == Numeric->Memory + Numeric->itail)
    {
	DEBUG2 ((" at top of tail ")) ;
	ASSERT (p->header.prevsize == 0) ;
    }
#endif

    if (p > Numeric->Memory + Numeric->itail)
    {
	ASSERT (p->header.prevsize > 0) ;
	pprev = p - 1 - p->header.prevsize ;
	DEBUG2 ((" prev: "ID" ", pprev-Numeric->Memory)) ;
	ASSERT (pprev >= Numeric->Memory + Numeric->itail) ;
	sprev = pprev->header.size ;
	if (sprev < 0)
	{
	    /* previous block is also free - merge it with current block */
	    ASSERT (p->header.prevsize == -sprev) ;
	    pprev->header.size = p->header.size + (-sprev) + 1 ;
	    p = pprev ;
	    DEBUG2 ((" PREV FREE ")) ;
	    /* note that p may now point to Numeric->itail */
	}
#ifndef NDEBUG
	else
	{
	    ASSERT (p->header.prevsize == sprev) ;
	}
#endif
    }

    /* ---------------------------------------------------------------------- */
    /* free the block, p */
    /* ---------------------------------------------------------------------- */

    pnext = p + 1 + p->header.size ;
    ASSERT (pnext < Numeric->Memory + Numeric->size) ;

    if (p == Numeric->Memory + Numeric->itail)
    {
	/* top block in list is freed */
	Numeric->itail = pnext - Numeric->Memory ;
	pnext->header.prevsize = 0 ;
	DEBUG2 ((" NEW TAIL : "ID" ", Numeric->itail)) ;
	ASSERT (pnext->header.size > 0) ;
	if (Numeric->ibig != EMPTY && Numeric->ibig <= Numeric->itail)
	{
	    /* the big free block is now above the tail */
	    Numeric->ibig = EMPTY ;
	}
    }
    else
    {
	/* keep track of the biggest free block seen */
	if (Numeric->ibig == EMPTY)
	{
	    Numeric->ibig = p - Numeric->Memory ;
	}
	else
	{
	    pbig = Numeric->Memory + Numeric->ibig ;
	    if (-(pbig->header.size) < p->header.size)
	    {
		Numeric->ibig = p - Numeric->Memory ;
	    }
	}
	/* flag the block as free, somewhere in the middle of the tail */
	pnext->header.prevsize = p->header.size ;
	p->header.size = -p->header.size ;
    }

    DEBUG2 (("new p: "ID" freesize: "ID"\n", p-Numeric->Memory,
	-p->header.size)) ;

#ifndef NDEBUG
    UMF_debug -= 10 ;
    UMF_dump_memory (Numeric) ;
    UMF_debug += 10 ;
#endif

}


```

sparse-matrix/umfpack/umf_triplet.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_triplet_map_x
(
    Int n_row,
    Int n_col,
    Int nz,
    const Int Ti [ ],
    const Int Tj [ ],
    Int Ap [ ],
    Int Ai [ ],
    Int Rp [ ],
    Int Rj [ ],
    Int W [ ],
    Int RowCount [ ]
    , const double Tx [ ]
    , double Ax [ ]
    , double Rx [ ]
#ifdef COMPLEX
    , const double Tz [ ]
    , double Az [ ]
    , double Rz [ ]
#endif
    , Int Map [ ]
    , Int Map2 [ ]
) ;

GLOBAL Int UMF_triplet_map_nox
(
    Int n_row,
    Int n_col,
    Int nz,
    const Int Ti [ ],
    const Int Tj [ ],
    Int Ap [ ],
    Int Ai [ ],
    Int Rp [ ],
    Int Rj [ ],
    Int W [ ],
    Int RowCount [ ]
    , Int Map [ ]
    , Int Map2 [ ]
) ;

GLOBAL Int UMF_triplet_nomap_x
(
    Int n_row,
    Int n_col,
    Int nz,
    const Int Ti [ ],
    const Int Tj [ ],
    Int Ap [ ],
    Int Ai [ ],
    Int Rp [ ],
    Int Rj [ ],
    Int W [ ],
    Int RowCount [ ]
    , const double Tx [ ]
    , double Ax [ ]
    , double Rx [ ]
#ifdef COMPLEX
    , const double Tz [ ]
    , double Az [ ]
    , double Rz [ ]
#endif
) ;

GLOBAL Int UMF_triplet_nomap_nox
(
    Int n_row,
    Int n_col,
    Int nz,
    const Int Ti [ ],
    const Int Tj [ ],
    Int Ap [ ],
    Int Ai [ ],
    Int Rp [ ],
    Int Rj [ ],
    Int W [ ],
    Int RowCount [ ]
) ;


```

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

/* -------------------------------------------------------------------------- */
/* 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_vector
(
    int n,
    const double X [ ],
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_dl_report_vector
(
    long n,
    const double X [ ],
    const double Control [UMFPACK_CONTROL]
) ;

int umfpack_zi_report_vector
(
    int n,
    const double Xx [ ], const double Xz [ ],
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_zl_report_vector
(
    long n,
    const double Xx [ ], const double Xz [ ],
    const double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int n, status ;
    double *X, Control [UMFPACK_CONTROL] ;
    status = umfpack_di_report_vector (n, X, Control) ;

double long Syntax:

    #include "umfpack.h"
    long n, status ;
    double *X, Control [UMFPACK_CONTROL] ;
    status = umfpack_dl_report_vector (n, X, Control) ;

complex int Syntax:

    #include "umfpack.h"
    int n, status ;
    double *Xx, *Xz, Control [UMFPACK_CONTROL] ;
    status = umfpack_zi_report_vector (n, Xx, Xz, Control) ;

complex long Syntax:

    #include "umfpack.h"
    long n, status ;
    double *Xx, *Xz, Control [UMFPACK_CONTROL] ;
    status = umfpack_zl_report_vector (n, Xx, Xz, Control) ;

Purpose:

    Verifies and prints a dense vector.

Returns:

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

    Otherwise:
    
    UMFPACK_OK if the vector is valid.
    UMFPACK_ERROR_argument_missing if X or Xx is missing.
    UMFPACK_ERROR_n_nonpositive if n <= 0.

Arguments:

    Int n ;		Input argument, not modified.

	X is a real or complex vector of size n.  Restriction: n > 0.

    double X [n] ;	Input argument, not modified.  For real versions.

	A real vector of size n.  X must not be (double *) NULL.

    double Xx [n or 2*n] ; Input argument, not modified.  For complex versions.
    double Xz [n or 0] ;   Input argument, not modified.  For complex versions.

	A complex vector of size n, in one of two storage formats.
	Xx must not be (double *) NULL.

	If Xz is not (double *) NULL, then Xx [i] is the real part of X (i) and
	Xz [i] is the imaginary part of X (i).  Both vectors are of length n.
	This is the "split" form of the complex vector X.

	If Xz is (double *) NULL, then Xx holds both real and imaginary parts,
	where Xx [2*i] is the real part of X (i) and Xx [2*i+1] is the imaginary
	part of X (i).  Xx is of length 2*n doubles.  If you have an ANSI C99
	compiler with the intrinsic double _Complex type, then Xx can be of
	type double _Complex in the calling routine and typecast to (double *)
	when passed to umfpack_*_report_vector (this is untested, however).
	This is the "merged" form of the complex vector X.

	Future work:  all complex routines in UMFPACK could use this same
	strategy for their complex arguments.  The split format is useful for
	MATLAB, which holds its real and imaginary parts in seperate arrays.
	The merged format is compatible with the intrinsic double _Complex
	type in ANSI C99, and is also compatible with SuperLU's method of
	storing complex matrices.  In the current version, only 
	umfpack_*_report_vector supports both formats.

    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/umfpack_report_triplet.h:
```
/* ========================================================================== */
/* === umfpack_report_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_report_triplet
(
    int n_row,
    int n_col,
    int nz,
    const int Ti [ ],
    const int Tj [ ],
    const double Tx [ ],
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_dl_report_triplet
(
    long n_row,
    long n_col,
    long nz,
    const long Ti [ ],
    const long Tj [ ],
    const double Tx [ ],
    const double Control [UMFPACK_CONTROL]
) ;

int umfpack_zi_report_triplet
(
    int n_row,
    int n_col,
    int nz,
    const int Ti [ ],
    const int Tj [ ],
    const double Tx [ ], const double Tz [ ],
    const double Control [UMFPACK_CONTROL]
) ;

long umfpack_zl_report_triplet
(
    long n_row,
    long n_col,
    long nz,
    const long Ti [ ],
    const long Tj [ ],
    const double Tx [ ], const double Tz [ ],
    const double Control [UMFPACK_CONTROL]
) ;

/*
double int Syntax:

    #include "umfpack.h"
    int n_row, n_col, nz, *Ti, *Tj, status ;
    double *Tx, Control [UMFPACK_CONTROL] ;
    status = umfpack_di_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Control) ;

double long Syntax:

    #include "umfpack.h"
    long n_row, n_col, nz, *Ti, *Tj, status ;
    double *Tx, Control [UMFPACK_CONTROL] ;
    status = umfpack_dl_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Control) ;

complex int Syntax:

    #include "umfpack.h"
    int n_row, n_col, nz, *Ti, *Tj, status ;
    double *Tx, *Tz, Control [UMFPACK_CONTROL] ;
    status = umfpack_zi_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Tz,
	Control) ;

complex long Syntax:

    #include "umfpack.h"
    long n_row, n_col, nz, *Ti, *Tj, status ;
    double *Tx, *Tz, Control [UMFPACK_CONTROL] ;
    status = umfpack_zl_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Tz,
	Control) ;

Purpose:

    Verifies and prints a matrix in triplet form.

Returns:

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

    Otherwise:

    UMFPACK_OK if the Triplet matrix is OK.
    UMFPACK_ERROR_argument_missing if 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 any row or column index in Ti and/or Tj
	is not in the range 0 to n_row-1 or 0 to n_col-1, respectively.

Arguments:

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

	A is an n_row-by-n_col matrix.

    Int nz ;		Input argument, not modified.

	The number of entries in the triplet form of the matrix.

    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 for complex versions) hold the "triplet" form of a
	sparse matrix.  The kth nonzero entry is in row i = Ti [k], column
	j = Tj [k], the real numerical value of a_ij is Tx [k], and 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 or 0 to
	n_col-1, respectively.  Duplicate entries may be present.  The
	"triplets" may be in any order.  Tx and Tz are optional; if Tx or Tz are
	not present ((double *) NULL pointers), then the numerical values are
	not printed.

	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.

    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_analyze.c:
```
/* ========================================================================== */
/* === UMF_analyze ========================================================== */
/* ========================================================================== */

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

/*
    Symbolic LL' factorization of A'*A, to get upper bounds on the size of
    L and U for LU = PAQ, and to determine the frontal matrices and
    (supernodal) column elimination tree.  No fill-reducing column pre-ordering
    is used.

    Returns TRUE if successful, FALSE if out of memory.  UMF_analyze can only
    run out of memory if anzmax (which is Ap [n_row]) is too small.

    Uses workspace of size O(nonzeros in A).  On input, the matrix A is
    stored in row-form at the tail end of Ai.  It is destroyed on output.
    The rows of A must be sorted by increasing first column index.
    The matrix is assumed to be valid.

    Empty rows and columns have already been removed.

*/

#include "umf_internal.h"
#include "umf_order_front_tree.h"
#include "umf_apply_order.h"

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

GLOBAL Int UMF_analyze
(
    Int n_row,		/* A is n_row-by-n_col */
    Int n_col,
    Int Ai [ ],		/* Ai [Ap [0]..Ap[n_row]-1]: column indices */
			/* destroyed on output.  Note that this is NOT the */
			/* user's Ai that was passed to UMFPACK_*symbolic */
			/* size of Ai, Ap [n_row] = anzmax >= anz + n_col */
			/* Ap [0] must be => n_col.  The space to the */
			/* front of Ai is used as workspace. */

    Int Ap [ ],		/* of size MAX (n_row, n_col) + 1 */
			/* Ap [0..n_row]: row pointers */
			/* Row i is in Ai [Ap [i] ... Ap [i+1]-1] */

			/* rows must have smallest col index first, or be */
			/* in sorted form.  Used as workspace of size n_col */
			/* and destroyed. */

			/* Note that this is NOT the */
			/* user's Ap that was passed to UMFPACK_*symbolic */

    Int Up [ ],		/* workspace of size n_col, and output column perm. */

    /* temporary workspaces: */
    Int W [ ],		/* W [0..n_col-1] */
    Int Link [ ],	/* Link [0..n_col-1] */

    /* output: information about each frontal matrix: */
    Int Front_ncols [ ],	/* size n_col */
    Int Front_nrows [ ],	/* of size n_col */
    Int Front_npivcol [ ],	/* of size n_col */
    Int Front_parent [ ],	/* of size n_col */
    Int *nfr_out,

    Int *p_ncompactions		/* number of compactions in UMF_analyze */
)
{
    /* ====================================================================== */
    /* ==== local variables ================================================= */
    /* ====================================================================== */

    Int j, j3, col, k, row, parent, j2, pdest, p, p2, thickness, npivots, nfr,
	frsize, i, *Winv, kk, npiv, jnext, krow, knext, pfirst,
	jlast, ncompactions, *Stack, *Front_maxfr, *Front_order, *Front_child,
	*Front_sibling, fprev, maxfrsize, bigf, fnext, bigfprev, f, Wflag,
	npivcol, fallrows, fallcols, fpiv, frows, fcols ;

#ifndef NDEBUG
    Int nfr2, nchild ;
#endif

    nfr = 0 ;
    DEBUG0 (("UMF_analyze: anzmax "ID" anrow "ID" ancol "ID"\n",
	Ap [n_row], n_row, n_col)) ;

    /* ====================================================================== */
    /* ==== initializations ================================================= */
    /* ====================================================================== */

    for (j = 0 ; j < n_col ; j++)
    {
	Link [j] = EMPTY ;
	W [j] = EMPTY ;
	Up [j] = EMPTY ;

	/* Frontal matrix data structure: */
	Front_npivcol [j] = 0 ;		/* number of pivot columns */
	Front_nrows [j] = 0 ;		/* number of rows, incl. pivot rows */
	Front_ncols [j] = 0 ;		/* number of cols, incl. pivot cols */
	Front_parent [j] = EMPTY ;	/* parent front */
	/* Note that only non-pivotal columns are stored in a front (a "row" */
	/* of U) during elimination. */
    }

    /* the rows must be sorted by increasing min col */
    krow = 0 ;
    pfirst = Ap [0] ;
    jlast = EMPTY ;
    jnext = EMPTY ;
    Wflag = 0 ;

    ASSERT (pfirst >= n_col) ;	/* Ai must be large enough */

    /* pdest points to the first free space in Ai */
    pdest = 0 ;
    ncompactions = 0 ;

    /* ====================================================================== */
    /* === compute symbolic LL' factorization (unsorted) ==================== */
    /* ====================================================================== */

    for (j = 0 ; j < n_col ; j = jnext)
    {
	DEBUG1 (("\n\n============Front "ID" starting. nfr = "ID"\n", j, nfr)) ;

	/* ================================================================== */
	/* === garbage collection =========================================== */
	/* ================================================================== */

	if (pdest + (n_col-j) > pfirst)
	{
	    /* we might run out ... compact the rows of U */

#ifndef NDEBUG
	    DEBUG0 (("UMF_analyze COMPACTION, j="ID" pfirst="ID"\n",
		j, pfirst)) ;
	    for (row = 0 ; row < j ; row++)
	    {
		if (Up [row] != EMPTY)
		{
		    /* this is a live row of U */
		    DEBUG1 (("Live row: "ID" cols: ", row)) ;
		    p = Up [row] ;
		    ASSERT (Front_ncols [row] > Front_npivcol [row]) ;
		    p2 = p + (Front_ncols [row] - Front_npivcol [row]) ;
		    for ( ; p < p2 ; p++)
		    {
			DEBUG1 ((ID, Ai [p])) ;
			ASSERT (p < pfirst) ;
			ASSERT (Ai [p] > row && Ai [p] < n_col) ;
		    }
		    DEBUG1 (("\n")) ;
		}
	    }
	    DEBUG1 (("\nStarting to compact:\n")) ;
#endif

	    pdest = 0 ;
	    ncompactions++ ;
	    for (row = 0 ; row < j ; row++)
	    {
		if (Up [row] != EMPTY)
		{
		    /* this is a live row of U */
		    DEBUG1 (("Live row: "ID" cols: ", row)) ;
		    ASSERT (row < n_col) ;
		    p = Up [row] ;
		    ASSERT (Front_ncols [row] > Front_npivcol [row]) ;
		    p2 = p + (Front_ncols [row] - Front_npivcol [row]) ;
		    Up [row] = pdest ;
		    for ( ; p < p2 ; p++)
		    {
			DEBUG1 ((ID, Ai [p])) ;
			ASSERT (p < pfirst) ;
			ASSERT (Ai [p] > row && Ai [p] < n_col) ;
			Ai [pdest++] = Ai [p] ;
			ASSERT (pdest <= pfirst) ;
		    }
		    DEBUG1 (("\n")) ;
		}
	    }

#ifndef NDEBUG
	    DEBUG1 (("\nAFTER COMPACTION, j="ID" pfirst="ID"\n", j, pfirst)) ;
	    for (row = 0 ; row < j ; row++)
	    {
		if (Up [row] != EMPTY)
		{
		    /* this is a live row of U */
		    DEBUG1 (("Live row: "ID" cols: ", row)) ;
		    p = Up [row] ;
		    ASSERT (Front_ncols [row] > Front_npivcol [row]) ;
		    p2 = p + (Front_ncols [row] - Front_npivcol [row]) ;
		    for ( ; p < p2 ; p++)
		    {
			DEBUG1 ((ID, Ai [p])) ;
			ASSERT (p < pfirst) ;
			ASSERT (Ai [p] > row && Ai [p] < n_col) ;
		    }
		    DEBUG1 (("\n")) ;
		}
	    }
#endif

	}

	if (pdest + (n_col-j) > pfirst)
	{
	   /* Out of memory!  This is not supposed to happen ... */
	   /* it can't, if pfirst >= n_col */
	   return (FALSE) ;	/* internal error! */
	}

	/* ------------------------------------------------------------------ */
	/* is the last front a child of this one? */
	/* ------------------------------------------------------------------ */

	if (jlast != EMPTY && Link [j] == jlast)
	{
	    /* yes - create row j by appending to jlast */
	    DEBUG1 (("GOT:last front is child of this one: j "ID" jlast "ID"\n",
		j, jlast)) ;
	    ASSERT (jlast >= 0 && jlast < j) ;

	    Up [j] = Up [jlast] ;
	    Up [jlast] = EMPTY ;

	    /* find the parent, delete column j, and update W */
	    parent = n_col ;
	    for (p = Up [j] ; p < pdest ; )
	    {
		j3 = Ai [p] ;
		DEBUG1 (("Initial row of U: col "ID" ", j3)) ;
		ASSERT (j3 >= 0 && j3 < n_col) ;
		DEBUG1 (("W: "ID" \n", W [j3])) ;
		ASSERT (W [j3] == Wflag) ;
		if (j == j3)
		{
		    DEBUG1 (("Found column j at p = "ID"\n", p)) ;
		    Ai [p] = Ai [--pdest] ;
		}
		else
		{
		    if (j3 < parent)
		    {
			parent = j3 ;
		    }
		    p++ ;
	    	}
	    }

	    /* delete jlast from the link list of j */
	    Link [j] = Link [jlast] ;

	    ASSERT (Front_nrows [jlast] > Front_npivcol [jlast]) ;
	    thickness = (Front_nrows [jlast] - Front_npivcol [jlast]) ;

	}
	else
	{
	    Up [j] = pdest ;
	    parent = n_col ;
	    /* thickness: number of (nonpivotal) rows in frontal matrix j */
	    thickness = 0 ;
	    Wflag = j ;
	}

	/* ================================================================== */
	/* === compute row j of A*A' ======================================== */
	/* ================================================================== */

	/* ------------------------------------------------------------------ */
	/* flag the diagonal entry in row U, but do not add to pattern */
	/* ------------------------------------------------------------------ */

	ASSERT (pdest <= pfirst) ;
	W [j] = Wflag ;

	DEBUG1 (("\nComputing row "ID" of A'*A\n", j)) ;
	DEBUG2 (("	col: "ID" (diagonal)\n", j)) ;

	/* ------------------------------------------------------------------ */
	/* find the rows the contribute to this column j */
	/* ------------------------------------------------------------------ */

	jnext = n_col ;
	for (knext = krow ; knext < n_row ; knext++)
	{
	    ASSERT (Ap [knext] < Ap [knext+1]) ;

		ASSERT (Ap [knext] >= pfirst && Ap [knext] <= Ap [n_row]) ;
		jnext = Ai [Ap [knext]] ;
		ASSERT (jnext >= j) ;
		if (jnext != j)
		{
		    break ;
		}
	}

	/* rows krow ... knext-1 all have first column index of j */
	/* (or are empty) */

	/* row knext has first column index of jnext */
	/* if knext = n_row, then jnext is n_col */
	if (knext == n_row)
	{
	    jnext = n_col ;
	}

	ASSERT (jnext > j) ;
	ASSERT (jnext <= n_col) ;

	/* ------------------------------------------------------------------ */
	/* for each nonzero A (k,j) in column j of A do: */
	/* ------------------------------------------------------------------ */

	for (k = krow ; k < knext ; k++)
	{
	    p = Ap [k] ;
	    p2 = Ap [k+1] ;
	    ASSERT (p < p2) ;

		/* merge row k of A into W */
		DEBUG2 (("	---- A row "ID" ", k)) ;
		ASSERT (k >= 0 && k < n_row) ;
		ASSERT (Ai [p] == j) ;
		DEBUG2 (("  p "ID" p2 "ID"\n        cols:", p, p2)) ;
		ASSERT (p  >= pfirst && p  < Ap [n_row]) ;
		ASSERT (p2 >  pfirst && p2 <= Ap [n_row]) ;
		for ( ; p < p2 ; p++)
		{
		    /* add to pattern if seen for the first time */
		    col = Ai [p] ;
		    ASSERT (col >= j && col < n_col) ;
		    DEBUG3 ((" "ID, col)) ;
		    if (W [col] != Wflag)
		    {
			Ai [pdest++] = col ;
			ASSERT (pdest <= pfirst) ;
			/* flag this column has having been seen for row j */
			W [col] = Wflag ;
			if (col < parent)
			{
			    parent = col ;
			}
		    }
		}
		DEBUG2 (("\n")) ;
		thickness++ ;

	}

#ifndef NDEBUG
	DEBUG3 (("\nRow "ID" of A'A:\n", j)) ;
	for (p = Up [j] ; p < pdest ; p++)
	{
	    DEBUG3 ((" "ID, Ai [p])) ;
	}
	DEBUG3 (("\n")) ;
#endif

	/* ------------------------------------------------------------------ */
	/* delete rows up to but not including knext */
	/* ------------------------------------------------------------------ */

	krow = knext ;
	pfirst = Ap [knext] ;

	/* we can now use Ai [0..pfirst-1] as workspace for rows of U */

	/* ================================================================== */
	/* === compute jth row of U ========================================= */
	/* ================================================================== */

	/* for each nonzero U (k,j) in column j of U (1:j-1,:) do */
	for (k = Link [j] ; k != EMPTY ; k = Link [k])
	{
	    /* merge row k of U into W */
	    DEBUG2 (("	---- U row "ID, k)) ;
	    ASSERT (k >= 0 && k < n_col) ;
	    ASSERT (Up [k] != EMPTY) ;
	    p = Up [k] ;
	    ASSERT (Front_ncols [k] > Front_npivcol [k]) ;
	    p2 = p + (Front_ncols [k] - Front_npivcol [k]) ;
	    DEBUG2 (("  p "ID" p2 "ID"\n        cols:", p, p2)) ;
	    ASSERT (p <= pfirst) ;
	    ASSERT (p2 <= pfirst) ;
	    for ( ; p < p2 ; p++)
	    {
		/* add to pattern if seen for the first time */
		col = Ai [p] ;
		ASSERT (col >= j && col < n_col) ;
		DEBUG3 ((ID, col)) ;
		if (W [col] != Wflag)
		{
		    Ai [pdest++] = col ;
		    ASSERT (pdest <= pfirst) ;
		    /* flag this col has having been seen for row j */
		    W [col] = Wflag ;
		    if (col < parent)
		    {
			parent = col ;
		    }
		}
	    }
	    DEBUG2 (("\n")) ;

	    /* mark the row k as deleted */
	    Up [k] = EMPTY ;

	    ASSERT (Front_nrows [k] > Front_npivcol [k]) ;
	    thickness += (Front_nrows [k] - Front_npivcol [k]) ;
	    ASSERT (Front_parent [k] == j) ;
	}

#ifndef NDEBUG
	DEBUG3 (("\nRow "ID" of U prior to supercolumn detection:\n", j));
	for (p = Up [j] ; p < pdest ; p++)
	{
	    DEBUG3 ((" "ID, Ai [p])) ;
	}
	DEBUG3 (("\n")) ;
#endif

	/* ================================================================== */
	/* === quicky mass elimination ====================================== */
	/* ================================================================== */

	/* this code detects some supernodes, but it might miss */
	/* some because the elimination tree (created on the fly) */
	/* is not yet post-ordered, and because the pattern of A'*A */
	/* is also computed on the fly. */

	/* j2 is incremented because the pivot columns are not stored */

	for (j2 = j+1 ; j2 < jnext ; j2++)
	{
	    ASSERT (j2 >= 0 && j2 < n_col) ;
	    if (W [j2] != Wflag || Link [j2] != EMPTY)
	    {
		break ;
	    }
	}

	/* the loop above terminated with j2 at the first non-supernode */
	DEBUG1 (("jnext = "ID"\n", jnext)) ;
	ASSERT (j2 <= jnext) ;
	jnext = j2 ;
	j2-- ;
	DEBUG1 (("j2 = "ID"\n", j2)) ;
	ASSERT (j2 < n_col) ;

	npivots = j2-j+1 ;

	/* rows j:j2 have the same nonzero pattern, except for columns j:j2-1 */

	if (j2 > j)
	{
	    /* supernode detected, prune the pattern of new row j */
	    ASSERT (parent == j+1) ;
	    ASSERT (j2 < n_col) ;
	    DEBUG1 (("Supernode detected, j "ID" to j2 "ID"\n", j, j2)) ;

	    parent = n_col ;
	    p2 = pdest ;
	    pdest = Up [j] ;
	    for (p = Up [j] ; p < p2 ; p++)
	    {
		col = Ai [p] ;
		ASSERT (col >= 0 && col < n_col) ;
		ASSERT (W [col] == Wflag) ;
		if (col > j2)
		{
		    /* keep this col in the pattern of the new row j */
		    Ai [pdest++] = col ;
		    if (col < parent)
		    {
			parent = col ;
		    }
		}
	    }
	}

	DEBUG1 (("Parent ["ID"] = "ID"\n", j, parent)) ;
	ASSERT (parent > j2) ;

	if (parent == n_col)
	{
	    /* this front has no parent - it is the root of a subtree */
	    parent = EMPTY ;
	}

#ifndef NDEBUG
	DEBUG3 (("\nFinal row "ID" of U after supercolumn detection:\n", j)) ;
	for (p = Up [j] ; p < pdest ; p++)
	{
	    ASSERT (Ai [p] >= 0 && Ai [p] < n_col) ;
	    DEBUG3 ((" "ID" ("ID")", Ai [p], W [Ai [p]])) ;
	    ASSERT (W [Ai [p]] == Wflag) ;
	}
	DEBUG3 (("\n")) ;
#endif

	/* ================================================================== */
	/* === frontal matrix =============================================== */
	/* ================================================================== */

	/* front has Front_npivcol [j] pivot columns */
	/* entire front is Front_nrows [j] -by- Front_ncols [j] */
	/* j is first column in the front */

	npivcol = npivots ;
	fallrows = thickness ;
	fallcols = npivots + pdest - Up [j] ;

	/* number of pivots in the front (rows and columns) */
	fpiv = MIN (npivcol, fallrows) ;

	/* size of contribution block */
	frows = fallrows - fpiv ;
	fcols = fallcols - fpiv ;

	if (frows == 0 || fcols == 0)
	{
	    /* front has no contribution block and thus needs no parent */
	    Up [j] = EMPTY ;
	    parent = EMPTY ;
	}

	Front_npivcol [j] = npivots ;
	Front_nrows [j] = fallrows ;
	Front_ncols [j] = fallcols ;
	Front_parent [j] = parent ;
	ASSERT (npivots > 0) ;

	/* Front_parent [j] is the first column of the parent frontal matrix */

	DEBUG1 (("\n\n==== Front "ID", pivot columns "ID":"ID" all front: "ID
	    "-by-"ID"\n", j, j,j+npivots-1, Front_nrows [j], Front_ncols [j])) ;
	nfr++ ;

	/* ================================================================== */
	/* === prepare this row for its parent ============================== */
	/* ================================================================== */

	if (parent != EMPTY)
	{
	    Link [j] = Link [parent] ;
	    Link [parent] = j ;
	}

	ASSERT (jnext > j) ;

	jlast = j ;
    }

    /* ====================================================================== */
    /* === scan the fronts ================================================== */
    /* ====================================================================== */

    *nfr_out = nfr ;

    /* use Ap for Front_child and use Link for Front_sibling [ */
    Front_child = Ap ;
    Front_sibling = Link ;

    /* use W for Front_maxfr [ */
    Front_maxfr = W ;

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

    DEBUG1 (("\n\n========================================FRONTS:\n")) ;

    /* ---------------------------------------------------------------------- */
    /* find max front size for tree rooted at node j, for each front j */
    /* ---------------------------------------------------------------------- */

    for (j = 0 ; j < n_col ; j++)
    {
	DEBUG1 ((""ID" : npiv "ID" nrows "ID" ncols "ID" parent "ID" ",
		j, Front_npivcol [j], Front_nrows [j], Front_ncols [j],
		Front_parent [j])) ;
	if (Front_npivcol [j] > 0)
	{
	    /* this is a frontal matrix */
	    parent = Front_parent [j] ;
	    frsize = Front_nrows [j] * Front_ncols [j] ;

	    DEBUG1 ((" a front, frsize "ID", true parent: "ID"\n", frsize,
		parent)) ;

	    Front_maxfr [j] = MAX (Front_maxfr [j], frsize) ;
	    DEBUG1 (("Front_maxfr [j = "ID"] = "ID"\n", j, Front_maxfr [j])) ;

	    if (parent != EMPTY)
	    {
		ASSERT (Front_npivcol [parent] > 0) ;
		ASSERT (parent > j) ;

		/* 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]));
	    }
	}
	DEBUG1 (("\n")) ;
    }

    /* ---------------------------------------------------------------------- */
    /* place the children in link lists - bigger fronts will tend to be last */
    /* ---------------------------------------------------------------------- */

    for (j = n_col-1 ; j >= 0 ; j--)
    {
	if (Front_npivcol [j] > 0)
	{
	    /* this is a frontal matrix */
	    parent = Front_parent [j] ;
	    if (parent != EMPTY)
	    {
		/* place the front in link list of the children its parent */
		Front_sibling [j] = Front_child [parent] ;
		Front_child [parent] = j ;
	    }
	}
    }

#ifndef NDEBUG
    DEBUG1 (("\n\n========================================FRONTS (again):\n")) ;
    nfr2 = 0 ;
    for (j = 0 ; j < n_col ; j++)
    {
	if (Front_npivcol [j] > 0)
	{
	    DEBUG1 (( ""ID" :  nfr "ID" npiv "ID" nrows "ID" ncols "ID
		" parent "ID" maxfr "ID"\n", j, nfr2,
		Front_npivcol [j], Front_nrows [j], Front_ncols [j],
		Front_parent [j], Front_maxfr [j])) ;

	    /* this is a frontal matrix */

	    /* dump the link list of children */
	    DEBUG1 (("    Children: ")) ;
	    for (f = Front_child [j] ; f != EMPTY ; f = Front_sibling [f])
	    {
		DEBUG1 ((ID, f)) ;
		ASSERT (Front_parent [f] == j) ;
	    }
	    DEBUG1 (("\n")) ;

	    parent = Front_parent [j] ;
	    if (parent != EMPTY)
	    {
		/* Assert that the parent front can absorb the child element */
		ASSERT (Front_npivcol [parent] > 0) ;
		ASSERT ((Front_nrows [j] - Front_npivcol [j])
		<= Front_nrows [parent]) ;
		ASSERT ((Front_ncols [j] - Front_npivcol [j])
		<= Front_ncols [parent]) ;
	    }
	    nfr2++ ;
	}
    }
    ASSERT (nfr == nfr2) ;
#endif

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

    for (i = 0 ; i < n_col ; i++)
    {
	if (Front_npivcol [i] > 0 && Front_child [i] != EMPTY)
	{

#ifndef NDEBUG
	    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++ ;
	    }
#endif

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

#ifndef NDEBUG
	    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) ;
#endif

	}
    }

    /* Front_maxfr no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* postorder the supernodal column elimination tree */
    /* ---------------------------------------------------------------------- */

    /* use W for Front_order ( */
    Front_order = W ;

    /* use Ai as Stack for UMF_order_front_tree [ */
    Stack = Ai ;

    for (i = 0 ; i < n_col ; i++)
    {
	Front_order [i] = EMPTY ;
    }

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

    k = 0 ;
    for (i = 0 ; i < n_col ; i++)
    {
	if (Front_parent [i] == EMPTY && Front_npivcol [i] != 0)
	{
	    DEBUG1 (("Root of front tree "ID"\n", i)) ;
	    k = UMF_order_front_tree (i, k, Front_child, Front_sibling,
		Front_order, Stack) ;
	}
    }

    /* Stack no longer needed ] */
    /* Front_child, Front_sibling no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* construct the column permutation (return in Up) */
    /* ---------------------------------------------------------------------- */

    /* Front_order [i] = k means that front i is kth front in the new order. */
    /* i is in the range 0 to n_col-1, and k is in the range 0 to nfr-1 */

    /* Use Ai as workspace for Winv [ */
    Winv = Ai ;
    for (k = 0 ; k < nfr ; k++)
    {
	Winv [k] = EMPTY ;
    }

    /* compute the inverse of Front_order, so that Winv [k] = i */
    /* if Front_order [i] = k */

    DEBUG1 (("\n\nComputing output column permutation:\n")) ;
    for (i = 0 ; i < n_col ; i++)
    {
	k = Front_order [i] ;
	if (k != EMPTY)
	{
	    DEBUG1 (("Front "ID" new order: "ID"\n", i, k)) ;
	    ASSERT (k >= 0 && k < nfr) ;
	    ASSERT (Winv [k] == EMPTY) ;
	    Winv [k] = i ;
	}
    }

    /* Use Up as output permutation */
    kk = 0 ;
    for (k = 0 ; k < nfr ; k++)
    {
	i = Winv [k] ;
	DEBUG1 (("Old Front "ID" New Front "ID" npivots "ID" nrows "ID" ncols "ID"\n",
	    i, k, Front_npivcol [i], Front_nrows [i], Front_ncols [i])) ;
	ASSERT (i >= 0 && i < n_col) ;
	ASSERT (Front_npivcol [i] > 0) ;
	for (npiv = 0 ; npiv < Front_npivcol [i] ; npiv++)
	{
	    Up [kk] = i + npiv ;
	    DEBUG1 (("    Cperm ["ID"] = "ID"\n", kk, Up [kk])) ;
	    kk++ ;
	}
    }
    ASSERT (kk == n_col) ;

    /* Winv no longer needed ] */

    /* ---------------------------------------------------------------------- */
    /* apply the postorder traversal to renumber the frontal matrices */
    /* ---------------------------------------------------------------------- */

    /* use Ai as workspace */

    UMF_apply_order (Front_npivcol, Front_order, Ai, n_col, nfr) ;
    UMF_apply_order (Front_nrows,   Front_order, Ai, n_col, nfr) ;
    UMF_apply_order (Front_ncols,   Front_order, Ai, n_col, nfr) ;
    UMF_apply_order (Front_parent,  Front_order, Ai, n_col, nfr) ;

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

    /* Front_order longer needed ) */

#ifndef NDEBUG
    DEBUG1 (("\nFinal frontal matrices:\n")) ;
    for (i = 0 ; i < nfr ; i++)
    {
	DEBUG1 (("Final front "ID": npiv "ID" nrows "ID" ncols "ID" parent "
	    ID"\n", i, Front_npivcol [i], Front_nrows [i],
	    Front_ncols [i], Front_parent [i])) ;
    }
#endif

    *p_ncompactions = ncompactions ;
    return (TRUE) ;
}


```

sparse-matrix/umfpack/umf_valid_symbolic.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_symbolic
(
    SymbolicType *Symbolic
) ;


```

sparse-matrix/umfpack/wrap_umf_ltsolve.c:
```
#define	CONJUGATE_SOLVE
#include "umf_ltsolve.c"

```

sparse-matrix/umfpack/umfpack_get_symbolic.c:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

/*
    User-callable.  Gets the symbolic information held in the Symbolic object.
    See umfpack_get_symbolic.h for a more detailed description.
*/

#include "umf_internal.h"
#include "umf_valid_symbolic.h"

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

GLOBAL Int UMFPACK_get_symbolic
(
    Int *p_n_row,
    Int *p_n_col,
    Int *p_nz,
    Int *p_nfr,
    Int *p_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 *SymbolicHandle
)
{
    SymbolicType *Symbolic ;
    Int k, n_row, n_col, nfr, nchains, *p ;

    /* ---------------------------------------------------------------------- */
    /* check inputs */
    /* ---------------------------------------------------------------------- */

    Symbolic = (SymbolicType *) SymbolicHandle ;
    if (!UMF_valid_symbolic (Symbolic))
    {
	return (UMFPACK_ERROR_invalid_Symbolic_object) ;
    }

    /* ---------------------------------------------------------------------- */
    /* get contents of Symbolic */
    /* ---------------------------------------------------------------------- */

    n_row = Symbolic->n_row ;
    n_col = Symbolic->n_col ;
    nfr = Symbolic->nfr ;
    nchains = Symbolic->nchains ;

    if (p_n_row)
    {
	*p_n_row = n_row ;
    }

    if (p_n_col)
    {
	*p_n_col = n_col ;
    }

    if (p_nz)
    {
	*p_nz = Symbolic->nz ;
    }

    if (p_nfr)
    {
	*p_nfr = nfr ;
    }

    if (p_nchains)
    {
	*p_nchains = nchains ;
    }

    if (Ptree)
    {
	p = Symbolic->Rperm_init ;
	for (k = 0 ; k < n_row ; k++)
	{
	    Ptree [k] = p [k] ;
	}
    }

    if (Qtree)
    {
	p = Symbolic->Cperm_init ;
	for (k = 0 ; k < n_col ; k++)
	{
	    Qtree [k] = p [k] ;
	}
    }

    if (Front_npivcol)
    {
	p = Symbolic->Front_npivcol ;
	for (k = 0 ; k <= nfr ; k++)
	{
	    Front_npivcol [k] = p [k] ;
	}
    }

    if (Front_parent)
    {
	p = Symbolic->Front_parent ;
	for (k = 0 ; k <= nfr ; k++)
	{
	    Front_parent [k] = p [k] ;
	}
    }

    if (Front_1strow)
    {
	p = Symbolic->Front_1strow ;
	for (k = 0 ; k <= nfr ; k++)
	{
	    Front_1strow [k] = p [k] ;
	}
    }

    if (Front_leftmostdesc)
    {
	p = Symbolic->Front_leftmostdesc ;
	for (k = 0 ; k <= nfr ; k++)
	{
	    Front_leftmostdesc [k] = p [k] ;
	}
    }

    if (Chain_start)
    {
	p = Symbolic->Chain_start ;
	for (k = 0 ; k <= nchains ; k++)
	{
	    Chain_start [k] = p [k] ;
	}
    }

    if (Chain_maxrows)
    {
	p = Symbolic->Chain_maxrows ;
	for (k = 0 ; k < nchains ; k++)
	{
	    Chain_maxrows [k] = p [k] ;
	}
	Chain_maxrows [nchains] = 0 ;
    }

    if (Chain_maxcols)
    {
	p = Symbolic->Chain_maxcols ;
	for (k = 0 ; k < nchains ; k++)
	{
	    Chain_maxcols [k] = p [k] ;
	}
	Chain_maxcols [nchains] = 0 ;
    }

    return (UMFPACK_OK) ;
}

```

sparse-matrix/umfpack/umf_free.c:
```
/* ========================================================================== */
/* === UMF_free ============================================================= */
/* ========================================================================== */

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

/*
    Free a block previously allocated by UMF_malloc and return NULL.
    Usage is p = UMF_free (p), to ensure that we don't free it twice.
    Also maintains the UMFPACK malloc count.
*/

#include "umf_internal.h"

#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG)
#include "umf_malloc.h"
#endif

GLOBAL void *UMF_free
(
    void *p
)
{
    if (p)
    {

	/* see umf_config.h for the memory allocator selection */
	FREE (p) ;

#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG)
	/* One more object has been free'd.  Keep track of the count. */
	/* (purely for sanity checks). */
	UMF_malloc_count-- ;
#endif

    }

    return ((void *) NULL) ;
}


```

sparse-matrix/umfpack/umf_ltsolve.c:
```
/* ========================================================================== */
/* === UMF_ltsolve ========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 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 L'x = b or L.'x=b, where L is the lower 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_lhsolve			/* solve L'x=b  (complex conjugate transpose) */
#else
UMF_ltsolve			/* solve L.'x=b (array transpose) */
#endif
(
    NumericType *Numeric,
    Entry X [ ],		/* b on input, solution x on output */
    Int Pattern [ ]		/* a work array of size n */
)
{
    Int k, deg, *ip, j, row, *Lpos, *Lilen, kstart, kend, *Lip, llen,
	lp, pos, npiv ;
    Entry *xp, xk ;

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

    if (Numeric->n_row != Numeric->n_col) return (0.) ;
    npiv = Numeric->npiv ;
    Lpos = Numeric->Lpos ;
    Lilen = Numeric->Lilen ;
    Lip = Numeric->Lip ;
    kstart = npiv ;

#ifndef NDEBUG
    DEBUG4 (("Ltsolve start:\n")) ;
    for (j = 0 ; j < Numeric->n_row ; j++)
    {
	DEBUG4 (("Ltsolve start "ID": ", j)) ;
	EDEBUG4 (X [j]) ;
	DEBUG4 (("\n")) ;
    }
#endif

    for (kend = npiv-1 ; kend >= 0 ; kend = kstart-1)
    {

	/* ------------------------------------------------------------------ */
	/* find the start of this Lchain */
	/* ------------------------------------------------------------------ */

	/* for (kstart = kend ; kstart >= 0 && Lip [kstart] > 0 ; kstart--) ; */
	kstart = kend ;
	while (kstart >= 0 && Lip [kstart] > 0)
	{
	    kstart-- ;
	}

	/* the Lchain goes from kstart to kend */

	/* ------------------------------------------------------------------ */
	/* scan the whole chain to find the pattern of the last column of L */
	/* ------------------------------------------------------------------ */

	deg = 0 ;
	DEBUG4 (("start of chain for column of L\n")) ;
	for (k = kstart ; k <= kend ; k++)
	{
	    ASSERT (k >= 0 && k < npiv) ;

	    /* -------------------------------------------------------------- */
	    /* make column k of L in Pattern [0..deg-1] */
	    /* -------------------------------------------------------------- */

	    /* remove pivot row */
	    pos = Lpos [k] ;
	    if (pos != EMPTY)
	    {
		DEBUG4 (("  k "ID" removing row "ID" at position "ID"\n",
		k, Pattern [pos], pos)) ;
		ASSERT (k != kstart) ;
		ASSERT (deg > 0) ;
		ASSERT (pos >= 0 && pos < deg) ;
		ASSERT (Pattern [pos] == k) ;
		Pattern [pos] = Pattern [--deg] ;
	    }

	    /* concatenate the pattern */
	    lp = Lip [k] ;
	    if (k == kstart)
	    {
		lp = -lp ;
	    }
	    ASSERT (lp > 0) ;
	    ip = (Int *) (Numeric->Memory + lp) ;
	    llen = Lilen [k] ;
	    for (j = 0 ; j < llen ; j++)
	    {
		row = *ip++ ;
		DEBUG4 (("  row "ID"  k "ID"\n", row, k)) ;
		ASSERT (row > k) ;
		Pattern [deg++] = row ;
	    }

	}
	/* Pattern [0..deg-1] is now the pattern of column kend */

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

	DEBUG4 (("Unwinding Lchain\n")) ;
	for (k = kend ; k >= kstart ; k--)
	{

	    /* -------------------------------------------------------------- */
	    /* use column k of L */
	    /* -------------------------------------------------------------- */

	    ASSERT (k >= 0 && k < npiv) ;
	    lp = Lip [k] ;
	    if (k == kstart)
	    {
		lp = -lp ;
	    }
	    ASSERT (lp > 0) ;
	    llen = Lilen [k] ;
	    xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ;
	    xk = X [k] ;
	    for (j = 0 ; j < deg ; j++)
	    {
		DEBUG4 (("  row "ID"  k "ID" value", Pattern [j], k)) ;
		EDEBUG4 (*xp) ;
		DEBUG4 (("\n")) ;
#ifdef CONJUGATE_SOLVE
		/* xk -= X [Pattern [j]] * conjugate (*xp) ; */
		MULT_SUB_CONJ (xk, X [Pattern [j]], *xp) ;
#else
		/* xk -= X [Pattern [j]] * (*xp) ; */
		MULT_SUB (xk, X [Pattern [j]], *xp) ;
#endif
		xp++ ;
	    }
	    X [k] = xk ;

	    /* -------------------------------------------------------------- */
	    /* construct column k-1 of L */
	    /* -------------------------------------------------------------- */

	    /* un-concatenate the pattern */
	    deg -= llen ;

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

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

    return (MULTSUB_FLOPS * ((double) Numeric->lnz)) ;
}


```

sparse-matrix/umfpack/umf_get_memory.c:
```
/* ========================================================================== */
/* === UMF_get_memory ======================================================= */
/* ========================================================================== */

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

/*
    Reallocate the workspace (Numeric->Memory) and shift elements downwards.
    needunits: increase in size so that the free space is at least this many
    Units (to which the tuple lengths is added).

    Return TRUE if successful, FALSE if out of memory.
*/

#include "umf_internal.h"
#include "umf_garbage_collection.h"
#include "umf_tuple_lengths.h"
#include "umf_build_tuples.h"
#include "umf_mem_free_tail_block.h"
#include "umf_realloc.h"

GLOBAL Int UMF_get_memory
(
    NumericType *Numeric,
    WorkType *Work,
    Int needunits
)
{
    Int i, minsize, newsize, newmem, costly, row, col, *Row_tlen, *Col_tlen,
	n_row, n_col, *Row_degree, *Col_degree ;
    Unit *mnew, *p ;
    double nsize, bsize ;

    /* ---------------------------------------------------------------------- */
    /* get and check parameters */
    /* ---------------------------------------------------------------------- */

    ASSERT (Numeric) ;
    ASSERT (Work) ;
    ASSERT (Numeric->Memory) ;

#ifndef NDEBUG
    DEBUG1 (("::::GET MEMORY::::\n")) ;
    UMF_dump_memory (Numeric) ;
#endif

    n_row = Work->n_row ;
    n_col = Work->n_col ;
    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro */
    Col_degree = Numeric->Cperm ;	/* for NON_PIVOTAL_COL macro */
    Row_tlen   = Numeric->Uilen ;
    Col_tlen   = Numeric->Lilen ;

    /* ---------------------------------------------------------------------- */
    /* initialize the tuple list lengths */
    /* ---------------------------------------------------------------------- */

    for (row = 0 ; row < n_row ; row++)
    {
	if (NON_PIVOTAL_ROW (row))
	{
	    Row_tlen [row] = 0 ;
	}
    }
    for (col = 0 ; col < n_col ; col++)
    {
	if (NON_PIVOTAL_COL (col))
	{
	    Col_tlen [col] = 0 ;
	}
    }

    /* ---------------------------------------------------------------------- */
    /* determine how much memory is needed for the tuples */
    /* ---------------------------------------------------------------------- */

    nsize = (double) needunits + 2 ;
    needunits += UMF_tuple_lengths (Numeric, Work, &nsize) ;
    needunits += 2 ;	/* add 2, so that newmem >= 2 is true if realloc'd */

    /* note: Col_tlen and Row_tlen are updated, but the tuple lists */
    /* themselves are not.  Do not attempt to scan the tuple lists. */
    /* They are now stale, and are about to be destroyed and recreated. */

    /* ---------------------------------------------------------------------- */
    /* determine the desired new size of memory */
    /* ---------------------------------------------------------------------- */

    DEBUG0 (("UMF_get_memory: needunits: "ID"\n", needunits)) ;

    minsize = Numeric->size + needunits ;
    nsize += (double) Numeric->size ;

    bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ;

    if (nsize > bsize)	    /* a double relop, but ignore NaN case */
    {
	return (FALSE) ;	/* the minimum size is larger than Int_MAX */
    }

    newsize = (Int) (UMF_REALLOC_INCREASE * ((double) minsize)) ;
    nsize *= UMF_REALLOC_INCREASE * (1.0 + MAX_EPSILON) ;

    if (newsize < 0 || nsize > bsize)
    {
	newsize = (Int) bsize ;	/* we cannot increase the size beyond bsize */
    }
    else
    {
	newsize = MAX (newsize, minsize) ;
    }

    DEBUG0 ((
    "REALLOC MEMORY: needunits "ID" old size: "ID" new size: "ID" Units \n",
	needunits, Numeric->size, newsize)) ;

    /* Forget where the biggest free block is (we no longer need it) */
    /* since garbage collection will occur shortly. */
    Numeric->ibig = EMPTY ;

    /* ---------------------------------------------------------------------- */
    /* reallocate the memory, if possible, and make it bigger */
    /* ---------------------------------------------------------------------- */

    mnew = (Unit *) NULL ;
    while (!mnew)
    {
	mnew = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ;
	if (!mnew)
	{
	    if (newsize == minsize)	/* last realloc attempt failed */
	    {
		/* We failed to get the minimum.  Just stick with the */
		/* current allocation and hope that garbage collection */
		/* can recover enough space. */
		mnew = Numeric->Memory ;	/* no new memory available */
		newsize = Numeric->size ;
	    }
	    else
	    {
		/* otherwise, reduce the request and keep trying */
		newsize = (Int) (UMF_REALLOC_REDUCTION * ((double) newsize)) ;
		newsize = MAX (minsize, newsize) ;
	    }
	}
    }
    ASSERT (mnew) ;

    /* see if realloc had to copy, rather than just extend memory */
    costly = (mnew != Numeric->Memory) ;

    /* ---------------------------------------------------------------------- */
    /* extend the tail portion of memory downwards */
    /* ---------------------------------------------------------------------- */

    Numeric->Memory = mnew ;

    newmem = newsize - Numeric->size ;
    ASSERT (newmem == 0 || newmem >= 2) ;

    if (newmem >= 2)
    {
	/* reallocation succeeded */

	/* point to the old tail marker block of size 1 + header */
	p = Numeric->Memory + Numeric->size - 2 ;

	/* create a new block out of the newly extended memory */
	p->header.size = newmem - 1 ;
	i = Numeric->size - 1 ;
	p += newmem ;

	/* create a new tail marker block */
	p->header.prevsize = newmem - 1 ;
	p->header.size = 1 ;

	Numeric->size = newsize ;

	/* free the new block */
	UMF_mem_free_tail_block (Numeric, i) ;

	Numeric->nrealloc++ ;

	if (costly)
	{
	    Numeric->ncostly++ ;
	}

    }
    DEBUG1 (("Done with realloc memory\n")) ;

    /* ---------------------------------------------------------------------- */
    /* garbage collection on the tail of Numeric->memory (destroys tuples) */
    /* ---------------------------------------------------------------------- */

    UMF_garbage_collection (Numeric, Work) ;

    /* ---------------------------------------------------------------------- */
    /* rebuild the tuples */
    /* ---------------------------------------------------------------------- */

    return (UMF_build_tuples (Numeric, Work)) ;

}


```

sparse-matrix/umfpack/umf_solve.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_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 [ ],
    double SolveWork [ ]
) ;


```

sparse-matrix/umfpack/umf_apply_order.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_apply_order
(
    Int Front [ ],
    const Int Order [ ],
    Int Temp [ ],
    Int n_col,
    Int nfr
) ;


```

sparse-matrix/umfpack/umf_dump.c:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

/* These routines, and external variables, are used only when debugging. */
/* If debugging is disabled (for normal operation) then this entire file */
/* becomes empty */

#include "umf_internal.h"

#ifndef NDEBUG

/* These global debugging variables and arrays do not exist if debugging */
/* is disabled at compile time (which is the default). */
GLOBAL Int UMF_debug = -1 ;
GLOBAL Int UMF_nbug = -999 ;
GLOBAL Int UMF_fbug = -999 ;
GLOBAL Int UMF_allocfail = FALSE ;
GLOBAL double UMF_gprob = -1.0 ;

/* static debugging arrays used only in UMF_dump_rowcol */
PRIVATE Int UMF_DBflag = 0 ;
PRIVATE Int UMF_DBpacked [UMF_DBMAX+1] ;
PRIVATE Int UMF_DBscatter [UMF_DBMAX+1] ;

/* ========================================================================== */
/* === UMF_DBinit =========================================================== */
/* ========================================================================== */

/* clear the debugging arrays */

PRIVATE void UMF_DBinit
(
    void
)
{
    Int i ;

    /* Int_MAX is defined in umfpack.h */
    if (UMF_DBflag < 1 || UMF_DBflag == Int_MAX)
    {
	/* clear the debugging arrays */
	UMF_DBflag = 0 ;
	for (i = 0 ; i <= UMF_DBMAX ; i++)
	{
	    UMF_DBscatter [i] = 0 ;
	    UMF_DBpacked  [i] = 0 ;
	}
    }

    UMF_DBflag++ ;

    /* UMF_DBflag > UMF_DBscatter [0...UMF_DBmax] is now true */
}

/* ========================================================================== */
/* === UMF_dump_dense ======================================================= */
/* ========================================================================== */

GLOBAL void UMF_dump_dense
(
    Entry *Fx,
    Int dim,
    Int m,
    Int n
)
{

    /* dump Fx [1..m,1..n], with column dimenstion dim */
    Int i, j;

    if (UMF_debug < 7) return ;
    if (!Fx)
    {
	DEBUG7 (("No dense matrix allocated\n")) ;
	return ;
    }
    DEBUG8 ((" dimension= "ID" rows= "ID" cols= "ID"\n", dim, m, n)) ;

    for (i = 0 ; i < m ; i++)
    {
	DEBUG9 ((ID": ", i)) ;
	for (j = 0 ; j < n ; j++)
	{
	    EDEBUG9 (Fx [i+j*dim]) ;
	    if (j % 6 == 5) DEBUG9 (("\n     ")) ;
	}
	DEBUG9 (("\n")) ;
    }

    for (i = 0 ; i < m ; i++)
    {
	for (j = 0 ; j < n ; j++)
	{
	    if (IS_ZERO (Fx [i+j*dim]))
	    {
		DEBUG8 ((".")) ;
	    }
	    else
	    {
		DEBUG8 (("X")) ;
	    }
	}
	DEBUG8 (("\n")) ;
    }
}

/* ========================================================================== */
/* === UMF_dump_element ===================================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_element
(
    NumericType *Numeric,
    WorkType *Work,
    Int e,
    Int clean
)
{

    Int i, j, k, *Rows, *Cols, nrows, ncols, *E, row, col,
	*Row_degree, *Col_degree ;
    Entry *C ;
    Element *ep ;
    Unit *p ;

    if (UMF_debug < 7) return ;
    DEBUG7 (("\n====================ELEMENT: "ID" ", e)) ;
    if (!Numeric || !Work || !Numeric->Memory)
    {
	DEBUG7 ((" No Numeric, Work\n")) ;
	return ;
    }
    DEBUG7 ((" nel: "ID" of "ID, e, Work->nel)) ;
    if (e <= Work->nelorig) DEBUG7 ((" Original ")) ;
    E = Work->E ;
    if (!E)
    {
	DEBUG7 ((" No elements\n")) ;
	return ;
    }
    if (e < 0 || e > Work->nel)
    {
	DEBUG7 (("e out of range!\n")) ;
	return ;
    }
    if (!E [e])
    {
	DEBUG7 ((" deallocated\n")) ;
	return ;
    }
    DEBUG7 (("\n")) ;
    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;

    p = Numeric->Memory + E [e] ;
    DEBUG7 (("ep "ID"\n", p-Numeric->Memory)) ;
    GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ;
    DEBUG7 (("nrows "ID" nrowsleft "ID"\n", nrows, ep->nrowsleft)) ;
    DEBUG7 (("ncols "ID" ncolsleft "ID"\n", ncols, ep->ncolsleft)) ;
    DEBUG7 (("cdeg-cdeg0 "ID" rdeg-rdeg0 "ID" next "ID"\n",
    ep->cdeg - Work->cdeg0, ep->rdeg - Work->rdeg0, ep->next)) ;

    DEBUG8 (("rows: ")) ;
    k = 0 ;
    for (i = 0 ; i < ep->nrows ; i++)
    {
	row = Rows [i] ;
	if (row >= 0)
	{
	    DEBUG8 ((" "ID, row)) ;
	    ASSERT (row < Work->n_row) ;
	    if ((k++ % 10) == 9) DEBUG8 (("\n")) ;
	    ASSERT (IMPLIES (clean, NON_PIVOTAL_ROW (row))) ;
	}
    }

    DEBUG8 (("\ncols: ")) ;
    k = 0 ;
    for (j = 0 ; j < ep->ncols ; j++)
    {
	col = Cols [j] ;
	if (col >= 0)
	{
	    DEBUG8 ((" "ID, col)) ;
	    ASSERT (col < Work->n_col) ;
	    if ((k++ % 10) == 9) DEBUG8 (("\n")) ;
	    ASSERT (IMPLIES (clean, NON_PIVOTAL_COL (col))) ;
	}
    }

    DEBUG8 (("\nvalues:\n")) ;
    if (UMF_debug >= 9)
    {
	for (i = 0 ; i < ep->nrows ; i++)
	{
	    row = Rows [i] ;
	    if (row >= 0)
	    {
		DEBUG9 ((ID": ", row)) ;
		k = 0 ;
		for (j = 0 ; j < ep->ncols ; j++)
		{
		    col = Cols [j] ;
		    if (col >= 0)
		    {
			EDEBUG9 (C [i+j*ep->nrows]) ;
			if (k++ % 6 == 5) DEBUG9 (("\n     ")) ;
		    }
		}
		DEBUG9 (("\n")) ;
	    }
	}
    }

    DEBUG7 (("====================\n")) ;
}


/* ========================================================================== */
/* === UMF_dump_rowcol ====================================================== */
/* ========================================================================== */

/* dump a row or a column, from one or more memory spaces */
/* return exact degree */

GLOBAL void UMF_dump_rowcol
(
    Int dumpwhich,		/* 0 for row, 1 for column */
    NumericType *Numeric,
    WorkType *Work,
    Int dumpindex,		/* row or column index to dump */
    Int check_degree	/* true if degree is to be checked */
)
{
    Int f, nrows, j, jj, len, e, deg, index, n_row, n_col, *Cols, *Rows, nn,
	dumpdeg, ncols, preve, *E, tpi, *Pattern, approx_deg, not_in_use ;
    Tuple *tp, *tend ;
    Element *ep ;
    Int *Row_tuples, *Row_degree, *Row_tlen ;
    Int *Col_tuples, *Col_degree, *Col_tlen ;
    Entry value, *C ;
    Unit *p ;
    Int is_there ;

    /* clear the debugging arrays */
    UMF_DBinit () ;

    if (dumpwhich == 0)
    {
	DEBUG7 (("\n====================ROW: "ID, dumpindex)) ;
    }
    else
    {
	DEBUG7 (("\n====================COL: "ID, dumpindex)) ;
    }

    if (dumpindex == EMPTY)
    {
	DEBUG7 ((" (EMPTY)\n")) ;
	return ;
    }

    deg = 0 ;
    approx_deg = 0 ;

    if (!Numeric || !Work)
    {
	DEBUG7 ((" No Numeric, Work\n")) ;
	return ;
    }

    n_row = Work->n_row ;
    n_col = Work->n_col ;
    nn = MAX (n_row, n_col) ;
    E = Work->E ;

    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;

    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;
    Col_tuples = Numeric->Lip ;
    Col_tlen   = Numeric->Lilen ;

	if (!E
	|| !Row_tuples || !Row_degree || !Row_tlen
	|| !Col_tuples || !Col_degree || !Col_tlen)
	{
	    DEBUG7 ((" No E, Rows, Cols\n")) ;
	    return ;
	}

	if (dumpwhich == 0)
	{
	    /* dump a row */
	    ASSERT (dumpindex >= 0 && dumpindex < n_row) ;
	    if (!NON_PIVOTAL_ROW (dumpindex))
	    {
		DEBUG7 ((" Pivotal\n")) ;
		return ;
	    }
	    len = Row_tlen [dumpindex] ;
	    dumpdeg = Row_degree [dumpindex] ;
	    tpi = Row_tuples [dumpindex] ;
	}
	else
	{
	    /* dump a column */
	    ASSERT (dumpindex >= 0 && dumpindex < n_col) ;
	    if (!NON_PIVOTAL_COL (dumpindex))
	    {
		DEBUG7 ((" Pivotal\n")) ;
		return ;
	    }
	    len = Col_tlen [dumpindex] ;
	    dumpdeg = Col_degree [dumpindex] ;
	    tpi = Col_tuples [dumpindex] ;
	}

	p = Numeric->Memory + tpi ;
	tp = (Tuple *) p ;
	if (!tpi)
	{
	    DEBUG7 ((" Nonpivotal, No tuple list tuples "ID" tlen "ID"\n",
		tpi, len)) ;
	    return ;
	}
	ASSERT (p >= Numeric->Memory + Numeric->itail) ;
	ASSERT (p <  Numeric->Memory + Numeric->size) ;

	DEBUG7 ((" degree: "ID" len: "ID"\n", dumpdeg, len)) ;
	not_in_use = (p-1)->header.size - UNITS (Tuple, len) ;
	DEBUG7 ((" Tuple list: p+1: "ID" size: "ID" units, "ID" not in use\n",
		p-Numeric->Memory, (p-1)->header.size, not_in_use)) ;
	ASSERT (not_in_use >= 0) ;
	tend = tp + len ;
	preve = 0 ;
	for ( ; tp < tend ; tp++)
	{
	    /* row/col of element e, offset is f: */
	    /* DEBUG8 (("    (tp="ID")\n", tp)) ; */
	    e = tp->e ;
	    f = tp->f ;
	    DEBUG8 (("    (e="ID", f="ID")\n", e, f)) ;
	    ASSERT (e > 0 && e <= Work->nel) ;
	    /* dump the pattern and values */
	    if (E [e])
	    {
		p = Numeric->Memory + E [e] ;
		GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ;
		if (dumpwhich == 0)
		{
		    Pattern = Cols ;
		    jj = ep->ncols ;
		    is_there = Rows [f] >= 0 ;
		    if (is_there) approx_deg += ep->ncolsleft ;
		}
		else
		{
		    Pattern = Rows ;
		    jj = ep->nrows ;
		    is_there = Cols [f] >= 0 ;
		    if (is_there) approx_deg += ep->nrowsleft ;
		}
		if (!is_there)
		{
			DEBUG8 (("\t\tnot present\n")) ;
		}
		else
		{
		    for (j = 0 ; j < jj ; j++)
		    {
			index = Pattern [j] ;
			value =
			    C [ (dumpwhich == 0) ? (f+nrows*j) : (j+nrows*f) ] ;
			if (index >= 0)
			{
			    DEBUG8 (("\t\t"ID":", index)) ;
			    EDEBUG8 (value) ;
			    DEBUG8 (("\n")) ;
			    if (dumpwhich == 0)
			    {
				/* col must be in the range 0..n_col-1 */
				ASSERT (index < n_col) ;
			    }
			    else
			    {
				/* row must be in the range 0..n_row-1 */
				ASSERT (index < n_row) ;
			    }

			    if (nn <= UMF_DBMAX)
			    {
				if (UMF_DBscatter [index] != UMF_DBflag)
				{
				    UMF_DBpacked [deg++] = index ;
				    UMF_DBscatter [index] = UMF_DBflag ;
				}
			    }
			}
		    }
		}
		/* the (e,f) tuples should be in order of their creation */
		/* this means that garbage collection will not jumble them */
		ASSERT (preve < e) ;
		preve = e ;
	    }
	    else
	    {
		DEBUG8 (("\t\tdeallocated\n")) ;
	    }
	}

    if (nn <= UMF_DBMAX)
    {
	if (deg > 0)
	{
	    DEBUG7 ((" Assembled, actual deg: "ID" : ", deg)) ;
	    for (j = 0 ; j < deg ; j++)
	    {
		index = UMF_DBpacked [j] ;
		DEBUG8 ((ID" ", index)) ;
		if (j % 20 == 19) DEBUG8 (("\n ")) ;
		ASSERT (UMF_DBscatter [index] == UMF_DBflag) ;
	    }
	    DEBUG7 (("\n")) ;
	}
    }

    if (check_degree)
    {
	DEBUG8 (("  approx_deg "ID"  dumpdeg "ID"\n", approx_deg, dumpdeg)) ;
	ASSERT (approx_deg == dumpdeg) ;
    }

    DEBUG7 (("====================\n")) ;

    /* deg is now the exact degree */
    /* if nn <= UMF_DBMAX, then UMF_DBscatter [i] == UMF_DBflag for every i */
    /* in the row or col, and != UMF_DBflag if not */

    return ;
}


/* ========================================================================== */
/* === UMF_dump_matrix ====================================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_matrix
(
    NumericType *Numeric,
    WorkType *Work,
    Int check_degree
)
{

    Int e, row, col, intfrag, frag, n_row, n_col, *E, fullsize, actualsize ;
    Element *ep ;
    Unit *p ;

    DEBUG6 (("=================================================== MATRIX:\n")) ;
    if (!Numeric || !Work)
    {
	DEBUG6 (("No Numeric or Work allocated\n")) ;
	return ;
    }
    if (!Numeric->Memory)
    {
	DEBUG6 (("No Numeric->Memory\n")) ;
	return ;
    }

	n_row = Work->n_row ;
	n_col = Work->n_col ;
	DEBUG6 (("n_row "ID" n_col "ID" nz "ID"\n", n_row, n_col, Work->nz)) ;
	DEBUG6 (("============================ ELEMENTS: "ID" \n", Work->nel)) ;
	intfrag = 0 ;
	E = Work->E ;
	if (!E)
	{
	    DEBUG6 (("No elements allocated\n")) ;
	}
	else
	{
	    for (e = 0 ; e <= Work->nel ; e++)
	    {
		UMF_dump_element (Numeric, Work, e, FALSE) ;
		if (e > 0 && E [e])
		{
		    p = Numeric->Memory + E [e] ;
		    ep = (Element *) p ;
		    ASSERT (ep->nrowsleft > 0 || ep->ncolsleft > 0) ;
		    fullsize = GET_BLOCK_SIZE (p) ;
		    actualsize = GET_ELEMENT_SIZE (ep->nrowsleft,ep->ncolsleft);
		    frag =  fullsize - actualsize ;
		    intfrag += frag ;
		    DEBUG7 (("dump el: "ID", full "ID" actual "ID" frag: "ID
			" intfrag: "ID"\n", e, fullsize, actualsize, frag,
			intfrag)) ;
		}
	    }
	}

	DEBUG6 (("CURRENT INTERNAL FRAG in elements: "ID" \n", intfrag)) ;



    DEBUG6 (("======================================== ROWS: "ID"\n", n_row)) ;
    UMF_debug -= 2 ;
    for (row = 0 ; row < n_row ; row++)
    {
	UMF_dump_rowcol (0, Numeric, Work, row, check_degree) ;
    }
    UMF_debug += 2 ;
    DEBUG6 (("======================================== COLS: "ID"\n", n_col)) ;
    UMF_debug -= 2 ;
    for (col = 0 ; col < n_col ; col++)
    {
	UMF_dump_rowcol (1, Numeric, Work, col, check_degree) ;
    }
    UMF_debug += 2 ;
    DEBUG6 (("============================================= END OF MATRIX:\n"));
}


/* ========================================================================== */
/* === UMF_dump_current_front =============================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_current_front
(
    NumericType *Numeric,
    WorkType *Work,
    Int check
)
{
    Entry *Fx ;
    Int fnrows_max, fncols_max, fnrows, fncols, fnpiv, *Frows, *Fcols,
	i, j, *Fcpos, *Frpos ;
    if (!Work) return ;
    DEBUG7 (("\n\n========CURRENT FRONTAL MATRIX:\n")) ;

    Fx = Work->Fx ;
    Frows = Work->Frows ;
    Fcols = Work->Fcols ;
    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;
    fnrows_max = Work->fnrows_max ;
    fncols_max = Work->fncols_max ;
    fnrows = Work->fnrows ;
    fncols = Work->fncols ;
    fnpiv = Work->fnpiv ;

    DEBUG6 (("=== fnpiv= "ID"\n", fnpiv)) ;
    DEBUG6 (("fncols_max      fnrows_max "ID" "ID"\n", fncols_max,fnrows_max)) ;
    DEBUG6 (("fncols          fnrows     "ID" "ID"\n", fncols, fnrows)) ;
    DEBUG6 (("Pivot row pattern:\n")) ;
    for (j = 0 ; j < fncols ; j++)
    {
	DEBUG7 ((ID" "ID" "ID" "ID"\n", j, Fcols [j], Fcpos [Fcols [j]],
	    j < fncols)) ;
	if (check)
	{
	    ASSERT (Fcols [j] >= 0 && Fcols [j] < Work->n_col) ;
	}
	ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ;
    }
    DEBUG6 (("Pivot col pattern:\n")) ;
    for (i = 0 ; i < fnrows ; i++)
    {
	DEBUG7 ((ID" "ID" "ID" "ID"\n", i, Frows [i], Frpos [Frows [i]],
	    i < fnrows)) ;
	if (check)
	{
	    ASSERT (Frows [i] >= 0 && Frows [i] < Work->n_row) ;
	}
	ASSERT (Frpos [Frows [i]] == i) ;
    }
    if (UMF_debug < 7) return ;

    DEBUG7 (("C  block: ")) ;
    UMF_dump_dense (Fx, fnrows_max, fnrows, fncols) ;
    DEBUG7 (("L  block: ")) ;
    UMF_dump_dense (Fx+(fncols_max-fnpiv)*fnrows_max, fnrows_max, fnrows,fnpiv);
    DEBUG7 (("U  block: ")) ;
    UMF_dump_dense (Fx+(fnrows_max-fnpiv), fnrows_max, fnpiv, fncols) ;
    if (fnpiv > 0)
    {
	DEBUG7 (("Pivot entry: ")) ;
	EDEBUG7 (Fx [(fnrows_max-fnpiv)+(fncols_max-fnpiv)*fnrows_max]) ;
	DEBUG7 (("\n")) ;
    }
}

/* ========================================================================== */
/* === UMF_dump_lu ========================================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_lu
(
    NumericType *Numeric
)
{
    Int i, n_row, n_col, *Cperm, *Rperm ;

    DEBUG6 (("=============================================== LU factors:\n")) ;
    if (!Numeric)
    {
	DEBUG6 (("No LU factors allocated\n")) ;
	return ;
    }
    n_row = Numeric->n_row ;
    n_col = Numeric->n_col ;
    DEBUG6 (("n_row: "ID" n_col: "ID"\n", n_row, n_col)) ;
    DEBUG6 (("nLentries: "ID" nUentries: "ID"\n",
	Numeric->nLentries, Numeric->nUentries)) ;

    if (Numeric->Cperm)
    {
	Cperm = Numeric->Cperm ;
	DEBUG7 (("Column permutations: (new: old)\n")) ;
	for (i = 0 ; i < n_col ; i++)
	{
	    if (Cperm [i] != EMPTY)
	    {
		DEBUG7 ((ID": "ID"\n", i, Cperm [i])) ;
	    }
	}
    }
    else
    {
	DEBUG7 (("No Numeric->Cperm allocatated\n")) ;
    }

    if (Numeric->Rperm)
    {
	Rperm = Numeric->Rperm ;
	DEBUG7 (("row permutations: (new: old)\n")) ;
	for (i = 0 ; i < n_row ; i++)
	{
	    if (Rperm [i] != EMPTY)
	    {
		DEBUG7 ((ID": "ID"\n", i, Rperm [i])) ;
	    }
	}
    }
    else
    {
	DEBUG7 (("No Numeric->Rperm allocatated\n")) ;
    }

    DEBUG6 (("========================================= END OF LU factors:\n"));
}


/* ========================================================================== */
/* === UMF_dump_memory ====================================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_memory
(
    NumericType *Numeric
)
{

    Unit *p ;
    Int prevsize, s ;
    Int found ;

    if (!Numeric)
    {
	DEBUG6 (("No memory space S allocated\n")) ;
	return ;
    }

    DEBUG6 (("\n ============================================== MEMORY:\n")) ;
    if (!Numeric || !Numeric->Memory)
    {
	DEBUG6 (("No memory space Numeric allocated\n")) ;
	return ;
    }

    DEBUG6 (("S: "ID"\n", (Int) Numeric)) ;
    DEBUG6 (("S->ihead           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("S->itail           : "ID"\n", Numeric->itail)) ;
    DEBUG6 (("S->size            : "ID"\n", Numeric->size)) ;
    DEBUG6 (("S->ngarbage        : "ID"\n", Numeric->ngarbage)) ;
    DEBUG6 (("S->nrealloc        : "ID"\n", Numeric->nrealloc)) ;
    DEBUG6 (("   in use at head           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("   free space               : "ID"\n",
	Numeric->itail - Numeric->ihead)) ;
    DEBUG6 (("   blocks in use at tail    : "ID"\n",
	Numeric->size - Numeric->itail)) ;
    DEBUG6 (("   total in use             : "ID"\n",
	Numeric->size - (Numeric->itail - Numeric->ihead))) ;

    prevsize = 0 ;
    found = FALSE ;

    ASSERT (0 <= Numeric->ihead) ;
    ASSERT (Numeric->ihead <= Numeric->itail) ;
    ASSERT (Numeric->itail <= Numeric->size) ;

    p = Numeric->Memory + Numeric->itail ;

    while (p < Numeric->Memory + Numeric->size)
    {
	DEBUG8 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID,
	    p-Numeric->Memory, p+1-Numeric->Memory,
	    p->header.prevsize, p->header.size)) ;
	if (p->header.size < 0)
	{
	    DEBUG8 ((" free")) ;
	}

	if (p == Numeric->Memory + Numeric->itail)
	{
	    ASSERT (p->header.prevsize == 0) ;
	}
	else
	{
	    ASSERT (p->header.prevsize > 0) ;
	}

	ASSERT (p->header.size != 0) ;
	s = prevsize >= 0 ? prevsize : -prevsize ;
	ASSERT (p->header.prevsize == s) ;
	/* no adjacent free blocks */
	ASSERT (p->header.size > 0 || prevsize > 0) ;
	if (Numeric->ibig != EMPTY)
	{
	    if (p == Numeric->Memory + Numeric->ibig)
	    {
		ASSERT (p->header.size < 0) ;
		DEBUG8 ((" <===== Numeric->ibig")) ;
		found = TRUE ;
	    }
	}
	s = p->header.size ;
	prevsize = s ;
	s = s >= 0 ? s : -s ;
	p = p + 1 + s ;
	DEBUG8 (("\n")) ;
    }

    ASSERT (p == Numeric->Memory + Numeric->size) ;
    ASSERT (IMPLIES (Numeric->ibig != EMPTY, found)) ;
    DEBUG6 (("============================================= END OF MEMORY:\n"));

}


/* ========================================================================== */
/* === UMF_dump_packed_memory =============================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_packed_memory
(
    NumericType *Numeric,
    WorkType *Work
)
{
    Unit *p, *p3 ;
    Int prevsize, col, row, *Rows, *Cols, ncols, nrows, k, esize,
	*Row_tuples, *Row_degree, *Col_tuples, *Col_degree ;
    Entry *C ;
    Element *ep ;

    Col_degree = Numeric->Cperm ;
    Row_degree = Numeric->Rperm ;
    Row_tuples = Numeric->Uip ;
    Col_tuples = Numeric->Lip ;

    DEBUG6 (("============================================ PACKED MEMORY:\n")) ;
    if (!Numeric || !Numeric->Memory)
    {
	DEBUG6 (("No memory space S allocated\n")) ;
	return ;
    }
    DEBUG6 (("S: "ID"\n", (Int) Numeric)) ;
    DEBUG6 (("S->ihead           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("S->itail           : "ID"\n", Numeric->itail)) ;
    DEBUG6 (("S->size            : "ID"\n", Numeric->size)) ;
    DEBUG6 (("S->ngarbage        : "ID"\n", Numeric->ngarbage)) ;
    DEBUG6 (("S->nrealloc        : "ID"\n", Numeric->nrealloc)) ;
    DEBUG6 (("   in use at head           : "ID"\n", Numeric->ihead)) ;
    DEBUG6 (("   free space               : "ID"\n",
	Numeric->itail - Numeric->ihead)) ;
    DEBUG6 (("   blocks in use at tail    : "ID"\n",
	Numeric->size - Numeric->itail)) ;
    DEBUG6 (("   total in use             : "ID"\n",
	Numeric->size - (Numeric->itail - Numeric->ihead))) ;

    ASSERT (0 <= Numeric->ihead) ;
    ASSERT (Numeric->ihead <= Numeric->itail) ;
    ASSERT (Numeric->itail <= Numeric->size) ;

    for (row = 0 ; row < Work->n_row ; row++)
    {
	ASSERT (IMPLIES (NON_PIVOTAL_ROW (row), !Row_tuples [row])) ;
    }
    for (col = 0 ; col < Work->n_col ; col++)
    {
	ASSERT (IMPLIES (NON_PIVOTAL_COL (col), !Col_tuples [col])) ;
    }

    prevsize = 0 ;
    p = Numeric->Memory + Numeric->itail ;
    while (p < Numeric->Memory + Numeric->size)
    {
	DEBUG9 (("====================\n")) ;
	DEBUG7 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID"\n",
	    p-Numeric->Memory, p+1-Numeric->Memory,
	    p->header.prevsize, p->header.size)) ;
	ASSERT (p->header.size > 0) ;

	if (p == Numeric->Memory + Numeric->itail)
	{
	    ASSERT (p->header.prevsize == 0) ;
	}
	else
	{
	    ASSERT (p->header.prevsize > 0) ;
	}

	ASSERT (p->header.prevsize == prevsize) ;
	prevsize = p->header.size ;

	if (p != Numeric->Memory + Numeric->size - 2)
	{

	    p3 = p + 1 ;

	    /* this is a packed element */
	    GET_ELEMENT (ep, p3, Cols, Rows, ncols, nrows, C) ;
	    DEBUG9 (("ep "ID"\n nrows "ID" ncols "ID"\n",
		(p+1)-Numeric->Memory, ep->nrows, ep->ncols)) ;
	    DEBUG9 (("rows:")) ;
	    for (k = 0 ; k < ep->nrows; k++)
	    {
		row = Rows [k] ;
		DEBUG9 ((" "ID, row)) ;
		ASSERT (row >= 0 && row <= Work->n_row) ;
		if ((k % 10) == 9) DEBUG9 (("\n")) ;
	    }
	    DEBUG9 (("\ncols:")) ;
	    for (k = 0 ; k < ep->ncols; k++)
	    {
		col = Cols [k] ;
		DEBUG9 ((" "ID, col)) ;
		ASSERT (col >= 0 && col <= Work->n_col) ;
		if ((k % 10) == 9) DEBUG9 (("\n")) ;
	    }
	    DEBUG9 (("\nvalues: ")) ;
	    if (UMF_debug >= 9)
	    {
		UMF_dump_dense (C, ep->nrows, ep->nrows, ep->ncols) ;
	    }
	    esize = GET_ELEMENT_SIZE (ep->nrows, ep->ncols) ;
	    DEBUG9 (("esize: "ID"\n", esize)) ;
	    ASSERT (esize <= p->header.size) ;

	}
	else
	{
	    /* this is the final marker block */
	    ASSERT (p->header.size == 1) ;
	}
	p = p + 1 + p->header.size ;
    }

    ASSERT (Numeric->ibig == EMPTY) ;
    ASSERT (p == Numeric->Memory + Numeric->size) ;
    DEBUG6 (("======================================END OF PACKED MEMORY:\n")) ;

}

/* ========================================================================== */
/* === UMF_dump_col_matrix ================================================== */
/* ========================================================================== */

/* This code is the same for real or complex matrices. */

GLOBAL void UMF_dump_col_matrix
(
    const double Ax [ ],	/* Ax [0..nz-1]: real values, in column order */
#ifdef COMPLEX
    const double Az [ ],	/* Az [0..nz-1]: imag values, in column order */
#endif
    const Int Ai [ ],		/* Ai [0..nz-1]: row indices, in column order */
    const Int Ap [ ],		/* Ap [0..n_col]: column pointers */
    Int n_row,			/* number of rows of A */
    Int n_col,			/* number of columns of A */
    Int nz			/* number of entries */
)
{
    Int col, p, p1, p2, row ;
    if (!Ai || !Ap) return ;
    DEBUG6 (("============================================ COLUMN FORM:\n")) ;


    ASSERT (n_col >= 0) ;
    nz = Ap [n_col] ;
    DEBUG2 (("UMF_dump_col:  nz "ID"\n", nz)) ;
    DEBUG2 (("n_row "ID"  & "ID"\n", n_row, &n_row)) ;
    DEBUG2 (("n_col "ID"  & "ID"\n", n_col, &n_col)) ;
    DEBUG2 (("Ap   & "ID" to & "ID"\n", Ap, Ap + (n_col+1) - 1)) ;
    DEBUG2 (("Ai   & "ID" to & "ID"\n", Ai, Ai + (nz) - 1)) ;
    if (Ax) DEBUG2 (("Ax   & "ID" to & "ID"\n", Ax, Ax + (nz) - 1)) ;

#ifdef COMPLEX
    if (Az) DEBUG2 (("Az   & "ID" to & "ID"\n", Az, Az + (nz) - 1)) ;
#endif

    DEBUG6 ((" n_row = "ID", n_col ="ID" nz = "ID" Ap [0] "ID", Ap [n] "ID"\n",
	n_row, n_col, nz, Ap [0], Ap [n_col])) ;
    ASSERT (Ap [0] == 0) ;
    ASSERT (Ap [n_col] == nz) ;
    for (col = 0 ; col < n_col ; col++)
    {
	p1 = Ap [col] ;
	p2 = Ap [col+1] ;
	DEBUG6 (("col: "ID", length "ID"\n", col, p2 - p1)) ;
	ASSERT (p2 >= p1) ;
	for (p = p1 ; p < p2 ; p++)
	{
	    row = Ai [p] ;
	    ASSERT (row >= 0 && row < n_row) ;
	    DEBUG6 (("\t"ID" ", row)) ;
	    if (Ax)
	    {
#ifdef COMPLEX
		if (Az)
		{
		    DEBUG6 ((" (%e+%ei) ", Ax [p], Az [p])) ;
		}
		else
		{
		    DEBUG6 ((" %e", Ax [p])) ;
		}
#else
		DEBUG6 ((" %e", Ax [p])) ;
#endif
	    }
	    DEBUG6 (("\n")) ;
	}
    }
    DEBUG6 (("========================================== COLUMN FORM done\n")) ;
}


/* ========================================================================== */
/* === UMF_dump_chain ======================================================= */
/* ========================================================================== */

GLOBAL void UMF_dump_chain
(
    Int frontid,
    Int Front_parent [ ],
    Int Front_npivcol [ ],
    Int Front_nrows [ ],
    Int Front_ncols [ ],
    Int nfr
)
{
    Int i, len = 0 ;

    /* print a list of contiguous parents */
    i = frontid ;
    ASSERT (Front_parent [i] == EMPTY ||
	(Front_parent [i] > i && Front_parent [i] < nfr)) ;

    len++ ;
    DEBUG3 (("Chain:\n	"ID" ["ID","ID"]("ID"-by-"ID")\n", i,
		Front_npivcol [i],
		MIN (Front_npivcol [i], Front_nrows [i]),
		Front_nrows [i],
		Front_ncols [i])) ;

    for (i = frontid ; i < nfr ; i++)
    {
	ASSERT (Front_parent [i] == EMPTY ||
	(Front_parent [i] > i && Front_parent [i] < nfr)) ;
	if (Front_parent [i] == i+1)
	{
	    len++ ;
	    DEBUG3 (("\t"ID" ["ID","ID"]("ID"-by-"ID")\n", i+1,
		Front_npivcol [i+1],
		MIN (Front_npivcol [i+1], Front_nrows [i+1]),
		Front_nrows [i+1],
		Front_ncols [i+1])) ;
	}
	else
	{
	    DEBUG2 (("Length of chain: "ID"\n", len)) ;
	    return ;
	}
    }
}


/* ========================================================================== */
/* === UMF_dump_start ======================================================= */
/* ========================================================================== */

GLOBAL void UMF_dump_start
(
    void
)
{
    FILE *ff ;

    /* get the debug print level from the "debug.umf" file, if it exists */
    UMF_debug = 0 ;
    ff = fopen ("debug.umf", "r") ;
    if (ff)
    {
	(void) fscanf (ff, ID, &UMF_debug) ;
	(void) fclose (ff) ;
    }

    DEBUG0 (("umfpack: debug version (SLOW!) ")) ;

    DEBUG0 ((" BLAS: ")) ;

#if defined (USE_NO_BLAS)
    DEBUG0 (("none.")) ;
#elif defined (USE_C_BLAS)
    DEBUG0 (("C-BLAS.")) ;
#elif defined (USE_MATLAB_BLAS)
    DEBUG0 (("built-in MATLAB BLAS.")) ;
#elif defined (USE_SUNPERF_BLAS)
    DEBUG0 (("Sun Performance Library BLAS.")) ;
#elif defined (USE_SCSL_BLAS)
    DEBUG0 (("SGI SCSL BLAS.")) ;
#elif defined (USE_FORTRAN_BLAS)
    DEBUG0 (("Fortran BLAS.")) ;
#endif

    DEBUG0 ((" MATLAB: ")) ;
#ifdef MATLAB_MEX_FILE
    DEBUG0 (("mexFunction.\n")) ;
#else
#ifdef MATHWORKS
    DEBUG0 (("yes (uses MathWorks internal ut* routines).\n")) ;
#else
    DEBUG0 (("no.\n")) ;
#endif
#endif

    UMF_gprob = -1.0 ;
    ff = fopen ("gprob.umf", "r") ;
    if (ff)
    {
	(void) fscanf (ff, "%lg", &UMF_gprob) ;
	(void) fclose (ff) ;
    }

    if (UMF_gprob > 1.0) UMF_gprob = 1.0 ;
    DEBUG1 (("factor: UMF_gprob: %e UMF_debug "ID"\n", UMF_gprob, UMF_debug)) ;

    DEBUG2 (("sizeof: (bytes / int / Units) \n")) ;
    DEBUG2 (("sizeof (Int)           %u %u %u\n",
    sizeof (Int), sizeof (Int) / sizeof (int), UNITS (Int, 1) )) ;
    DEBUG2 (("sizeof (int)           %u %u %u\n",
    sizeof (int), sizeof (int) / sizeof (int), UNITS (int, 1) )) ;
    DEBUG2 (("sizeof (size_t)        %u %u %u\n",
    sizeof (size_t), sizeof (size_t) / sizeof (size_t), UNITS (size_t, 1) )) ;
    DEBUG2 (("sizeof (long)          %u %u %u\n",
    sizeof (long), sizeof (long) / sizeof (long), UNITS (long, 1) )) ;
    DEBUG2 (("sizeof (double)        %u %u %u\n",
    sizeof (double), sizeof (double) / sizeof (int), UNITS (double, 1) )) ;
    DEBUG2 (("sizeof (Unit)          %u %u %u\n",
    sizeof (Unit), sizeof (Unit) / sizeof (int), UNITS (Unit, 1) )) ;
    DEBUG2 (("sizeof (Entry)         %u %u %u\n",
    sizeof (Entry), sizeof (Entry) / sizeof (int), UNITS (Entry, 1) )) ;
    DEBUG2 (("sizeof (Tuple)         %u %u %u\n",
    sizeof (Tuple), sizeof (Tuple) / sizeof (int), UNITS (Tuple, 1) )) ;
    DEBUG2 (("sizeof (Tuple *)       %u %u %u\n",
    sizeof (Tuple *), sizeof (Tuple *) / sizeof (int), UNITS (Tuple *, 1) )) ;
    DEBUG2 (("sizeof (Element)       %u %u %u\n",
    sizeof (Element), sizeof (Element) / sizeof (int), UNITS (Element, 1) )) ;
    DEBUG2 (("sizeof (Element *)     %u %u %u\n",
    sizeof (Element *), sizeof (Element *) / sizeof (int),
    UNITS (Element *, 1) )) ;
    DEBUG2 (("sizeof (WorkType)      %u %u %u\n",
    sizeof (WorkType), sizeof (WorkType) / sizeof (int),
    UNITS (WorkType, 1) )) ;
    DEBUG2 (("sizeof (NumericType)   %u %u %u\n",
    sizeof (NumericType), sizeof (NumericType) / sizeof (int),
    UNITS (NumericType, 1) )) ;
    DEBUG2 (("sizeof (SymbolicType)  %u %u %u\n",
    sizeof (SymbolicType), sizeof (SymbolicType) / sizeof (int),
    UNITS (SymbolicType, 1) )) ;

}


/* ========================================================================== */
/* === UMF_dump_rowmerge ==================================================== */
/* ========================================================================== */

GLOBAL void UMF_dump_rowmerge
(
    NumericType *Numeric,
    SymbolicType *Symbolic,
    WorkType *Work
)
{
    Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1, row2,
	fleftmost, nfr, n_row, *Row_degree, i, frontid, row ;

    nfr = Symbolic->nfr ;
    DEBUG3 (("\n================== Row merge sets: nfr "ID"\n", nfr)) ;
    Front_leftmostdesc = Symbolic->Front_leftmostdesc ;
    Front_1strow = Symbolic->Front_1strow ;
    Front_new1strow = Work->Front_new1strow ;
    n_row = Symbolic->n_row ;
    Row_degree = Numeric->Rperm ;
    frontid = Work->frontid ;

    for (i = frontid ; i <= nfr ; i++)
    {
	DEBUG3 (("----------------------\n")) ;
	if (i == nfr) DEBUG3 (("Dummy: ")) ;
	DEBUG3 (("Front "ID" 1strow "ID" new1strow "ID" leftmostdesc "ID,
	    i, Front_1strow [i], Front_new1strow [i], Front_leftmostdesc [i])) ;
	DEBUG3 ((" parent "ID" pivcol\n", Symbolic->Front_parent [i],
	    Symbolic->Front_npivcol [i])) ;

	if (i == nfr)
	{
	    fleftmost = -1 ;
	    row1 = Front_new1strow [i] ;
	    row2 = n_row-1 ;
	}
	else
	{
	    fleftmost = Front_leftmostdesc [i] ;
	    row1 = Front_new1strow [fleftmost] ;
	    row2 = Front_1strow [i+1] - 1 ;
	}
	DEBUG3 (("Leftmost: "ID"  Rows ["ID" to "ID"], search ["ID" to "ID"]\n",
	    fleftmost, Front_1strow [i], row2, row1, row2)) ;

	for (row = row1 ; row <= row2 ; row++)
	{
	    ASSERT (row >= 0 && row < n_row) ;
	    DEBUG3 (("   Row "ID"  live: "ID"\n", row, NON_PIVOTAL_ROW (row))) ;
	}
    }
}

#endif /* NDEBUG */

```

sparse-matrix/umfpack/umf_local_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_local_search
(
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
) ;


```

sparse-matrix/umfpack/umfpack_transpose.c:
```
/* ========================================================================== */
/* === 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                       */
/* -------------------------------------------------------------------------- */

/*

    User callable.  Computes a permuted transpose, R = (A (P,Q))' in MATLAB
    notation.  See umfpack_transpose.h for details.  A and R can be rectangular.
    The matrix A may be singular.
    The complex version can do transpose (') or array transpose (.').

    Dynamic memory usage:

	A single call to UMF_malloc is made, for a workspace of size
	max (n_row,n_col,1) * sizeof(Int).  This is then free'd on return,
	via UMF_free.

*/

#include "umf_internal.h"
#include "umf_transpose.h"
#include "umf_malloc.h"
#include "umf_free.h"

#ifndef NDEBUG
PRIVATE Int init_count ;
#endif

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

GLOBAL Int UMFPACK_transpose
(
    Int n_row,
    Int n_col,
    const Int Ap [ ],	/* size n_col+1 */
    const Int Ai [ ],	/* size nz = Ap [n_col] */
    const double Ax [ ], /* size nz, if present */
#ifdef COMPLEX
    const double Az [ ], /* size nz, if present */
#endif

    const Int P [ ],	/* P [k] = i means original row i is kth row in A(P,Q)*/
			/* P is identity if not present */
			/* size n_row, if present */

    const Int Q [ ],	/* Q [k] = j means original col j is kth col in A(P,Q)*/
			/* Q is identity if not present */
			/* size n_col, if present */

    Int Rp [ ],		/* size n_row+1 */
    Int Ri [ ],		/* size nz */
    double Rx [ ]	/* size nz, if present */
#ifdef COMPLEX
    , double Rz [ ]	/* size nz, if present */
    , Int do_conjugate	/* if true, then to conjugate transpose */
			/* otherwise, do array transpose */
#endif
)
{

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

    Int status, *W, nn ;

#ifndef NDEBUG
    init_count = UMF_malloc_count ;
    UMF_dump_start ( ) ;
#endif

    /* ---------------------------------------------------------------------- */
    /* allocate workspace */
    /* ---------------------------------------------------------------------- */

    nn = MAX (n_row, n_col) ;
    nn = MAX (nn, 1) ;
    W = (Int *) UMF_malloc (nn, sizeof (Int)) ;
    if (!W)
    {
	ASSERT (UMF_malloc_count == init_count) ;
	return (UMFPACK_ERROR_out_of_memory) ;
    }
    ASSERT (UMF_malloc_count == init_count + 1) ;

    /* ---------------------------------------------------------------------- */
    /* C = (A (P,Q))' or (A (P,Q)).' */
    /* ---------------------------------------------------------------------- */

    status = UMF_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, n_col, Rp, Ri, Rx,
	W, TRUE
#ifdef COMPLEX
	, Az, Rz, do_conjugate
#endif
	) ;

    /* ---------------------------------------------------------------------- */
    /* free the workspace */
    /* ---------------------------------------------------------------------- */

    (void) UMF_free ((void *) W) ;
    ASSERT (UMF_malloc_count == init_count) ;

    return (status) ;
}


```

sparse-matrix/y12m/index.html:
```
<!-- X-URL: http://www.netlib.org/y12m/index.html -->
<!-- Date: Sun, 15 Sep 2002 16:59:37 GMT -->
<!-- Last-Modified: Fri, 21 Jun 2002 14:14:39 GMT -->
<BASE HREF="http://www.netlib.org/y12m/index.html">
<head>
<title>y12m</title>
<meta name="waisindex" value="nse">
</head>
<h1>y12m</h1>
<p>
Click <A HREF="http://www.netlib.org/master_counts2.html#y12m">here</A> to see the number of accesses to this library.
<p><hr>
<pre>
file    <a href="doc">doc</a>
for     documentation for the package
size    125 kilobytes

file    <a href="y12m.f">y12m.f</a>  <a href="/cgi-bin/netlibfiles.pl?filename=/y12m/y12m.f">y12m.f plus dependencies</a>
for     solution of linear systems, matrices are large and sparse

file    <a href="y12mae.f">y12mae.f</a>  <a href="/cgi-bin/netlibfiles.pl?filename=/y12m/y12mae.f">y12mae.f plus dependencies</a>
for     sparse matrix, solve one system, single right hand side
prec    single

file    <a href="y12maf.f">y12maf.f</a>  <a href="/cgi-bin/netlibfiles.pl?filename=/y12m/y12maf.f">y12maf.f plus dependencies</a>
for     sparse matrix, solve one system, single right hand side
prec    double

file    <a href="y12mfe.f">y12mfe.f</a>  <a href="/cgi-bin/netlibfiles.pl?filename=/y12m/y12mfe.f">y12mfe.f plus dependencies</a>
for     Solves a large sparse system of linear algebraic equations
,       using Gaussian elimination and sparse matrix techniques.
,       Iterative refinement is applied in order to improve accuracy.
gams    d2a4

lib     <a href="ex/">ex</a>
for     test programs
</pre>
</body>
</html>


```

sparse-matrix/y12m/README:
```
This directory contains the Harwell Y12M sparse LU decompositon routines,
as obtained from netlib (url given in ./www) in September 2002.

Y12MAE		single precision driver
Y12MAF		double precision driver

Y12MF		iterative improvement

Y12MB[EF]	analyse
Y12MC[EF]	factor (LU decompose)
Y12MD[EF]	solve given LU decomposition

```

sparse-matrix/y12m/www:
```
http://www.netlib.org/y12m/index.html

```

sparse-matrix/y12m/doc:
```

The Y12M documentation:

C                =================================
C                Documentation of subroutine Y12MA
C                =================================
C
C     1.  Purpose
C
C
C     Y12MA   solves   sparse  systems  of  linear  algebraic
C     equations by Gaussian elimination.  The subroutine is a
C     "black  box  subroutine"  designed to solve efficiently
C     problems which contain only one system  with  a  single
C     right  hand side. The number of the input parameters is
C     minimized. The user must assign values only to NN, NN1,
C     N,  Z,  A,  SNR,  RNR, IHA and B according to the rules
C     described in Section 2.4 (see below).  It is  extremely
C     easy  to  modify  the  subroutine  to  the cases: (a) a
C     sequence of systems with  the  same  matrix  is  to  be
C     solved (note that one system with many right hand sides
C     can be rewritten as a sequence of systems with the same
C     matrix),  (b)  a sequence of systems whose matrices are
C     different but of the same structure is to be solved and
C     (c)  a  sequence  of  systems whose matrices are of the
C     same structure and some of them are the same is  to  be
C     solved.   These  cases  are  defined as case (ii), case
C     (iii) and case (iv)  in  Section  1.1.  "Scope  of  the
C     Y12M".   The recommendations in Section 1.3.6 should be
C     followed in order to modify the  subroutine  Y12MA  for
C     the  above  cases.  If  a  sequence  of  systems  whose
C     matrices are of different structure (this case  appears
C     as  case  (v)  in  Section  1.1)  is  to be solved then
C     subroutine Y12MA should be called in  the  solution  of
C     each system in the sequence.
C
C     2.  Calling sequence and declaration of the parameters
C
C     The  subroutine  is  written in FORTRAN and it has been
C     extensively tested with the FOR and FTN compilers on  a
C     UNIVAC  1100/82  computer,  at  the  Regional Computing
C     Centre at the University of  Copenhagen  (RECKU).  Many
C     examples  have  been run on an IBM 3033 computer at the
C     Northern Europe University Computing Centre (NEUCC) and
C     on  a  CDC Cyber 173 computer at the Regional Computing
C     Centre  at  the  University  of  Aarhus  (RECAU).   Two
C     different  versions  are  available: a single precision
C     version named Y12MAE and  a  double  precision  version
C     named  Y12MAF.  The calls of these two versions and the
C     declarations of the parameters are as follows.
C
C     A). Single precision version: Y12MAE
C
C           SUBROUTINE Y12MAE(N, Z, A, SNR, NN, RNR, NN1,
C          1  PIVOT, HA, IHA, AFLAG, IFLAG, B, IFAIL)
C
C           REAL A(NN), PIVOT(N), B(N), AFLAG(8)
C           INTEGER SNR(NN), RNR(NN1), HA(IHA,11), IFLAG(10)
C           INTEGER N, Z, NN, NN1, IHA, IFAIL
C
C     B). Double precision version: Y12MAF
C
C           SUBROUTINE Y12MAF(N, Z, A, SNR, NN, RNR, NN1,
C          1  PIVOT, HA, IHA, AFLAG, IFLAG, B, IFAIL)
C
C           DOUBLE PRECISION A(NN), PIVOT(N), B(N), AFLAG(8)
C           INTEGER SNR(NN), RNR(NN1), HA(IHA,11), IFLAG(10)
C           INTEGER N, Z, NN, NN1, IHA, IFAIL
C
C     These two versions can be used on many other  computers
C     also. However some alterations may be needed and/or may
C     ensure greater efficiency of  the  performance  of  the
C     subroutine. For example, it will be much more efficient
C     to declare arrays SNR, RNR  and  (if  possible)  HA  as
C     INTEGER*2 arrays on some IBM installations.
C
C
C     3.  Method
C
C     The system Ax = b is solved  by  Gaussian  elimination.
C     Pivotal interchanges are used in an attempt to preserve
C     both the stability of the computations and the sparsity
C     of  the original matrix. In this way a decomposition LU
C     = PAQ is normally calculated. P and Q  are  permutation
C     matrices, L is a lower triangular matrix, U is an upper
C     triangular matrix. The right hand side vector b is also
C     modified  during  the  decomposition so that the vector
C     c=L**(-1)*P*b  is  available  after  the  decomposition
C     stage.  In  this  way  there  is  no  need to store the
C     non-zero elements of matrix L. Therefore these elements
C     are  not  stored.  This  leads  to  a  reduction of the
C     storage requirements (the length of arrays  A  and  SNR
C     can  be  decreased).  An  approximation to the solution
C     vector is found by solving U*tr(T)*x=c, where tr(T)  is
C     the  transpose.  The  subroutine calculates the rate at
C     which the  elements  of  the  matrix  grow  during  the
C     decomposition  (see  parameter  AFLAG(5) below) and the
C     minimal  in  absolute  value   pivotal   element   (see
C     parameter  AFLAG(8)  below).  These  two numbers can be
C     used to decide whether the  computed  approximation  is
C     acceptable   or  not.  Positive  values  of  a  special
C     parameter IFAIL indicate that the subroutine is  unable
C     to  solve  the problem. The error diagnostics, given in
C     Section 7, describe the probable cause.
C
C     4.  Parameters of the subroutine
C
C
C     N     - INTEGER On entry N must contain the  number  of
C             equations  in  the  system   Ax=b. Unchanged on
C             exit.
C
C     Z     - INTEGER. On entry Z must contain the number  of
C             non-zero  elements  in the coefficient matrix A
C             of the system Ax = b. Unchanged on exit.
C
C     A     - REAL (in the single precision  version  Y12MAE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MAF) array of length NN (see below).
C             On  entry the first Z locations of array A must
C             contain   the   non-zero   elements   of    the
C             coefficient  matrix A of the system Ax = b. The
C             order  of  the   non-zero   elements   may   be
C             completely arbitrary. The content of array A is
C             modified by  subroutine  Y12MA.  On  successful
C             exit array A will contain the non-zero elements
C             of the upper triangular matrix U  (without  the
C             diagonal  elements  of  matrix  U  which can be
C             found in array PIVOT, see below).
C
C     SNR   - INTEGER array of  length  NN  (see  below).  On
C             entry  SNR(j),  j  =  1(1)Z,  must  contain the
C             column number of the non-zero element stored in
C             A(j).   The content of array SNR is modified by
C             subroutine Y12MA.  On successful exit array SNR
C             will contain the column numbers of the non-zero
C             elements  of  the  upper  triangular  matrix  U
C             (without  the  column  numbers  of the diagonal
C             elements of matrix U).
C
C     NN    - INTEGER. On entry NN must contain the length of
C             arrays   A  and  SNR.  Restriction:  NN.GE.2*Z.
C             Recommended value: 2*Z.LE.NN.LE.3*Z.  Unchanged
C             on exit.
C
C     RNR   - INTEGER  array  of  length  NN1 (see below). On
C             entry RNR(i), i = 1(1)Z, must contain  the  row
C             number  of the non-zero element stored in A(i).
C             The  content  of  array  RNR  is  modified   by
C             subroutine   Y12MA.   On  successful  exit  all
C             components of array RNR will normally be  zero.
C
C     NN1   - INTEGER. On entry NN1 must contain  the  length
C             of   the  array  RNR.   Restriction:  NN1.ge.Z.
C             Recommended      value:      2*Z.LE.NN1.LE.3*Z.
C             Unchanged on exit.
C
C     PIVOT - REAL  (in  the single precision version Y12MAE)
C             or DOUBLE PRECISION (in  the  double  precision
C             version  Y12MAF) array of length N. The content
C             of array PIVOT is modified by subroutine Y12MA.
C             On successful exit array PIVOT will contain the
C             pivotal  elements  (the  diagonal  elements  of
C             matrix  U). This means that a small element (or
C             small elements) in  array  PIVOT  on  exit  may
C             indicate    numerical    singularity   of   the
C             coefficient matrix A. Note that the smallest in
C             absolute  value  element in array PIVOT is also
C             stored in AFLAG(8), see below.
C
C     HA    - INTEGER two-dimensional array.  The  length  of
C             the  first  dimension  is  IHA (see below). The
C             length of the  second  dimension  is  11.  This
C             array  is  used  as  a work space by subroutine
C             Y12MA.
C
C     IHA   - INTEGER. On entry IHA must contain  the  length
C             of   the   first   dimension   of   array   HA.
C             Restriction:  IHA.GE.N. Unchanged on exit.
C
C     AFLAG - REAL (in the single precision  version  Y12MAE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MAF) array of length 8. The  content
C             of array AFLAG is modified by subroutine Y12MA.
C             The content of the components of this array can
C             be described as follows.
C
C             AFLAG(1) - Stability factor. An element can  be
C                        chosen  as  pivotal  element only if
C                        this element is larger (in  absolute
C                        value)  than  the  absolute value of
C                        the  largest  element  in  its   row
C                        divided   by  AFLAG(1).   Subroutine
C                        Y12MA  sets  AFLAG(1)  =  16.   This
C                        value  has  been  found satisfactory
C                        for a wide range of test-examples.
C
C             AFLAG(2) - Drop-tolerance. An element, which in
C                        the   process  of  the  computations
C                        becomes smaller (in absolute  value)
C                        than  the drop-tolerance, is removed
C                        from array A (and its column and row
C                        numbers  are removed from arrays SNR
C                        and  RNR).   Subroutine  Y12MA  sets
C                        AFLAG(2)  =  1.0E-12. This value has
C                        been found satisfactory for  a  wide
C                        range   of  test-matrices.  By  this
C                        choice it is assumed that the matrix
C                        is not too badly scaled and that the
C                        magnitude of the elements is 1.
C
C             AFLAG(3) - The   subroutine   will   stop   the
C                        computations  when the growth factor
C                        (parameter  AFLAG(5),   see   below)
C                        becomes  larger  than  AFLAG(3). Our
C                        experiments show that if AFLAG(3)  >
C                        1.0E6  then the solution is normally
C                        quite wrong.   Therefore  subroutine
C                        Y12MA sets AFLAG(3) = 1.0E6.
C
C             AFLAG(4) - The   subroutine   will   stop   the
C                        computations when the absolute value
C                        of  a  current  pivotal  element  is
C                        smaller    than    AFLAG(4)*AFLAG(6)
C                        (where  the  absolute  value  of the
C                        largest (in absolute value)  element
C                        of  the original matrix is stored in
C                        AFLAG(6),    see     below).     Our
C                        experiments show that
C                        AFLAG(4)=1.0E-12  will normally be a
C                        good  choice.  Therefore  subroutine
C                        Y12MA sets  AFLAG(4)=1.0E-12.
C
C             AFLAG(5) - Growth factor. After each  stage  of
C                        the   elimination  subroutine  Y12MA
C                        sets  AFLAG(5)  =  AFLAG(7)/AFLAG(6)
C                        (the   description   of   parameters
C                        AFLAG(6)  and  AFLAG(7)   is   given
C                        below).  Large  values  of  AFLAG(5)
C                        indicate that an  appreciable  error
C                        in    the   computed   solution   is
C                        possible. In an  extreme  case  when
C                        AFLAG(5)  becomes larger than 1.0E6,
C                        see parameter  AFLAG(3),  subroutine
C                        Y12MA  will stop the computations in
C                        an attempt to prevent overflows.
C
C             AFLAG(6) - Subroutine Y12MA sets AFLAG(6) equal
C                        to  max(abs(a(i,j))), i = 1(1)N, j =
C                        1(1)N.
C
C             AFLAG(7) - Subroutine Y12MA sets AFLAG(7) equal
C                        to max(abs(a(i,j;s))),  1.lt.s.lt.k,
C                        after each step  k,   k=1(1)N-1,  of
C                        the elimination.
C
C             AFLAG(8) - Subroutine Y12MA sets AFLAG(8) equal
C                        to min(abs(a(i,i))), i= 1(1)N.  This
C                        means   that   the  minimal  pivotal
C                        element (in absolute value) will  be
C                        stored  in  AFLAG(8)  on  successful
C                        exit.   Small  values  of   AFLAG(8)
C                        indicate  numerical  singularity  of
C                        the original matrix.  We advise  the
C                        user to check this parameter on exit
C                        very carefully.
C
C     IFLAG - INTEGER array of length 10. The content of this
C             array is  modified  by  subroutine  Y12MA.  The
C             content  of the components of this array can be
C             described as follows.
C
C             IFLAG(1) - Subroutine  Y12MA uses IFLAG(1) as a
C                        work space.
C
C             IFLAG(2) - Subroutine Y12MA sets IFLAG(2) =  3.
C                        This means that at each stage of the
C                        Gaussian  elimination  (except   the
C                        last  one)  the  pivotal  search  is
C                        carried out in the 3 rows which have
C                        least  numbers of non-zero elements.
C
C             IFLAG(3) - Subroutine  Y12MA sets IFLAG(3) = 1.
C                        This means that the pivotal strategy
C                        for  general  matrices will be used.
C                        For  some  special   matrices   (for
C                        example positive definite) this will
C                        be  inefficient.   Subroutine  Y12MA
C                        can  easily  be  modified  for  such
C                        matrices;   only    the    statement
C                        IFLAG(3)  = 1 should be changed (for
C                        positive  definite   matrices   e.g.
C                        IFLAG(3)  =  2  can  be  used). More
C                        details  about  the  use   of   this
C                        parameter  with special matrices are
C                        given in Section 1.3.1
C
C             IFLAG(4) - Subroutine Y12MA sets IFLAG(4) =  0.
C                        This  is the best choice in the case
C                        where only one system Ax = b  is  to
C                        be   solved,  see  more  details  in
C                        Section 1.3.
C
C             IFLAG(5) - Subroutine Y12MA sets IFLAG(5) =  1.
C                        This   means   that   the   non-zero
C                        elements  of  the  lower  triangular
C                        matrix  L will be removed during the
C                        decomposition   stage,   see    more
C                        details in Section 1.3.3.
C
C             IFLAG(6) - On  successful exit IFLAG(6) will be
C                        equal to  the  number  of  "garbage"
C                        collections in the row ordered list.
C                        If IFLAG(6)  is  large  then  it  is
C                        better  to  choose a larger value of
C                        NN in the next calls  of  subroutine
C                        Y12MA  with  the  same  or a similar
C                        matrix  A.  This  will  lead  to   a
C                        reduction in the computing time.
C
C             IFLAG(7) - On  successful exit IFLAG(7) will be
C                        equal to  the  number  of  "garbage"
C                        collections  in  the  column ordered
C                        list.  If IFLAG(7) is large then  it
C                        is  better  to choose a larger value
C                        of  NN1  in  the   next   calls   of
C                        subroutine  Y12MA with the same or a
C                        similar matrix A. This will lead  to
C                        a reduction in the computing time.
C
C             IFLAG(8) - On  successful exit IFLAG(8) will be
C                        equal  to  the  maximal  number   of
C                        non-zero elements kept in array A at
C                        any   stage    of    the    Gaussian
C                        elimination.  If  IFLAG(8)  is  much
C                        smaller then NN (or  NN1)  then  the
C                        length  NN  (or  NN1)  can be chosen
C                        smaller  in  the   next   calls   of
C                        subroutine  Y12MA with the same or a
C                        similar matrix A. This will lead  to
C                        a reduction of the storage needed.
C
C             IFLAG(9) - This   parameter   is   ignored   by
C                        subroutine Y12MA.  It will  be  used
C                        in  some  of  the  other subroutines
C                        when IFLAG(4) = 1  is  specified  on
C                        entry.
C
C             IFLAG(10)- This   parameter   is   ignored   by
C                        subroutine Y12MA.  It will  be  used
C                        in  some  of  the  other subroutines
C                        when IFLAG(4) = 1  is  specified  on
C                        entry.
C
C     B     - REAL (in the single precision  version  Y12MAE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MAF) array of length N. On entry the
C             right  hand  side vector b of the system Ax = b
C             must be stored in array B. The content of array
C             B   is   modified   by  subroutine  Y12MA.   On
C             successful exit the  computed  solution  vector
C             will be stored in array B.
C
C     IFAIL - Error  diagnostic  parameter.  The  content  of
C             parameter  IFAIL  is  modified  by   subroutine
C             Y12MA.  On exit IFAIL = 0 if the subroutine has
C             not detected  any  error.  Positive  values  of
C             IFAIL  on  exit  show  that some error has been
C             detected by the subroutine. Many of  the  error
C             diagnostics  are  common for all subroutines in
C             the package.  Therefore the  error  diagnostics
C             are listed in a separate section, Section 7, of
C             this book. We advise  the  user  to  check  the
C             value of this parameter on exit.
C
C     5.  Error diagnostics
C
C     Error diagnostics are given by positive values  of  the
C     parameter  IFAIL  (see  above).   We advise the user to
C     check carefully the value of this  parameter  on  exit.
C     The error messages are listed in Section 7.
C
C     6.  Auxiliary subroutines
C
C     Y12MA  calls  three other subroutines: Y12MB, Y12MC and
C     Y12MD.
C
C     7.  Timing
C
C     The time taken depends  on  the  order  of  the  matrix
C     (parameter  N),  the number of the non-zero elements in
C     the matrix (parameter Z), the magnitude of the non-zero
C     elements and their distribution in the matrix.
C
C     8.  Storage
C
C     There are no internally declared arrays.
C
C     9.  Accuracy
C
C     It  is  difficult  to  evaluate  the  accuracy  of  the
C     computed solution. Large values of parameter  AFLAG(5),
C     the   growth  factor,  indicate  unstable  computations
C     during  the  decomposition  stage.  Small   values   of
C     parameter  AFLAG(8)  can  be considered as a signal for
C     numerical singularity.  We  must  emphasize  here  that
C     normally much more reliable evaluations of the accuracy
C     achieved  can  be  found  by  the  use   of   iterative
C     refinement, i.e. by the use of subroutine Y12MF. By the
C     use of the latter subroutine the  computing  time  will
C     often be reduced too. However, the storage requirements
C     may be increased sometimes.
C
C     10.  Some remarks
C
C     Remark 1   Subroutine  Y12MA  may also be considered as
C                an example of how to initialize the first  4
C                components  of  array  AFLAG and the first 5
C                components of  array  IFLAG  when  only  one
C                system  with  a single right hand side is to
C                be  solved.  An   efficient   use   of   the
C                subroutines  Y12MB,  Y12MC and Y12MD for the
C                other cases discussed in Section 1.1 may  be
C                achieved  by  a single modification of Y12MA
C                according to  the  rules  given  in  Section
C                1.3.6   (a   change   of  only  one  or  two
C                statements in Y12MA is needed to obtain such
C                a modification).
C
C     Remark 2   The last 4 components of array AFLAG and the
C                last 5 components of array IFLAG can be used
C                as  output  parameters.   The  user  is  not
C                obliged to do this.  However,  we  recommend
C                the check of these parameters on exit.
C                =================================
C                Documentation of subroutine Y12MB
C                =================================
C
C     1.  Purpose
C
C     Y12MB prepares a system of linear  algebraic  equations
C     to  be  factorized (by subroutine Y12MC) and solved (by
C     subroutine Y12MD).  Each call of subroutine Y12MC  must
C     be  preceded  by a call of Y12MB. It is very convenient
C     to perform some operations on the non-zero elements  of
C     the  coefficient  matrix  between  calls  of  Y12MB and
C     Y12MC.  For  example,  when  iterative  refinement   is
C     carried  out  the information needed in the calculation
C     of the residual vector is copied in some  extra  arrays
C     between  the  calls of Y12MB and Y12MC. It is also very
C     easy to perform any kind of scaling after the  call  of
C     Y12MB.
C
C     2.  Calling sequence and declaration of the parameters
C
C     The  subroutine  is  written  in  FORTRAN  and has been
C     extensively tested with the FOR and FTN compilers on  a
C     UNIVAC  1100/82  computer  at  the  Regional  Computing
C     Centre at the University of  Copenhagen  (RECKU).  Many
C     examples  have  been run on an IBM 3033 computer at the
C     Northern Europe University Computing Centre (NEUCC) and
C     on  a  CDC Cyber 173 computer at the Regional Computing
C     Centre  at  the  University  of  Aarhus  (RECAU).   Two
C     different  versions  are available:  a single precision
C     version Y12MBE and a double precision  version  Y12MBF.
C     The  calls of these two versions and the declaration of
C     the parameters are as follows:
C
C     A) Single precision version Y12MBE
C
C           SUBROUTINE Y12MBE(N, Z, A, SNR, NN, RNR, NN1,
C          1  HA, IHA, AFLAG, IFLAG, IFAIL)
C
C           REAL A(NN), AFLAG(8)
C           INTEGER SNR(NN), RNR(NN1), HA(N,11), IFLAG(10)
C           INTEGER N, Z, IHA, IFAIL
C
C     B) Double precision version Y12MBF
C
C           SUBROUTINE Y12MBF(N, Z, A, SNR, NN, RNR, NN1,
C          1  HA, IHA, AFLAG, IFLAG, IFAIL)
C
C           DOUBLE PRECISION A(NN), AFLAG(8)
C           INTEGER SNR(NN), RNR(NN1), HA(N,11), IFLAG(10)
C           INTEGER N, Z, IHA, IFAIL
C
C     These  two versions can be used on many other computers
C     also. However some alterations may be needed and/or may
C     ensure  greater  efficiency  of  the performance of the
C     subroutine. For example, it will be much more efficient
C     to  declare  arrays  SNR,  RNR  and (if possible) HA as
C     INTEGER*2 arrays on some IBM installations.
C
C
C     3.  Method
C
C     The  non-zero  elements of matrix A are ordered by rows
C     and stored in the first Z positions  of  array  A.  The
C     order   of  the  non-zero  elements  within  a  row  is
C     arbitrary. The column numbers of the non-zero  elements
C     are  stored  in  the  first Z positions of array SNR so
C     that if  A(J)=a(i,j),  J=1(1)Z, then  SNR(J)=j. The row
C     numbers  of  the  non-zero  elements  are stored in the
C     first Z positions of array RNR so that the row  numbers
C     of  the non-zero elements in the first column of matrix
C     A are located before the row numbers  of  the  non-zero
C     elements  in  the  second  column  of matrix A, the row
C     numbers of the non-zero elements in the  second  column
C     of  matrix  A are located before the row numbers of the
C     non-zero elements in the third column of matrix  A  and
C     so  on. Some additional information, e.g. about the row
C     starts, row ends, column starts  and  column  ends,  is
C     stored  in  the  work array HA. This storage scheme has
C     been proposed by Gustavson.
C
C     4.  Parameters of the subroutine
C
C
C     N     - INTEGER. On entry N must contain the number  of
C             equations  in  the  system   Ax=b. Unchanged on
C             exit.
C
C     Z     - INTEGER. On entry Z must contain the number  of
C             non-zero  elements  in the coefficient matrix A
C             of the system Ax = b. Unchanged on exit.
C
C     A     - REAL (in the single precision  version  Y12MBE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MBF) array of length NN (see below).
C             On  entry the first Z locations of array A must
C             contain   the   non-zero   elements   of    the
C             coefficient  matrix A of the system Ax = b. The
C             order  of  the   non-zero   elements   may   be
C             completely arbitrary. The content of array A is
C             modified by  subroutine  Y12MB.  On  successful
C             exit  the  first  Z  positions  of array A will
C             contain the non-zero elements of the  matrix  A
C             ordered by rows.
C
C     SNR   - INTEGER  array  of  length  NN  (see below). On
C             entry SNR(j),  j  =  1(1)Z,  must  contain  the
C             column number of the non-zero element stored in
C             A(j).  The content of array SNR is modified  by
C             subroutine  Y12MB.   On successful exit SNR(j),
C             j=1(1)Z, will contain the column number of  the
C             non-zero element stored in A(j).
C
C     NN    - INTEGER. On entry NN must contain the length of
C             arrays  A  and  SNR.  Restriction:   NN.ge.2*Z.
C             Recommended  value: 2*Z.le.NN.le.3*Z.  See also
C             the description of NN in Y12MC.   Unchanged  on
C             exit.
C
C     RNR   - INTEGER  array  of  length  NN1 (see below). On
C             entry RNR(i), i = 1(1)Z, must contain  the  row
C             number  of the non-zero element stored in A(i).
C             The  content  of  array  RNR  is  modified   by
C             subroutine  Y12MB.  On  successful exit the row
C             numbers of the non-zero elements  of  matrix  A
C             are  stored  in  the first Z positions of array
C             RNR, so that the row numbers  of  the  non-zero
C             elements  in  the  first column of matrix A are
C             before the row numbers of the non-zero elements
C             in  the  second  column  of  matrix  A, the row
C             numbers of the non-zero elements in the  second
C             column  of  matrix A are before the row numbers
C             of the non-zero elements in the third column of
C             matrix A and so on. This means that on exit the
C             row number of the non-zero  element  stored  in
C             A(i),  i  =  1(1)Z, is not stored in RNR(i), in
C             general.
C
C     NN1   - INTEGER. On entry NN1 must contain  the  length
C             of   the  array  RNR.   Restriction:  NN1.ge.Z.
C             Recommended value: 2*Z.le.NN1.le.3*Z.  See also
C             the  desription  of NN1 in Y12MC.  Unchanged on
C             exit.
C
C     HA    - INTEGER two-dimensional array.  The  length  of
C             the  first  dimension of HA is IHA (see below).
C             The length of the second dimension of HA is 11.
C             The contents of some elements of this array are
C             modified by subroutine Y12MB, the  contents  of
C             the others are ignored. On successful exit:
C
C             (1) HA(i,1) contains the position  in  array  A
C                         where the first element of row i, i
C                         = 1(1)N, is stored.
C             (2) HA(i,3) contains  the  position  in array A
C                         where the last element of row i,  i
C                         =  1(1)N,  is  stored (all non-zero
C                         elements of  row   i   are  located
C                         between    HA(i,1)    and   HA(i,3)
C                         compactly; the number  of  non-zero
C                         elements  in  row  i  is  HA(i,3) -
C                         HA(i,1) + 1).
C             (3) HA(i,4) contains  the position in array RNR
C                         where the row number of  the  first
C                         non-zero  element  of  column  j is
C                         stored (j = 1(1)N).
C             (4) HA(j,6) contains  the position in array RNR
C                         where the row number  of  the  last
C                         element  in column j, j = 1(1)N, is
C                         stored  (all  row  numbers  of  the
C                         non-zero  elements  of column j are
C                         located between HA(j,4) and HA(j,6)
C                         compactly,  the  number of non-zero
C                         elements    in    column    j    is
C                         HA(j,6)-HA(j,4)+1).
C
C             (5)         Some   information  needed  in  the
C                         pivotal    search    during     the
C                         decomposition  is stored in columns
C                         7,8 and 11 of array HA.
C
C             (6)         The other columns of HA are  either
C                         ignored  by Y12MB or used as a work
C                         space.
C
C     IHA   - INTEGER.  On  entry IHA must contain the length
C             of   the   first   dimension   of   array   HA.
C             Restriction:  IHA.ge.N. Unchanged on exit.
C
C     AFLAG - REAL  (in  the single precision version Y12MBE)
C             or DOUBLE PRECISION (in  the  double  precision
C             version Y12MBF) array of length 8. The contents
C             of  all  locations  of  array   AFLAG,   except
C             AFLAG(6), are ignored by subroutine Y12MB.  The
C             content of AFLAG(6) is modified  by  Y12MB.  On
C             successful exit AFLAG(6) contains
C             max(abs(i,j)).
C
C     IFLAG - INTEGER array of length 10. The user should set
C             IFLAG(1).ge.0  before  the call of package Y12M
C             (i.e. before the first call of a subroutine  of
C             this  package).  IFLAG(1)  is used in the error
C             checks by the subroutines  and  should  not  be
C             altered  by the user between any two successive
C             calls of subroutines of the  package.  IFLAG(1)
C             will  be  equal to  -1  on successful exit from
C             subroutine Y12MB. Thus, the content of IFLAG(1)
C             is  modified  by  subroutine  Y12MB.  On  entry
C             IFLAG(4) must contain 0, 1 or 2. IFLAG(4)  =  0
C             is  the  best choice when only one system is to
C             be solved, when the first system of a  sequence
C             of systems with the same matrix is to be solved
C             and when any system of a  sequence  of  systems
C             whose matrices are of different structure is to
C             be solved. IFLAG(4) = 1 is the best choice when
C             the  first system in a sequence of systems with
C             the same structure is to be solved. IFLAG(4)  =
C             2  is the best choice when any system after the
C             first  one  in  a  sequence  of  systems  whose
C             matrices  are  of  the  same structure is to be
C             solved. The content of IFLAG(4) is unchanged by
C             subroutine  Y12MB. The other locations of IFLAG
C             are either ignored by Y12MB or used as  a  work
C             space.
C
C     IFAIL - Error  diagnostic  parameter.  The  content  of
C             parameter  IFAIL  is  modified  by   subroutine
C             Y12MB.  On exit IFAIL = 0 if the subroutine has
C             not detected  any  error.  Positive  values  of
C             IFAIL  on  exit  show  that some error has been
C             detected by the subroutine. Many of  the  error
C             diagnostics  are  common for all subroutines in
C             the package.  Therefore the  error  diagnostics
C             are listed in a separate section, Section 7, of
C             this book. We advise  the  user  to  check  the
C             value of this parameter on exit.
C
C     5.  Error diagnostics
C
C     Error diagnostics are given by positive values  of  the
C     parameter  IFAIL  (see  above).  We  advise the user to
C     check carefully the value of this  parameter  on  exit.
C     The error messages are listed in Section 7.
C
C     6.  Auxiliary subroutines
C
C     None.
C
C     7.  Timing
C
C     The  time  taken  depends  on  the  number  of non-zero
C     elements (parameter Z) in matrix A.
C
C     8.  Storage
C
C     There are no internally declared arrays.
C
C     9.  Some remarks
C
C     Remark 1 The use of subroutine Y12MB is followed by the
C              use of Y12MC and Y12MD. Subroutines Y12MA  and
C              Y12MF  can be considered as examples of how to
C              use Y12MB, Y12MC and Y12MD.
C
C     Remark 2 The contents of N, Z, A, SNR,  NN,  RNR,  NN1,
C              columns  1,  3,  4, 6, 7, 8 and 11 of HA, IHA,
C              AFLAG(6), IFLAG(1), IFLAG(4) and IFAIL  should
C              not  be  altered  between  calls  of Y12MB and
C              Y12MC.
C
C     Remark 3 If IFAIL > 0 on exit  of  Y12MB  there  is  no
C              sense  in  calling  Y12MC and Y12MD. Therefore
C              the computations should  be  stopped  in  this
C              case  and  one  should  investigate (using the
C              error diagnostics  given  in  Section  7)  why
C              Y12MB  has assigned a positive value to IFAIL.
C                =================================
C                Documentation of subroutine Y12MC
C                =================================
C
C     1.  Purpose
C
C     Y12MC   decomposes  a  matrix  A  into  two  triangular
C     matrices L and U. Each call of subroutine Y12MC  should
C     be  preceded  by  a  call of Y12MB. Subroutine Y12MD is
C     normally called one or several times after the call  of
C     Y12MC.
C
C     2.  Calling sequence and declaration of the parameters
C
C     The  subroutine  is  written  in  FORTRAN  and has been
C     extensively tested with the FOR and  FTN  compilers  on
C     the  UNIVAC  1100/82 computer at the Regional Computing
C     Centre at the University of  Copenhagen  (RECKU).  Many
C     examples  have  been run on an IBM 3033 computer at the
C     Northern Europe University Computing Centre (NEUCC) and
C     on  a  CDC Cyber 173 computer at the Regional Computing
C     Centre  at  the  University  of  Aarhus  (RECAU).   Two
C     different  versions  are  available: a single precision
C     version named Y12MCE and  a  double  precision  version
C     named  Y12MCF.  The calls of these two versions and the
C     declaration of the parameters are as follows.
C
C     A). Single precision version: Y12MCE
C
C           SUBROUTINE Y12MCE(N, Z, A, SNR, NN, RNR, NN1,
C          1  PIVOT, B, HA, IHA, AFLAG, IFLAG, IFAIL)
C
C           REAL A(NN), PIVOT(N), B(N), AFLAG(8)
C           INTEGER SNR(NN), RNR(NN1), HA(IHA,11), IFLAG(10)
C           INTEGER N, Z, NN, NN1, IHA, IFAIL
C
C     B). Double precision version: Y12MCF
C
C           SUBROUTINE Y12MCF(N, Z, A, SNR, NN, RNR, NN1,
C          1  PIVOT, B, HA, IHA, AFLAG, IFLAG, IFAIL)
C
C           DOUBLE PRECISION A(NN), PIVOT(N), B(N), AFLAG(8)
C           INTEGER SNR(NN), RNR(NN1), HA(IHA,11), IFLAG(10)
C           INTEGER N, Z, NN, NN1, IHA, IFAIL
C
C     These  two versions can be used on many other computers
C     also. However some alterations may be needed and/or may
C     ensure  greater  efficiency  of  the performance of the
C     subroutine. For example, it will be much more efficient
C     to  declare  arrays  SNR,  RNR  and (if possible) HA as
C     INTEGER*2 arrays on some IBM installations.
C
C
C     3.  Method
C
C     Gaussian  elimination  is  used in the decomposition of
C     matrix A. Pivotal interchanges are  implemented  as  an
C     attempt   to   preserve   both  the  stability  of  the
C     computations  and  the  sparsity  of  the   coefficient
C     matrix. Two triangular matrices L and U are computed so
C     that  LU=PAQ  (where P and Q are permutation matrices).
C     The  right  hand side vector b is also modified so that
C     vector c = L**(-1)*P*b is computed on successful  exit.
C     In  this  way  matrix  L is sometimes not needed in the
C     further computations and may be removed if this is  so.
C
C     4.  Parameters of the subroutine
C
C
C     N     - INTEGER. On entry N must contain the number  of
C             equations  in  the  system   Ax=b. Unchanged on
C             exit.
C
C     Z     - INTEGER. On entry Z must contain the number  of
C             non-zero  elements  in the coefficient matrix A
C             of the system  Ax=b. Unchanged on exit.
C
C     A     - REAL (in the single precision  version  Y12MCE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MCF) array of length NN (see below).
C             On  entry the first Z locations of array A must
C             contain   the   non-zero   elements   of    the
C             coefficient  matrix  A  of  the  system  Ax = b
C             ordered by  rows  (in  the  preceding  call  of
C             Y12MB).  The  content of array A is modified by
C             subroutine Y12MC.  On successful exit  array  A
C             will contain the non-zero elements of the upper
C             triangular  matrix  U  (without  the   diagonal
C             elements  of  matrix  U  which  can be found in
C             array PIVOT, see below) and sometimes also  the
C             non-zero   elements  of  the  lower  triangular
C             matrix  L (without the diagonal elements  which
C             are not stored).
C
C     SNR   - INTEGER  array  of  length  NN  (see below). On
C             entry SNR(j),  j  =  1(1)Z,  must  contain  the
C             column number of the non-zero element stored in
C             A(j).   This  is  accomplished  by   subroutine
C             Y12MB.  The content of array SNR is modified by
C             subroutine Y12MC.  On successful exit array SNR
C             will contain the column numbers of the non-zero
C             elements  of  the  upper  triangular  matrix  U
C             (without  the  column  numbers  of the diagonal
C             elements of matrix U) and  sometimes  also  the
C             column  numbers of the non-zero elements of the
C             lower triangular matrix L (without  the  column
C             numbers of the diagonal elements of L).
C
C     NN    - INTEGER. On entry NN must contain the length of
C             array  A  and  SNR.   Restriction:   NN.ge.2*Z.
C             Recommended  value:  2*Z .le.NN.le.  3*Z if the
C             non-zero elements of matrix L will  be  removed
C             by  subroutine  Y12MC  (i.e. if Y12MC is called
C             with IFLAG(5) = 1) or 3*Z.le.  NN.le.5*Z if the
C             non-zero  elements  of matrix L will be kept by
C             subroutine Y12MC (i.e. if Y12MC is called  with
C             IFLAG(5) = 2).  Unchanged on exit.
C
C     RNR   - INTEGER  array  of  length  NN1 (see below). On
C             entry the first Z locations of array  RNR  must
C             contain   the   row  numbers  of  the  non-zero
C             elements of the coefficient  matrix  A  of  the
C             system  Ax  =  b so that the row numbers of the
C             non-zero elements in the first column of matrix
C             A  are  located  before  the row numbers of the
C             non-zero  elements  in  the  second  column  of
C             matrix  A,  the  row  numbers  of  the non-zero
C             elements in the second column of matrix  A  are
C             located  before the row numbers of the non-zero
C             elements in the third column of matrix A and so
C             on  (this  ordereing  is prepared by subroutine
C             Y12MB). The content of array RNR is modified by
C             subroutine   Y12MC.   On  successful  exit  all
C             components of array RNR will normally be  zero.
C
C     NN1   - INTEGER. On entry NN1 must contain  the  length
C             of   the  array  RNR.   Restriction:  NN1.ge.Z.
C             Recommended     value:       2*Z.le.NN1.le.3*Z.
C             Unchanged on exit.
C
C     PIVOT - REAL  (in  the single precision version Y12MCE)
C             or DOUBLE PRECISION (in  the  double  precision
C             version  Y12MCF) array of length N. The content
C             of array PIVOT is modified by subroutine Y12MC.
C             On successful exit array PIVOT will contain the
C             pivotal elements (  the  diagonal  elements  of
C             matrix  U). This means that a small element (or
C             small elements) in  array  PIVOT  on  exit  may
C             indicate    numerical    singularity   of   the
C             coefficient matrix A. Note that the smallest in
C             absolute value element in array PIVOT is stored
C             in AFLAG(8), see below.
C
C     B -     REAL (in the single precision  version  Y12MCE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MCF) array of length  N.  The  right
C             hand side vector b of the system Ax = b must be
C             stored in B on entry. The content of array B is
C             modified  by  subroutine  Y12MC.  On successful
C             exit array B will  contain  the  components  of
C             vector c = L**(-1)*P*b.
C
C     HA    - INTEGER  two-dimensional  array.  The length of
C             the first dimension of HA is IHA  (see  below).
C             The length of the second dimension of HA is 11.
C             The content of columns 1, 3, 4, 6, 7, 8 and  11
C             is  prepared by Y12MB (and thus they should not
C             be altered between calls of Y12MB  and  Y12MC).
C             The   content   of  array  HA  is  modified  by
C             subroutine   Y12MC.   The   non-zero   elements
C             (without  the  diagonal  elements)  of row i in
C             matrix L  (when  this  matrix  is  stored)  are
C             between HA(i,1) and HA(i,2) -1. If the non-zero
C             elements  of  matrix  L  are  not  stored  then
C             HA(i,1)=HA(i,2), i=1(1)N. The non-zero elements
C             of row i in  matrix  U  (without  the  diagonal
C             elements)   are   stored  between  HA(i,2)  and
C             HA(i,3).  Information concerning  the  row  and
C             column  interchanges  is  stored in HA(i,7) and
C             HA(i,8),   i=1(1)N-1.   Information  about  the
C             largest  number  of  non-zero elements found in
C             row i /  column  j  during  any  stage  of  the
C             elimination  is  stored (when IFLAG(4)=1  only)
C             in  HA(i,9)/HA(j,10).  The  other   information
C             stored  in  array HA is not used in the further
C             computations.
C
C     IHA   - INTEGER. On entry IHA must contain  the  length
C             of   the   first   dimension   of   array   HA.
C             Restriction: IHA
C            .ge.N. Unchanged on exit.
C
C     AFLAG - REAL  (in  the single precision version Y12MCE)
C             or DOUBLE PRECISION (in  the  double  precision
C             version  Y12MCF) array of length 8. The content
C             of the array can be described as follows:
C
C             AFLAG(1) -  Stability factor. An element can be
C                         chosen as pivotal element  only  if
C                         this element is larger (in absolute
C                         value) than the absolute  value  of
C                         the  largest  element  in  its  row
C                         divided  by  AFLAG(1).   On   entry
C                         AFLAG(1)   should  contain  a  real
C                         number larger than 1.0. If this  is
C                         not  so  then  the  subroutine sets
C                         AFLAG(1)  =   1.0005.   Recommended
C                         values:   AFLAG(1) ranging from 4.0
C                         to 16.0.  Unchanged on  exit  (when
C                         correctly initialized).
C
C             AFLAG(2)  - Drop-tolerance. An element which in
C                         the  process  of  the  computations
C                         becomes smaller (in absolute value)
C                         than the drop-tolerance is  removed
C                         from  array  A  (and  its  row  and
C                         column  numbers  are  removed  from
C                         arrays   RNR  and  SNR).  On  entry
C                         AFLAG(2)   should   contain    some
C                         positive   small  number  or  zero.
C                         Recommended   value   AFLAG(2)    =
C                         1.0E-12  when  matrix  A is not too
C                         badly scaled and the  magnitude  of
C                         the   elements   is   1,  otherwise
C                         smaller values of  AFLAG(2)  should
C                         be used.  Unchanged on exit.
C
C              AFLAG(3) - The   subroutine   will   stop  the
C                         computation when the growth  factor
C                         (parameter   AFLAG(5),  see  below)
C                         becomes larger  than  AFLAG(3).  On
C                         entry  AFLAG(3)  should  contain  a
C                         large    positive    number.     If
C                         AFLAG(3)<1.0e5  then the subroutine
C                         sets  AFLAG(3)=1.0e5.   Recommended
C                         value   AFLAG(3)=1.0E6.   Unchanged
C                         on     exit     (when     correctly
C                         initialized).
C
C              AFLAG(4) - The   subroutine   will   stop  the
C                         computation when the absolute value
C                         of  a  current  pivotal  element is
C                         smaller   than    AFLAG(4)*AFLAG(6)
C                         (parameter  AFLAG(6)  is  described
C                         below).  On  entry  AFLAG(4)   must
C                         contain    a   small   non-negative
C                         number.  If  AFLAG(4)<0   on  entry
C                         then     the     subroutine    sets
C                         AFLAG(4)=-AFLAG(4).     Recommended
C                         value  AFLAG(4)=1.0E-12.  Unchanged
C                         on     exit     (when     correctly
C                         initialized).
C
C             AFLAG(5)  - Growth   factor.   The  content  of
C                         parameter AFLAG(5) is  modified  by
C                         subroutine  Y12MC. After each stage
C                         of   the    Gaussian    elimination
C                         subroutine  Y12MC  sets  AFLAG(5) =
C                         AFLAG(7)/AFLAG(6)       (parameters
C                         AFLAG(6) and AFLAG(7) are described
C                         below). On  exit  large  values  of
C                         parameters  AFLAG(5)  indicate that
C                         an   appreciable   error   in   the
C                         computed  solution  is possible. In
C                         an    extreme    case    ,    where
C                         AFLAG(5)>AFLAG(3)   the  subroutine
C                         will terminate the computations  in
C                         an attempt to prevent overflow (and
C                         IFAIL will be set equal to 4).
C
C             AFLAG(6)  - On entry AFLAG(6) is equal  to  the
C                         largest  element in the coefficient
C                         matrix A of the system Ax = b  (set
C                         by subroutine Y12MB).  Unchanged on
C                         exit.
C
C             AFLAG(7)  - On exit the  largest  (in  absolute
C                         value)  element  found  during  any
C                         stage of the  elimination  will  be
C                         stored  in AFLAG(7). The content of
C                         parameter AFLAG(7) is  modified  by
C                         subroutine Y12MC.
C
C             AFLAG(8)  - On  entry  the minimal (in absolute
C                         value)  pivotal  element  will   be
C                         stored in AFLAG(8). Small values of
C                         AFLAG(8)     indicate     numerical
C                         singularity   of   the  coefficient
C                         matrix A. We  advise  the  user  to
C                         check  this  parameter on exit from
C                         the calculation very carefully. The
C                         content  of  parameter  AFLAG(8) is
C                         modified by the subroutine Y12MC.
C
C     IFLAG - INTEGER array of length 10. The content of this
C             array is  modified  by  subroutine  Y12MC.  The
C             contents of the components of this array can be
C             described as follows.
C
C             IFLAG(1) - This parameter is used in connection
C                        with the error diagnostics. The user
C                        should  set IFLAG(1).ge.0 before the
C                        call of package  Y12M  (i.e.  before
C                        the  first  call  of a subroutine of
C                        this package). IFLAG(1) is  used  in
C                        the  error checks by the subroutines
C                        and should not  be  altered  by  the
C                        user   between  any  two  successive
C                        calls of subroutines of the package.
C                        IFLAG(1)  will  be  equal  to  -2 on
C                        successful  exit   from   subroutine
C                        Y12MC. Thus, the content of IFLAG(1)
C                        is modified by subroutine Y12MC.
C
C             IFLAG(2) - On entry IFLAG(2) must contain  some
C                        positive  integer smaller than N. We
C                        recommend IFLAG(2).le.3. If IFLAG(3)
C                        =  0  then this parameter is ignored
C                        by     subroutine     Y12MC.      If
C                        IFLAG((3).ge.0   then   the  pivotal
C                        search   at   any   stage   of   the
C                        elimination (except possibly some of
C                        the last stages) is carried  out  in
C                        the  IFLAG(2)  rows which have least
C                        numbers   of   non-zero    elements.
C                        Unchanged on exit.
C
C             IFLAG(3) - On  entry IFLAG(3) must contain 0, 1
C                        or 2.  For  general  pivotal  search
C                        IFLAG(3)  should  be set equal to 1.
C                        If IFLAG(3) = 2 then  only  diagonal
C                        elements of the coefficient matrix A
C                        will   be   selected   as    pivotal
C                        elements.  If  IFLAG(3) = 0 then the
C                        Gaussian elimination will be carried
C                        out     without     any    pivoting.
C                        IFLAG(3)=0 or IFLAG(3)=2  (i.e.  one
C                        of the special pivotal strategies is
C                        to be applied) should be  used  very
C                        carefully    because    the    error
C                        diagnostics algorithm may not  trace
C                        all   errors   in  this  case.   For
C                        example, if the user attempts to use
C                        IFLAG(3)=0   for   matrix   A  which
C                        contains zero elements on  the  main
C                        diagonal, then the run will often be
C                        stopped because a division  by  zero
C                        occurs.  Unchanged on exit.
C
C             IFLAG(4) - On  entry IFLAG(4) must contain 0, 1
C                        or 2.  IFLAG(4)  =  0  is  the  best
C                        choice  when  (i) only one system is
C                        to be solved, (ii) the first  system
C                        of  a  sequence  of systems with the
C                        same matrix (Ax = b1, Ax = b2,  ...,
C                        Ax = bp) is to be solved, (iii) when
C                        any system in a sequence of  systems
C                        whose   matrices  are  of  different
C                        structure is to be solved.  IFLAG(4)
C                        =  1  is  the  best  choice when the
C                        first  system  of  a   sequence   of
C                        systems  whose  matrices  are of the
C                        same structure is to be  solved.  In
C                        this case IFLAG(4) = 2 is to be used
C                        in the solution of any system  after
C                        the first one.  Unchanged on exit.
C
C             IFLAG(5) - On  entry IFLAG(5) must contain 1 or
C                        2.   If  IFLAG(5)  =  1   then   the
C                        non-zero  elements  of matrix L will
C                        be removed. If IFLAG(5) = 2 then the
C                        non-zero  elements  of matrix L will
C                        be stored.  Unchanged on exit.
C
C             IFLAG(6) - On successful exit IFLAG(6) will  be
C                        equal  to  the  number  of "garbage"
C                        collections in the row ordered list.
C                        If  IFLAG(6)  is  large  then  it is
C                        better to choose a larger  value  of
C                        NN  with  next  calls  of subroutine
C                        Y12MC with the  same  or  a  similar
C                        matrix   A.  This  will  lead  to  a
C                        reduction  in  the  computing  time.
C                        The  content of IFLAG(6) is modified
C                        by the subroutine Y12MC.
C
C             IFLAG(7) - On successful exit IFLAG(7) will  be
C                        equal  to  the  number  of "garbage"
C                        collections in  the  column  ordered
C                        list.   If IFLAG(7) is large then it
C                        is better to choose a  larger  value
C                        of   NN1   in   the  next  calls  of
C                        subroutine Y12MC with the same or  a
C                        similar  matrix A. This will lead to
C                        a reduction in the  computing  time.
C                        The  content of IFLAG(7) is modified
C                        by subroutine Y12MC.
C
C             IFLAG(8) - On successful exit IFLAG(8) will  be
C                        equal   to  the  maximal  number  of
C                        non-zero elements kept in array A at
C                        any    stage    of    the   Gaussian
C                        elimination.  If  IFLAG(8)  is  much
C                        smaller  then  NN  (or NN1) then the
C                        length NN (or  NN1)  can  be  chosen
C                        smaller  in next calls of subroutine
C                        Y12MC with the  same  or  a  similar
C                        matrix   A.  This  will  lead  to  a
C                        reduction of the storage needed. The
C                        content  of  IFLAG(8) is modified by
C                        subroutine Y12MC.
C
C             IFLAG(9) - The content of IFLAG(3) is  modified
C                        by  subroutine Y12MC when IFLAG(4) =
C                        1  and   ignored   otherwise.    The
C                        minimal  length  NN1 such that Y12MC
C                        can solve systems whose matrices are
C                        of   the   same   structure  without
C                        "garbage" collections in the  column
C                        ordered   list   and   "movings"  of
C                        columns at the  end  of  the  column
C                        ordered  list  is stored in IFLAG(9)
C                        after  the  solution  of  the  first
C                        system   in   the   sequence   (with
C                        IFLAG(4) = 1).
C
C             IFLAG(10)- The content of IFLAG(10) is modified
C                        by   the   subroutine   Y12MC   when
C                        IFLAG(4) = 1 and ignored  otherwise.
C                        The  minimal  length  NN  such  that
C                        subroutine Y12MC can  solve  systems
C                        whose   matrices  are  of  the  same
C                        structure     without      "garbage"
C                        collections  in the row ordered list
C                        and "movings" of rows to the end  of
C                        the  row  ordered  list is stored in
C                        IFLAG(10) after the solution of  the
C                        first  system  in the sequence (with
C                        IFLAG(4) = 1).
C
C     IFAIL - Error  diagnostics  parameter.  The  content of
C             parameter  IFAIL  is  modified  by   subroutine
C             Y12MC.  On exit IFAIL = 0 if the subroutine has
C             not detected  any  error.  Positive  values  of
C             IFAIL  on  exit  show  that some error has been
C             detected by the subroutine. Many of  the  error
C             diagnostics  are  common for all subroutines in
C             the package.  Therefore the  error  diagnostics
C             are listed in a separate section, Section 7, of
C             this book. We advise  the  user  to  check  the
C             value of this parameter on exit.
C
C     5.  Error diagnostics
C
C     Error diagnostics are given by positive values  of  the
C     parameter  IFAIL  (see  above).   We advise the user to
C     check carefully the value of this  parameter  on  exit.
C     The error messages are listed in Section 7.
C
C     6.  Auxiliary subroutines
C
C     None.
C
C     7.  Timing
C
C     The  time  taken  depends  on  the  order of the matrix
C     (parameter N), the number of the non-zero  elements  in
C     the matrix (parameter Z), the magnitude of the non-zero
C     elements and their distribution in the matrix.
C
C     8.  Storage
C
C     There are no internally declared arrays.
C
C     9.  Accuracy
C
C     It  is  difficult  to  evaluate  the  accuracy  of  the
C     computed  solution. Large values of parameter AFLAG(5),
C     the  growth  factor,  indicate  unstable   computations
C     during   the   decomposition  stage.  Small  values  of
C     parameter AFLAG(8) can be considered as  a  signal  for
C     numerical  singularity.  We  must  emphasize  here that
C     normally much more reliable evaluations of the accuracy
C     achieved   can   be  found  by  the  use  of  iterative
C     refinement, i.e. by the use of subroutine Y12MF. By the
C     use  of  the  latter subroutine the computing time will
C     often be reduced too.
C
C     10.  Some remarks
C
C     Remark 1   The values on entry of Y12MC are the same as
C                the  values  on  exit  of  Y12MB  for   many
C                parameters.  Therefore  the content of N, Z,
C                A, SNR, NN, RNR, NN1, columns 1, 3, 4, 6, 7,
C                8 and 11 of HA, AFLAG(6), IFLAG(1), IFLAG(4)
C                and IFAIL  should  not  be  changed  between
C                calls of Y12MB and Y12MC.
C
C     Remark 2   The  call  of  Y12MC is normally followed by
C                one or several calls of Y12MD.  The  content
C                of N, A, SNR, NN, B, PIVOT, columns 1, 2, 3,
C                7  and  8  of  HA,  IHA,  AFLAG,   IFLAG(1),
C                IFLAG(3),  IFLAG(4)  and IFAIL should not be
C                altered between calls of Y12MC and Y12MD.
C
C     Remark 3   If IFAIL > 0 on exit from Y12MC there is  no
C                justification  for  calling Y12MD. Therefore
C                the computations should  be  terminated  and
C                the user should investigate (using the error
C                diagnostics given in Section  7)  why  Y12MC
C                has assigned a positive value to IFAIL.
C                =================================
C                Documentation of subroutine Y12MD
C                =================================
C
C     1.  Purpose
C
C     Y12MD   solves   sparse  systems  of  linear  algebraic
C     equations.
C
C     2.  Calling sequence and declaration of the parameters
C
C     The subroutine is written in FORTRAN and  it  has  been
C     extensively  tested  with  the FOR and FTN compilers on
C     the UNIVAC 1100/82 computer at the  Regional  Computing
C     Centre  at  the  University of Copenhagen (RECKU). Many
C     examples have been run on an IBM 3033 computer  at  the
C     Northern Europe University Computing Centre (NEUCC) and
C     on a CDC Cyber 173 computer at the  Regional  Computing
C     Centre  at  the  University  of  Aarhus  (RECAU).   Two
C     different versions are available:  a  single  precision
C     version  named  Y12MDE  and  a double precision version
C     named Y12MDF. The calls of these two versions  and  the
C     declarations of the parameters are as follows.
C
C     A). Single precision version: Y12MDE
C
C           SUBROUTINE Y12MDE(N, A, NN, B, PIVOT, SNR,
C          1                  HA, IHA,, IFLAG, IFAIL)
C           REAL A(NN), PIVOT(N), B(N)
C           INTEGER SNR(NN), HA(IHA,11), IFLAG(10)
C           INTEGER N, NN, IHA, IFAIL
C
C     B). Double precision version: Y12MDF
C
C           SUBROUTINE Y12MDF(N, A, NN, B, PIVOT, SNR,
C          1                  HA, IHA,, IFLAG, IFAIL)
C           DOUBLE PRECISION A(NN), PIVOT(N), B(N)
C           INTEGER SNR(NN), HA(IHA,11), IFLAG(10)
C           INTEGER N, NN, IHA, IFAIL
C
C     These two versions can be used on many others computers
C     also. However some alterations may be needed and/or may
C     ensure  greater  efficiency  of  the performance of the
C     subroutine. For example, it will be much more efficient
C     to declare arrays SNR and (if possible) HA as INTEGER*2
C     arrays on some IBM installations.
C
C
C     3.  Method
C
C     The  LU  decomposition  (or  only  the upper triangular
C     matrix U if the  vector  c  =  L**(-1)*P*b  is  already
C     computed)  is  used  to  obtain an approximation to the
C     solution vector  x  of  the  system  Ax  =  b.  The  LU
C     decomposition  (or only matrix U ) must be available on
C     entry. This means that if a system with a new matrix is
C     solved  then  the  call  of  subroutine  Y12MD  must be
C     preceded by a call of Y12MC and the  contents  of  some
C     parameters and arrays should not be altered between the
C     calls of Y12MC and Y12MD. If a  system  with  the  same
C     matrix  has already been solved, then Y12MD only should
C     be called (but the  contents  of  some  parameters  and
C     arrays  used  in the preceding call of Y12MD should not
C     be altered).
C
C     4.  Parameters of the subroutine
C
C     N     - INTEGER.  On entry N must contain the number of
C             equations in the  system   Ax=b.  Unchanged  on
C             exit.
C
C     A     - REAL  (in  the single precision version Y12MDE)
C             or DOUBLE PRECISION (in  the  double  precision
C             version Y12MDF) array of length NN (see below).
C             On entry array  A  must  contain  the  non-zero
C             elements  of  the  upper  triangular  matrix  U
C             (without the diagonal elements)  and  sometimes
C             also   the   non-zero  elements  of  the  lower
C             triangular matrix L under the  diagonal  (these
C             elements   are  calculated  by  the  subroutine
C             Y12MC, the non-zero elements of L are stored by
C             Y12MC  only when this subroutine is called with
C             IFLAG(5) = 2). The content of the  array  A  is
C             not modified by subroutine Y12MD.
C
C     NN   -  INTEGER. On entry NN must contain the length of
C             the arrays A and SNR.   Restriction:  NN  >  Z.
C             Recommended  value:  2*Z  <  NN  <  3*Z  if the
C             non-zero elements of matrix L will  be  removed
C             during  the  decomposition  (i.e. if subroutine
C             Y12MC is called with IFLAG(5) = 1) and 3*Z < NN
C             < 5*Z if the non-zero elements of matrix L will
C             be stored during the decomposition (i.e. if the
C             subroutine  Y12MC is called with IFLAG(5) = 2).
C             Unchanged on exit.
C
C     B -     REAL (in the single precision  version  Y12MDE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version  Y12MDF)  array   of   length   N.   If
C             subroutine   Y12MC   has  been  called  in  the
C             solution  of  system   Ax   =   b   (i.e.    if
C             IFLAG(5)<3), then on entry array B must contain
C             vector c = L**(-1)*P*b (computed by Y12MC).  If
C             subroutine  Y12MC  has  not  been called in the
C             solution of system Ax = b (i.e  a  system  with
C             the  same matrix has been solved before solving
C             Ax = b and the old LU decomposition is used  by
C             setting  IFLAG(5)  =  3, see below) the array B
C             must contain the right hand side  vector  b  on
C             entry.   The content of the array B is modified
C             by the subroutine Y12MD. On successful exit  an
C             approximation  to  the  true solution x will be
C             found in B.
C
C     PIVOT - REAL (in the single precision  version  Y12MDE)
C             or  DOUBLE  PRECISION  (in the double precision
C             version Y12MDF) array of length N. On entry the
C             diagonal   elements  of  the  upper  triangular
C             matrix U must be stored in array  PIVOT  (these
C             elements  are calculated and stored in PIVOT by
C             subroutine Y12MC).  Unchanged on exit.
C
C     SNR   - INTEGER array of  length  NN  (see  below).  On
C             entry   the  column  numbers  of  the  non-zero
C             elements  of  the  upper  triangular  matrix  U
C             (without  the  column  numbers  of the non-zero
C             elements on the diagonal)  and  sometimes  also
C             the  column numbers of the non-zero elements of
C             the lower  triangular  matrix  L  (without  the
C             column  numbers of the non-zero elements on the
C             diagonal) must be stored in  array  SNR.   This
C             structure  is prepared by Y12MC. The content of
C             array SNR is not  modified  by  the  subroutine
C             Y12MD.
C
C     HA    - INTEGER  two-dimesional  array.   The length of
C             the first dimension of HA is IHA  (see  below).
C             The length of the second dimension of HA is 11.
C             The content of columns 4, 5, 6, 9, 10 and 11 is
C             ignored  by  subroutine  Y12MD.  The content of
C             columns  1,  2,  3,  7  and  8  is  stored   by
C             subroutine  Y12MC  and  should  not  be altered
C             between calls of Y12MC and Y12MD. Unchanged  on
C             exit.
C
C     IHA   - INTEGER.  On  entry IHA must contain the length
C             of   the   first   dimension   of   array   HA.
C             Restriction: IHA .ge. N. Unchanged on exit.
C
C     IFLAG - INTEGER array of length 10. The contents of all
C             locations   in   this  array  except  IFLAG(1),
C             IFLAG(3), IFLAG(4) and IFLAG(5) are ignored  by
C             subroutine  Y12MD. The user should set IFLAG(1)
C             .ge. 0 before the call of  package  Y12M  (i.e.
C             before  the  first call of a subroutine of this
C             package). IFLAG(1) is used in the error  checks
C             by the subroutines and should not be altered by
C             the user between any two  successive  calls  of
C             subroutines  of  the package. IFLAG(1) is equal
C             to  -2  both on entry and  on  successful  exit
C             from  subroutine  Y12MD.  Thus,  the content of
C             IFLAG(1) is not modified by  subroutine  Y12MD.
C             The  same  values  of  IFLAG(3) and IFLAG(4) as
C             those in the preceding call of Y12MC should  be
C             used.  If  subroutine  Y12MC has been called in
C             the solution of the system  Ax  =  b,  then  on
C             entry  IFLAG(5)  must have the same value as on
C             entry of Y12MC. If  subroutine  Y12MC  has  not
C             been  called in the solution of the system Ax =
C             b (i.e. a system with the same matrix has  been
C             solved  before  solving  Ax  =  b  and  the  LU
C             decomposition of matrix A  is  thus  available)
C             then  on entry IFLAG(5) must contain the number
C             3. The content of array IFLAG is  unchanged  on
C             exit.
C
C     IFAIL - Error  diagnostics  parameter.  The  content of
C             parameter  IFAIL  is  modified  by   subroutine
C             Y12MA.  On exit IFAIL = 0 if the subroutine has
C             not detected  any  error.  Positive  values  of
C             IFAIL  on  exit  show  that some error has been
C             detected by the subroutine. Many of  the  error
C             diagnostics  are  common for all subroutines in
C             the package.  Therefore the  error  diagnostics
C             are listed in a separate section, Section 7, of
C             this book. We advise  the  user  to  check  the
C             value of this parameter on exit.
C
C     5.  Error diagnostics
C
C     Error diagnostics are given by positive values  of  the
C     parameter  IFAIL  (see  above).   We advise the user to
C     check carefully the value of this  parameter  on  exit.
C     The error messages are listed in Section 7.
C
C     6.  Auxiliary subroutines
C
C     None.
C
C     7.  Timing
C
C     The  time  taken  depends  on  the  order of the matrix
C     (parameter N) and the number of the  non-zero  elements
C     in the LU-decomposition of matrix A.
C
C     8.  Storage
C
C     There are no internally declared arrays.
C
C     9.  Accuracy
C
C     It  is  difficult  to  evaluate  the  accuracy  of  the
C     computed solution. Large values of parameter  AFLAG(5),
C     the   growth  factor,  indicate  unstable  computations
C     during  the  decomposition  stage.  Small   values   of
C     parameter AFLAG(8), the minimal pivotal element, can be
C     considered as a signal for  numerical  singularity.  We
C     must  emphasize  here  that normally much more reliable
C     evaluations of the accuracy achieved can  be  found  by
C     the  use  of  iterative  refinement, i.e. by the use of
C     subroutine Y12MF. By the use of the  latter  subroutine
C     the  computing time will often be reduced too. However,
C     the storage requirements may sometimes be increased.
C
C     10.  Some remarks
C
C     Remark 1 The content of N, A, SNR, NN, columns 1, 2, 3,
C              7 and 8 of HA,  IHA,  IFLAG(3),  IFLAG(4)  and
C              IFAIL  should  not be altered between calls of
C              Y12MC and Y12MD.
C
C     Remark 2 If   the  LU  decomposition  of  matrix  A  is
C              available (i.e. a system  with  matrix  A  has
C              already  been  solved)  then  Y12MB  and Y12MC
C              should not be called.  The  user  should  only
C              assign the new right hand side vector to array
C              B, set IFLAG(5) = 3 and call Y12MD.
C                =================================
C                Documentation of subroutine Y12MF
C                =================================
C
C     1.  Purpose
C
C     Large  and sparse systems of linear algebraic equations
C     with  real  coefficients  are  solved  by  the  use  of
C     Gaussian   elimination  and  sparse  matrix  technique.
C     Iterative refinement is applied in order to improve the
C     accuracy.
C
C     2.  Calling sequence and declaration of the parameters
C
C     The  subroutine  is  written  in  FORTRAN  and has been
C     extensively tested with the FOR and  FTN  compilers  on
C     the  UNIVAC  1100/82 computer at the Regional Computing
C     Centre at the University of  Copenhagen  (RECKU).  Many
C     examples  have  been run on an IBM 3033 computer at the
C     Northern Europe University Computing Centre (NEUCC) and
C     on  a  CDC Cyber 173 computer at the Regional Computing
C     Centre at the University of  Aarhus  (RECAU).   Only  a
C     single precision version, Y12MFE, of this subroutine is
C     available.  The  call  of  the   subroutine   and   the
C     declarations of the parameters are as follows.
C
C           SUBROUTINE Y12MFE(N, A, SNR, NN, RNR, NN1, A1,
C     SN, NZ,
C          1                  HA, IHA, B, B1, X, Y, AFLAG,
C     IFLAG, IFAIL)
C           REAL A(NN), B(N), B1(N), X(N), Y(N), A1(NZ),
C     AFLAG(11)
C           INTEGER SNR(NN), RNR(NN1), HA(IHA,13), SN(NZ),
C     IFLAG(12)
C           INTEGER N, NN, NN1, NZ, IHA, IFAIL
C
C     This  subroutine  can  be  used on many other computers
C     also. However, some alterations may  be  needed  and/or
C     may ensure greater efficiency of the performance of the
C     subroutine.   For  example,  it  will  be   much   more
C     efficient  to  declare  arrays  SNR,  RNR,  SN  and (if
C     possible)  HA  as  INTEGER*2   arrays   on   some   IBM
C     instalations.  Note too that the subroutine accumulates
C     some inner products in double precision and then rounds
C     them  to  single  precision.  Therefore  if  the use of
C     double precision is very expensive (in comparison  with
C     the  use  of  single  precision) on the computer at the
C     user's disposal then it will not be  efficient  to  use
C     subroutine  Y12MF. The single precision versions of the
C     other subroutines should be used in this situation.
C
C     3.  Method
C
C     Consider the system Ax = b, where matrix A  is  sparse.
C     The  non-zero  elements of matrix A are ordered by rows
C     (subroutine Y12MB is called to perform this  operation)
C     and  then  factorized  into two triangular matrices, an
C     upper triangular matrix U and a unit  lower  triangular
C     matrix  L  (subroutine Y12MC is called to calculate the
C     non-zero elements of U amd L). The factorized system is
C     solved  (subroutine  Y12MD  is  called  to calculate an
C     approximation x1 to  the  solution  vector  x)  and  an
C     attempt  to  improve  the  first  solution by iterative
C     refinement is carried out in the following way:
C
C                      r(k-1) = b - A*x(k-1)
C
C                d(k-1) = Q*U**(-1)*L(-1)*P*r(k-1)
C
C                    x(k)    = X(k-1) + d(k-1)
C
C                     where k = 2, 3, ..., p.
C
C     Normally, this process will improve the accuracy of the
C     first solution x1. If the process is not convergent  or
C     the convegence is very slow, then information about the
C     trouble can be  obtained  by  checking  the  parameters
C     AFLAG(10)  (the  max-norm  of  the last residual vector
C     r(p-1) computed by Y12MF) and AFLAG(9) (the max-norm of
C     the last correction vector, d(p-1), computed by Y12MF).
C     We advise the user to check carefully these  parameters
C     on exit. Large values of these parameters show that the
C     computed solution is not very accurate.
C
C     4.  Parameters of the subroutine
C
C     N     - INTEGER.  On entry N must contain the number of
C             equations in the  system   Ax=b.  Unchanged  on
C             exit.
C
C     A     - REAL  array  of  length  NN (see below). If the
C             coefficient matrix must be factorized (in  this
C             case  IFLAG(5) = 2), then on entry the first NZ
C             locations of array A must contain the  non-zero
C             elements of matrix A, the order of the elements
C             can  be  arbitrary.  If  the  factorization  of
C             matrix  A  is available (i.e. a system with the
C             same matrix has been solved in a previous  call
C             of  Y12MF)  then  on entry array A must contain
C             the non-zero elements of U and L (IFLAG(5) =  3
C             should  also  be  assigned  in  this case). The
C             content of array A is modified  by  Y12MF  when
C             the  coefficient  matrix  has to be factorized.
C             Unchanged on exit when an old LU  factorization
C             is  used.  The content of array A should not be
C             altered between successive calls of  Y12MF  for
C             the solution of systems with the same matrices.
C
C     SNR   - INTEGER  array of the length NN (see below). If
C             the coefficient matrix  A  must  be  factorized
C             then on entry the first NZ positions of array A
C             must contain the column numbers of the non-zero
C             elements  of  matrix A (ordered as the non-zero
C             elements in array A). The content of array  SNR
C             is  modified  by  the  subroutine Y12MF in this
C             case. If the LU factorization of  matrix  A  is
C             available  then on entry array SNR must contain
C             the column numbers of the non-zero elements  of
C             U  and L. The content of array SNR is unchanged
C             on exit in this case. The content of array  SNR
C             should  not be altered between successive calls
C             of Y12MF for the solution of systems  with  the
C             same matrices.
C
C     NN    - INTEGER. On entry NN must contain the length of
C             array A and  SNR.  Restriction:  NN  .ge.  2*Z.
C             Recommended  value:  2*Z   .le.  NN  .le.   3*Z
C             Unchanged on exit.
C
C     RNR   - INTEGER array of length NN1 (see below). If the
C             coefficient  matrix  A must be factorized, then
C             on entry the first NZ positions  of  array  RNR
C             must  contain  the  row numbers of the non-zero
C             elements of matrix A (ordered as  the  non-zero
C             elements  in array A). The content of array RNR
C             is modified by the subroutine in this case.  If
C             the  LU  factorization of matrix A is available
C             then the content of array RNR is ignored by the
C             subroutine Y12MF.
C
C     NN1   - INTEGER.  On  entry NN1 must contain the length
C             of the array RNR.   Restriction:  NN1  .ge.  Z.
C             Recommended  value:  1.5*Z  .le.  NN1 .le. 2*Z.
C             Unchanged on exit.
C
C     A1    - REAL array  of  length  NZ  (see  below).   The
C             content  of  array A1 is modified by subroutine
C             Y12MF  when  the  LU   factorization   of   the
C             coefficient   matrix   A   is   not  available.
C             Subroutine Y12MF copies the first NZ  locations
C             of  array  A  into array A1 in this case (after
C             the internal call of  Y12MB;  this  means  that
C             array  A1 contains the non-zero elements of the
C             coefficient matrix A ordered by rows on  exit).
C             If  the  LU  factorization  of  the coefficient
C             matrix A is available then the content of array
C             A1  is unchanged on exit.  The content of array
C             A1 should  not  be  altered  between  successve
C             calls  of Y12MF in the solution of systems with
C             the same matrices.
C
C     SN    - INTEGER array of length  NZ  (see  below).  The
C             content  of  array SN is modified by subroutine
C             Y12MF  when  the  LU   factorization   of   the
C             coefficient   matrix   A   is   not  available.
C             Subroutine Y12MF copies the first NZ  locations
C             of  array SNR into array SN in this case (after
C             the internal call of  Y12MB;  this  means  that
C             array  SN  contains  the  column numbers of the
C             non-zero elements ordered by rows on exit).  If
C             the  LU  factorization  is  available  then the
C             content of array SN is unchanged on  exit.  The
C             content  of  array  SN  should  not  be altered
C             between  successive  calls  of  Y12MF  in   the
C             solution of systems with the same matrices.
C
C     NZ    - INTEGER. On entry NZ must contain the number of
C             non-zero elements in the coefficient  matrix  A
C             of the system Ax = b. Unchanged on exit.
C
C     HA    - INTEGER  two-dimensional  array.  The length of
C             the first dimension of HA is IHA  (see  below).
C             The length of the second dimension of HA is 13.
C             If a new decomposition  should  be  calculated,
C             then  subroutine Y12MF modifies the contents of
C             HA. The contents on exit of some of the columns
C             of  array HA are described in the documentation
C             of  subroutine  Y12MC.  The  last  two  columns
C             (12'th  and  13'th) contain on exit information
C             about the row starts and the row  ends  in  the
C             original matrix (after the non-zero elements of
C             this matrix have been ordered  by  rows);  this
C             means  that  the  non-zero elements in row i of
C             the coefficient matrix A  are  located  between
C             positions  HA(i,12)  and  HA(i,13)  in array A1
C             (the column numbers of the non-zero elements in
C             row   i  are  also  located  between  positions
C             HA(i,12) and HA(i,13)  in  array  SN).  If  the
C             matrix  of the system has the same structure as
C             the matrix of a system which is already solved,
C             then  subroutine Y12MF will use the information
C             stored in columns 7, 8, 9 and 10 of  HA  during
C             the  previous  call (therefore this information
C             should not be altered). If the LU decomposition
C             of  the  coefficient  matrix is available, then
C             the contents of array HA are unchanged on exit.
C             The  contents  of columns 1, 2, 3, 7, 8, 12 and
C             13 of array HA should not  be  altered  between
C             successive  calls  of  Y12MF in the solution of
C             systems with the same matrix.
C
C     IHA   - INTEGER. On entry IHA must contain  the  length
C             of   the   first   dimension   of   array   HA.
C             Restriction: IHA .ge. N. Unchanged on exit.
C
C     B     - REAL array of length N. On entry the right-hand
C             side vector b of  the  system   Ax=b   must  be
C             stored  in  array  B. The content of array B is
C             modified by subroutine  Y12MF.   On  successful
C             exit  the  components  of  the  last correction
C             vector d(p-1) are stored in array B.
C
C     B1 -    REAL array of length N. The content of array B1
C             is modified by subroutine Y12MF. The right hand
C             side vector of the system Ax = b is  stored  in
C             array B1 on successful exit.
C
C     X -     REAL  array of length N. The content of array X
C             is  modified  by  the  subroutine   Y12MF.   On
C             successful  exit  the corrected solution vector
C             is stored in array X.
C
C     Y -     REAL array of length N. The content of array  Y
C             is  modified by subroutine Y12MF. On successful
C             exit array Y will contain the pivotal  elements
C             (the diagonal elements of matrix U). This means
C             that a small element  (or  small  elements)  in
C             array   Y   on   exit  may  indicate  numerical
C             singularity of the coefficient matrix  A.  Note
C             that  the smallest in absolute value element in
C             array Y is also stored in AFLAG(8), see  below.
C
C     AFLAG - REAL array of length 11.  The  content  of  the
C             array can be described as follows:
C
C             AFLAG(1) -  Stability factor. An element can be
C                         chosen  as  pivotal element only if
C                         this element is larger (in absolute
C                         value)  than  the absolute value of
C                         the  largest  element  in  its  row
C                         divided   by   AFLAG(1).  On  entry
C                         AFLAG(1)  should  contain  a   real
C                         number  larger than 1.0. If this is
C                         not so  then  the  subroutine  sets
C                         AFLAG(1)   =   1.0005.  Recommended
C                         values of AFLAG(1) ranging from 4.0
C                         to  16.0.   Unchanged on exit (when
C                         correctly initialized).
C
C             AFLAG(2) -  Drop-tolerance. An element which in
C                         the  process  of  the  computations
C                         becomes smaller(in absolute  value)
C                         than  the drop-tolerance is removed
C                         from  array  A  (and  its  row  and
C                         column  numbers  are  removed  from
C                         arrays RNR  and  SNR).  Recommended
C                         value:   on entry  AFLAG(2)  should
C                         be in the interval
C                         (a*1.0E-4,a*1.0E-1)  where a is the
C                         magnitude of the elements.  If  the
C                         magnitude    a    of  the  non-zero
C                         elements is  not  known,  then  the
C                         user  should intialize the required
C                         drop-tolerance multiplied  by   -1.
C                         Let a(i) be the maximal element (in
C                         absolute value) in row i (  i=1(1)N
C                         ).   Let  a  be  the minimal number
C                         among all a(i). If a negative value
C                         of   AFLAG(2)   is   assigned,  the
C                         subroutine computes  a  and uses  a
C                         drop-tolerance       equal       to
C                         -AFLAG(2)*a.     In    the    above
C                         considerations  it  is assumed that
C                         the coefficient matrix A is not too
C                         badly  scaled.  If  this  is not so
C                         then perform  row  scaling  of  the
C                         coefficient   matrix  so  that  the
C                         largest elements in the rows are of
C                         magnitude  1 and choose AFLAG(2) in
C                         the    interval    (1.0E-5,1.0E-3).
C                         Unchanged on exit.
C
C             AFLAG(3) -  The   subroutine   will   stop  the
C                         computation when the growth  factor
C                         (parameter   AFLAG(5),  see  below)
C                         becomes larger  than  AFLAG(3).  On
C                         entry  AFLAG(3)  should  contain  a
C                         large    positive    number.     If
C                         AFLAG(3)<1.0E5  then the subroutine
C                         sets  AFLAG(3)=1.0E5.   Recommended
C                         value   AFLAG(3)=1.0E16.  Unchanged
C                         on     exit     (when     correctly
C                         initialized).
C
C             AFLAG(4) -  The   subroutine   will   stop  the
C                         computation when the absolute value
C                         of  a  current  pivotal  element is
C                         smaller   than    AFLAG(4)*AFLAG(6)
C                         (parameter  AFLAG(6)  is  described
C                         below).  On  entry  AFLAG(4)   must
C                         contain    a   small   non-negative
C                         number.  If  AFLAG(4)<0   then  the
C                         subroutine sets
C                         AFLAG(4)=-AFLAG(4).     Recommended
C                         value   AFLAG(4)=1.0E-12. Unchanged
C                         on     exit     (when     correctly
C                         intialized).
C
C             AFLAG(5) -  Growth   factor.   The  content  of
C                         parameter AFLAG(5) is  modified  by
C                         subroutine  Y12MF. After each stage
C                         of   the    Gaussian    elimination
C                         subroutine  Y12MF  sets  AFLAG(5) =
C                         AFLAG(7)/AFLAG(6)       (parameters
C                         AFLAG(6) and AFLAG(7) are described
C                         below). On  exit  large  values  of
C                         parameters  AFLAG(5)  indicate that
C                         an   appreciable   error   in   the
C                         computed  solution  is possible. In
C                         an extreme case, where
C                         AFLAG(5)>AFLAG(3),  the  subroutine
C                         will terminate the computations  in
C                         an attempt to prevent overflow.
C
C             AFLAG(6) -  On  exit  AFLAG(6)  is equal to the
C                         largest element in the  coefficient
C                         matrix  A of the system  Ax=b  (set
C                         by subroutine Y12MB). Unchanged  on
C                         exit.
C
C             AFLAG(7) -  On  exit  the  largest (in absolute
C                         value)  element  found  during  any
C                         stage  of  the  elimination will be
C                         stored in AFLAG(7). The content  of
C                         parameter  AFLAG(7)  is modified by
C                         subroutine Y12MF.
C
C             AFLAG(8) -  On exit the  minimal  (in  absolute
C                         value)   pivotal  element  will  be
C                         stored in AFLAG(8). Small values of
C                         AFLAG(8)     indicate     numerical
C                         singularity  of   the   coefficient
C                         matrix  A.  We  advise  the user to
C                         check this parameter on  exit  from
C                         the calculation very carefully. The
C                         content of  parameter  AFLAG(8)  is
C                         modified by the subroutine Y12MF.
C
C             AFLAG(9) -  The content of AFLAG(9) is modified
C                         by  the   subroutine   Y12MF.   The
C                         max-norm  of  the  last  correction
C                         vector d(p-1)  will  be  stored  in
C                         AFLAG(9)  on  successful  exit.  We
C                         advise the user to check  carefully
C                         this parameter.
C
C             AFLAG(10) - The   content   of   AFLAG(10)   is
C                         modified by the  subroutine  Y12MF.
C                         The  max-norm  of the last residual
C                         vector r(p-1)  will  be  stored  in
C                         AFLAG(10)  on  successful  exit. We
C                         advise the user to check  carefully
C                         this parameter.
C
C             AFLAG(11) - The   content   of   AFLAG(11)   is
C                         modified by the  subroutine  Y12MF.
C                         On  exit AFLAG(11) will contain the
C                         max-norm of the corrected  solution
C                         vector.  If  the value of AFLAG(11)
C                         is  not  to  close  to  zero   then
C                         AFLAG(9)/AFLAG(11)   will  normally
C                         give an excellent evaluation of the
C                         relative   error  in  the  solution
C                         vector.
C
C     IFLAG - INTEGER array of length 12. The content of this
C             array can be described as follows:
C
C             IFLAG(1) - This  parameter  is  used  as a work
C                        space by subroutine Y12MF.
C
C             IFLAG(2) - On entry IFLAG(2) must contain  some
C                        positive  integer smaller than N. We
C                        recommend  IFLAG(2)   .le.   3.   If
C                        IFLAG(3)  = 0 then this parameter is
C                        ignored  by  subroutine  Y12MF.   If
C                        IFLAG((2)  .ge.  0  then the pivotal
C                        search   at   any   stage   of   the
C                        elimination (except possibly some of
C                        the last stages) is carried  out  in
C                        the  IFLAG(2)  rows which have least
C                        number   of    non-zero    elements.
C                        Unchanged on exit.
C
C             IFLAG(3) - On  entry IFLAG(3) must contain 0, 1
C                        or 2.  For  general  pivotal  search
C                        IFLAG(3)  should  be set equal to 1.
C                        If IFLAG(3) = 2 then  only  diagonal
C                        elements of the coefficient matrix A
C                        can be selected as pivotal elements.
C                        If  IFLAG(3)  =  0 then the Gaussian
C                        elimination  will  be  carried   out
C                        without any pivoting.  IFLAG(3)=0 or
C                        IFLAG(3)=2 (i.e. one of the  special
C                        pivotal strategies is to be applied)
C                        should  be   used   very   carefully
C                        because    the   error   diagnostics
C                        algorithm may not trace  all  errors
C                        in this case.  Unchanged on exit.
C
C             IFLAG(4) - On  entry IFLAG(4) must contain 0, 1
C                        or 2.  IFLAG(4)  =  0  is  the  best
C                        choice  when  (i) only one system is
C                        to be solved, (ii) the first  system
C                        of  a  sequence  of systems with the
C                        same  matrix  (Ax   =   b1,   Ax   =
C                        b2,......Ax  =  bp) is to be solved,
C                        (iii) when any system in a  sequence
C                        of  systems  whose  matrices  are of
C                        different structure is to be solved.
C                        IFLAG(4) = 1 is the best choice when
C                        the first system of  a  sequence  of
C                        systems  whose  matrices  are of the
C                        same structure is to be  solved.  In
C                        this  case  IFLAG(4) = 2 can be used
C                        in the solution of any system  after
C                        the first one.  Unchanged on exit.
C
C             IFLAG(5) - If   the  LU  factorization  of  the
C                        coefficient matrix is not available,
C                        then  IFLAG(5)  must  be set to 2 on
C                        entry. If the  LU  factorization  of
C                        the coefficient matrix is available,
C                        then IFLAG(5) must be set  to  3  on
C                        entry.  Unchanged on exit.
C
C             IFLAG(6) - On  successful exit IFLAG(6) will be
C                        equal to  the  number  of  "garbage"
C                        collections in the row ordered list.
C                        If IFLAG(6)  is  large  then  it  is
C                        better  to  choose a larger value of
C                        NN with  next  calls  of  subroutine
C                        Y12MF   with  the  same  or  similar
C                        matrix  A.  This  will  lead  to   a
C                        reduction  in  the  computing  time.
C                        The content of IFLAG(6) is  modified
C                        by the subroutine Y12MF.
C
C             IFLAG(7) - On  successful exit IFLAG(7) will be
C                        equal to  the  number  of  "garbage"
C                        collections  in  the  column ordered
C                        list.  If IFLAG(7) is large then  it
C                        is  better  to choose a larger value
C                        of  NN1  in  the   next   calls   of
C                        subroutine  Y12MF  with  the same or
C                        similar matrix A. This will lead  to
C                        a  reduction  of the computing time.
C                        The content of IFLAG(7) is  modified
C                        by subroutine Y12MF.
C
C             IFLAG(8) - On  successful exit IFLAG(8) will be
C                        equal  to  the  maximal  number   of
C                        non-zero elements kept in array A at
C                        any   stage    of    the    Gaussian
C                        elimination.  If  IFLAG(8)  is  much
C                        smaller than NN (or  NN1)  then  the
C                        length  NN  (or  NN1)  can be chosen
C                        smaller in next calls of  subroutine
C                        Y12MF   with  the  same  or  similar
C                        matrix  A.  This  will  lead  to   a
C                        reduction of the storage needed. The
C                        content of IFLAG(8) is  modified  by
C                        subroutine Y12MF.
C
C             IFLAG(9) - The  minimal  length  NN1  such that
C                        Y12MF  can   solve   systems   whose
C                        matrices  are  of the same structure
C                        without "garbage" collections in the
C                        column ordered list and "movings" of
C                        columns at the  end  of  the  column
C                        ordered  list  is stored in IFLAG(9)
C                        after  the  solution  of  the  first
C                        system   in   the   sequence   (with
C                        IFLAG(4)=1).    The    content    of
C                        IFLAG(9)  is  modified by subroutine
C                        Y12MF when IFLAG(4) = 1 and  ignored
C                        otherwise.
C
C             IFLAG(10)  The  minimal  length  NN  such  that
C                        subroutine Y12MF can  solve  systems
C                        whose   matrices  are  of  the  same
C                        structure     without      "garbage"
C                        collections  in the row ordered list
C                        and "movings" of rows to the end  of
C                        the  row  ordered  list is stored in
C                        IFLAG(10) after the solution of  the
C                        first  system  in the sequence (with
C                        IFLAG(4)  =  1).  The   content   of
C                        IFLAG(10)   is   modified   by   the
C                        subroutine Y12MF when IFLAG(4)  =  1
C                        and ignored otherwise.
C
C             IFLAG(11)  The   maximum   allowed   number  of
C                        iterations  must  be  contained   in
C                        IFLAG(11)  on  entry.   Restriction:
C                        IFLAG(11)>1.   Recommended  value  :
C                        IFLAG(11)< 33.  Unchanged on exit.
C
C             IFLAG(12)  The content of IFLAG(12) is modified
C                        by the subroutine  Y12MF.   On  exit
C                        the  number  of  iterations actually
C                        performed   will   be   stored    in
C                        IFLAG(12).
C
C     IFAIL - Error  diagnostic  parameter.  The  content  of
C             parameter   IFAIL  is  modified  by  subroutine
C             Y12MF.  On exit IFAIL = 0 if the subroutine has
C             not  detected  any  error.  Positive  values of
C             IFAIL on exit show that  some  error  has  been
C             detected  by  the subroutine. Many of the error
C             diagnostics are common for all  subroutines  in
C             the  package.   Therefore the error diagnostics
C             are listed in a separate section, Section 7, of
C             this  book.  We  advise  the  user to check the
C             value of this parameter on exit.
C
C     5.  Error diagnostics
C
C     Error  diagnostics  are  given  by  positive  values of
C     parameter IFAIL (see above).  We  advise  the  user  to
C     check  carefully  the  value of this parameter on exit.
C     The error messages are listed in Section 7.
C
C     6.  Auxiliary subroutines
C
C     Y12MF calls three other subroutines: Y12MB,  Y12MC  and
C     Y12MD.
C
C     7.  Timing
C
C     The  time  taken  depends  on  the  order of the matrix
C     (parameter N), the number of the non-zero  elements  in
C     the matrix (parameter Z), the magnitude of the non-zero
C     elements and their distribution in the matrix.
C
C     8.  Storage
C
C     There are no internally declared arrays.
C
C     9.  Accuracy
C
C     Normally the accuracy achieved can  be  estimated  very
C     well  by  means  of  AFLAG(9)  and  AFLAG(10).  Further
C     information  about  the  accuracy  can   sometimes   be
C     obtained  by  inspection of the growth factor AFLAG(5),
C     and the smallest pivotal element  (in  absolute  value)
C     stored in AFLAG(8).
C
C     10.  Some remarks
C
C     Remark 1 Following the recommendations given in Section
C              1.3.6 the user can write a subroutine (similar
C              to the subroutine Y12MA) where the recommended
C              values of the parameters AFLAG(1) to AFLAG(4),
C              IFLAG(2)  to  IFLAG(5)   and   IFLAG(11)   are
C              initialized.  If this is done then only N, NN,
C              NN1, IHA, A, SNR, RNR and B should be assigned
C              before the call of this new subroutine.
C
C     Remark 2 The  information  stored in some of the arrays
C              should not be altered between successive calls
C              of  subroutine  Y12MF  (see  more  details  in
C              Section 1.3.6).
C                        =================
C                        Error diagnostics
C                        =================
C
C     IFAIL is the error diagnostics parameter.  On exit from
C     each  subroutine  IFAIL  =  0  if  no  error  has  been
C     detected. IFAIL > 0 indicates that some error has  been
C     detected  and  the  computations  have  been terminated
C     immediately after  the  detection  of  the  error.  The
C     errors  corresponding  to the different positive values
C     of IFAIL are listed below.
C
C     IFAIL = 1    The    coefficient   matrix   A   is   not
C                  factorized, i.e. the  call  of  subroutine
C                  Y12MD  was not preceded by a call of Y12MC
C                  during the solution of   Ax=b   or  during
C                  the  solution  of  the  first  system in a
C                  sequence ( Ax1 = b1 , Ax2 = b2,.....,Axp =
C                  bp)  of  systems with the same coefficient
C                  matrix. This will work in all  cases  only
C                  if  the  user  sets IFLAG(1) .ge. 0 before
C                  the call of package Y12M (i.e. before  the
C                  first   call   of  a  subroutine  of  this
C                  package).
C
C     IFAIL = 2    The coefficient matrix A is  not  ordered,
C                  i.e.  the call of subroutine Y12MC was not
C                  preceded by a call  of  Y12MB.  This  will
C                  work  in  all  cases only if the user sets
C                  IFLAG(1) .ge. 0 before the call of package
C                  Y12M  (i.e.  before  the  first  call of a
C                  subroutine of this package).
C
C     IFAIL = 3    A pivotal element abs(a(i,i;j)) < AFLAG(4)
C                  *  AFLAG(6) is selected.  When AFLAG(4) is
C                  sufficiently small this is  an  indication
C                  that the coefficient matrix is numerically
C                  singular.
C
C     IFAIL = 4    AFLAG(5), the  growth  factor,  is  larger
C                  than    AFLAG(3).    When    AFLAG(3)   is
C                  sufficiently large this indicates that the
C                  elements  of the coefficient matrix A grow
C                  so quickly during the  factorization  that
C                  the continuation of the computation is not
C                  justified.  The  choice   of   a   smaller
C                  stability   factor,   AFLAG(1),  may  give
C                  better results in this case.
C
C     IFAIL = 5    The length NN of arrays A and SNR  is  not
C                  sufficient.   Larger  values  of  NN  (and
C                  possibly of NN1) should be used.
C
C     IFAIL = 6    The  length  NN1  of  array  RNR  is   not
C                  sufficient.   Larger  values  of  NN1 (and
C                  possibly of NN) should be used.
C
C     IFAIL = 7    A row without  non-zero  elements  in  its
C                  active    part   is   found   during   the
C                  decomposition.  If   the   drop-tolerance,
C                  AFLAG(2),   is  sufficiently  small,  then
C                  IFAIL = 7 indicates  that  the  matrix  is
C                  numerically  singular. If a large value of
C                  the drop-tolerance AFLAG(2) is used and if
C                  IFAIL = 7  on exit, this is not certain. A
C                  run  with  a  smaller  value  of  AFLAG(2)
C                  and/or  a  careful check of the parameters
C                  AFLAG(8) and AFLAG(5)  is  recommended  in
C                  the latter case.
C
C     IFAIL = 8    A  column without non-zero elements in its
C                  active   part   is   found   during    the
C                  decomposition.   If   the  drop-tolerance,
C                  AFLAG(2),  is  sufficiently  small,   then
C                  IFAIL  =  8  indicates  that the matrix is
C                  numerically singular. If a large value  of
C                  the drop-tolerance AFLAG(2) is used and if
C                  IFAIL = 8  on exit, this is not certain. A
C                  run  with  a  smaller  value  of  AFLAG(2)
C                  and/or a careful check of  the  parameters
C                  AFLAG(8)  and  AFLAG(5)  is recommended in
C                  the latter case.
C
C     IFAIL = 9    A pivotal element  is  missing.  This  may
C                  occur  if  AFLAG(2)  >  0 and IFLAG(4) = 2
C                  (i.e. some system after the first one in a
C                  sequence   of   systems   with   the  same
C                  structure is solved using a positive value
C                  for  the drop-tolerance). The value of the
C                  drop-tolerance   AFLAG(2),    should    be
C                  decreased  and  the  coefficient matrix of
C                  the system refactorized.  This  error  may
C                  also occur when one of the special pivotal
C                  strategies (IFLAG(3)=0 or  IFLAG(3)=2)  is
C                  used  and  the  matrix is not suitable for
C                  such a strategy.
C
C     IFAIL = 10   Subroutine Y12MF is called with IFLAG(5) =
C                  1  (i.e.  with  a  request  to  remove the
C                  non-zero elements of the lower  triangular
C                  matrix    L).     IFLAG(5)=2     must   be
C                  initialized instead of IFLAG(5)=1.
C
C     IFAIL = 11   The coefficient matrix A contains at least
C                  two  elements  in the same position (i,j).
C                  The  input   data   should   be   examined
C                  carefully in this case.
C
C     IFAIL = 12   The number of equations in the system Ax=b
C                  is smaller than 2 (i.e.  N<2).  The  value
C                  of N should be checked.
C
C     IFAIL = 13   The  number  of  non-zero  elements of the
C                  coefficient matrix is  non-positive  (i.e.
C                  Z.le.0  ).   The  value of the parameter Z
C                  (renamed NZ in Y12MF) should be checked.
C
C     IFAIL = 14   The number of  non-zero  elements  in  the
C                  coefficient  matrix  is  smaller  than the
C                  number of equations (i.e. Z  <  N  ).   If
C                  there  is no mistake (i.e. if parameter Z,
C                  renamed NZ in Y12MF, is correctly assigned
C                  on  entry)  then the coefficient matrix is
C                  structurally singular in this case.
C
C     IFAIL = 15   The length IHA of the first  dimension  of
C                  array  HA  is  smaller  than  N.  IHA.ge.N
C                  should be assigned.
C
C     IFAIL = 16   The value of  parameter  IFLAG(4)  is  not
C                  assigned  correctly.  IFLAG(4)  should  be
C                  equal to 0, 1 or 2. See  more  details  in
C                  the description of this parameter.
C
C     IFAIL = 17   A  row  without non-zero elements has been
C                  found in the coefficient matrix A  of  the
C                  system  before the Gaussian elimination is
C                  initiated.  Matrix   A   is   structurally
C                  singular.
C
C     IFAIL = 18   A  column  without  non-zero  elements has
C                  been found in the coefficient matrix A  of
C                  the system before the Gaussian elimination
C                  is initiated.  Matrix  A  is  structurally
C                  singular.
C
C     IFAIL = 19   Parameter  IFLAG(2) is smaller than 1. The
C                  value of IFLAG(2)  should  be  a  positive
C                  integer (IFLAG(2) = 3 is recommended).
C
C     IFAIL = 20   Parameter   IFLAG(3)   is  out  of  range.
C                  IFLAG(3) should be equal to 0, 1 or 2.
C
C     IFAIL = 21   Parameter  IFLAG(5)  is  out   of   range.
C                  IFLAG(5) should be equal to 1, 2 or 3 (but
C                  when IFLAG(5) = 3 Y12MB and  Y12MC  should
C                  not  be  called;  see also the message for
C                  IFAIL = 22 below).
C
C     IFAIL = 22   Either  subroutine  Y12MB  or   subroutine
C                  Y12MC is called with IFLAG(5) = 3. Each of
C                  these subroutines should  be  called  with
C                  IFLAG(5) equal to 1 or 2.
C
C     IFAIL = 23   The    number    of   allowed   iterations
C                  (parameter IFLAG(11) when Y12MF  is  used)
C                  is  smaller  than  2.   IFLAG(11)  .ge.  2
C                  should be assigned.
C
C     IFAIL = 24   At least one element whose  column  number
C                  is  either larger than N or smaller than 1
C                  is found.
C
C     IFAIL = 25   At least one element whose row  number  is
C                  either  larger than N or smaller than 1 is
C                  found.
C                           ==========
C                           References
C                           ==========
C        1.  Bjorck, A. -
C                  "Methods for Sparse  Linear  Least-Squares
C                  Problems".
C                  In: "Sparse Matrix Computations"
C                  (J.Bunch and D.Rose, eds.), pp.177-199.
C                  Academic Press, New York, 1976.
C
C        2.  Clasen, R.J. -
C                  "Techniques  for  Automatic  Tolerance  in
C                  Linear Programming",
C                  Comm. ACM 9, pp. 802-803, 1966.
C
C        3.  Cline, A.K.,  Moler,  C.B.,  Stewart,  G.W.  and
C            Wilkinson, J.H. -
C                  "An estimate for the condition number of a
C                  matrix",
C                  SIAM J. Numer. Anal. 16, 368-375, 1979.
C
C        4.  Dongarra,  J.J.,  Bunch,  J.R.,  Moler, C.B. and
C            Stewart, G.W. -
C                  "LINPACK User's Guide",
C                  SIAM, Philadelphia, 1979.
C
C        5.  Duff, I.S. -
C                  "MA28 - a Set of FORTRAN Subroutines
C                  for Sparse Unsymmetric Matrices",
C                  Report    No.   R8730,   A.E.R.E.,Harwell,
C                  England, 1977.
C
C        6.  Duff, I.S. and Reid, J.K. -
C                  "Some  Design  Features of a Sparse Matrix
C                  Code",
C                  ACM Trans. Math. Software  5,  pp.  18-35,
C                  1979.
C
C        7.  Forsythe, G.E., Malcolm, M.A., and Moler, C.B. -
C                  "Computer   Methods    for    Mathematical
C                  Computations",
C                  Prentice-Hall,   Englewood  Cliffs,  N.J.,
C                  1977.
C
C        8.  Forsythe, G.E. and Moler, C.B. -
C                  "Computer  Solution  of  Linear  Algebraic
C                  Equations",
C                  Prentice-Hall,  Englewood  Cliffs,   N.J.,
C                  1967.
C
C        9.  Gustavson, F.G. -
C                  "Some Basic Techniques for Solving Sparse
C                  Systems of Linear Equations".
C                  In:    "Sparse    Matrices    and    Their
C                  Applications",
C                  (D.J.  Rose and R.A. Willoughby, eds.), pp
C                  41-52,
C                  Plenum Press, New York, 1972.
C
C       10.  Gustavson, F.G. -
C                  "Two  Fast Algorithms for Sparse Matrices:
C                  Multiplication and Permuted
C                  Transposition",
C                  ACM Trans. Math. Software, 4, pp. 250-269,
C                  1978.
C
C       11.  Houbak, N. and Thomsen, P.G. -
C                  "SPARKS  -  a   FORTRAN   Subroutine   for
C                  Solution
C                  of  Large  Systems  of  Stiff  ODE's  with
C                  Sparse Jacobians",
C                  Report  79-02,  Institute  for   Numerical
C                  Analysis,
C                  Technical  University  of Denmark, Lyngby,
C                  Denmark, 1979.
C
C       12.  Moler, C.B. -
C                  "Three  Research  Problems  in   Numerical
C                  Linear Algebra".
C                  In:   "Proceedings   of  the  Symposia  in
C                  Applied Mathematics"
C                  (G.H. Golub and J. Oliger, eds.), Vol. 22,
C                  pp. 1-18,
C                  American Mathematical Society,
C                  Providence, Rhode Island, 1978.
C
C       13.  NAG Library -
C                  Fortran Manual, Mark 7, Vol. 3, 4,
C                  Numerical Algorithms Group,
C                  7 Banbury Road,  Oxford  OX2  6NN,  United
C                  Kingdom.
C
C       14.  Reid, J.K. -
C                  "A  Note  on  the  Stability  of  Gaussian
C                  Elimination",
C                  J.  Inst.  Math.  Appl.,  8,  pp. 374-375,
C                  1971.
C
C       15.  Reid, J.K. -
C                  "Fortran  Subroutines  for Handling Sparse
C                  Linear Programming Bases",
C                  Report R8269, A.E.R.E., Harwell,  England,
C                  1976.
C
C       16.  Schaumburg, K. and Wasniewski, J. -
C                  "Use   of   a   Semiexplicit   Runge-Kutta
C                  Integration  Algorithm  in a Spectroscopic
C                  Problem"
C                  Computers  and  Chemistry  2,  pp.  19-25,
C                  1978.
C
C       17.  Schaumburg, K., Wasniewski, J. and Zlatev, Z. -
C                  "Solution   of    Ordinary    Differential
C                  Equations.   Development of a Semiexplicit
C                  Runge-Kutta Algorithm and Application to a
C                  Spectroscopic Problem",
C                  Computers  and  Chemistry,  3,  pp. 57-63,
C                  1979.
C
C       18.  Schaumburg, K., Wasniewski, J. and Zlatev, Z. -
C                  "The Use of Sparse Matrix Technique in the
C                  Numerical Integration of Stiff Systems  of
C                  Linear Ordinary Differential Equations",
C                  Computers  and  Chemistry,  4,  pp.  1-12,
C                  1980.
C
C       19.  Stewart, G.W. -
C                  "Introduction to Matrix Computations",
C                  Academic Press, New York, 1973.
C
C       20.  Tewarson, R.P. -
C                  "Sparse Matrices",
C                  Academic Press, New York, 1973.
C
C       21.  Thomsen, P.G. -
C                  "Numerical  Solution of Large Systems with
C                  Sparse Jacobians".
C                  In: "Working Papers for  the  1979  SIGNUM
C                  Meeting on Numerical Ordinary Differential
C                  Equations" (R.D. Skeel, ed.),
C                  Computer Science Department,
C                  University  of  Illinois   at   Urbana   -
C                  Champaign, Urbana, Illinois, 1979.
C
C       22.  Wasniewski, J., Zlatev, Z. and Schaumburg, K. -
C                  "A Multibanking  Option  of  an  Iterative
C                  Refinement Subroutine".
C                  In:  "Conference Proceedings and Technical
C                  Papers",
C                  Spring   Conference   of   Univac    Users
C                  Assotiation/Europe, Geneva, 1981.
C
C       23.  Wilkinson, J.H. -
C                  "Rounding Errors in Algebraic Processes",
C                  Prentice-Hall,  Englewood  Cliffs,   N.J.,
C                  1963.
C
C       24.  Wilkinson, J.H. -
C                  "The Algebraic Eigenvalue Problem",
C                  Oxford University Press, London, 1965.
C
C       25.  Wilkinson, J.H and Reinsch, C. -
C                  "Handbook for Automatic Computation",
C                  Vol II, Linear Algebra, pp. 50-56,
C                  Springer, Heidelberg, 1971.
C
C       26.  Wolfe, P. -
C                  "Error   in   the   Solution   of   Linear
C                  Programming Problems",
C                  In:   "Error   in   Digital   Computation"
C                  (L.B.Rall, ed.), Vol 2, pp. 271-284,
C                  Wiley, New York, 1965.
C
C       27.  Zlatev, Z. -
C                  "Use   of   Iterative  Refinement  in  the
C                  Solution of Sparse Linear Systems",
C                  Report 1/79, Institute of Mathematics  and
C                  Statistics,   The   Royal  Veterinary  and
C                  Agricultural University,
C                  Copenhagen, Denmark, 1979
C                  (to appear in SIAM J. Numer. Anal.).
C
C       28.  Zlatev, Z. -
C                  "On  Some  Pivotal  Strategies in Gaussian
C                  Elimination by Sparse Technique",
C                  SIAM J. Numer. Anal. 17, pp. 18-30,  1980.
C
C       29.  Zlatev, Z. -
C                  "On Solving Some Large Linear Problems  by
C                  Direct Methods",
C                  Report   111,   Department   of   Computer
C                  Science,  University  of  Aarhus,  Aarhus,
C                  Denmark, 1980.
C
C       30.  Zlatev, Z. -
C                  "Modified  Diagonally Implicit Runge-Kutta
C                  Methods",
C                  Report No.  112,  Department  of  Computer
C                  Science,  University  of  Aarhus,  Aarhus,
C                  Denmark, 1980
C                  (to appear in SIAM Journal  on  Scientific
C                  and Statistical Computing).
C
C       31.  Zlatev, Z. -
C                  "Comparison  of  Two Pivotal Strategies in
C                  Sparse Plane Rotations",
C                  Report   122,   Department   of   Computer
C                  Science,  University  of  Aarhus,  Aarhus,
C                  Denmark, 1980.
C                  (to appear in  Computers  and  Mathematics
C                  with Applications).
C
C       32.  Zlatev, Z., Barker, V.A. and Thomsen, P.G. -
C                  "SSLEST -  a  FORTRAN  IV  Subroutine  for
C                  Solving Sparse Systems of Linear Equations
C                  (USER's GUIDE)",
C                  Report  78-01,  Institute  for   Numerical
C                  Analysis,
C                  Technical  University  of Denmark, Lyngby,
C                  Denmark, 1978.
C
C       33.  Zlatev, Z. and Nielsen, H.B. -
C                  "Least  - Squares Solution of Large Linear
C                  Problems".
C                  In:  "Symposium i Anvendt Statistik 1980"
C                  (A. Hoskuldsson, K.  Conradsen,  B.  Sloth
C                  Jensen and K.Esbensen, eds.), pp. 17-52.
C                  NEUCC, Technical University of Denmark,
C                  Lyngby, Denmark, 1980.
C
C       34.  Zlatev, Z., Schaumburg, K. and Wasniewski, J. -
C                  "Implementation of an Iterative Refinement
C                  Option in a  Code  for  Large  and  Sparse
C                  Systems".
C                  Computers  and  Chemistry,  4,  pp. 87-99,
C                  1980.
C
C       35.  Zlatev, Z. and Thomsen., P.G. -
C                  "ST  -  a  FORTRAN  IV  Subroutine for the
C                  Solution  of  Large  Systems   of   Linear
C                  Algebraic Equations with Real Coefficients
C                  by Use of Sparse Technique",
C                  Report  76-05,  Institute  for   Numerical
C                  Analysis,
C                  Technical  University  of Denmark, Lyngby,
C                  Denmark, 1976.
C
C       36.  Zlatev, Z. and Thomsen, P.G. -
C                  "An   Algorithm   for   the   Solution  of
C                  Parabolic Partial  Differential  Equations
C                  Based on Finite Element Discretization",
C                  Report   77-09,  Institute  for  Numerical
C                  Analysis,
C                  Technical University of  Denmark,  Lyngby,
C                  Denmark, 1977.
C
C       37.  Zlatev, Z. and Thomsen, P.G. -
C                  "Application of  Backward  Differentiation
C                  Methods  to the Finite Element Solution of
C                  Time Dependent Problems",
C                  International   Journal   for    Numerical
C                  Methods  in  Engineering,  14,  pp. 1051 -
C                  1061, 1979.
C
C       38.  Zlatev, Z., Wasniewski, J. and Schaumburg, K. -
C                  "Comparison  of Two Algorithms for Solving
C                  Large Linear Systems".
C                  Report No 80/9,
C                  Regional   Computing   Centre    at    the
C                  University  of Copenhagen, Vermundsgade 5,
C                  DK-2100 Copenhagen, Denmark, 1980.
C
C       39.  Zlatev, Z., Wasniewski, J. and Schaumburg, K. -
C                  "Classification of the Systems of Ordinary
C                  Differential   Equations   and   Practical
C                  Aspects in the  Numerical  Integration  of
C                  Large Systems",
C                  Computers  and  Chemistry,  4,  pp. 13-18,
C                  1980.
C
C       40.  Zlatev, Z., Wasniewski, J., Schaumburg, K. -
C                  "A Testing Scheme For Subroutines  Solving
C                  Large Linear Problems",
C                  Report No 81/1,
C                  Regional    Computing    Centre   at   the
C                  University of Copenhagen, Vermundsgade  5,
C                  DK-2100 Copenhagen, Denmark, 1981
C                  (to  appear in Computers and Chemistry, 5,
C                  1981).
Acknowledge-To: <NEUJW@vm.uni-c.dk>


```

sparse-matrix/y12m/ex/matrd1.f:
```
      subroutine matrd1(n,z,c,nn,nn1,a,snr,rnr)                                 
c                                                                               
c                                                                               
c  a matrix which depends on two parameters(n-the dimension of the mat-         
c  rix and c-a structure parameter)is generated by the subroutine matrd1        
c  the non-zero elements of the matrix (in any order) are stored                
c  in the first  z  positions (z  is the number of the non-zero                 
c  elements) of the one-dimensional array  a ,  their column and                
c  row numbers in the corresponding positions of array  snr  and                
c  array  rnr.                                                                  
c                                                                               
c                                                                               
      implicit real(a-b,g,p,t-v),integer(c,f,h-n,r-s,z)                         
      dimension a(nn)                                                           
c     integer*2 snr(nn),rnr(nn1)                                                
      integer snr(nn),rnr(nn1)                                                  
      do 20 i=1,n                                                               
      a(i)=1.0                                                                  
      snr(i)=i                                                                  
   20 rnr(i)=i                                                                  
      do 21 i=1,n                                                               
      r=n+i                                                                     
      s=c+i                                                                     
      a(r)=i+1                                                                  
      if(s.le.n)snr(r)=s                                                        
      if(s.gt.n)snr(r)=s-n                                                      
   21 rnr(r)=i                                                                  
      l=2*n                                                                     
      do 22 i=1,n                                                               
      r=l+i                                                                     
      s=c+i+1                                                                   
      a(r)=-i                                                                   
      if(s.le.n)snr(r)=s                                                        
      if(s.gt.n)snr(r)=s-n                                                      
   22 rnr(r)=i                                                                  
      k=3*n                                                                     
      do 23 i=1,n                                                               
      r=k+i                                                                     
      s=c+i+2                                                                   
      a(r)=16.0                                                                 
      if(s.le.n)snr(r)=s                                                        
      if(s.gt.n)snr(r)=s-n                                                      
   23 rnr(r)=i                                                                  
      rr1=10                                                                    
      rr2=4*n                                                                   
      rr3=1                                                                     
   25 continue                                                                  
      do 26 i=1,rr1                                                             
      a(rr2+i)=100.0*i                                                          
      snr(rr2+i)=n-rr1+i                                                        
      rnr(rr2+i)=rr3                                                            
26    continue                                                                  
      if(rr1.eq.1)    go to 27                                                  
      rr2=rr2+rr1                                                               
      rr1=rr1-1                                                                 
      rr3=rr3+1                                                                 
      go to 25                                                                  
   27 z=4*n+55                                                                  
      return                                                                    
      end                                                                       

```

sparse-matrix/y12m/ex/index.html:
```
<!-- X-URL: http://www.netlib.org/y12m/ex/ -->
<!-- Date: Sun, 15 Sep 2002 17:01:04 GMT -->
<!-- Last-Modified: Fri, 21 Jun 2002 14:14:40 GMT -->
<BASE HREF="http://www.netlib.org/y12m/ex/">
<head>
<title>y12m/ex</title>
<meta name="waisindex" value="nse">
</head>
<h1>y12m/ex</h1>
<p>
Click <A HREF="http://www.netlib.org/master_counts2.html#y12m/ex">here</A> to see the number of accesses to this library.
<p><hr>
<pre>
file    <a href="maind.d">maind.d</a>
for     output from (y12m/ex/maind.f)

file    <a href="maind.f">maind.f</a>
for     test program for (y12m/y12mae)

file    <a href="maine.f">maine.f</a>
for     test program for the (y12m/y12m) package

file    <a href="mainf.f">mainf.f</a>
for     test program for (y12m/y12mhe)

file    <a href="mainf2.f">mainf2.f</a>
for     test program for the (y12m/y12m) package

file    <a href="matrd1.f">matrd1.f</a>
for     auxillary routine needed by (y12m/ex/maind.f)

file    <a href="matre1.f">matre1.f</a>
for     auxillary routine needed by (y12m/ex/maind.f)

file    <a href="matrf2.f">matrf2.f</a>
for     auxillary routine needed by (y12m/ex/mainf.f)

file    <a href="test1.f">test1.f</a>
for     template for (y12m/y12mae) use, user must supply data

file    <a href="test2.f">test2.f</a>
for     template for (y12m/y12mfe) use, user must supply data

</pre>
</body>
</html>


```

sparse-matrix/y12m/ex/test1.f:
```
      parameter (iha = 10,nn = 200,nn1 = 100)
      implicit real (a-b,g,p,t-v),integer(c,f,h-n,r-s,z)
      real a(nn),pivot(iha),b(iha),aflag(8)
      integer snr(nn),rnr(nn1),ha(iha,11),iflag(10)
      data nin/5/, nout/6/
c
c     initialization of the parameters.
c
      read (nin,101)n,z
101   format(2i4)
c
c     initialize the non-zero elements of matrix
c     a  in arbitrary order.
c
      do 120 k = 1,z
      read(nin,110) rnr(k), snr(k), a(k)
110   format(2i4,f12.6)
120   continue
c
c
c     initialize the components of the right-hand side vector  b.
c
      read(nin,130) (b(k),k = 1,n)
130   format(6f12.6)
c
c     call the subroutine  y12mae.
c
      call y12mae(n,z,a,snr,nn,rnr,nn1,pivot,ha,iha,aflag,
     1 iflag,b,ifail)
c
c     print the results.
c
      write(nout,1)ifail
1     format('1the error diagnostic parameter  ifail',
     1 '  is equal to',i4)
      if(ifail.gt.0)go to 5
      write(nout,2)
    2 format('0the solution vector is given below.')
      do 4 i = 1,6
      write(nout,3)i,b(i)
    3 format(' ',i10,f20.5)
    4 continue
c
c     print the auxiliary information about the solution.
c     this is optionally.
c
      write(nout,11)aflag(6)
11    format('0the largest element in the original',
     1 ' matrix is:'f12.5)
      write(nout,12)aflag(7)
   12 format('0the largest element found in the elimination',
     1f12.5)
      write(nout,13)aflag(8)
   13 format('0the minimal(in absolute value)pivotal element',
     1 f12.5)
      write(nout,14)aflag(5)
   14 format('0the growth factor is:',1pd12.2)
      write(nout,15)iflag(6)
   15 format('0the number of collections in the row list',i5)
      write(nout,16)iflag(7)
   16 format('0the number of collections in the column list',i5)
      write(nout,17)iflag(8)
   17 format('0the largest number of elements found in array a',
     1 i9)
    5 continue
      stop
      end

```

sparse-matrix/y12m/ex/matre1.f:
```
      subroutine matre1(n,z,c,nn,nn1,a,snr,rnr)                                 
      implicit real(a-b,g,t-v),integer(c,f,h-n,r-s,z)                           
      real a(nn)                                                                
c     integer*2 snr(nn),rnr(nn1)                                                
      integer snr(nn),rnr(nn1)                                                  
      do 1 i=1,n                                                                
      a(i)=4.                                                                   
      snr(i)=i                                                                  
    1 rnr(i)=i                                                                  
      r=n-1                                                                     
      do 2 i=1,r                                                                
      r1=n+i                                                                    
      a(r1)=-1.                                                                 
      snr(r1)=i+1                                                               
    2 rnr(r1)=i                                                                 
      r2=2*n-1                                                                  
      do 3 i=1,r                                                                
      r1=r2+i                                                                   
      a(r1)=-1.                                                                 
      snr(r1)=i                                                                 
    3 rnr(r1)=i+1                                                               
      r=n-c                                                                     
      r2=3*n-2                                                                  
      do 4 i=1,r                                                                
      r1=r2+i                                                                   
      a(r1)=-1.                                                                 
      snr(r1)=i+c                                                               
    4 rnr(r1)=i                                                                 
      r2=4*n-2-c                                                                
      do 5 i=1,r                                                                
      r1=r2+i                                                                   
      a(r1)=-1.                                                                 
      snr(r1)=i                                                                 
    5 rnr(r1)=i+c                                                               
      z=5*n-2*c-2                                                               
      return                                                                    
      end                                                                       

```

sparse-matrix/y12m/ex/test2.f:
```
      parameter (iha=10,nn=200,nn1=100,iha1=60)
      implicit real(a-b,g,p,t-v),integer(c,f,h-n,r-s,z)
      real a(nn),y(iha),b(iha),aflag(11),b1(iha),x(iha),a1(iha1)
      integer snr(nn),rnr(nn1),ha(iha,13),iflag(12),sn(iha1)
      data nin/5/, nout/6/
c
c     initialization of the parameters.
c
      aflag(1)=128.0
      aflag(2)=1.e-3
      aflag(3)=1.e+16
      aflag(4)=1.e-12
      iflag(2)=2
      iflag(3)=1
      iflag(4)=1
      iflag(5)=2
      iflag(11)=25
      read (nin,101)n,z
101   format(2i4)
c
c     initialize the non-zero elements of matrix  a  in arbitrary order.
c
      do 120 k=1,z
      read(nin,110) rnr(k), snr(k), a(k)
110   format(2i4,f12.6)
120   continue
c
c
c     initialize the components of the right-hand side vector  b.
c
      read(nin,130) (b(k),k=1,n)
130   format(6f12.6)
c
c     call the subroutine  y12mfe.
c
      call y12mfe(n,a,snr,nn,rnr,nn1,a1,sn,z,ha,iha,b,b1,x,y,
     1 aflag,iflag,ifail)
c
c     print the results.
c
      write(nout,1)ifail
    1 format('1the error diagnostic parameter  ifail  is equal to',i4)
      if(ifail.gt.0)go to 5
      write(nout,2)
    2 format('0the solution vector is given below.')
      do 4 i=1,6
      write(nout,3)i,x(i)
    3 format(' ',i10,f20.5)
    4 continue
c
c     print the auxiliary information about the solution.
c     this is optionally.
c
      write(nout,11)aflag(6)
   11 format('0the largest element in the original matrix is:'f12.5)
      write(nout,12)aflag(7)
   12 format('0the largest element found in the elimination',f12.5)
      write(nout,13)aflag(8)
   13 format('0the minimal(in absolute value)pivotal element',f12.5)
      write(nout,14)aflag(5)
   14 format('0the growth factor is:',1pe12.2)
      write(nout,15)iflag(6)
   15 format('0the number of collections in the row list',i5)
      write(nout,16)iflag(7)
   16 format('0the number of collections in the column list',i5)
      write(nout,17)iflag(8)
   17 format('0the largest number of elements found in array a',i9)
      write(nout,18) iflag(12)
   18 format('0the number of iterations is:,'i5)
      write(nout,19) aflag(9)
   19 format('0estimation of the absolute error is:',1pe10.2)
    5 continue
      stop
      end

```

sparse-matrix/y12m/ex/maind.f:
```
      parameter (ibdim=1000, iadim=25*ibdim, icdim=20*ibdim)
c     parameter (ibdim=600, iadim=25*ibdim, icdim=20*ibdim)
c     implicit real(a-b,g,p,t-v),integer(c,f,h-n,r-s,z)
c     real a(15000),pivot(600),b(600)
c     integer*2 snr(15000),rnr(12000),ha(600,11)
c     integer snr(15000),rnr(12000),ha(600,11)
c     ibdim=600
c     iadim=25*ibdim
c     icdim=20*ibdim
c     call main0(ibdim,iadim,icdim,a,pivot,b,snr,rnr,ha)
c     stop
c     end
c     subroutine main0(ibdim,iadim,icdim,a,pivot,b,snr,rnr,ha)
c
c     this program tests the subroutine of package y12m by
c     means of matrices of class d(n,c) and/or class e(n,c).
c     the program uses data consisting of 7 integers.
c     the first of these integers (in the program it is de-
c     noted by indexp) should be equal to:
c         1     if the matrices of class d(n,c) has to be used.
c         2     if the matrices of class e(n,c) has to be used.
c         3     if the matrices of both classes has to be used.
c     the next three integers (called nstart,nincr and nend
c     in the program) are used to determine the range of the
c     parameter n. nstart should be larger than 22. nincr
c     should be larger than zero. nend should be larger than
c     or equal to nstart. if these conditions are satisfied
c     then the range of n for the systems solved is:
c            n = nstart(nincr)nend
c     the last three integers (called cstart,cincr and cend
c     in the program) are used in the program to determine
c     the range of parameter c (the sparsity of the matrix
c     is changed by c). cstart should be chosen between 2 and
c     n-13. cincr should be larger than 1. cend should be
c     smaller than n-13. if these conditions are satisfied,
c     then the range of c for the systems solved is given as:
c            c = cstart(cincr)cend
c     in the version which is distributed with the package
c     the data read in line 12 are:
c         3   250  50  600  4  40  204
c     if these data are changed,then the parameter ibdim
c     skould be set equal to or larger than the value for
c     nend.
c     parameter ibdim=600, iadim=25*ibdim, icdim=20*ibdim
      implicit real(a-b,g,p,t-v),integer(c,f,h-n,r-s,z)
      real a(iadim),pivot(ibdim),b(ibdim),aflag(8)
      real time, y12cck
c     integer*2 snr(iadim),rnr(icdim),ha(ibdim,11),iflag(10)
      integer snr(iadim),rnr(icdim),ha(ibdim,11),iflag(10)
      data nin/5/, nout /6/
c
c     call y12cqe('y12mae      ')
      iha=ibdim
      write(nout,50)
50    format(' indexp,nstart,nincr,nend,cstart,cincr,cend:')
      read(nin,*)indexp,nstart,nincr,nend,cstart,cincr,cend
      write(nout,*)indexp,nstart,nincr,nend,cstart,cincr,cend
52    format()
c52    format(10(1x,i5))
      if(indexp.gt.0.and.indexp.lt.4)go to 301
      write(nout,302)
302   format(' indexp is out of range')
      go to 54
301   continue
      if(nstart.gt.21)go to 303
      write(nout,304)
304   format(' nstart is out of range')
      go to 54
303   continue
      if(nincr.ge.1)go to 305
      write(nout,306)
306   format(' nincr is out of range')
      go to 54
305   continue
      if(nend.ge.nstart)go to 307
      write(nout,308)
308   format(' nend is out of range')
      go to 54
307   continue
      if(cstart.gt.1.and.cstart.lt.nend-13)go to 309
      write(nout,310)
310   format(' cstart is out of range')
      go to 54
309   continue
      if(cend.ge.cstart.and.cend.lt.nend-13)go to 311
      write(nout,312)
312   format(' cend is out of range')
      go to 54
311   continue
      if(cincr.gt.0)go to 313
      write(nout,314)
314   format(' cincr is out of range')
      go to 54
313   continue
      n=nstart
      c=cstart
      if(indexp.eq.2)index=2
      if(indexp.eq.1)index=1
      if(indexp.eq.3)index=1
      nn=iadim
      nn1=icdim
   51 continue
      if(n.gt.ibdim) go to 410
      if(index.eq.1)print 41,n,c
   41 format('1','matrix of class d,with parameters n=',i4,'  c=',i4)
      if(index.eq.2)print 33,n,c
   33 format('1matrix of class e: n=',i4,' c='i4)
      if(index.eq.1) call matrd1(n,z,c,nn,nn1,a,snr,rnr)
       if(index.eq.2) call matre1(n,z,c,nn,nn1,a,snr,rnr)
      write(nout,53)z
53    format('0the number of non-zero elements in the original',
     1 ' matrix is equal to:',10x,i8)
      do 1 i=1,n
      b(i)=0.0
1     continue
      do 2 i=1,z
      lrow=rnr(i)
      lcol=snr(i)
2     b(lrow)=b(lrow)+a(i)
      ifail1=0
      time=y12cck(1,ifail1)
      call y12mae(n,z,a,snr,nn,rnr,nn1,pivot,ha,iha,aflag,iflag,b,ifail)
      ifail1=0
      time=-time+y12cck(1,ifail1)
      if(ifail.ne.0)go to 3
      write(nout,110)time
110   format(' subs after y12mae: ',f12.2)
      t=0.0
      do 4 i=1,n
      tt=abs(b(i)-1.)
      if(tt.gt.t)t=tt
4     continue
      write(nout,102) t
102   format('0the largest error is: ',1pe10.2)
3     continue
      write(nout,104)iflag(8)
  104 format('0the largest number of elements in a:',i8)
      write(nout,105)iflag(6)
  105 format('0the number of collections in row list',i4)
      write(nout,106)iflag(7)
  106 format('0the number of collections in column list',i4)
      write(nout,107)aflag(6)
  107 format('0the largest element in original matrix',1pe10.2)
      write(nout,108)aflag(7)
  108 format('0the largest element in lu-matrix',1pe10.2)
      write(nout,109)aflag(5)
  109 format('0the growth factor is:',1pe10.2)
      write(nout,100)aflag(8)
  100 format('0the minimal pivotal element',1pe10.2)
      write(nout,204) aflag(2),aflag(1)
204   format('0the drop tolerance: ',1pe10.2,
     1 ',     the stability factor: ',1pe10.2)
      write(nout,103) ifail
103   format('0the error diagnostic parameter = ',i4)
      c=c+cincr
      if(c.le.cend)go to 51
      c=cstart
      n=n+nincr
      if(n.le.nend)go to 51
      if(indexp.ne.3)go to 54
      c=cstart
      n=nstart
      index=index+1
      if(index.eq.2)go to 51
54    continue
c     call y12cqe('term.       ')
      stop
410   continue
      write(nout,420) ibdim,n
420   format(' maxn = ',i4,' is greater than n = ',i4)
      stop
      end

```

sparse-matrix/y12m/ex/matrf2.f:
```
      subroutine matrf2(m,n,c,index,alpha,nn,nn1,nz,a,snr,rnr,ifejlm)           
c                                                                               
c                                                                               
c   purpose.                                                                    
c   - - - - -                                                                   
c                                                                               
c                                                                               
c   the subroutine generates sparse  (rectangular or square)  matrices.         
c   the dimensions of the matrix and the average number of non-zero             
c   elements per row can be specified by the user.     moreover,                
c   the user can also change the sparsity pattern and the condition num-        
c   ber of the matrix.   the non-zero elements of the desired matrix            
c   will be accumulated  (in an arbitrary order)  in the first   nz             
c   positions of array   a.   the column and the row numbers of the non-        
c   zero element stored in   a(i),   i=1(1)nz,   will be found in               
c   snr(i)   and   rnr(i),   respectively.                                      
c                                                                               
c                                                                               
c                                                                               
c                                                                               
c   input parameters.                                                           
c                                                                               
c                                                                               
c   m       - integer.  the number of the rows in the desired matrix.           
c             n < m+1 < maxint+1    must be specified.                          
c                                                                               
c   n       - integer.  the number of the columns in the desired matrix.        
c             21 < n < maxint+1   must be specified.                            
c                                                                               
c   c       - integer.  the sparsity pattern can be changed by means of         
c             this parameter.   10 < c < n-10   must be specified.              
c                                                                               
c   index   - integer.   the average number of non-zero elements per row        
c             in the matrix will be equal to   index.                           
c             1 < index < n-c-8    must be specified.                           
c                                                                               
c   alpha   - real.   the condition number of the matrix can be changed         
c             by this parameter.   alpha > 0.0   must be specified.             
c             if   alpha   is approximately equal to   1.0   then the           
c             generated matrix is well-conditioned.   large values of           
c             alpha   will usually produce ill-conditioned matrices.            
c             note that no round-off errors during the computations in          
c             this subroutine are made if   alpha = 2**i   (where   i           
c             is an arbitrary integer which produces numbers in the             
c             machine range).                                                   
c                                                                               
c   nn      - integer.   the length of arrays   a   and   snr   (see            
c             below). index*m+109 < nn < maxint+1    must be specified.         
c                                                                               
c   nn1     - integer.   the length of array   rnr   (see below).               
c             index*m+109 < nn1 < maxint+1   must be specified.                 
c                                                                               
c                                                                               
c                                                                               
c                                                                               
c                                                                               
c   output parameters.                                                          
c                                                                               
c                                                                               
c   nz      - integer.   the number of non-zero elements in the matrix.         
c                                                                               
c   a(nn)   - real array. the non-zero elements of the matrix generated         
c             are accumulated in the first   nz   locations of array  a.        
c                                                                               
c   snr(nn) - integer*2 array.   the column number of the non-zero ele-         
c             ment kept in   a(i),   i=1(1)nz,   is stored in   snr(i).         
c                                                                               
c   rnr(nn1)- integer*2 array.   the row number of the non-zero element         
c             kept in   a(i),   i=1(1)nz,   is stored in   rnr(i).              
c                                                                               
c   ifejlm   - integer.   ifejlm=0  indicates that the call is successful.      
c             error diagnostics are given by means of positive values of        
c             this parameter as follows:                                        
c                  ifejlm=1   -   n       is out of range.                      
c                  ifejlm=2   -   m       is out of range.                      
c                  ifejlm=3   -   c       is out of range.                      
c                  ifejlm=4   -   index   is out of range.                      
c                  ifejlm=5   -   nn      is out of range.                      
c                  ifejlm=6   -   nn1     is out of range.                      
c                  ifejlm=7   -   alpha   is out of range.                      
c                                                                               
c                                                                               
c                                                                               
c                                                                               
      real a,alpha,alpha1                                                       
      integer m,n,nz,c,nn,nn1,ifejlm,m1,nz1,rr1,rr2,rr3,k,m2,n1,n2              
c     integer*2 snr,rnr                                                         
      integer snr,rnr                                                           
      dimension a(nn),snr(nn),rnr(nn1)                                          
c     maxint -- the larget integer such that          maxint                    
c     and      -maxint     are representable on the computer                    
c     maxint=32768                                                              
      maxint=int(2.0**31-1.0)                                                   
      m1=m                                                                      
      ifejlm=0                                                                  
      nz1=index*m+110                                                           
      k=1                                                                       
      alpha1=alpha                                                              
      index1=index-1                                                            
c                                                                               
c  check the parameters.                                                        
c                                                                               
      if(n.ge.22)go to 1                                                        
    2 ifejlm=1                                                                  
      return                                                                    
    1 if(n.gt.maxint)go to 2                                                    
      if(m.ge.n)go to 3                                                         
    4 ifejlm=2                                                                  
      return                                                                    
    3 if(m.gt.maxint)go to 4                                                    
      if(c.lt.11)go to 6                                                        
      if(n-c.ge.11)go to 5                                                      
    6 ifejlm=3                                                                  
      return                                                                    
    5 if(index.lt.2)go to 12                                                    
      if(n-c-index.ge.9)go to 13                                                
   12 ifejlm=4                                                                  
   13 if(nn.ge.nz1)go to 7                                                      
    8 ifejlm=5                                                                  
      return                                                                    
    7 if(nn.gt.maxint)go to 8                                                   
      if(nn1.ge.nz1)go to 9                                                     
   10 ifejlm=6                                                                  
      return                                                                    
    9 if(nn1.gt.maxint)go to 10                                                 
      if(alpha.gt.0.)go to 11                                                   
      ifejlm=7                                                                  
      return                                                                    
   11 continue                                                                  
c                                                                               
c  end of the error check.   begin to generate the non-zero elements of         
c  the required matrix.                                                         
c                                                                               
      do 20 i=1,n                                                               
      a(i)=1.                                                                   
      snr(i)=i                                                                  
   20 rnr(i)=i                                                                  
      nz=n                                                                      
      j1=1                                                                      
      do 21 j=1,index1                                                          
      j1=-j1                                                                    
      do 22 i=1,n                                                               
      a(nz+i)=j1*j*i                                                            
      if(i+c+j-1.le.n)snr(nz+i)=i+c+j-1                                         
      if(i+c+j-1.gt.n)snr(nz+i)=c+i+j-1-n                                       
   22 rnr(nz+i)=i                                                               
   21 nz=nz+n                                                                   
      rr1=10                                                                    
      rr2=nz                                                                    
      rr3=1                                                                     
   25 continue                                                                  
      do 26 i=1,rr1                                                             
      a(rr2+i)=alpha*i                                                          
      snr(rr2+i)=n-rr1+i                                                        
      rnr(rr2+i)=rr3                                                            
   26 continue                                                                  
      if(rr1.eq.1)go to 27                                                      
      rr2=rr2+rr1                                                               
      rr1=rr1-1                                                                 
      rr3=rr3+1                                                                 
      go to 25                                                                  
   27 nz=nz+55                                                                  
   29 m1=m1-n                                                                   
      alpha=1./alpha                                                            
      if(m1.le.0)go to 28                                                       
      n2=k*n                                                                    
      if(m1.ge.n)m2=n                                                           
      if(m1.lt.n)m2=m1                                                          
      l=0                                                                       
      if(m2.le.10)l=11-m2                                                       
      do 30 i=1,m2                                                              
      a(nz+i)=alpha*(k+1)                                                       
      snr(nz+i)=i+l                                                             
   30 rnr(nz+i)=n2+i                                                            
      nz=nz+m2                                                                  
      j1=1                                                                      
      do 41 j=1,index1                                                          
      j1=-j1                                                                    
      do 42 i=1,m2                                                              
      a(nz+i)=alpha*j*j1*((k+1)*i+1.)                                           
      if(i+c+j-1.le.n)snr(nz+i)=i+c+j-1                                         
      if(i+c+j-1.gt.n)snr(nz+i)=c+i+j-1-n                                       
   42 rnr(nz+i)=n2+i                                                            
   41 nz=nz+m2                                                                  
      k=k+1                                                                     
      go to 29                                                                  
   28 continue                                                                  
      alpha=1./alpha1                                                           
      rr1=1                                                                     
      rr2=nz                                                                    
   35 continue                                                                  
      do 36 i=1,rr1                                                             
      a(rr2+i)=alpha*(rr1+1-i)                                                  
      snr(rr2+i)=i                                                              
      rnr(rr2+i)=m-10+rr1                                                       
   36 continue                                                                  
      if(rr1.eq.10)go to 34                                                     
      rr2=rr2+rr1                                                               
      rr1=rr1+1                                                                 
      go to 35                                                                  
   34 nz=nz+55                                                                  
      alpha=alpha1                                                              
      return                                                                    
      end                                                                       

```

sparse-matrix/y12m/ex/maine.f:
```
      parameter (ibdim=2000, iadim=25*ibdim, icdim=20*ibdim,
     1 iddim=5*ibdim)
c     parameter (ibdim=600, iadim=25*ibdim, icdim=20*ibdim,
c    1 iddim=5*ibdim)
c     implicit real(a-b,g,p,t-v),integer(c,f,h-n,r-s,z)
c     real a(15000),pivot(600),b(600)
c     real a1(3000),x(600),b1(600)
c     integer*2 snr(15000),rnr(12000),ha(600,13),sn(3000)
c     integer snr(15000),rnr(12000),ha(600,13),sn(3000)
c     ibdim=600
c     iadim=25*ibdim
c     icdim=20*ibdim
c     iddim=5*ibdim
c     call main0(ibdim,iadim,icdim,a,pivot,b,snr,rnr,ha,
c    1 iddim,b1,x,a1,sn)
c     stop
c     end
c     subroutine main0(ibdim,iadim,icdim,a,pivot,b,snr,rnr,ha,
c    1 iddim,b1,x,a1,sn)
c
c     this program tests the subroutine of package y12m by
c     means of matrices of class d(n,c) and/or class e(n,c).
c     the program uses data consisting of 7 integers.
c     the first of these integers (in the program it is de-
c     noted by indexp) should be equal to:
c         1     if the matrices of class d(n,c) has to be used.
c         2     if the matrices of class e(n,c) has to be used.
c         3     if the matrices of both classes has to be used.
c     the next three integers (called nstart,nincr and nend
c     in the program) are used to determine the range of the
c     parameter n. nstart should be larger than 22. nincr
c     should be larger than zero. nend should be larger than
c     or equal to nstart. if these conditions are satisfied
c     then the range of n for the systems solved is:
c            n = nstart(nincr)nend
c     the last three integers (called cstart,cincr and cend
c     in the program) are used in the program to determine
c     the range of parameter c (the sparsity of the matrix
c     is changed by c). cstart should be chosen between 2 and
c     n-13. cincr should be larger than 1. cend should be
c     smaller than n-13. if these conditions are satisfied,
c     then the range of c for the systems solved is given as:
c            c = cstart(cincr)cend
c     in the version which is distributed with the package
c     the data read in line 12 are:
c         3   250  50  600  4  40  204
c     if these data are changed,then the parameter ibdim
c     should be set equal to or larger than the value for
c     nend.
c     the values of aflag(1)-aflag(4),iflag(2)-iflag(5) and iflag(11)
c     are read; see the description of these parameters
c
c     parameter (ibdim=600, iadim=25*ibdim, icdim=20*ibdim,
c    1 iddim=5*ibdim)
      implicit real(a-b,g,p,t-v),integer(c,f,h-n,r-s,z)
      real a(iadim),pivot(ibdim),b(ibdim),aflag(11),
     1 b1(ibdim),x(ibdim),a1(iddim)
      real time, y12cck
c     integer*2 snr(iadim),rnr(icdim),ha(ibdim,13),iflag(12),
      integer snr(iadim),rnr(icdim),ha(ibdim,13),iflag(12),
     1 sn(iddim)
      data nin/5/, nout /6/
c
c     call y12cqe('y12mfe      ')
      iha=ibdim
      write(nout,50)
50    format(' indexp,nstart,nincr,nend,cstart,cincr,cend:')
      read(nin,*)indexp,nstart,nincr,nend,cstart,cincr,cend
      write(nout,*)indexp,nstart,nincr,nend,cstart,cincr,cend
52    format()
c52    format(10(1x,i5))
      write(nout,60)
60    format(' (aflag(j),j=1,4),(iflag(j),j=2,5),iflag(11):')
      read(nin,*)(aflag(j),j=1,4),(iflag(j),j=2,5),iflag(11)
520   format(4(1x,f8.2),5i5)
521   format(4(1x,e8.2),5i5)
      write(nout,521)(aflag(j),j=1,4),(iflag(j),j=2,5),iflag(11)
      if(indexp.gt.0.and.indexp.lt.4)go to 301
      write(nout,302)
302   format(' indexp is out of range')
      go to 54
301   continue
      if(nstart.gt.21)go to 303
      write(nout,304)
304   format(' nstart is out of range')
      go to 54
303   continue
      if(nincr.ge.1)go to 305
      write(nout,306)
306   format(' nincr is out of range')
      go to 54
305   continue
      if(nend.ge.nstart)go to 307
      write(nout,308)
308   format(' nend is out of range')
      go to 54
307   continue
      if(cstart.gt.1.and.cstart.lt.nend-13)go to 309
      write(nout,310)
310   format(' cstart is out of range')
      go to 54
309   continue
      if(cend.ge.cstart.and.cend.lt.nend-13)go to 311
      write(nout,312)
312   format(' cend is out of range')
      go to 54
311   continue
      if(cincr.gt.0)go to 313
      write(nout,314)
314   format(' cincr is out of range')
      go to 54
313   continue
      n=nstart
      c=cstart
      if(indexp.eq.2)index=2
      if(indexp.eq.1)index=1
      if(indexp.eq.3)index=1
      nn=iadim
      nn1=icdim
   51 continue
      if(n.gt.ibdim) go to 410
      if(index.eq.1)print 41,n,c
   41 format('1','matrix of class d,with parameters n=',i4,'  c=',i4)
      if(index.eq.2)print 33,n,c
   33 format('1matrix of class e: n=',i4,' c='i4)
      if(index.eq.1) call matrd1(n,z,c,nn,nn1,a,snr,rnr)
       if(index.eq.2) call matre1(n,z,c,nn,nn1,a,snr,rnr)
      write(nout,53)z
53    format('0the number of non-zero elements in the original',
     1 ' matrix is equal to:',10x,i8)
      do 1 i=1,n
      b(i)=0.0
1     continue
      do 2 i=1,z
      lrow=rnr(i)
      lcol=snr(i)
2     b(lrow)=b(lrow)+a(i)
      ifail1=0
      time=y12cck(1,ifail1)
      call y12mfe(n,a,snr,nn,rnr,nn1,a1,sn,z,ha,iha,b,b1,x,
     1 pivot,aflag,iflag,ifail)
      ifail1=0
      time=-time+y12cck(1,ifail1)
      if(ifail.ne.0)go to 3
      write(nout,110)time
110   format(' subs after y12mfe: ',f12.2)
      t=0.0
      do 4 i=1,n
      tt=abs(x(i)-1.0)
      if(tt.gt.t)t=tt
4     continue
      write(nout,102) t
102   format('0the largest error is: ',1pe10.2)
3     continue
      write(nout,104)iflag(8)
  104 format('0the largest number of elements in a:',i8)
      write(nout,105)iflag(6)
  105 format('0the number of collections in row list',i4)
      write(nout,106)iflag(7)
  106 format('0the number of collections in column list',i4)
      write(nout,107)aflag(6)
  107 format('0the largest element in original matrix',1pe10.2)
      write(nout,108)aflag(7)
  108 format('0the largest element in lu-matrix',1pe10.2)
      write(nout,109)aflag(5)
  109 format('0the growth factor is:',1pe10.2)
      write(nout,100)aflag(8)
  100 format('0the minimal pivotal element',1pe10.2)
      write(nout,204) aflag(2),aflag(1)
204   format('0the drop tolerance: ',1pe10.2,
     1 ',     the stability factor: ',1pe10.2)
      write(nout,103) ifail
103   format('0the error diagnostic parameter = ',i4)
      c=c+cincr
      if(c.le.cend)go to 51
      c=cstart
      n=n+nincr
      if(n.le.nend)go to 51
      if(indexp.ne.3)go to 54
      c=cstart
      n=nstart
      index=index+1
      if(index.eq.2)go to 51
54    continue
c     call y12cqe('term.       ')
      stop
410   continue
      write(nout,420) ibdim,n
420   format(' maxn = ',i4,' is greater than n = ',i4)
      stop
      end

```


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 solution:
src_code: /* ========================================================================== */
/* === UMF_lsolve =========================================================== */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* 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 Lx = b, where L is the lower 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_lsolve
(
    NumericType *Numeric,
    Entry X [ ],		/* b on input, solution x on output */
    Int Pattern [ ]		/* a work array of size n */
)
{
    Int k, deg, *ip, j, row, *Lpos, *Lilen, *Lip, llen, lp, newLchain,
	pos, npiv ;
    Entry *xp, xk ;

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

    if (Numeric->n_row != Numeric->n_col) return (0.) ;
    npiv = Numeric->npiv ;
    Lpos = Numeric->Lpos ;
    Lilen = Numeric->Lilen ;
    Lip = Numeric->Lip ;
    deg = 0 ;

#ifndef NDEBUG
    DEBUG4 (("Lsolve start:\n")) ;
    for (j = 0 ; j < Numeric->n_row ; j++)
    {
	DEBUG4 (("Lsolve start "ID": ", j)) ;
	EDEBUG4 (X [j]) ;
	DEBUG4 (("\n")) ;
    }
#endif

    for (k = 0 ; k < npiv ; k++)
    {

	/* ------------------------------------------------------------------ */
	/* make column of L in Pattern [0..deg-1] */
	/* ------------------------------------------------------------------ */

	lp = Lip [k] ;
	newLchain = (lp < 0) ;
	if (newLchain)
	{
	    lp = -lp ;
	    deg = 0 ;
	    DEBUG4 (("start of chain for column of L\n")) ;
	}

	/* remove pivot row */
	pos = Lpos [k] ;
	if (pos != EMPTY)
	{
	    DEBUG4 (("  k "ID" removing row "ID" at position "ID"\n",
	    k, Pattern [pos], pos)) ;
	    ASSERT (!newLchain) ;
	    ASSERT (deg > 0) ;
	    ASSERT (pos >= 0 && pos < deg) ;
	    ASSERT (Pattern [pos] == k) ;
	    Pattern [pos] = Pattern [--deg] ;
	}

	/* concatenate the pattern */
	ip = (Int *) (Numeric->Memory + lp) ;
	llen = Lilen [k] ;
	for (j = 0 ; j < llen ; j++)
	{
	    row = *ip++ ;
	    DEBUG4 (("  row "ID"  k "ID"\n", row, k)) ;
	    ASSERT (row > k) ;
	    Pattern [deg++] = row ;
	}

	/* ------------------------------------------------------------------ */
	/* use column k of L */
	/* ------------------------------------------------------------------ */

	xk = X [k] ;
	if (IS_NONZERO (xk))
	{
	    xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ;
	    for (j = 0 ; j < deg ; j++)
	    {
		DEBUG4 (("  row "ID"  k "ID" value", Pattern [j], k)) ;
		EDEBUG4 (*xp) ;
		DEBUG4 (("\n")) ;
		/* X [Pattern [j]] -= xk * (*xp) ; */
		MULT_SUB (X [Pattern [j]], xk, *xp) ;
		xp++ ;
	    }
	}
    }

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

    return (MULTSUB_FLOPS * ((double) Numeric->lnz)) ;
}