/*!\page LICENSE LICENSE
 
Copyright (C) 2003 by the Board of Trustees of Massachusetts Institute of
Technology, hereafter designated as the Copyright Owners.
 
License to use, copy, modify, sell and/or distribute this software and
its documentation for any purpose is hereby granted without royalty,
subject to the following terms and conditions:
 
1.  The above copyright notice and this permission notice must
appear in all copies of the software and related documentation.
 
2.  The names of the Copyright Owners may not be used in advertising or
publicity pertaining to distribution of the software without the specific,
prior written permission of the Copyright Owners.
 
3.  THE SOFTWARE IS PROVIDED "AS-IS" AND THE COPYRIGHT OWNERS MAKE NO
REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, BY WAY OF EXAMPLE, BUT NOT
LIMITATION.  THE COPYRIGHT OWNERS MAKE NO REPRESENTATIONS OR WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
SOFTWARE WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS TRADEMARKS OR OTHER
RIGHTS. THE COPYRIGHT OWNERS SHALL NOT BE LIABLE FOR ANY LIABILITY OR DAMAGES
WITH RESPECT TO ANY CLAIM BY LICENSEE OR ANY THIRD PARTY ON ACCOUNT OF, OR
ARISING FROM THE LICENSE, OR ANY SUBLICENSE OR USE OF THE SOFTWARE OR ANY
SERVICE OR SUPPORT.
 
LICENSEE shall indemnify, hold harmless and defend the Copyright Owners and
their trustees, officers, employees, students and agents against any and all
claims arising out of the exercise of any rights under this Agreement,
including, without limiting the generality of the foregoing, against any
damages, losses or liabilities whatsoever with respect to death or injury to
person or damage to property arising from or out of the possession, use, or
operation of Software or Licensed Program(s) by LICENSEE or its customers.
 
*//*
 *  TEST MODULE for the sparse matrix routines
 *
 *  Author:                     Advisor:
 *     Kenneth S. Kundert           Alberto Sangiovanni-Vincentelli
 *     UC Berkeley
 *
 *  This file contains the test routine for the sparse matrix routines.
 *  They are able to read matrices from files and solve them.
 *
 *  >>> Functions contained in this file:
 *  main
 *  ReadMatrixFromFile
 *  PrintMatrixErrorMessage
 *  InterpretCommandLine
 *  GetProgramName
 *  Usage
 */


/*
 *  Revision and copyright information.
 *
 *  Copyright (c) 1985,86,87,88
 *  by Kenneth S. Kundert and the University of California.
 *
 *  Permission to use, copy, modify, and distribute this software and
 *  its documentation for any purpose and without fee is hereby granted,
 *  provided that the copyright notices appear in all copies and
 *  supporting documentation and that the authors and the University of
 *  California are properly credited.  The authors and the University of
 *  California make no representations as to the suitability of this
 *  software for any purpose.  It is provided `as is', without express
 *  or implied warranty.
 */

#ifndef lint
static char copyright[] =
    "Sparse1.3: Copyright (c) 1985,86,87,88 by Kenneth S. Kundert";
static char RCSid[] =
    "@(#)$Header: spTest.c,v 1.3 88/06/24 05:03:16 kundert Exp $";
#endif



/*
 *  IMPORTS
 *
 *  >>> Import descriptions:
 *  stdio.h math.h ctype.h
 *      Standard C libraries.
 *  spConfig.h
 *      Macros that customize the sparse matrix package. It is not normally
 *      necessary, nor is normally particularly desirable to include this
 *      file into the calling routines.  Nor should spINSIDE_SPARSE be defined.
 *      It is done in this test file so that the complex test routines may be
 *      removed when they don't exist in Sparse.
 *  spMatrix.h
 *      Macros and declarations to be imported by the user.
 */

#define  YES    1
#define  NO     0

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#define spINSIDE_SPARSE
#include "spConfig.h"
#undef spINSIDE_SPARSE
#include "spMatrix.h"

/* Declarations that should be in stdio.h. */
extern char *malloc();
#ifdef ultrix
    extern void free();
    extern void exit();
    extern void perror();
#else
    extern free();
    extern exit();
    extern perror();
#endif

/* Declarations that should be in strings.h. */
extern int strcmp(), strncmp(), strlen();

#ifdef lint
#undef  MULTIPLICATION
#undef  DETERMINANT

#define  MULTIPLICATION                 YES
#define  DETERMINANT                    YES

#define LINT    YES
#else /* not lint */
#define LINT    NO
#endif /* not lint */







/*
 *   TIMING
 */

#ifndef vms
#   include <sys/param.h>
#endif
#ifdef notdef  /* __STDC__ */
/* Deleted because some ANSI C compilers to not yet have complete h files. */
#   include <time.h>
#   define HZ   CLK_TCK
#endif
#ifndef HZ
#   ifdef vax
#       ifdef unix
#           define HZ   60
#       endif
#       ifdef vms
#           define HZ   100
#       endif
#   endif
#   ifdef hpux
#       ifdef hp300
#           define HZ   50
#       endif
#       ifdef hp500
#           define HZ   60
#       endif
#   endif
#endif

/* Routine that queries the system to find the process time. */
double
Time()
{
    struct time {long user, system, childuser, childsystem;} time;
    (void)times(&time);
    return (double)time.user / (double)HZ;
}





/*
 *  MACROS
 */

#define  BOOLEAN  int
#define  NOT  !
#define  AND  &&
#define  OR  ||
#define  ALLOC(type,number) ((type *)malloc((unsigned)(sizeof(type)*(number))))
#define  ABS(a)         ((a) < 0.0 ? -(a) : (a))
#ifdef MAX
#undef  MAX
#endif
#ifdef MIN
#undef  MIN
#endif
#define  MAX(a,b)       ((a) > (b) ? (a) : (b))
#define  MIN(a,b)       ((a) < (b) ? (a) : (b))






/*
 *  IMAGINARY VECTORS
 *
 *  The imaginary vectors iRHS and iSolution are only needed when the
 *  options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set.  The following
 *  macro makes it easy to include or exclude these vectors as needed.
 */

#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS
#define IMAG_VECTORS    , iRHS, iSolution
#else
#define IMAG_VECTORS
#endif







/*
 *   GLOBAL DECLARATIONS
 *
 *   These variables, types, and macros are used throughout this file.
 *
 *   >>> Macros
 *   PRINT_LIMIT
 *       The maximum number of terms to be printed form the solution vector.
 *       May be overridden by the -n option.
 *
 *   >>> Variable types:
 *   ElementRecord
 *       A structure for holding data for each matrix element.
 *
 *   >>> Global variables:
 *  ProgramName  (char *)
 *      The name of the test program with any path stripped off.
 *  FileName  (char *)
 *      The name of the current file name.
 *  Complex  (BOOLEAN)
 *      The flag that indicates whether the matrix is complex or real.
 *  Element  (ElementRecord[])
 *      Array used to hold information about all the elements.
 *  Matrix  (char *)
 *      The pointer to the matrix.
 *  Size  (int)
 *      The size of the matrix.
 *  RHS  (RealVector)
 *      The right-hand side vector (b in Ax = b).
 *  Solution  (RealVector)
 *      The solution vector (x in Ax = b).
 *  RHS_Verif  (RealVector)
 *      The calculated RHS vector.
 */

/* Begin global declarations. */
#define  PRINT_LIMIT  9

typedef  spREAL  RealNumber, *RealVector;

typedef  struct
{   RealNumber Real;
    RealNumber Imag;
} ComplexNumber, *ComplexVector;

static char *ProgramName;
static char *FileName;
static int  Size, MaxSize = 0;
static int  PrintLimit = PRINT_LIMIT, Iterations = 1, ColumnAsRHS = 1;
static BOOLEAN  Complex, SolutionOnly = NO, RealAsComplex = NO, Transposed = NO;
static BOOLEAN  CreatePlotFiles = NO, UseColumnAsRHS = NO, PrintLimitSet = NO;
static BOOLEAN ExpansionWarningGiven, DiagPivoting = DIAGONAL_PIVOTING;
static char  Message[BUFSIZ], *Matrix = NULL;
static RealNumber AbsThreshold = (RealNumber)0, RelThreshold = (RealNumber)0;
static RealVector RHS, Solution, RHS_Verif;
static RealVector iRHS, iSolution, iRHS_Verif;
static ComplexVector  cRHS, cSolution, cRHS_Verif;
int Init();









/*
 *  MAIN TEST ROUTINE for sparse matrix routines
 *
 *  This routine reads a matrix from a file and solves it several
 *  times.  The solution is printed along with some statistics.
 *  The format expected for the input matrix is the same as what is output by
 *  spFileMatrix and spFileVector.
 *
 *  >>> Local variables:
 *  Determinant  (RealNumber)
 *      Real portion of the determinant of the matrix.
 *  iDeterminant  (RealNumber)
 *      Imaginary portion of the determinant of the matrix.
 *  Error  (int)
 *      Holds error status.
 *  Last  (int)
 *      The index of the last term to be printed of the solution.
 *  Iterations  (int)
 *      The number of times that the matrix will be factored and solved.
 *  MaxRHS  (RealNumber)
 *      The largest term in the given RHS vector.
 *  Residual  (RealNumber)
 *      The sum of the magnitude of the differences in each corresponding
 *      term of the given and calculated RHS vector.
 *  Exponent  (int)
 *      Exponent for the determinant.
 *  Growth  (RealNumber)
 *      The growth that has occurred in the matrix during the factorization.
 *
 *  >>> Timing variables:
 *  BuildTime  (double)
 *      The time required to build up the matrix including the time to clear
 *      the matrix.
 *  ConditionTime  (double)
 *      The time required to compute the condition number.
 *  DeterminantTime  (double)
 *      The time required to compute the determinant.
 *  FactorTime  (double)
 *      The time required to factor the matrix without ordering.
 *  InitialFactorTime  (double)
 *      The time required to factor the matrix with ordering.
 *  SolveTime  (double)
 *      The time required to perform the forward and backward elimination.
 *  StartTime  (double)
 *      The time that a timing interval was started.
 *
 *  >>> Global variables:
 *  Complex  <used>
 *  Matrix  <set>
 *  Size  <used>
 *  RHS  <used>
 *  iRHS  <used>
 */


main(ArgCount, Args)

int ArgCount;
char *Args[];
{
register  long I;
int  Error, Last, Elements, Fillins;
RealNumber  MaxRHS, Residual;
RealNumber  Determinant, iDeterminant;
int  Exponent;
RealNumber  ConditionNumber, PseudoCondition;
RealNumber  LargestBefore, LargestAfter, Roundoff, InfNorm;
BOOLEAN StandardInput;
char j, PlotFile[BUFSIZ], ErrMsg[BUFSIZ];
double  StartTime, BeginTime, BuildTime, FactorTime, SolveTime, PartitionTime;
double  InitialFactorTime, ConditionTime, DeterminantTime;
extern double  Time();
extern char *sbrk();

/* Begin `main'. */

    BeginTime = Time();
    ArgCount = InterpretCommandLine( ArgCount, Args );

/* Assure that the Sparse is compatible with this test program.*/
#   if NOT EXPANDABLE OR NOT INITIALIZE OR NOT ARRAY_OFFSET
        fprintf(stderr, 
                "%s: Sparse is configured inappropriately for test program.\n",
                ProgramName);
        fprintf(stderr,
      "    Enable EXPANDABLE, INITIALIZE, and ARRAY_OFFSET in `spConfig.h'.\n");
        exit(1);
#   endif

    do
    {
/* Initialization. */
        BuildTime = FactorTime = SolveTime = 0.0;
        ExpansionWarningGiven = NO;
    
/* Create matrix. */
        Matrix = spCreate( 0, spCOMPLEX, &Error );
        if (Matrix == NULL)
        {   fprintf(stderr, "%s: insufficient memory available.\n",
                            ProgramName);
            exit(1);
        }
        if( Error >= spFATAL ) goto End;

/* Read matrix. */
        if (ArgCount == 0)
            FileName = NULL;
        else
            FileName = *(++Args);
        Error = ReadMatrixFromFile();
        if( Error) goto End;
        StandardInput = (FileName == NULL);

/* Clear solution vector if row and column numbers are not densely packed. */
        if (spGetSize(Matrix, YES) != spGetSize(Matrix, NO))
        {   if (Complex OR RealAsComplex)
            {   for (I = Size; I > 0; I--)
                    cSolution[I].Real = cSolution[I].Imag = 0.0;
            }
            else
            {   for (I = Size; I > 0; I--)
                    Solution[I] = 0.0;
            }
        }

/* Perform initial build, factor, and solve. */
        (void)spInitialize(Matrix, Init);

#if MODIFIED_NODAL
        spMNA_Preorder( Matrix );
#endif
#if DOCUMENTATION
        if (CreatePlotFiles)
        {   if (StandardInput)
                (void)sprintf(PlotFile, "bef");
            else
                (void)sprintf(PlotFile, "%s.bef", FileName);
            if (NOT spFileMatrix( Matrix, PlotFile, FileName, NO, NO, NO ))
            {   (void)sprintf(ErrMsg,"%s: plotfile `%s'",ProgramName,PlotFile);
                perror( ErrMsg );
            }
        }
#if NO
        spPrint( Matrix, NO /*reodered*/, NO /*data*/, NO /*header*/ );
#endif
#endif /* DOCUMENTATION */
#if STABILITY
        if (NOT SolutionOnly) LargestBefore = spLargestElement(Matrix);
#endif
#if CONDITION
        if (NOT SolutionOnly) InfNorm = spNorm(Matrix);
#endif

        StartTime = Time();
        Error = spOrderAndFactor( Matrix, RHS, RelThreshold, AbsThreshold,
                                  DiagPivoting );
        InitialFactorTime = Time() - StartTime;
        PrintMatrixErrorMessage( Error );
        if( Error >= spFATAL )
            goto End;

#if DOCUMENTATION
        if (CreatePlotFiles)
        {   if (StandardInput)
                (void)sprintf(PlotFile, "aft");
            else
                (void)sprintf(PlotFile, "%s.aft", FileName);
            if (NOT spFileMatrix( Matrix, PlotFile, FileName, YES, NO, NO ))
            {   (void)sprintf(ErrMsg,"%s: plotfile `%s'",ProgramName,PlotFile);
                perror( ErrMsg );
            }
        }
#if NO
        spFileStats( Matrix, FileName, "stats" );
#endif
#endif /* DOCUMENTATION */

/*
 * IMAG_VECTORS is a macro that replaces itself with `, iRHS, iSolution'
 * if the options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set,
 * otherwise it disappears without a trace.
 */
#if TRANSPOSE
        if (Transposed)
            spSolveTransposed( Matrix, RHS, Solution IMAG_VECTORS );
        else
#endif
            spSolve( Matrix, RHS, Solution IMAG_VECTORS );

        if (SolutionOnly)
            Iterations = 0;
        else
        {
#if STABILITY
            LargestAfter = spLargestElement(Matrix);
            Roundoff = spRoundoff(Matrix, LargestAfter);
#endif
#if CONDITION
            StartTime = Time();
            ConditionNumber = spCondition(Matrix, InfNorm, &Error);
            ConditionTime = Time() - StartTime;
            PrintMatrixErrorMessage( Error );
#endif
#if PSEUDOCONDITION
            PseudoCondition = spPseudoCondition(Matrix);
#endif

            StartTime = Time();
            spPartition( Matrix, spDEFAULT_PARTITION );
            PartitionTime = Time() - StartTime;
        }

/* Solve system of equations Iterations times. */
        for(I = 1; I <= Iterations; I++)
        {   StartTime = Time();
            (void)spInitialize(Matrix, Init);
            BuildTime += Time() - StartTime;

            StartTime = Time();
            Error = spFactor( Matrix );
            FactorTime += Time() - StartTime;
            if( Error != spOKAY ) PrintMatrixErrorMessage( Error );
            if( Error >= spFATAL ) goto End;

            StartTime = Time();
/*
 * IMAG_VECTORS is a macro that replaces itself with `, iRHS, iSolution'
 * if the options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set,
 * otherwise it disappears without a trace.
 */
#if TRANSPOSE
            if (Transposed)
                spSolveTransposed(Matrix, RHS, Solution IMAG_VECTORS );
            else
#endif
                spSolve( Matrix, RHS, Solution IMAG_VECTORS );
            SolveTime += Time() - StartTime;
        }

/* Print Solution. */
        if (SolutionOnly)
        {   if (PrintLimitSet)
                Last = MIN( PrintLimit, Size );
            else
                Last = Size;
            j = ' ';
        }
        else
        {   Last = (PrintLimit > Size) ? Size : PrintLimit;
            if (Last > 0) printf("Solution:\n");
            j = 'j';
        }

        if (Complex OR RealAsComplex)
        {
#if spSEPARATED_COMPLEX_VECTORS
            for (I = 1; I <= Last; I++)
            {   printf("%-16.9lg   %-.9lg%c\n", (double)Solution[I],
                                                (double)iSolution[I], j);
            }
#else
            for (I = 1; I <= Last; I++)
            {   printf("%-16.9lg   %-.9lg%c\n", (double)cSolution[I].Real,
                                                (double)cSolution[I].Imag, j);
            }
#endif
        }
        else
        {   for (I = 1; I <= Last; I++)
                printf("%-.9lg\n", (double)Solution[I]);
        }
        if (Last < Size AND Last != 0)
            printf("Solution list truncated.\n");
        printf("\n");

#if DETERMINANT
/* Calculate determinant. */
        if (NOT SolutionOnly)
        {   StartTime = Time();
#if spCOMPLEX
            spDeterminant( Matrix, &Exponent, &Determinant, &iDeterminant );
#else
            spDeterminant( Matrix, &Exponent, &Determinant );
#endif
            DeterminantTime = Time() - StartTime;
            if (Complex OR RealAsComplex)
            {   Determinant = hypot( Determinant, iDeterminant );
                while (Determinant >= 10.0)
                {   Determinant *= 0.1;
                    Exponent++;
                }
            }
        }
#else
        Determinant = 0.0;   Exponent = 0;
#endif

#if MULTIPLICATION
        if (NOT SolutionOnly)
        {
/* Calculate difference between actual RHS vector and RHS vector
 * calculated from solution. */

/* Find the largest element in the given RHS vector. */
            MaxRHS = 0.0;

            if (Complex OR RealAsComplex)
            {
#if spSEPARATED_COMPLEX_VECTORS
                for (I = 1; I <= Size; I++)
                {   if (ABS(RHS[I]) > MaxRHS)
                        MaxRHS = ABS(RHS[I]);
                    if (ABS(iRHS[I]) > MaxRHS)
                        MaxRHS = ABS(iRHS[I]);
                }
#else
                for (I = 1; I <= Size; I++)
                {   if (ABS(cRHS[I].Real) > MaxRHS)
                        MaxRHS = ABS(cRHS[I].Real);
                    if (ABS(cRHS[I].Imag) > MaxRHS)
                        MaxRHS = ABS(cRHS[I].Imag);
                }
#endif
            }
            else
            {   for (I = 1; I <= Size; I++)
                {   if (ABS(RHS[I]) > MaxRHS)
                        MaxRHS = ABS(RHS[I]);
                }
            }

/* Rebuild matrix. */
            (void)spInitialize(Matrix, Init);
#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS
            if (Transposed)
            {   spMultTransposed( Matrix, RHS_Verif, Solution,
                                          iRHS_Verif, iSolution);
            }
            else spMultiply(Matrix, RHS_Verif, Solution, iRHS_Verif, iSolution);
#else
            if (Transposed)
                spMultTransposed( Matrix, RHS_Verif, Solution );
            else
                spMultiply( Matrix, RHS_Verif, Solution );
#endif

/* Calculate residual. */
            Residual = 0.0;
            if (Complex OR RealAsComplex)
            {
#if spSEPARATED_COMPLEX_VECTORS
                for (I = 1; I <= Size; I++)
                {   Residual += ABS(RHS[I] - RHS_Verif[I])
                                   + ABS(iRHS[I] - iRHS_Verif[I]);
                }
#else
                for (I = 1; I <= Size; I++)
                {   Residual += ABS(cRHS[I].Real - cRHS_Verif[I].Real)
                                   + ABS(cRHS[I].Imag - cRHS_Verif[I].Imag);
                }
#endif
            }
            else
            {   for (I = 1; I <= Size; I++)
                    Residual += ABS(RHS[I] - RHS_Verif[I]);
            }
        }
#endif

/* Print statistics. */
        if (NOT SolutionOnly)
        {   Elements = spElementCount(Matrix);
            Fillins = spFillinCount(Matrix);
            
            printf("Initial factor time = %.2lf.\n", InitialFactorTime);
            printf("Partition time = %.2lf.\n", PartitionTime);
            if (Iterations > 0)
            {   printf("Build time = %.3lf.\n", (BuildTime/Iterations));
                printf("Factor time = %.3lf.\n",(FactorTime/Iterations));
                printf("Solve time = %.3lf.\n", (SolveTime/Iterations));
            }
#if STABILITY
            printf("Condition time = %.2lf.\n", ConditionTime);
#endif
#if DETERMINANT
            printf("Determinant time = %.2lf.\n", DeterminantTime);
#endif
            printf("\nTotal number of elements = %d.\n", Elements);
            printf("Average number of elements per row initially = %.2lf.\n",
                        (double)(Elements - Fillins) /
                        (double)spGetSize(Matrix, NO));
            printf("Total number of fill-ins = %d.\n", Fillins);
#if DETERMINANT OR MULTIPLICATION OR PSEUDOCONDITION OR CONDITION OR STABILITY
            putchar('\n');
#endif
#if STABILITY
            if (LargestBefore != 0.0)
                printf("Growth = %.2lg.\n", LargestAfter / LargestBefore);
            printf("Max error in matrix = %.2lg.\n", Roundoff);
#endif
#if STABILITY
            if(ABS(ConditionNumber) > 10 * SMALLEST_REAL);
                printf("Condition number = %.2lg.\n", 1.0 / ConditionNumber);
#endif
#if CONDITION AND STABILITY
            printf("Estimated upper bound of error in solution = %.2lg.\n",
                    Roundoff / ConditionNumber);
#endif
#if PSEUDOCONDITION
            printf("PseudoCondition = %.2lg.\n", PseudoCondition);
#endif
#if DETERMINANT
            printf("Determinant = %.3lg", (double)Determinant );
            if (Determinant != 0.0 AND Exponent != 0)
                printf("e%d", Exponent);
            putchar('.'); putchar('\n');
#endif
#if MULTIPLICATION
            if (MaxRHS != 0.0)
                printf("Normalized residual = %.2lg.\n", (Residual / MaxRHS));
#endif
        }

End:;
        (void)fflush(stdout);
        CheckInAllComplexArrays();
        spDestroy(Matrix);
        Matrix = NULL;
    } while( --ArgCount > 0 );

    if (NOT SolutionOnly)
    {   printf("\nAggregate resource usage:\n");
        printf("    Time required = %.2lf seconds.\n", Time() - BeginTime);
        printf("    Virtual memory used = %d kBytes.\n\n", ((int)sbrk(0))/1000);
    }
    exit (0);
}











/*
 *   READ MATRIX FROM FILE
 *
 *   This function reads the input file for the matrix and the RHS vector.
 *   If no RHS vector exists, one is created.  If there is an error in the
 *   file, the appropriate error messages are delivered to standard output.
 *
 *   >>> Returned:
 *   The error status is returned.  If no error occurred, a zero is returned.
 *   Otherwise, a one is returned.
 *
 *   >>> Local variables:
 *   pMatrixFile  (FILE *)
 *       The pointer to the file that holds the matrix.
 *   InputString  (char [])
 *       String variable for holding input from the matrix file.
 *   Message  (char [])
 *       String variable that contains a one line descriptor of the matrix.
 *       Descriptor is taken from matrix file.
 *
 *   >>> Global variables:
 *   Complex  <set>
 *   Size  <set>
 *   Element  <set>
 *   RHS  <set>
 *   iRHS  <set>
 */

static int
ReadMatrixFromFile()

{
long I, Reads;
FILE *pMatrixFile;
char  sInput[BUFSIZ], sType[BUFSIZ], *p;
int Error, Row, Col, Count = 0, LineNumber, RHS_Col, IntSize;
double Real, Imag = 0.0;
ComplexNumber *pValue, *pInitInfo, *CheckOutComplexArray();
RealNumber *pElement;
static char *EndOfFile = "%s: unexpected end of file `%s' at line %d.\n";
static char *Syntax = "%s: syntax error in file `%s' at line %d.\n";

/* Begin `ReadMatrixFromFile'. */

/* Open matrix file in read mode. */
    if (NOT SolutionOnly) putchar('\n');

    if (FileName == NULL)
    {   FileName = "standard input";
        pMatrixFile = stdin;
    }
    else
    {   pMatrixFile = fopen(FileName, "r");
        if (pMatrixFile == NULL)
        {   fprintf(stderr, "%s: file %s was not found.\n",
                    ProgramName, FileName);
            return 1;
        }
    }

    Complex = NO;
    LineNumber = 1;

/* Read and print label. */
    if (NULL == fgets( Message, BUFSIZ, pMatrixFile ))
    {   fprintf(stderr, EndOfFile, ProgramName, FileName, LineNumber);
        return 1;
    }

/* For compatibility with the old file syntax. */
    if (!strncmp( Message, "Starting", 8 ))
    {   /* Test for complex matrix. */
        if (strncmp( Message, "Starting complex", 15 ) == 0)
            Complex = YES;
        LineNumber++;
        if (NULL == fgets( Message, BUFSIZ, pMatrixFile ))
        {   fprintf(stderr, EndOfFile, ProgramName, FileName, LineNumber);
            return 1;
        }
    }
    if (NOT SolutionOnly) printf("%-s\n", Message);

/* Read size of matrix and determine type of matrix. */
    LineNumber++;
    if (NULL == fgets( sInput, BUFSIZ, pMatrixFile ))
    {   fprintf(stderr, EndOfFile, ProgramName, FileName, LineNumber);
        return 1;
    }
    if ((Reads = sscanf( sInput,"%d %s", &Size, sType )) < 1)
    {   fprintf(stderr, Syntax, ProgramName, FileName, LineNumber);
        return 1;
    }
    if (Reads == 2)
    {   for (p = sType; *p != '\0'; p++)
            if (isupper(*p)) *p += 'a'-'A';
        if (strncmp( sType, "complex", 7 ) == 0)
            Complex = YES;
        else if (strncmp( sType, "real", 7 ) == 0)
            Complex = NO;
        else
        {   fprintf(stderr, Syntax, ProgramName, FileName, LineNumber);
            return 1;
        }
    }
    EnlargeVectors( Size, YES );
    RHS_Col = MIN( Size, ColumnAsRHS );

#if NOT spCOMPLEX
    if (Complex)
    {   fprintf(stderr,
                    "%s: Sparse is not configured to solve complex matrices.\n",
                    ProgramName);
        fprintf(stderr,"    Enable spCOMPLEX in `spConfig.h'.\n");
        return 1;
    }
#endif
#if NOT REAL
    if (NOT (Complex OR RealAsComplex))
    {   fprintf(stderr,
                    "%s: Sparse is not configured to solve real matrices.\n",
                    ProgramName);
        fprintf(stderr,"    Enable REAL in `spConfig.h'.\n");
        return 1;
    }
#endif

/* Read matrix elements. */
    do
    {   if (Count == 0)
            pValue = CheckOutComplexArray( Count = 1000 );

        LineNumber++;
        if (NULL == fgets( sInput, BUFSIZ, pMatrixFile ))
        {   fprintf(stderr, "%s: unexpected end of file `%s' at line %d.\n",
                    ProgramName, FileName, LineNumber);
            return 1;
        }

        if (Complex)
        {   Reads = sscanf( sInput,"%d%d%lf%lf", &Row, &Col, &Real, &Imag );
            if (Reads != 4)
            {   fprintf(stderr, "%s: syntax error in file `%s' at line %d.\n",
                        ProgramName, FileName, LineNumber);
                return 1;
            }
        }
        else
        {   Reads = sscanf( sInput,"%d%d%lf", &Row, &Col, &Real );
            if (Reads != 3)
            {   fprintf(stderr, "%s: syntax error in file `%s' at line %d.\n",
                        ProgramName, FileName, LineNumber);
                return 1;
            }
        }
        if(Row < 0 OR Col < 0)
        {   fprintf(stderr, "%s: index not positive in file `%s' at line %d.\n",
                        ProgramName, FileName, LineNumber);
            return 1;
        }
        if(Row > Size OR Col > Size)
        {   if (NOT ExpansionWarningGiven)
            {   fprintf( stderr,
         "%s: computed and given matrix size differ in file `%s' at line %d.\n",
                        ProgramName, FileName, LineNumber);
                ExpansionWarningGiven = YES;
            }
            Size = MAX(Row, Col);
            EnlargeVectors( Size, NO );
        }
        pElement = spGetElement( Matrix, Row, Col );
        if (pElement == NULL)
        {   fprintf(stderr, "%s: insufficient memory available.\n",
                            ProgramName);
            exit(1);
        }
        pInitInfo = (ComplexNumber *)spGetInitInfo( pElement );
        if (pInitInfo == NULL)
        {   pValue[--Count].Real = Real;
            pValue[Count].Imag = Imag;
            spInstallInitInfo( pElement, (char *)(pValue + Count) );
        }
        else
        {   pInitInfo->Real += Real;
            pInitInfo->Imag += Imag;
        }

/* Save into RHS vector if in desired column. */
        if (Col == RHS_Col)
        {   if (Complex OR RealAsComplex)
            {
#if spSEPARATED_COMPLEX_VECTORS
                RHS[Row] = Real;
                iRHS[Row] = Imag;
#else
                cRHS[Row].Real = Real;
                cRHS[Row].Imag = Imag;
#endif
            }
            else RHS[Row] = Real;
        }
    } while (Row != 0 AND Col != 0);

    Size = spGetSize( Matrix, YES );
    if (Error = spError( Matrix ) != spOKAY)
    {   PrintMatrixErrorMessage( Error );
        if( Error >= spFATAL ) return 1;
    }

/* Read RHS vector. */
    if (NOT UseColumnAsRHS AND (NULL != fgets( sInput, BUFSIZ, pMatrixFile )))
    {
/* RHS vector exists, read it. */
        LineNumber++;
        for (I = 1; I <= Size; I++)
        {   if (I != 1 OR (strncmp( sInput, "Beginning", 8 ) == 0))
            {   LineNumber++;
                if (NULL == fgets( sInput, BUFSIZ, pMatrixFile ))
                {   fprintf(stderr,
                            "%s: unexpected end of file `%s' at line %d.\n",
                            ProgramName, FileName, LineNumber);
                    return 1;
                }
            }

            if (Complex)
            {
#if spSEPARATED_COMPLEX_VECTORS
                Reads = sscanf( sInput,"%lf%lf", &RHS[I], &iRHS[I] );
#else
                Reads = sscanf( sInput, "%lf%lf", &cRHS[I].Real,
                                                  &cRHS[I].Imag );
#endif
                if (Reads != 2)
                {   fprintf(stderr,
                            "%s: syntax error in file `%s' at line %d.\n",
                            ProgramName, FileName, LineNumber);
                    return 1;
                }
            }
            else /* Not complex. */
            {   Reads = sscanf( sInput, "%lf", &RHS[I] );
                if (Reads != 1)
                {   fprintf(stderr,
                            "%s: syntax error in file `%s' at line %d.\n",
                            ProgramName, FileName, LineNumber);
                    return 1;
                }
            }
        }

        if (RealAsComplex AND NOT Complex)
        {
#if spSEPARATED_COMPLEX_VECTORS
            for (I = 1; I <= Size; I++) iRHS[I] = 0.0;
#else
            for (I = Size; I > 0; I--)
            {   cRHS[I].Real = RHS[I];
                cRHS[I].Imag = 0.0;
            }
#endif
        }
    }

/* Print out the size and the type of the matrix. */
    if (NOT SolutionOnly)
    {   IntSize = spGetSize( Matrix, NO );
        printf("Matrix is %d x %d ", IntSize, IntSize);
        if (IntSize != Size)
            printf("(external size is %d x %d) ", Size, Size);
        if (Complex OR RealAsComplex)
            printf("and complex.\n",Size,Size);
        else
            printf("and real.\n",Size,Size);
    }


    if (Complex OR RealAsComplex)
        spSetComplex( Matrix );
    else
        spSetReal( Matrix );

    (void)fclose(pMatrixFile);
    return 0;
}










/*
 *   INITIALIZE MATRIX ELEMENT
 *
 *   This function copys the InitInfo to the Real and Imag matrix element
 *   values.
 *
 *   >>> Returned:
 *   A zero is returns, signifying that no error can be made.
 */

/*ARGSUSED*/

static int
Init( pElement, pInitInfo, Row, Col)

RealNumber *pElement;
char *pInitInfo;
int Row, Col;
{
/* Begin `Init'. */
    *pElement = *(RealNumber *)pInitInfo;
    if (Complex OR RealAsComplex)
        *(pElement+1) = *((RealNumber *)pInitInfo+1);
    return 0;
}








/*
 *  COMPLEX ARRAY ALLOCATION
 *
 *  These routines are used to check out and in arrays of complex numbers.
 */

static struct Bin {
    ComplexVector   Array;
    struct Bin     *Next;
}  *FirstArray, *CurrentArray = NULL;


static ComplexVector
CheckOutComplexArray( Count )

int Count;
{
struct Bin Bin, *pBin;
ComplexVector Temp;

/* Begin `CheckOutComplexArray'. */

    if (CurrentArray == NULL OR CurrentArray->Next == NULL)
    {   pBin = ALLOC( struct Bin, 1);
        Temp = ALLOC( ComplexNumber, Count );
        if (pBin == NULL OR Temp == NULL)
        {   fprintf( stderr, "%s: insufficient memory available.\n",
                             ProgramName);
            exit(1);
        }
        Bin.Array = Temp;
        Bin.Next = NULL;
        *pBin = Bin;
        if (CurrentArray == NULL)
            FirstArray = CurrentArray = pBin;
        else
        {   CurrentArray->Next = pBin;
            CurrentArray = pBin;
        }
    }
    else
    {   pBin = CurrentArray;
        CurrentArray = pBin->Next;
    }

    return pBin->Array;
}


static CheckInAllComplexArrays()
{

/* Begin `CheckInAllComplexArrays'. */
    if (CurrentArray != NULL)
        CurrentArray = FirstArray;
    return;
}






/*
 *  PRINT ERROR MESSAGE
 *
 *  Prints error message for matrix routines if one is required.
 *
 *  >>> Argument:
 *  Error  <input>  (int)
 *      The error status of the matrix routines.
 */

static
PrintMatrixErrorMessage( Error )

int  Error;
{
int Row, Col;

/* Begin `PrintMatrixErrorMessage'. */

    if (Error == spOKAY)
        return;
    if (Error >= spFATAL)
    {   fprintf(stderr, "%s: fatal error detected in file `%s'.\n",
                ProgramName, FileName );
    }
    else fprintf(stderr, "%s: warning in `%s'.\n", ProgramName, FileName );

    if (Error == spNO_MEMORY)
        fprintf(stderr, "    Insufficient memory available.\n");
    else if (Error == spSINGULAR)
    {   spWhereSingular( Matrix, &Row, &Col );
        printf("    Singular matrix (detected at row %d, column %d).\n",
                Row, Col );
    }
    else if (Error == spZERO_DIAG)
    {   spWhereSingular( Matrix, &Row, &Col );
        printf("    Zero diagonal detected at row %d, column %d.\n", Row, Col );
    }
    else if (Error == spPANIC)
        fprintf(stderr, "    Matrix routines being used improperly.\n");
    else if (Error == spSMALL_PIVOT)
        fprintf(stderr, "    A pivot was chosen that is smaller than the absolute threshold.\n");
    return;
}








/*
 *  INTERPRET THE COMMAND LINE ARGUMENTS
 */

static int
InterpretCommandLine( ArgCount, Args )

int ArgCount;
char *Args[];
{
int I, FileCount = 0;
char *GetProgramName();

/* Begin `InterpretCommandLine'. */

/* Determine the name of the program. */
    ProgramName = GetProgramName(Args[0]);

/* Step through the argument list, interpreting and deleting the options. */
    for (I = 1; I < ArgCount; I++)
    {   if (!strcmp(Args[I], "-a"))
        {   if (ArgCount == I OR (sscanf(Args[I+1],"%lf", &AbsThreshold) != 1))
            {   AbsThreshold = 0.0;
                Usage(Args[I]);
            }
            else I++;
        }
        else if (!strcmp(Args[I], "-r"))
        {   if (ArgCount == I OR (sscanf(Args[I+1],"%lf", &RelThreshold) != 1))
            {   RelThreshold = 0.0;
                Usage(Args[I]);
            }
            else I++;
        }
        else if (!strcmp(Args[I], "-x"))
        {
#if spCOMPLEX
            RealAsComplex = YES;
#else
            fprintf(stderr,
                    "%s: Sparse is not configured to solve complex matrices.\n",
                    ProgramName);
            fprintf(stderr,"    Enable spCOMPLEX in `spConfig.h'.\n");
#endif
        }
        else if (!strcmp(Args[I], "-s"))
            SolutionOnly = YES;
        else if (!strcmp(Args[I], "-c"))
            DiagPivoting = NO;
        else if (!strcmp(Args[I], "-t"))
        {
#if TRANSPOSE
            Transposed = YES;
#else
            fprintf(stderr,
                   "%s: Sparse is not configured to solve transposed system.\n",
                    ProgramName);
            fprintf(stderr,"    Enable TRANSPOSE in `spConfig.h'.\n");
#endif
        }
        else if (!strcmp(Args[I], "-n"))
        {   if (ArgCount == I OR (sscanf(Args[I+1],"%ld", &PrintLimit) != 1))
            {   PrintLimit = PRINT_LIMIT;
                Usage(Args[I]);
            }
            else
            {   PrintLimitSet = YES;
                I++;
            }
        }
        else if (!strcmp(Args[I], "-i"))
        {   if (ArgCount == I OR (sscanf(Args[I+1],"%ld", &Iterations) != 1))
            {   Iterations = 1;
                Usage(Args[I]);
            }
            else I++;
        }
        else if (!strcmp(Args[I], "-b"))
        {   if (ArgCount == I OR (sscanf(Args[I+1],"%ld", &ColumnAsRHS) != 1))
            {   ColumnAsRHS = 1;
                UseColumnAsRHS = YES;
            }
            else
            {   UseColumnAsRHS = YES;
                ColumnAsRHS = MAX( ColumnAsRHS, 1 );
                I++;
            }
        }
        else if (!strcmp(Args[I], "-p"))
        {
#if DOCUMENTATION
            CreatePlotFiles = YES;
#else
            fprintf(stderr,
                    "%s: Sparse is not configured to generate plot files.\n",
                    ProgramName);
            fprintf(stderr,"    Enable DOCUMENTATION in `spConfig.h'.\n");
#endif
        }
        else if (!strcmp(Args[I], "-u"))
            Usage( (char *)NULL );
        else if (Args[I][0] == '-')
            Usage(Args[I]);
        else Args[++FileCount] = Args[I];
    }

    return FileCount;
}







/*
 *  PROGRAM NAME
 *
 *  Removes path from argv[0] and returns program name.
 *  Assumes UNIX style path names.
 */

static char *
GetProgramName( Arg0 )

char *Arg0;
{
char *pTail;
/* Begin `GetProgramName'. */

    pTail = Arg0 + strlen(Arg0)-1;
    while (pTail != Arg0 AND *(pTail-1) != '/')
        --pTail;
    return pTail;
}





/*
 *  USAGE WARNING
 *
 *  Sends a warning to the user when the command line syntax is wrong.
 */

static
Usage( sBadOpt )

char *sBadOpt;
{
/* Begin `Usage'. */

    if (sBadOpt != NULL)
    {   fprintf(stderr, "%s: unknown or deformed option `%s'.\n",
                ProgramName, sBadOpt);
    }

    fprintf(stderr, "\nUsage: %s [options] [file names]\n\n", ProgramName);
    fprintf(stderr, "Options:\n");
    fprintf(stderr,
    "    -s      Print solution rather than run statistics.\n");
    fprintf( stderr, "    -r x    Use x as relative threshold.\n");
    fprintf( stderr, "    -a x    Use x as absolute threshold.\n");
    fprintf( stderr, "    -n n    Print first n terms of solution vector.\n");
    fprintf( stderr, "    -i n    Repeat build/factor/solve n times.\n");
    fprintf( stderr, "    -b n    Use n'th column of matrix as b in Ax=b.\n");
#if DIAGONAL_PIVOTING
    fprintf( stderr,
    "    -c      Use complete (as opposed to diagonal) pivoting.\n");
#endif
#if DOCUMENTATION
    fprintf( stderr,
    "    -p      Create plot files `filename.bef' and `filename.aft'.\n");
#endif
#if spCOMPLEX
    fprintf( stderr,
"    -x      Treat real matrix as complex with imaginary part zero.\n");
#endif
#if TRANSPOSE
    fprintf( stderr, "    -t      Solve transposed system.\n");
#endif
    fprintf( stderr, "    -u      Print usage message.\n");
    exit(1);
}






/*
 *  ENLARGE VECTORS
 *
 *  Allocate or enlarge vectors.
 */

static
EnlargeVectors( NewSize, Clear )

int NewSize, Clear;
{
int I, PrevSize = MaxSize;
RealVector OldRHS = RHS, iOldRHS = iRHS;
ComplexVector cOldRHS = cRHS;
#if spCOMPLEX
#   define SCALE 2
#else
#   define SCALE 1
#endif

/* Begin `EnlargeVectors'. */
    if (NewSize > PrevSize)
    {   if (MaxSize != 0)
        {   free( (char *)Solution );
            free( (char *)RHS_Verif );
        }
        RHS = ALLOC( RealNumber, SCALE*(NewSize+1) );
        Solution = ALLOC( RealNumber, SCALE*(NewSize+1) );
        RHS_Verif = ALLOC( RealNumber, SCALE*(NewSize+1) );
        if (NOT RHS OR NOT Solution OR NOT RHS_Verif)
        {   fprintf(stderr, "%s: insufficient memory available.\n",
                            ProgramName);
            exit(1);
        }
        cRHS = (ComplexVector)RHS;
        cSolution = (ComplexVector)Solution;
        cRHS_Verif = (ComplexVector)RHS_Verif;
        iRHS = RHS + NewSize + 1;
        iSolution = Solution + NewSize + 1;
        iRHS_Verif = RHS_Verif + NewSize + 1;

/* Copy data from old RHS to new RHS. */
        if (NOT Clear)
        {
/* Copy old RHS vector to new. */
            if (Complex OR RealAsComplex)
            {   for (I = PrevSize; I > 0; I--)
                {
#if spSEPARATED_COMPLEX_VECTORS OR LINT
                    RHS[I] = OldRHS[I];
                    iRHS[I] = iOldRHS[I];
#endif
#if spSEPARATED_COMPLEX_VECTORS OR LINT
                    cRHS[I] = cOldRHS[I];
#endif
                }
            }
            else
            {   for (I = PrevSize; I > 0; I--)
                    RHS[I] = OldRHS[I];
            }
        }
        if (MaxSize != 0) free( (char *)OldRHS );
        MaxSize = NewSize;
    }

/* Either completely clear or clear remaining portion of RHS vector. */
    if ((NewSize > PrevSize) OR Clear)
    {   if (Clear)
        {   NewSize = MAX( NewSize, PrevSize );
            PrevSize = 0;
        }

        if (Complex OR RealAsComplex)
        {   for (I = NewSize; I > PrevSize; I--)
            {
#if spSEPARATED_COMPLEX_VECTORS OR LINT
                RHS[I] = 0.0;
                iRHS[I] = 0.0;
#endif
#if NOT spSEPARATED_COMPLEX_VECTORS OR LINT
                cRHS[I].Real = 0.0;
                cRHS[I].Imag = 0.0;
#endif
            }
        }
        else
        {   for (I = NewSize; I > PrevSize; I--)
                RHS[I] = 0.0;
        }
    }

    return;
}

