#!/bin/sh # This is a shell archive (produced by GNU sharutils 4.7). # To extract the files from this archive, save it to some FILE, remove # everything before the `#!/bin/sh' line above, then type `sh FILE'. # lock_dir=_sh09884 # Made on 2016-10-12 10:46 PDT by . # Source directory was `/nfs/guille/ams/users/karti/ece521/c-code/hw2'. # # Existing files will *not* be overwritten, unless `-c' is specified. # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 311 -rw------- Makefile # 3572 -rw------- main.c # 1843 -rw------- res.c # 1864 -rw------- utils.c # 658 -rw------- res.h # 1025 -rw------- sparse/Makefile # 148 -rw------- sparse/mat0 # 1182 -rw------- sparse/mat1 # 560 -rw------- sparse/mat2 # 1080 -rw------- sparse/mat3 # 100 -rw------- sparse/mat5 # 1634 -rw------- sparse/smpl-test.c # 21591 -rw------- sparse/spAllocate.c # 32504 -rw------- sparse/spBuild.c # 20494 -rw------- sparse/spConfig.h # 34667 -rw------- sparse/spDefs.h # 257781 -rw------- sparse/spDoc.pdf # 98805 -rw------- sparse/spFactor.c # 56802 -rw------- sparse/spFortran.c # 1474 -rw------- sparse/spLicense # 12377 -rw------- sparse/spMatrix.h # 22792 -rw------- sparse/spOutput.c # 3994 -rw------- sparse/spRevision # 7999 -rw------- sparse/spSMP.c # 22041 -rw------- sparse/spSolve.c # 12073 -rw------- sparse/spSpice3.h # 38455 -rw------- sparse/spTest.c # 68909 -rw------- sparse/spUtils.c # MD5SUM=${MD5SUM-md5sum} f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'` test -n "${f}" && md5check=true || md5check=false ${md5check} || \ echo 'Note: not verifying md5sums. Consider installing GNU coreutils.' save_IFS="${IFS}" IFS="${IFS}:" gettext_dir=FAILED locale_dir=FAILED first_param="$1" for dir in $PATH do if test "$gettext_dir" = FAILED && test -f $dir/gettext \ && ($dir/gettext --version >/dev/null 2>&1) then case `$dir/gettext --version 2>&1 | sed 1q` in *GNU*) gettext_dir=$dir ;; esac fi if test "$locale_dir" = FAILED && test -f $dir/shar \ && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) then locale_dir=`$dir/shar --print-text-domain-dir` fi done IFS="$save_IFS" if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED then echo=echo else TEXTDOMAINDIR=$locale_dir export TEXTDOMAINDIR TEXTDOMAIN=sharutils export TEXTDOMAIN echo="$gettext_dir/gettext -s" fi if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null then if (echo -n test; echo 1,2,3) | grep n >/dev/null then shar_n= shar_c=' ' else shar_n=-n shar_c= ; fi else shar_n= shar_c='\c' ; fi f=shar-touch.$$ st1=200112312359.59 st2=123123592001.59 st2tr=123123592001.5 # old SysV 14-char limit st3=1231235901 if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \ test ! -f ${st1} && test -f ${f}; then shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"' elif touch -am ${st2} ${f} >/dev/null 2>&1 && \ test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"' elif touch -am ${st3} ${f} >/dev/null 2>&1 && \ test ! -f ${st3} && test -f ${f}; then shar_touch='touch -am $3$4$5$6$2 "$8"' else shar_touch=: echo ${echo} 'WARNING: not restoring timestamps. Consider getting and' ${echo} 'installing GNU `touch'\'', distributed in GNU coreutils...' echo fi rm -f ${st1} ${st2} ${st2tr} ${st3} ${f} # if test ! -d ${lock_dir} then : ; else ${echo} 'lock directory '${lock_dir}' exists' exit 1 fi if mkdir ${lock_dir} then ${echo} 'x - created lock directory `'${lock_dir}\''.' else ${echo} 'x - failed to create lock directory `'${lock_dir}\''.' exit 1 fi # ============= Makefile ============== if test -f 'Makefile' && test "$first_param" != -c; then ${echo} 'x -SKIPPING Makefile (file already exists)' else ${echo} 'x - extracting Makefile (text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile' && CC = gcc CFLAGS= -g CSRC= main.c utils.c res.c isrc.c vsrc.c vcvs.c vccs.c ccvs.c cccs.c dio.c COBJ= main.o utils.o res.o isrc.o vsrc.o vcvs.o vccs.o ccvs.o cccs.o dio.o X X.c.o: ${CSRC} X ${CC} ${CFLAGS} -c $*.c myspice: ${COBJ} X ${CC} ${CFLAGS} -o myspice ${COBJ} sparse/sparse.a -lm clean: X rm myspice ${COBJ} SHAR_EOF (set 20 05 04 12 10 57 36 'Makefile'; eval "$shar_touch") && chmod 0600 'Makefile' if test $? -ne 0 then ${echo} 'restore of Makefile failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'Makefile: MD5 check failed' ) << \SHAR_EOF 831b23a675b06980825ae112ef219783 Makefile SHAR_EOF else test `LC_ALL=C wc -c < 'Makefile'` -ne 311 && \ ${echo} 'restoration warning: size of Makefile is not 311' fi fi # ============= main.c ============== if test -f 'main.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING main.c (file already exists)' else ${echo} 'x - extracting main.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'main.c' && #include #include "macros.h" #include "defs.h" #include "res.h" #include "isrc.h" #include "vsrc.h" #include "vcvs.h" #include "cccs.h" #include "vccs.h" #include "ccvs.h" #include "dio.h" #include "sparse/spMatrix.h" X int NumNodes = 0; int NumBranches = 0; char **NodeArray; char **BranchArray; X main(ac, av) char **av; { X char *inFile; X FILE *fopen(), *fpIn; X char buf[MAXLINE]; X resistor *Res[MAXELEM]; X isource *Isrc[MAXELEM]; X vsource *Vsrc[MAXELEM]; X vcvs *Esrc[MAXELEM]; X cccs *Fsrc[MAXELEM]; X vccs *Gsrc[MAXELEM]; X ccvs *Hsrc[MAXELEM]; X diode *Dio[MAXELEM]; X int i = 0; X int j = 0; X int numRes = 0; X int numIsrc = 0; X int numVsrc = 0; X int numEsrc = 0; X int numFsrc = 0; X int numGsrc = 0; X int numHsrc = 0; X int numDio = 0; X int numEqns; X char *cktMatrix; X double *Rhs, *Sol; X BOOLEAN foundError(); X int error; X X switch (ac) { X case 2: X inFile = av[1]; X break; X default: X printf( "\n File Name Required \n"); X exit(-1); X } X /* initialization */ X NodeArray = CALLOC(char *, MAXNODE); X BranchArray = CALLOC(char *, MAXBRANCH); X for(i = 0; i < MAXNODE; i++) { X NodeArray[i] = CALLOC(char, MAXFIELD); X } X for(i = 0; i < MAXBRANCH; i++ ) { X BranchArray[i] = CALLOC(char, MAXFIELD) ; X } X /* fill in ground node */ X strcpy(NodeArray[0], (char *)"0"); X X X fpIn = fopen( inFile, "r" ); X while (fgets( buf, MAXLINE, fpIn ) != NULL) { X if(tolower(buf[0]) == 'r') X { X /* resistor */ X numRes++; X makeRes(Res, numRes, buf); X } X else if(tolower(buf[0]) == 'i') X { X /* isource */ X numIsrc++; X makeIsrc(Isrc, numIsrc, buf); X } X else if(tolower(buf[0]) == 'v') X { X /* vsource */ X numVsrc++; X makeVsrc(Vsrc, numVsrc, buf); X } X else if(tolower(buf[0]) == 'e') X { X /* vcvs */ X numEsrc++; X makeEsrc(Esrc, numEsrc, buf); X } X else if(tolower(buf[0]) == 'f') X { X /* cccs */ X numFsrc++; X makeFsrc(Fsrc, numFsrc, buf); X } X else if(tolower(buf[0]) == 'g') X { X /* vccs */ X numGsrc++; X makeGsrc(Gsrc, numGsrc, buf); X } X else if(tolower(buf[0]) == 'h') X { X /* ccvs */ X numHsrc++; X makeHsrc(Hsrc, numHsrc, buf); X } X else if(tolower(buf[0]) == 'd') X { X /* diode */ X numDio++; X makeDio(Dio, numDio, buf); X } X } X fclose( fpIn ); X X /* print circuit elements */ X printRes(Res, numRes); X printIsrc(Isrc, numIsrc); X printVsrc(Vsrc, numVsrc); X printEsrc(Esrc, numEsrc); X printFsrc(Fsrc, numFsrc); X printGsrc(Gsrc, numGsrc); X printHsrc(Hsrc, numHsrc); X printDio(Dio, numDio); X X /* setup circuit matrix */ X numEqns = NumNodes+NumBranches; X cktMatrix = spCreate( numEqns, 0, &error ); X if( error IS spNO_MEMORY ) { X printf( "\n: --- NO MEMORY ---" ); X exit( -1 ); X } X /* Allocate RHS and Solution vectors */ X Rhs = CALLOC(double, numEqns+1); X Sol = CALLOC(double, numEqns+1); X X /* do any preprocessing */ X setupRes(cktMatrix, Res, numRes); X X /* load circuit matrix */ X loadRes(cktMatrix, Rhs, Res, numRes); X X /* print circuit matrix */ X printf("\n"); X spPrint(cktMatrix, 0, 1, 0); X X /* compute DC solution */ X /* first Factor the matrix and then Forward/Back solve */ X error = spFactor( cktMatrix ); X if( foundError( error ) ) { X exit( -1 ); X } X spSolve( cktMatrix, Rhs, Sol ); X X /* print solution */ X printf("Solution\n"); X for(i = 1; i<= numEqns; i++) { X printf("X[%d] = %g\n", i, Sol[i]); X } X } SHAR_EOF (set 20 05 04 12 14 34 57 'main.c'; eval "$shar_touch") && chmod 0600 'main.c' if test $? -ne 0 then ${echo} 'restore of main.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'main.c: MD5 check failed' ) << \SHAR_EOF 0915ea2c42d0121464f3c4e42fa2db93 main.c SHAR_EOF else test `LC_ALL=C wc -c < 'main.c'` -ne 3572 && \ ${echo} 'restoration warning: size of main.c is not 3572' fi fi # ============= res.c ============== if test -f 'res.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING res.c (file already exists)' else ${echo} 'x - extracting res.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'res.c' && #include "macros.h" #include "defs.h" #include "res.h" #include "sparse/spMatrix.h" X void makeRes(Res, numRes, buf) resistor *Res[]; int numRes; char *buf; { X resistor *inst; X int j, nodeA, nodeB, atoi(); X char name[MAXFIELD], node[MAXFIELD], num[MAXFIELD]; X double value, atof(); X X j = 0; X j = getNextField(buf, name, j); X j = getNextField(buf, node, j); X nodeA = getMappedNode(node); X j = getNextField(buf, node, j); X nodeB = getMappedNode(node); X j = getNextField(buf, num, j); X value = atof(num); X X inst = CALLOC(resistor, 1); X inst->name = (char *)strdup(name); X inst->pNode = nodeA; X inst->nNode = nodeB; X inst->value = value; X Res[numRes] = inst; } X void printRes(Res, numRes) resistor *Res[]; int numRes; { X int i; X resistor *inst; X X for(i = 1; i <= numRes; i++) { X inst = Res[i]; X printf("%s\t%s\t%s\t%f\n", inst->name, NodeArray[inst->pNode], NodeArray[inst->nNode], inst->value); X } } X void setupRes(Matrix, Res, numRes) char *Matrix; resistor *Res[]; int numRes; { X int i, n1, n2; X resistor *inst; X X /* do any preprocessing steps here */ X for(i = 1; i <= numRes; i++) { X inst = Res[i]; X inst->conduct = 1.0/inst->value; X n1 = inst->pNode; X n2 = inst->nNode; X /* setup matrix and pointers */ X inst->pn1n1 = spGetElement(Matrix, n1, n1); X inst->pn1n2 = spGetElement(Matrix, n1, n2); X inst->pn2n2 = spGetElement(Matrix, n2, n2); X inst->pn2n1 = spGetElement(Matrix, n2, n1); X } } X void loadRes(Matrix, Rhs, Res, numRes) char *Matrix; double *Rhs; resistor *Res[]; int numRes; { X int i; X resistor *inst; X double conduct; X X /* load matrix */ X for(i = 1; i <= numRes; i++) { X inst = Res[i]; X conduct = inst->conduct; X *(inst->pn1n1) += conduct; X *(inst->pn1n2) -= conduct; X *(inst->pn2n2) += conduct; X *(inst->pn2n1) -= conduct; X } } SHAR_EOF (set 20 05 04 12 10 47 16 'res.c'; eval "$shar_touch") && chmod 0600 'res.c' if test $? -ne 0 then ${echo} 'restore of res.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'res.c: MD5 check failed' ) << \SHAR_EOF bd7f7b601f26f836145114f28f59e450 res.c SHAR_EOF else test `LC_ALL=C wc -c < 'res.c'` -ne 1843 && \ ${echo} 'restoration warning: size of res.c is not 1843' fi fi # ============= utils.c ============== if test -f 'utils.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING utils.c (file already exists)' else ${echo} 'x - extracting utils.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'utils.c' && X #include #include #include "macros.h" #include "defs.h" #include "sparse/spMatrix.h" X int getNextField(input, field, start) char *input, *field; int start; { X int i, j; X for(i=0; i < MAXFIELD; i++, j++) { X field[i] = '\0'; X } X for(i = 0, j = start; i < MAXFIELD; i++, j++) { X if (input[j] == ' ' OR input[j] == '\n') break; X field[i] = input[j]; X } X /* strip trailing blanks */ X while( input[j] == ' ') j++; X return( j ); } X /* map a nodename to an integer */ int getMappedNode(nodeName) char *nodeName; { X int i; X for(i = 0; i <= NumNodes; i++) { X if(!strcmp(NodeArray[i], nodeName)) return( i ); X } X /* node doesn't exist in NodeArray - so insert */ X NumNodes++; X strcpy(NodeArray[NumNodes], nodeName); X return(NumNodes); } X /* map a branch name to an integer */ int getMappedBranch(branchName) char *branchName; { X int i; X for(i = 0; i <= NumBranches; i++) { X if(!strcmp(BranchArray[i], branchName)) return( i ); X } X /* branch doesn't exist in BranchArray - so insert */ X NumBranches++; X strcpy(BranchArray[NumBranches], branchName); X return(NumBranches); } X BOOLEAN foundError( error ) int error; { X BOOLEAN matrixError; X switch( error ) { X case spSMALL_PIVOT: X printf( "\n *****Error in Decomp: SMALL_PIVOT" ); X matrixError = TRUE; X break; X case spPANIC: X printf( "\n *****Error in Decomp: RANGE" ); X matrixError = TRUE; X break; X case spSINGULAR: X printf( "\n *****Error in Decomp: SINGULAR" ); X matrixError = TRUE; X break; X case spZERO_DIAG: X printf( "\n *****Error in Decomp: ZERO PIVOT" ); X matrixError = TRUE; X break; X case spNO_MEMORY: X printf( "\n *****Error in Decomp: NO_MEMORY" ); X matrixError = TRUE; X break; X default: X matrixError = FALSE; X break; X } X return( matrixError ); } SHAR_EOF (set 20 05 04 12 10 59 37 'utils.c'; eval "$shar_touch") && chmod 0600 'utils.c' if test $? -ne 0 then ${echo} 'restore of utils.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'utils.c: MD5 check failed' ) << \SHAR_EOF 3f56461f1d01834c101fd524c764e0f2 utils.c SHAR_EOF else test `LC_ALL=C wc -c < 'utils.c'` -ne 1864 && \ ${echo} 'restoration warning: size of utils.c is not 1864' fi fi # ============= res.h ============== if test -f 'res.h' && test "$first_param" != -c; then ${echo} 'x -SKIPPING res.h (file already exists)' else ${echo} 'x - extracting res.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'res.h' && /* information used to describe a single instance */ X typedef struct resistor{ X char *name; /* pointer to character string naming this instance */ X int pNode; /* number of positive node of resistor */ X int nNode; /* number of negative node of resistor */ X X double value; /* resistance */ X double conduct; /* conductance */ X double *pn1n1; /*pointer to sparse-matrix location (pNode, pNode)*/ X double *pn1n2; /*pointer to sparse-matrix location (pNode, nNode)*/ X double *pn2n2; /*pointer to sparse-matrix location (nNode, nNode)*/ X double *pn2n1; /*pointer to sparse-matrix location (nNode, pNode)*/ } resistor ; SHAR_EOF (set 20 05 04 12 09 59 45 'res.h'; eval "$shar_touch") && chmod 0600 'res.h' if test $? -ne 0 then ${echo} 'restore of res.h failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'res.h: MD5 check failed' ) << \SHAR_EOF d28c1575a510adb1aecb480deb27a567 res.h SHAR_EOF else test `LC_ALL=C wc -c < 'res.h'` -ne 658 && \ ${echo} 'restoration warning: size of res.h is not 658' fi fi # ============= sparse/Makefile ============== if test ! -d 'sparse'; then mkdir 'sparse' if test $? -eq 0 then ${echo} 'x - created directory `sparse'\''.' else ${echo} 'x - failed to create directory `sparse'\''.' exit 1 fi fi if test -f 'sparse/Makefile' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/Makefile (file already exists)' else ${echo} 'x - extracting sparse/Makefile (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/Makefile' && # Makefile for Sparse1.4 # # Ken Kundert # kundert@users.sourceforge.net # # # To run tests ... # On Linux machines run: make # On Unix machines run: gnumake X CFLAGS = -O -std=c89 LINTFLAGS = -lc -lm SHELL = /bin/sh CC = gcc X HFILES = spConfig.h spDefs.h spMatrix.h CFILES = spAllocate.c spBuild.c spFactor.c spOutput.c spSolve.c spUtils.c \ X spFortran.c OFILES = spAllocate.o spBuild.o spFactor.o spOutput.o spSolve.o spUtils.o \ X spFortran.o LIBRARY = sparse.a DESTINATION = sparse TESTC = spTest.c TESTO = spTest.o X SOURCE = $(HFILES) $(CFILES) X $(DESTINATION) : $(LIBRARY) $(TESTO) X $(CC) $(CFLAGS) -o $(DESTINATION) $(TESTO) $(LIBRARY) -lm X $(LIBRARY) : $(OFILES) X ar r $(LIBRARY) $? X ranlib $(LIBRARY) X lint : X @lint $(LINTFLAGS) $(CFILES) $(TESTC) | grep -v "but never used" X clean : X rm -f $(OFILES) $(LIBRARY) $(TESTO) $(DESTINATION) core X touch : X touch -c $(OFILES) $(LIBRARY) X ranlib $(LIBRARY) X tags : $(SOURCE) $(TESTC) X ctags -t -w $(SOURCE) $(TESTC) X $(OFILES) : $(HFILES) $(TESTO) : $(HFILES) SHAR_EOF (set 20 16 10 12 10 43 57 'sparse/Makefile'; eval "$shar_touch") && chmod 0600 'sparse/Makefile' if test $? -ne 0 then ${echo} 'restore of sparse/Makefile failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/Makefile: MD5 check failed' ) << \SHAR_EOF 7028bcae10bafabadb59a1a45d6f5e59 sparse/Makefile SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/Makefile'` -ne 1025 && \ ${echo} 'restoration warning: size of sparse/Makefile is not 1025' fi fi # ============= sparse/mat0 ============== if test ! -d 'sparse'; then mkdir 'sparse' if test $? -eq 0 then ${echo} 'x - created directory `sparse'\''.' else ${echo} 'x - failed to create directory `sparse'\''.' exit 1 fi fi if test -f 'sparse/mat0' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/mat0 (file already exists)' else ${echo} 'x - extracting sparse/mat0 (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/mat0' && mat0 -- Simple matrix. 4 real 1 1 2.0 1 2 -1.0 2 1 -1.0 2 2 3.0 2 3 -1.0 3 2 -1.0 3 3 3.0 3 4 -1.0 4 3 -1.0 4 4 3.0 0 0 0.0 34.0 0.0 0.0 0.0 SHAR_EOF (set 20 05 04 12 10 08 33 'sparse/mat0'; eval "$shar_touch") && chmod 0600 'sparse/mat0' if test $? -ne 0 then ${echo} 'restore of sparse/mat0 failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/mat0: MD5 check failed' ) << \SHAR_EOF e902c724bc37bd19771d9ea1a58cce4c sparse/mat0 SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/mat0'` -ne 148 && \ ${echo} 'restoration warning: size of sparse/mat0 is not 148' fi fi # ============= sparse/mat1 ============== if test -f 'sparse/mat1' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/mat1 (file already exists)' else ${echo} 'x - extracting sparse/mat1 (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/mat1' && mat1 -- Typical circuit matrix, somewhat ill-conditioned. 23 real 1 1 0.0201303 1 6 -0.0108325 1 19 -0.00992221 2 2 0.00196826 2 6 -7.00819e-05 2 16 -0.000149258 3 3 0.00233598 3 6 -6.89538e-05 3 13 -3.81701e-05 3 14 -0.000161645 4 4 0.00181074 4 6 -7.11072e-05 5 5 0.00193524 5 6 -8.35603e-05 5 8 -0.000160605 6 1 -1.72443e-05 6 2 -9.41688e-05 6 3 -4.87603e-05 6 4 -9.88527e-05 6 5 -1.01501e-05 6 6 0.0237867 7 7 0.0020023 7 20 -0.002 8 5 -6.08416e-05 8 8 0.000306666 8 11 3.52493e-05 9 9 0.00012008 10 10 0.00200237 10 22 -0.002 11 11 9.74511e-05 12 12 0.000182758 13 3 -0.000458012 13 13 0.00205898 13 21 -0.002 14 3 -4.65368e-05 14 14 0.000305252 14 17 3.7642e-05 15 15 0.00012008 16 2 -0.000156934 16 16 0.00217081 16 23 -0.002 17 17 9.5915e-05 18 18 0.000182758 19 1 -0.00413682 19 19 0.0189592 20 7 -0.002 20 20 10.0037 20 21 -10 21 13 -0.002 21 20 -10 21 21 10.002 22 10 -0.002 22 22 10.0037 22 23 -10 23 16 -0.002 23 22 -10 23 23 10.002 0 0 0.0 -2.57565e-05 -4.48769e-07 -4.13483e-06 -1.01714e-06 -1.37342e-05 2.90276e-07 1.51576e-09 -1.56322e-07 0 5.2198e-08 0 0 -4.63536e-07 1.99061e-06 0 1.95645e-06 0 0 3.74634e-05 6.29115e-06 -4.69183e-06 -3.57397e-06 1.88248e-06 SHAR_EOF (set 20 05 04 12 10 08 33 'sparse/mat1'; eval "$shar_touch") && chmod 0600 'sparse/mat1' if test $? -ne 0 then ${echo} 'restore of sparse/mat1 failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/mat1: MD5 check failed' ) << \SHAR_EOF 1270a381de6e5ce471ccfdd392fe4e9c sparse/mat1 SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/mat1'` -ne 1182 && \ ${echo} 'restoration warning: size of sparse/mat1 is not 1182' fi fi # ============= sparse/mat2 ============== if test -f 'sparse/mat2' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/mat2 (file already exists)' else ${echo} 'x - extracting sparse/mat2 (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/mat2' && mat2 -- Typical circuit matrix. 8 real 1 1 0.0010686 1 2 -1.66e-05 1 5 -0.001 1 6 -4.2e-05 2 1 5.9554e-05 2 2 0.000131298 2 6 -9.16434e-05 2 7 0.000148333 3 3 0.00213033 3 5 -0.002 3 7 0.000190213 3 8 -4.2e-05 4 4 0.000275862 4 6 -4.6936e-05 5 1 -0.001 5 3 -0.002 5 5 0.00308858 5 8 0.000666678 6 1 -0.000118154 6 2 -6.68894e-08 6 4 -4.71518e-05 6 6 0.000366644 7 2 -0.000200677 7 3 -1.8e-06 7 7 0.000333767 8 3 -0.000198585 8 5 -7.2e-06 8 8 0.000431315 0 0 0.0 4.03934e-07 -3.044e-07 -1.53475e-05 5.28676e-08 1.5958e-05 -9.9608e-08 -2.13155e-07 4.15313e-09 SHAR_EOF (set 20 05 04 12 10 08 33 'sparse/mat2'; eval "$shar_touch") && chmod 0600 'sparse/mat2' if test $? -ne 0 then ${echo} 'restore of sparse/mat2 failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/mat2: MD5 check failed' ) << \SHAR_EOF efd12c706227c2a2e90b65337aa19e86 sparse/mat2 SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/mat2'` -ne 560 && \ ${echo} 'restoration warning: size of sparse/mat2 is not 560' fi fi # ============= sparse/mat3 ============== if test -f 'sparse/mat3' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/mat3 (file already exists)' else ${echo} 'x - extracting sparse/mat3 (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/mat3' && mat3 -- Ill-conditioned matrix. 22 real 1 1 0.0201303 1 6 -0.0108325 1 19 -0.00992221 2 2 0.00196826 2 6 -7.00819e-05 2 16 -0.000149258 3 3 0.00233598 3 6 -6.89538e-05 3 13 -3.81701e-05 3 14 -0.000161645 4 4 0.00181074 4 6 -7.11072e-05 5 5 0.00193524 5 6 -8.35603e-05 5 8 -0.000160605 6 1 -1.72443e-05 6 2 -9.41688e-05 6 3 -4.87603e-05 6 4 -9.88527e-05 6 5 -1.01501e-05 6 6 0.0237867 7 5 -6.08416e-05 7 8 0.000306666 7 11 3.52493e-05 8 9 0.00012008 9 10 0.00200237 9 22 -0.002 10 11 9.74511e-05 11 12 0.000182758 12 3 -0.000458012 12 13 0.00205898 12 21 -0.002 13 3 -4.65368e-05 13 14 0.000305252 13 17 3.7642e-05 14 15 0.00012008 15 2 -0.000156934 15 16 0.00217081 16 17 9.5915e-05 17 18 0.000182758 18 1 -0.00413682 18 19 0.0189592 19 7 -0.002 19 20 10.0037 19 21 -10 20 13 -0.002 20 20 -10 20 21 10.002 21 10 -0.002 21 22 10.0037 22 16 -0.002 22 22 -10 0 0 0.0 -2.57565e-05 -4.48769e-07 -4.13483e-06 -1.01714e-06 -1.37342e-05 2.90276e-07 1.51576e-09 -1.56322e-07 0 5.2198e-08 0 0 -4.63536e-07 1.99061e-06 0 1.95645e-06 0 0 3.74634e-05 6.29115e-06 -4.69183e-06 -3.57397e-06 SHAR_EOF (set 20 05 04 12 10 08 33 'sparse/mat3'; eval "$shar_touch") && chmod 0600 'sparse/mat3' if test $? -ne 0 then ${echo} 'restore of sparse/mat3 failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/mat3: MD5 check failed' ) << \SHAR_EOF e0d4826ba846411eb2d4e687011a2501 sparse/mat3 SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/mat3'` -ne 1080 && \ ${echo} 'restoration warning: size of sparse/mat3 is not 1080' fi fi # ============= sparse/mat5 ============== if test -f 'sparse/mat5' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/mat5 (file already exists)' else ${echo} 'x - extracting sparse/mat5 (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/mat5' && mat5 -- Test of complete pivoting capability. 3 real 1 2 1.0 2 3 1.0 3 1 1.0 0 0 0.0 2.0 3.0 1.0 X SHAR_EOF (set 20 05 04 12 10 08 33 'sparse/mat5'; eval "$shar_touch") && chmod 0600 'sparse/mat5' if test $? -ne 0 then ${echo} 'restore of sparse/mat5 failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/mat5: MD5 check failed' ) << \SHAR_EOF 8a4083ec8ae1f46a0d9218e9351cf000 sparse/mat5 SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/mat5'` -ne 100 && \ ${echo} 'restoration warning: size of sparse/mat5 is not 100' fi fi # ============= sparse/smpl-test.c ============== if test -f 'sparse/smpl-test.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/smpl-test.c (file already exists)' else ${echo} 'x - extracting sparse/smpl-test.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/smpl-test.c' && /* X * Simple test (and example) for Sparse X * X * This test builds a simple ladder network and then performs an AC analysis X * to compute is transfer characteristics versus frequency. X * X * Assumes Sparse is configured for complex matrices and that X * spSEPARATED_COMPLEX_VECTORS is NO. X */ X #include #include #include "spMatrix.h" X int main( int argc, char **argv ) { spMatrix A; struct spTemplate Stamp[3]; spError err; struct complex { double re; double im; } x[3], b[3]; double f, omega; X /* Create and build the matrix. */ X A = spCreate( 2, 1, &err ); X if (err >= spFATAL) { X spErrorMessage( A, stderr, argv[0] ); X return 1; X } X spGetAdmittance( A, 1, 0, &Stamp[0] ); X spGetAdmittance( A, 1, 2, &Stamp[1] ); X spGetAdmittance( A, 2, 0, &Stamp[2] ); X if (spErrorState( A ) >= spFATAL) { X spErrorMessage( A, stderr, argv[0] ); X return 1; X } X /* Drive the circuit at node 1. */ X b[1].re = 1.0; b[1].im = 0.0; X b[2].re = 0.0; b[2].im = 0.0; X /* Perform AC analysis over a range of frequencies. */ X for (f = 0.0; f <= 100000.0; f += 1000.0) { X omega = 2.0 * M_PI * f; X /* Load the matrix. */ X spClear( A ); X spADD_COMPLEX_QUAD( Stamp[0], 1/50.0, 1e-6*omega ); X spADD_REAL_QUAD( Stamp[1], 1/200.0 ); X spADD_COMPLEX_QUAD( Stamp[2], 1/50.0, 1e-6*omega ); X /* Solve the matrix equations Ax = b for x. */ X err = spFactor( A ); X if (err >= spFATAL) { X spErrorMessage( A, stderr, argv[0] ); X return 1; X } X spSolve( A, (spREAL *)b, (spREAL *)x ); X printf( "f = %f, h = %f\n", f, sqrt(x[2].re*x[2].re + x[2].im*x[2].im) ); X } X return 0; } X X SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/smpl-test.c'; eval "$shar_touch") && chmod 0600 'sparse/smpl-test.c' if test $? -ne 0 then ${echo} 'restore of sparse/smpl-test.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/smpl-test.c: MD5 check failed' ) << \SHAR_EOF a951634082dc60763c8b6b3328c6fc3d sparse/smpl-test.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/smpl-test.c'` -ne 1634 && \ ${echo} 'restoration warning: size of sparse/smpl-test.c is not 1634' fi fi # ============= sparse/spAllocate.c ============== if test -f 'sparse/spAllocate.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spAllocate.c (file already exists)' else ${echo} 'x - extracting sparse/spAllocate.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spAllocate.c' && /* X * MATRIX ALLOCATION MODULE X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*!\file X * This file contains functions for allocating and freeing matrices, configuring them, and for X * accessing global information about the matrix (size, error status, etc.). X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ /* >>> User accessible functions contained in this file: X * spCreate X * spDestroy X * spErrorState X * spWhereSingular X * spGetSize X * spSetReal X * spSetComplex X * spFillinCount X * spElementCount X * X * >>> Other functions contained in this file: X * spcGetElement X * InitializeElementBlocks X * spcGetFillin X * RecordAllocation X * AllocateBlockOfAllocationList X * EnlargeMatrix X * ExpandTranslationArrays X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spAllocate.c,v 1.3 2003/06/29 04:19:52 kundert Exp $"; #endif X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X X X X X /* X * Global strings X */ X char spcMatrixIsNotValid[] = "Matrix passed to Sparse is not valid"; char spcErrorsMustBeCleared[] = "Error not cleared"; char spcMatrixMustBeFactored[] = "Matrix must be factored"; char spcMatrixMustNotBeFactored[] = "Matrix must not be factored"; X X X X /* X * Function declarations X */ X static spError ReserveElements( MatrixPtr, int ); static void InitializeElementBlocks( MatrixPtr, int, int ); static void RecordAllocation( MatrixPtr, void* ); static void AllocateBlockOfAllocationList( MatrixPtr ); X X X /*! X * Allocates and initializes the data structures associated with a matrix. X * X * \return X * A pointer to the matrix is returned cast into \a spMatrix (typically a X * pointer to a void). This pointer is then passed and used by the other X * matrix routines to refer to a particular matrix. If an error occurs, X * the \a NULL pointer is returned. X * X * \param Size X * Size of matrix or estimate of size of matrix if matrix is \a EXPANDABLE. X * \param Complex X * Type of matrix. If \a Complex is 0 then the matrix is real, otherwise X * the matrix will be complex. Note that if the routines are not set up X * to handle the type of matrix requested, then an \a spPANIC error will occur. X * Further note that if a matrix will be both real and complex, it must X * be specified here as being complex. X * \param pError X * Returns error flag, needed because function \a spErrorState() will X * not work correctly if \a spCreate() returns \a NULL. Possible errors X * include \a spNO_MEMORY and \a spPANIC. X */ /* >>> Local variables: X * AllocatedSize (int) X * The size of the matrix being allocated. X * Matrix (MatrixPtr) X * A pointer to the matrix frame being created. X */ X spMatrix spCreate( X int Size, X int Complex, X spError *pError ) { register unsigned SizePlusOne; register MatrixPtr Matrix; register int I; int AllocatedSize; X /* Begin `spCreate'. */ /* Clear error flag. */ X *pError = spOKAY; X /* Test for valid size. */ X vASSERT( (Size >= 0) AND (Size != 0 OR EXPANDABLE), "Invalid size" ); X /* Test for valid type. */ #if NOT spCOMPLEX X ASSERT( NOT Complex ); #endif #if NOT REAL X ASSERT( Complex ); #endif X /* Create Matrix. */ X AllocatedSize = MAX( Size, MINIMUM_ALLOCATED_SIZE ); X SizePlusOne = (unsigned)(AllocatedSize + 1); X X if ((Matrix = ALLOC(struct MatrixFrame, 1)) == NULL) X { *pError = spNO_MEMORY; X return NULL; X } X /* Initialize matrix */ X Matrix->ID = SPARSE_ID; X Matrix->Complex = Complex; X Matrix->PreviousMatrixWasComplex = Complex; X Matrix->Factored = NO; X Matrix->Elements = 0; X Matrix->Error = *pError; X Matrix->Fillins = 0; X Matrix->Reordered = NO; X Matrix->NeedsOrdering = YES; X Matrix->NumberOfInterchangesIsOdd = NO; X Matrix->Partitioned = NO; X Matrix->RowsLinked = NO; X Matrix->InternalVectorsAllocated = NO; X Matrix->SingularCol = 0; X Matrix->SingularRow = 0; X Matrix->Size = Size; X Matrix->AllocatedSize = AllocatedSize; X Matrix->ExtSize = Size; X Matrix->AllocatedExtSize = AllocatedSize; X Matrix->CurrentSize = 0; X Matrix->ExtToIntColMap = NULL; X Matrix->ExtToIntRowMap = NULL; X Matrix->IntToExtColMap = NULL; X Matrix->IntToExtRowMap = NULL; X Matrix->MarkowitzRow = NULL; X Matrix->MarkowitzCol = NULL; X Matrix->MarkowitzProd = NULL; X Matrix->DoCmplxDirect = NULL; X Matrix->DoRealDirect = NULL; X Matrix->Intermediate = NULL; X Matrix->RelThreshold = DEFAULT_THRESHOLD; X Matrix->AbsThreshold = 0.0; X X Matrix->TopOfAllocationList = NULL; X Matrix->RecordsRemaining = 0; X Matrix->ElementsRemaining = 0; X Matrix->FillinsRemaining = 0; X X RecordAllocation( Matrix, (void *)Matrix ); X if (Matrix->Error == spNO_MEMORY) goto MemoryError; X /* Take out the trash. */ X Matrix->TrashCan.Real = 0.0; #if spCOMPLEX X Matrix->TrashCan.Imag = 0.0; #endif X Matrix->TrashCan.Row = 0; X Matrix->TrashCan.Col = 0; X Matrix->TrashCan.NextInRow = NULL; X Matrix->TrashCan.NextInCol = NULL; #if INITIALIZE X Matrix->TrashCan.pInitInfo = NULL; #endif X /* Allocate space in memory for Diag pointer vector. */ X CALLOC( Matrix->Diag, ElementPtr, SizePlusOne); X if (Matrix->Diag == NULL) X goto MemoryError; X /* Allocate space in memory for FirstInCol pointer vector. */ X CALLOC( Matrix->FirstInCol, ElementPtr, SizePlusOne); X if (Matrix->FirstInCol == NULL) X goto MemoryError; X /* Allocate space in memory for FirstInRow pointer vector. */ X CALLOC( Matrix->FirstInRow, ElementPtr, SizePlusOne); X if (Matrix->FirstInRow == NULL) X goto MemoryError; X /* Allocate space in memory for IntToExtColMap vector. */ X if (( Matrix->IntToExtColMap = ALLOC(int, SizePlusOne)) == NULL) X goto MemoryError; X /* Allocate space in memory for IntToExtRowMap vector. */ X if (( Matrix->IntToExtRowMap = ALLOC(int, SizePlusOne)) == NULL) X goto MemoryError; X /* Initialize MapIntToExt vectors. */ X for (I = 1; I <= AllocatedSize; I++) X { Matrix->IntToExtRowMap[I] = I; X Matrix->IntToExtColMap[I] = I; X } X #if TRANSLATE /* Allocate space in memory for ExtToIntColMap vector. */ X if (( Matrix->ExtToIntColMap = ALLOC(int, SizePlusOne)) == NULL) X goto MemoryError; X /* Allocate space in memory for ExtToIntRowMap vector. */ X if (( Matrix->ExtToIntRowMap = ALLOC(int, SizePlusOne)) == NULL) X goto MemoryError; X /* Initialize MapExtToInt vectors. */ X for (I = 1; I <= AllocatedSize; I++) X { Matrix->ExtToIntColMap[I] = -1; X Matrix->ExtToIntRowMap[I] = -1; X } X Matrix->ExtToIntColMap[0] = 0; X Matrix->ExtToIntRowMap[0] = 0; #endif X /* Allocate space for fill-ins and initial set of elements. */ X InitializeElementBlocks( Matrix, SPACE_FOR_ELEMENTS*AllocatedSize, X SPACE_FOR_FILL_INS*AllocatedSize ); X if (Matrix->Error == spNO_MEMORY) X goto MemoryError; X X return (char *)Matrix; X MemoryError: X /* Deallocate matrix and return no pointer to matrix if there is not enough X memory. */ X *pError = spNO_MEMORY; X spDestroy( (char *)Matrix); X return NULL; } X X X X X X X X X /* X * ELEMENT ALLOCATION X * X * This routine allocates space for matrix elements. It requests large blocks X * of storage from the system and doles out individual elements as required. X * This technique, as opposed to allocating elements individually, tends to X * speed the allocation process. X * X * >>> Returned: X * A pointer to an element. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * X * >>> Local variables: X * pElement (ElementPtr) X * A pointer to the first element in the group of elements being allocated. X * X * >>> Possible errors: X * spNO_MEMORY X */ X ElementPtr spcGetElement( MatrixPtr Matrix ) { ElementPtr pElement; X /* Begin `spcGetElement'. */ X /* Allocate block of MatrixElements if necessary. */ X if (Matrix->ElementsRemaining == 0) X { pElement = ALLOC(struct MatrixElement, ELEMENTS_PER_ALLOCATION); X RecordAllocation( Matrix, (void *)pElement ); X if (Matrix->Error == spNO_MEMORY) return NULL; X Matrix->ElementsRemaining = ELEMENTS_PER_ALLOCATION; X Matrix->NextAvailElement = pElement; X } X /* Update Element counter and return pointer to Element. */ X Matrix->ElementsRemaining--; X return Matrix->NextAvailElement++; } X X X X X X X X /* X * ELEMENT ALLOCATION INITIALIZATION X * X * This routine allocates space for matrix fill-ins and an initial set of X * elements. Besides being faster than allocating space for elements one X * at a time, it tends to keep the fill-ins physically close to the other X * matrix elements in the computer memory. This keeps virtual memory paging X * to a minimum. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * InitialNumberOfElements (int) X * This number is used as the size of the block of memory, in X * MatrixElements, reserved for elements. If more than this number of X * elements are generated, then more space is allocated later. X * NumberOfFillinsExpected (int) X * This number is used as the size of the block of memory, in X * MatrixElements, reserved for fill-ins. If more than this number of X * fill-ins are generated, then more space is allocated, but they may X * not be physically close in computer's memory. X * X * >>> Local variables: X * pElement (ElementPtr) X * A pointer to the first element in the group of elements being allocated. X * X * >>> Possible errors: X * spNO_MEMORY X */ X static void InitializeElementBlocks( X MatrixPtr Matrix, X int InitialNumberOfElements, X int NumberOfFillinsExpected ) { ElementPtr pElement; X /* Begin `InitializeElementBlocks'. */ X /* Allocate block of MatrixElements for elements. */ X pElement = ALLOC(struct MatrixElement, InitialNumberOfElements); X RecordAllocation( Matrix, (void *)pElement ); X if (Matrix->Error == spNO_MEMORY) return; X Matrix->ElementsRemaining = InitialNumberOfElements; X Matrix->NextAvailElement = pElement; X /* Allocate block of MatrixElements for fill-ins. */ X pElement = ALLOC(struct MatrixElement, NumberOfFillinsExpected); X RecordAllocation( Matrix, (void *)pElement ); X if (Matrix->Error == spNO_MEMORY) return; X Matrix->FillinsRemaining = NumberOfFillinsExpected; X Matrix->NextAvailFillin = pElement; X /* Allocate a fill-in list structure. */ X Matrix->FirstFillinListNode = ALLOC(struct FillinListNodeStruct,1); X RecordAllocation( Matrix, (void *)Matrix->FirstFillinListNode ); X if (Matrix->Error == spNO_MEMORY) return; X Matrix->LastFillinListNode = Matrix->FirstFillinListNode; X X Matrix->FirstFillinListNode->pFillinList = pElement; X Matrix->FirstFillinListNode->NumberOfFillinsInList =NumberOfFillinsExpected; X Matrix->FirstFillinListNode->Next = NULL; X X return; } X X X X X X X X X X /* X * FILL-IN ALLOCATION X * X * This routine allocates space for matrix fill-ins. It requests large blocks X * of storage from the system and doles out individual elements as required. X * This technique, as opposed to allocating elements individually, tends to X * speed the allocation process. X * X * >>> Returned: X * A pointer to the fill-in. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * X * >>> Possible errors: X * spNO_MEMORY X */ X ElementPtr spcGetFillin( MatrixPtr Matrix ) { struct FillinListNodeStruct *pListNode; ElementPtr pFillins; X /* Begin `spcGetFillin'. */ X #if NOT STRIP OR LINT X if (Matrix->FillinsRemaining == 0) X return spcGetElement( Matrix ); #endif #if STRIP OR LINT X X if (Matrix->FillinsRemaining == 0) X { pListNode = Matrix->LastFillinListNode; X /* First see if there are any stripped fill-ins left. */ X if (pListNode->Next != NULL) X { Matrix->LastFillinListNode = pListNode = pListNode->Next; X Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList; X Matrix->NextAvailFillin = pListNode->pFillinList; X } X else X { /* Allocate block of fill-ins. */ X pFillins = ALLOC(struct MatrixElement, ELEMENTS_PER_ALLOCATION); X RecordAllocation( Matrix, (void *)pFillins ); X if (Matrix->Error == spNO_MEMORY) return NULL; X Matrix->FillinsRemaining = ELEMENTS_PER_ALLOCATION; X Matrix->NextAvailFillin = pFillins; X /* Allocate a fill-in list structure. */ X pListNode->Next = ALLOC(struct FillinListNodeStruct,1); X RecordAllocation( Matrix, (void *)pListNode->Next ); X if (Matrix->Error == spNO_MEMORY) return NULL; X Matrix->LastFillinListNode = pListNode = pListNode->Next; X X pListNode->pFillinList = pFillins; X pListNode->NumberOfFillinsInList = ELEMENTS_PER_ALLOCATION; X pListNode->Next = NULL; X } X } #endif X /* Update Fill-in counter and return pointer to Fill-in. */ X Matrix->FillinsRemaining--; X return Matrix->NextAvailFillin++; } X X X X X X X X X /* X * RECORD A MEMORY ALLOCATION X * X * This routine is used to record all memory allocations so that the memory X * can be freed later. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * AllocatedPtr (void *) X * The pointer returned by malloc or calloc. These pointers are saved in X * a list so that they can be easily freed. X * X * >>> Possible errors: X * spNO_MEMORY X */ X static void RecordAllocation( X MatrixPtr Matrix, X void *AllocatedPtr ) { /* Begin `RecordAllocation'. */ /* X * If Allocated pointer is NULL, assume that malloc returned a NULL pointer, X * which indicates a spNO_MEMORY error. X */ X if (AllocatedPtr == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X /* Allocate block of MatrixElements if necessary. */ X if (Matrix->RecordsRemaining == 0) X { AllocateBlockOfAllocationList( Matrix ); X if (Matrix->Error == spNO_MEMORY) X { FREE(AllocatedPtr); X return; X } X } X /* Add Allocated pointer to Allocation List. */ X (++Matrix->TopOfAllocationList)->AllocatedPtr = AllocatedPtr; X Matrix->RecordsRemaining--; X return; X } X X X X X X X X /* X * ADD A BLOCK OF SLOTS TO ALLOCATION LIST X * X * This routine increases the size of the allocation list. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * X * >>> Local variables: X * ListPtr (AllocationListPtr) X * Pointer to the list that contains the pointers to segments of memory X * that were allocated by the operating system for the current matrix. X * X * >>> Possible errors: X * spNO_MEMORY X */ X static void AllocateBlockOfAllocationList( MatrixPtr Matrix ) { register int I; register AllocationListPtr ListPtr; X /* Begin `AllocateBlockOfAllocationList'. */ /* Allocate block of records for allocation list. */ X ListPtr = ALLOC(struct AllocationRecord, (ELEMENTS_PER_ALLOCATION+1)); X if (ListPtr == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X /* String entries of allocation list into singly linked list. List is linked X such that any record points to the one before it. */ X X ListPtr->NextRecord = Matrix->TopOfAllocationList; X Matrix->TopOfAllocationList = ListPtr; X ListPtr += ELEMENTS_PER_ALLOCATION; X for (I = ELEMENTS_PER_ALLOCATION; I > 0; I--) X { ListPtr->NextRecord = ListPtr - 1; X ListPtr--; X } X /* Record allocation of space for allocation list on allocation list. */ X Matrix->TopOfAllocationList->AllocatedPtr = (void *)ListPtr; X Matrix->RecordsRemaining = ELEMENTS_PER_ALLOCATION; X X return; } X X X X X X X X /*! X * Destroys a matrix and frees all memory associated with it. X * X * \param eMatrix X * Pointer to the matrix frame which is to be destroyed. X */ /* >>> Local variables: X * ListPtr (AllocationListPtr) X * Pointer into the linked list of pointers to allocated data structures. X * Points to pointer to structure to be freed. X * NextListPtr (AllocationListPtr) X * Pointer into the linked list of pointers to allocated data structures. X * Points to the next pointer to structure to be freed. This is needed X * because the data structure to be freed could include the current node X * in the allocation list. X */ X void spDestroy( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register AllocationListPtr ListPtr, NextListPtr; X /* Begin `spDestroy'. */ X ASSERT_IS_SPARSE( Matrix ); X /* Deallocate the vectors that are located in the matrix frame. */ X FREE( Matrix->IntToExtColMap ); X FREE( Matrix->IntToExtRowMap ); X FREE( Matrix->ExtToIntColMap ); X FREE( Matrix->ExtToIntRowMap ); X FREE( Matrix->Diag ); X FREE( Matrix->FirstInRow ); X FREE( Matrix->FirstInCol ); X FREE( Matrix->MarkowitzRow ); X FREE( Matrix->MarkowitzCol ); X FREE( Matrix->MarkowitzProd ); X FREE( Matrix->DoCmplxDirect ); X FREE( Matrix->DoRealDirect ); X FREE( Matrix->Intermediate ); X /* Sequentially step through the list of allocated pointers freeing pointers X * along the way. */ X ListPtr = Matrix->TopOfAllocationList; X while (ListPtr != NULL) X { NextListPtr = ListPtr->NextRecord; X free( ListPtr->AllocatedPtr ); X ListPtr = NextListPtr; X } X return; } X X X X X X X /*! X * This function returns the error status of the given matrix. X * X * \return X * The error status of the given matrix. X * X * \param eMatrix X * The pointer to the matrix for which the error status is desired. X */ X spError spErrorState( spMatrix eMatrix ) { /* Begin `spErrorState'. */ X X if (eMatrix != NULL) X { ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); X return ((MatrixPtr)eMatrix)->Error; X } X else return spNO_MEMORY; /* This error may actually be spPANIC, X * no way to tell. */ } X X X X X X X X X /*! X * This function returns the row and column number where the matrix was X * detected as singular (if pivoting was allowed on the last factorization) X * or where a zero was detected on the diagonal (if pivoting was not X * allowed on the last factorization). Pivoting is performed only in X * spOrderAndFactor(). X * X * \param eMatrix X * The matrix for which the error status is desired. X * \param pRow X * The row number. X * \param pCol X * The column number. X */ X void spWhereSingular( X spMatrix eMatrix, X int *pRow, X int *pCol ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; X /* Begin `spWhereSingular'. */ X ASSERT_IS_SPARSE( Matrix ); X X if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG) X { *pRow = Matrix->SingularRow; X *pCol = Matrix->SingularCol; X } X else *pRow = *pCol = 0; X return; } X X X X X X /*! X * Returns the size of the matrix. Either the internal or external size of X * the matrix is returned. X * X * \param eMatrix X * Pointer to matrix. X * \param External X * If \a External is set true, the external size , i.e., the value of the X * largest external row or column number encountered is returned. X * Otherwise the true size of the matrix is returned. These two sizes X * may differ if the \a TRANSLATE option is set true. X */ X int spGetSize( X spMatrix eMatrix, X int External ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; X /* Begin `spGetSize'. */ X ASSERT_IS_SPARSE( Matrix ); X #if TRANSLATE X if (External) X return Matrix->ExtSize; X else X return Matrix->Size; #else X return Matrix->Size; #endif } X X X X X X X X /*! X * Forces matrix to be real. X * X * \param eMatrix X * Pointer to matrix. X */ X void spSetReal( spMatrix eMatrix ) { /* Begin `spSetReal'. */ X X ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); X vASSERT( REAL, "Sparse not compiled to handle real matrices" ); X ((MatrixPtr)eMatrix)->Complex = NO; X return; } X X /*! X * Forces matrix to be complex. X * X * \param eMatrix X * Pointer to matrix. X */ X void spSetComplex( spMatrix eMatrix ) { /* Begin `spSetComplex'. */ X X ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); X vASSERT( spCOMPLEX, "Sparse not compiled to handle complex matrices" ); X ((MatrixPtr)eMatrix)->Complex = YES; X return; } X X X X X X X X X /*! X * This function returns the number of fill-ins that currently exists in a matrix. X * X * \param eMatrix X * Pointer to matrix. X */ X int spFillinCount( spMatrix eMatrix ) { /* Begin `spFillinCount'. */ X X ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); X return ((MatrixPtr)eMatrix)->Fillins; } X X /*! X * This function returns the total number of elements (including fill-ins) that currently exists in a matrix. X * X * \param eMatrix X * Pointer to matrix. X */ X int spElementCount( spMatrix eMatrix ) { /* Begin `spElementCount'. */ X X ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); X return ((MatrixPtr)eMatrix)->Elements; } SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spAllocate.c'; eval "$shar_touch") && chmod 0600 'sparse/spAllocate.c' if test $? -ne 0 then ${echo} 'restore of sparse/spAllocate.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spAllocate.c: MD5 check failed' ) << \SHAR_EOF fdb2efb0c741f961e2a722b4123ca339 sparse/spAllocate.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spAllocate.c'` -ne 21591 && \ ${echo} 'restoration warning: size of sparse/spAllocate.c is not 21591' fi fi # ============= sparse/spBuild.c ============== if test -f 'sparse/spBuild.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spBuild.c (file already exists)' else ${echo} 'x - extracting sparse/spBuild.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spBuild.c' && /* X * MATRIX BUILD MODULE X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*!\file X * This file contains the routines associated with clearing, loading and X * preprocessing the matrix. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ /* >>> User accessible functions contained in this file: X * spClear X * spFindElement X * spGetElement X * spGetAdmittance X * spGetQuad X * spGetOnes X * spInstallInitInfo X * spGetInitInfo X * spInitialize X * X * >>> Other functions contained in this file: X * Translate X * spcFindDiag X * spcCreateElement X * spcLinkRows X * EnlargeMatrix X * ExpandTranslationArrays X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spBuild.c,v 1.3 2003/06/29 04:19:52 kundert Exp $"; #endif X X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X X X X X /* X * Function declarations X */ X static void Translate( MatrixPtr, int*, int* ); static void EnlargeMatrix( MatrixPtr, int ); static void ExpandTranslationArrays( MatrixPtr, int ); X X X X X X /*! X * Sets every element of the matrix to zero and clears the error flag. X * X * \param eMatrix X * Pointer to matrix that is to be cleared. X */ /* >>> Local variables: X * pElement (ElementPtr) X * A pointer to the element being cleared. X */ X void spClear( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register int I; X /* Begin `spClear'. */ X ASSERT_IS_SPARSE( Matrix ); X /* Clear matrix. */ #if spCOMPLEX X if (Matrix->PreviousMatrixWasComplex OR Matrix->Complex) X { for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { pElement->Real = 0.0; X pElement->Imag = 0.0; X pElement = pElement->NextInCol; X } X } X } X else #endif X { for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { pElement->Real = 0.0; X pElement = pElement->NextInCol; X } X } X } X /* Empty the trash. */ X Matrix->TrashCan.Real = 0.0; #if spCOMPLEX X Matrix->TrashCan.Imag = 0.0; #endif X X Matrix->Error = spOKAY; X Matrix->Factored = NO; X Matrix->SingularCol = 0; X Matrix->SingularRow = 0; X Matrix->PreviousMatrixWasComplex = Matrix->Complex; X return; } X X X X X X X X X X /*! X * This routine is used to find an element given its indices. It will not X * create it if it does not exist. X * X * \return X * A pointer to the desired element, or \a NULL if it does not exist. X * X * \param eMatrix X * Pointer to matrix. X * \param Row X * Row index for element. X * \param Col X * Column index for element. X * X * \see spGetElement() X */ /* >>> Local variables: X * pElement (ElementPtr) X * Pointer to an element in the matrix. X */ X spElement * spFindElement( X spMatrix eMatrix, X int Row, X int Col ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; long StartAt, Min = LARGEST_LONG_INTEGER; #define BorderRight 0 /* Start at left border, move right. */ #define BorderDown 1 /* Start at top border, move down. */ #define DiagRight 2 /* Start at diagonal, move right. */ #define DiagDown 3 /* Start at diagonal, move down. */ X /* Begin `spFindElement'. */ X if (Row == Col) return &Matrix->Diag[Row]->Real; X /* Determine where to start the search. */ X if (Matrix->RowsLinked) X { if ((Col >= Row) AND Matrix->Diag[Row]) X { Min = Col - Row; X StartAt = DiagRight; X } X else X { Min = Col; X StartAt = BorderRight; X } X } X if ((Row >= Col) AND Matrix->Diag[Col]) X { if (Row - Col < Min) X StartAt = DiagDown; X } X else if (Row < Min) X StartAt = BorderDown; X /* Search column for element. */ X if ((StartAt == BorderDown) OR (StartAt == DiagDown)) X { if (StartAt == BorderDown) X pElement = Matrix->FirstInCol[Col]; X else X pElement = Matrix->Diag[Col]; X X while ((pElement != NULL) AND (pElement->Row < Row)) X pElement = pElement->NextInCol; X if (pElement AND (pElement->Row == Row)) X return &pElement->Real; X else X return NULL; X } X /* Search row for element. */ X if (StartAt == BorderRight) X pElement = Matrix->FirstInRow[Row]; X else X pElement = Matrix->Diag[Row]; X X while ((pElement != NULL) AND (pElement->Col < Col)) X pElement = pElement->NextInRow; X if (pElement AND (pElement->Col == Col)) X return &pElement->Real; X else X return NULL; } X X X X X X X X X /*! X * Finds element [Row,Col] and returns a pointer to it. If element is X * not found then it is created and spliced into matrix. This routine X * is only to be used after spCreate() and before spMNA_Preorder(), X * spFactor() or spOrderAndFactor(). Returns a pointer to the X * real portion of an \a spElement. This pointer is later used by X * \a spADD_xxx_ELEMENT to directly access element. X * X * \return X * Returns a pointer to the element. This pointer is then used to directly X * access the element during successive builds. X * X * \param eMatrix X * Pointer to the matrix that the element is to be added to. X * \param Row X * Row index for element. Must be in the range of [0..Size] unless X * the options \a EXPANDABLE or \a TRANSLATE are used. Elements placed in X * row zero are discarded. In no case may \a Row be less than zero. X * \param Col X * Column index for element. Must be in the range of [0..Size] unless X * the options \a EXPANDABLE or \a TRANSLATE are used. Elements placed in X * column zero are discarded. In no case may \a Col be less than zero. X X * \see spFindElement() X */ /* >>> Local variables: X * pElement (RealNumber *) X * Pointer to the element. X * X * >>> Possible errors: X * spNO_MEMORY X * Error is not cleared in this routine. X */ X spElement * spGetElement( X spMatrix eMatrix, X int Row, X int Col ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; ElementPtr pElement; X /* Begin `spGetElement'. */ X ASSERT_IS_SPARSE( Matrix ); X vASSERT( Row >= 0 AND Col >= 0, "Negative row or column number" ); X X if ((Row == 0) OR (Col == 0)) X return &Matrix->TrashCan.Real; X #if NOT TRANSLATE X vASSERT( NOT Matrix->Reordered, X "Set TRANSLATE to add elements to a reordered matrix" ); #endif X #if TRANSLATE X Translate( Matrix, &Row, &Col ); X if (Matrix->Error == spNO_MEMORY) return NULL; #endif X #if NOT TRANSLATE #if NOT EXPANDABLE X vASSERT( (Row <= Matrix->Size) AND (Col <= Matrix->Size), X "Row or column number too large" ); #endif X #if EXPANDABLE /* Re-size Matrix if necessary. */ X if ((Row > Matrix->Size) OR (Col > Matrix->Size)) X EnlargeMatrix( Matrix, MAX(Row, Col) ); X if (Matrix->Error == spNO_MEMORY) return NULL; #endif #endif X X if ((Row != Col) OR ((pElement = Matrix->Diag[Row]) == NULL)) X { /* X * Element does not exist or does not reside along diagonal. Search X * for element and if it does not exist, create it. X */ X pElement = spcCreateElement( Matrix, Row, Col, X &(Matrix->FirstInRow[Row]), X &(Matrix->FirstInCol[Col]), NO ); X } /* X * Cast pointer into a pointer to a RealNumber. This requires that Real X * be the first record in the MatrixElement structure. X */ X return &pElement->Real; } X X X X X X X #if TRANSLATE X /* X * TRANSLATE EXTERNAL INDICES TO INTERNAL X * X * Convert internal row and column numbers to internal row and column numbers. X * Also updates Ext/Int maps. X * X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * Row (int *) X * Upon entry Row is either a external row number of an external node X * number. Upon entry, the internal equivalent is supplied. X * Col (int *) X * Upon entry Column is either a external column number of an external node X * number. Upon entry, the internal equivalent is supplied. X * X * >>> Local variables: X * ExtCol (int) X * Temporary variable used to hold the external column or node number X * during the external to internal column number translation. X * ExtRow (int) X * Temporary variable used to hold the external row or node number during X * the external to internal row number translation. X * IntCol (int) X * Temporary variable used to hold the internal column or node number X * during the external to internal column number translation. X * IntRow (int) X * Temporary variable used to hold the internal row or node number during X * the external to internal row number translation. X */ X static void Translate( X MatrixPtr Matrix, X int *Row, X int *Col ) { register int IntRow, IntCol, ExtRow, ExtCol; X /* Begin `Translate'. */ X ExtRow = *Row; X ExtCol = *Col; X /* Expand translation arrays if necessary. */ X if ((ExtRow > Matrix->AllocatedExtSize) OR X (ExtCol > Matrix->AllocatedExtSize)) X { X ExpandTranslationArrays( Matrix, MAX(ExtRow, ExtCol) ); X if (Matrix->Error == spNO_MEMORY) return; X } X /* Set ExtSize if necessary. */ X if ((ExtRow > Matrix->ExtSize) OR (ExtCol > Matrix->ExtSize)) X Matrix->ExtSize = MAX(ExtRow, ExtCol); X /* Translate external row or node number to internal row or node number. */ X if ((IntRow = Matrix->ExtToIntRowMap[ExtRow]) == -1) X { Matrix->ExtToIntRowMap[ExtRow] = ++Matrix->CurrentSize; X Matrix->ExtToIntColMap[ExtRow] = Matrix->CurrentSize; X IntRow = Matrix->CurrentSize; X #if NOT EXPANDABLE X vASSERT( IntRow <= Matrix->Size, "Matrix size fixed" ); #endif X #if EXPANDABLE /* Re-size Matrix if necessary. */ X if (IntRow > Matrix->Size) X EnlargeMatrix( Matrix, IntRow ); X if (Matrix->Error == spNO_MEMORY) return; #endif X X Matrix->IntToExtRowMap[IntRow] = ExtRow; X Matrix->IntToExtColMap[IntRow] = ExtRow; X } X /* Translate external column or node number to internal column or node number.*/ X if ((IntCol = Matrix->ExtToIntColMap[ExtCol]) == -1) X { Matrix->ExtToIntRowMap[ExtCol] = ++Matrix->CurrentSize; X Matrix->ExtToIntColMap[ExtCol] = Matrix->CurrentSize; X IntCol = Matrix->CurrentSize; X #if NOT EXPANDABLE X vASSERT( IntCol <= Matrix->Size, "Matrix size fixed" ); #endif X #if EXPANDABLE /* Re-size Matrix if necessary. */ X if (IntCol > Matrix->Size) X EnlargeMatrix( Matrix, IntCol ); X if (Matrix->Error == spNO_MEMORY) return; #endif X X Matrix->IntToExtRowMap[IntCol] = ExtCol; X Matrix->IntToExtColMap[IntCol] = ExtCol; X } X X *Row = IntRow; X *Col = IntCol; X return; } #endif X X X X X X #if QUAD_ELEMENT /*! X * Performs same function as spGetElement() except rather than one X * element, all four matrix elements for a floating two terminal X * admittance component are added. This routine also works if component X * is grounded. Positive elements are placed at [Node1,Node2] and X * [Node2,Node1]. This routine is only to be used after spCreate() X * and before spMNA_Preorder(), spFactor() or spOrderAndFactor(). X * X * \return X * Error code. Possible errors include \a spNO_MEMORY. X * Error is not cleared in this routine. X * X * \param Matrix X * Pointer to the matrix that component is to be entered in. X * \param Node1 X * Row and column indices for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Node zero is the X * ground node. In no case may \a Node1 be less than zero. X * \param Node2 X * Row and column indices for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Node zero is the X * ground node. In no case may \a Node2 be less than zero. X * \param Template X * Collection of pointers to four elements that are later used to directly X * address elements. User must supply the template, this routine will X * fill it. X */ X spError spGetAdmittance( X spMatrix Matrix, X int Node1, X int Node2, X struct spTemplate *Template ) { X /* Begin `spGetAdmittance'. */ X Template->Element1 = spGetElement(Matrix, Node1, Node1 ); X Template->Element2 = spGetElement(Matrix, Node2, Node2 ); X Template->Element3Negated = spGetElement( Matrix, Node2, Node1 ); X Template->Element4Negated = spGetElement( Matrix, Node1, Node2 ); X if X ( (Template->Element1 == NULL) X OR (Template->Element2 == NULL) X OR (Template->Element3Negated == NULL) X OR (Template->Element4Negated == NULL) X ) return spNO_MEMORY; X X if (Node1 == 0) X SWAP( RealNumber*, Template->Element1, Template->Element2 ); X X return spOKAY; } #endif /* QUAD_ELEMENT */ X X X X X X X X X #if QUAD_ELEMENT /*! X * Similar to spGetAdmittance(), except that spGetAdmittance() only X * handles 2-terminal components, whereas spGetQuad() handles simple X * 4-terminals as well. These 4-terminals are simply generalized X * 2-terminals with the option of having the sense terminals different X * from the source and sink terminals. spGetQuad() adds four X * elements to the matrix. Positive elements occur at [Row1,Col1] X * [Row2,Col2] while negative elements occur at [Row1,Col2] and [Row2,Col1]. X * The routine works fine if any of the rows and columns are zero. X * This routine is only to be used after spCreate() and before X * spMNA_Preorder(), spFactor() or spOrderAndFactor() X * unless \a TRANSLATE is set true. X * X * \return X * Error code. Possible errors include \a spNO_MEMORY. X * Error is not cleared in this routine. X * X * \param Matrix X * Pointer to the matrix that component is to be entered in. X * \param Row1 X * First row index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may Row1 be less than zero. X * \param Row2 X * Second row index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may Row2 be less than zero. X * \param Col1 X * First column index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground column. In no case may Col1 be less than zero. X * \param Col2 X * Second column index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground column. In no case may Col2 be less than zero. X * \param Template X * Collection of pointers to four elements that are later used to directly X * address elements. User must supply the template, this routine will X * fill it. X */ X spError spGetQuad( X spMatrix Matrix, X int Row1, X int Row2, X int Col1, X int Col2, X struct spTemplate *Template ) { /* Begin `spGetQuad'. */ X Template->Element1 = spGetElement( Matrix, Row1, Col1); X Template->Element2 = spGetElement( Matrix, Row2, Col2 ); X Template->Element3Negated = spGetElement( Matrix, Row2, Col1 ); X Template->Element4Negated = spGetElement( Matrix, Row1, Col2 ); X if X ( (Template->Element1 == NULL) X OR (Template->Element2 == NULL) X OR (Template->Element3Negated == NULL) X OR (Template->Element4Negated == NULL) X ) return spNO_MEMORY; X X if (Template->Element1 == &((MatrixPtr)Matrix)->TrashCan.Real) X SWAP( RealNumber *, Template->Element1, Template->Element2 ); X X return spOKAY; } #endif /* QUAD_ELEMENT */ X X X X X X X X X #if QUAD_ELEMENT /*! X * Addition of four structural ones to matrix by index. X * Performs similar function to spGetQuad() except this routine is X * meant for components that do not have an admittance representation. X * X * The following stamp is used: \code X * Pos Neg Eqn X * Pos [ . . 1 ] X * Neg [ . . -1 ] X * Eqn [ 1 -1 . ] X * \endcode X * X * \return X * Error code. Possible errors include \a spNO_MEMORY. X * Error is not cleared in this routine. X * X * \param Matrix X * Pointer to the matrix that component is to be entered in. X * \param Pos X * See stamp above. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Pos be less than zero. X * \param Neg X * See stamp above. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Neg be less than zero. X * \param Eqn X * See stamp above. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Eqn be less than zero. X * \param Template X * Collection of pointers to four elements that are later used to directly X * address elements. User must supply the template, this routine will X * fill it. X */ X spError spGetOnes( X spMatrix Matrix, X int Pos, X int Neg, X int Eqn, X struct spTemplate *Template ) { /* Begin `spGetOnes'. */ X Template->Element4Negated = spGetElement( Matrix, Neg, Eqn ); X Template->Element3Negated = spGetElement( Matrix, Eqn, Neg ); X Template->Element2 = spGetElement( Matrix, Pos, Eqn ); X Template->Element1 = spGetElement( Matrix, Eqn, Pos ); X if X ( (Template->Element1 == NULL) X OR (Template->Element2 == NULL) X OR (Template->Element3Negated == NULL) X OR (Template->Element4Negated == NULL) X ) return spNO_MEMORY; X X spADD_REAL_QUAD( *Template, 1.0 ); X return spOKAY; } #endif /* QUAD_ELEMENT */ X X X X X X X /* X * FIND DIAGONAL X * X * This routine is used to find a diagonal element. It will not X * create it if it does not exist. X * X * >>> Returned: X * A pointer to the desired element, or NULL if it does not exist. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Index (int) X * Row, Col index for diagonal element. X * X * >>> Local variables: X * pElement (ElementPtr) X * Pointer to an element in the matrix. X */ X ElementPtr spcFindDiag( X MatrixPtr Matrix, X register int Index ) { register ElementPtr pElement; X /* Begin `spcFindDiag'. */ X pElement = Matrix->FirstInCol[Index]; X /* Search column for element. */ X while ((pElement != NULL) AND (pElement->Row < Index)) X pElement = pElement->NextInCol; X if (pElement AND (pElement->Row == Index)) X return pElement; X else X return NULL; } X X X X X X X X /* X * CREATE AND SPLICE ELEMENT INTO MATRIX X * X * This routine is used to create new matrix elements and splice them into the X * matrix. X * X * >>> Returned: X * A pointer to the element that was created is returned. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Row (int) X * Row index for element. X * Col (int) X * Column index for element. X * ppToLeft (ElementPtr *) X * This contains the address of the pointer to an element to the left X * of the one being created. It is used to speed the search and if it X * is immediately to the left, it is updated with address of the X * created element. X * ppAbove (ElementPtr *) X * This contains the address of the pointer to an element above the X * one being created. It is used to speed the search and it if it X * is immediatley above, it is updated with address of the created X * element. X * Fillin (BOOLEAN) X * Flag that indicates if created element is to be a fill-in. X * X * >>> Local variables: X * pElement (ElementPtr) X * Pointer to an element in the matrix. X * pCreatedElement (ElementPtr) X * Pointer to the desired element, the one that was just created. X * X * >>> Possible errors: X * spNO_MEMORY X */ X ElementPtr spcCreateElement( X MatrixPtr Matrix, X int Row, X register int Col, X register ElementPtr *ppToLeft, X register ElementPtr *ppAbove, X BOOLEAN Fillin ) { register ElementPtr pElement, pCreatedElement; X /* Begin `spcCreateElement'. */ X /* Find element immediately above the desired element. */ X pElement = *ppAbove; X while ((pElement != NULL) AND (pElement->Row < Row)) X { ppAbove = &pElement->NextInCol; X pElement = *ppAbove; X } X if ((pElement != NULL) AND (pElement->Row == Row)) X return pElement; X /* The desired element does not exist, create it. */ X if (Fillin) X { pCreatedElement = spcGetFillin( Matrix ); X Matrix->Fillins++; X /* Update Markowitz counts and products. */ X ++Matrix->MarkowitzRow[Row]; X spcMarkoProd( Matrix->MarkowitzProd[Row], X Matrix->MarkowitzRow[Row], X Matrix->MarkowitzCol[Row] ); X if ((Matrix->MarkowitzRow[Row] == 1) AND X (Matrix->MarkowitzCol[Row] != 0)) X { X Matrix->Singletons--; X } X ++Matrix->MarkowitzCol[Col]; X spcMarkoProd( Matrix->MarkowitzProd[Col], X Matrix->MarkowitzCol[Col], X Matrix->MarkowitzRow[Col] ); X if ((Matrix->MarkowitzRow[Col] != 0) AND X (Matrix->MarkowitzCol[Col] == 1)) X { X Matrix->Singletons--; X } X } X else X { pCreatedElement = spcGetElement( Matrix ); X Matrix->NeedsOrdering = YES; X } X if (pCreatedElement == NULL) return NULL; X Matrix->Elements++; X /* Initialize Element. */ X pCreatedElement->Row = Row; X pCreatedElement->Col = Col; X pCreatedElement->Real = 0.0; #if spCOMPLEX X pCreatedElement->Imag = 0.0; #endif #if INITIALIZE X pCreatedElement->pInitInfo = NULL; #endif X /* If element is on diagonal, store pointer in Diag. */ X if (Row == Col) Matrix->Diag[Row] = pCreatedElement; X /* Splice element into column. */ X pCreatedElement->NextInCol = *ppAbove; X *ppAbove = pCreatedElement; X /* Find Element immediately to the left of the fill-in. */ X if (Matrix->RowsLinked) X { pElement = *ppToLeft; X while (pElement != NULL) X { if (pElement->Col < Col) X { ppToLeft = &pElement->NextInRow; X pElement = *ppToLeft; X } X else break; /* while loop */ X } X /* Splice element into row. */ X pCreatedElement->NextInRow = *ppToLeft; X *ppToLeft = pCreatedElement; X } X return pCreatedElement; } X X X X X X X X /* X * X * LINK ROWS X * X * This routine is used to generate the row links. The spGetElement() X * routines do not create row links, which are needed by the spFactor() X * routines. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * X * >>> Local variables: X * pElement (ElementPtr) X * Pointer to an element in the matrix. X * FirstInRowEntry (ElementPtr *) X * A pointer into the FirstInRow array. Points to the FirstInRow entry X * currently being operated upon. X * FirstInRowArray (ArrayOfElementPtrs) X * A pointer to the FirstInRow array. Same as Matrix->FirstInRow but X * resides in a register and requires less indirection so is faster to X * use. X * Col (int) X * Column currently being operated upon. X */ X void spcLinkRows( MatrixPtr Matrix ) { register ElementPtr pElement, *FirstInRowEntry; register ArrayOfElementPtrs FirstInRowArray; register int Col; X /* Begin `spcLinkRows'. */ X FirstInRowArray = Matrix->FirstInRow; X for (Col = Matrix->Size; Col >= 1; Col--) X FirstInRowArray[Col] = NULL; X X for (Col = Matrix->Size; Col >= 1; Col--) X { /* Generate row links for the elements in the Col'th column. */ X pElement = Matrix->FirstInCol[Col]; X X while (pElement != NULL) X { pElement->Col = Col; X FirstInRowEntry = &FirstInRowArray[pElement->Row]; X pElement->NextInRow = *FirstInRowEntry; X *FirstInRowEntry = pElement; X pElement = pElement->NextInCol; X } X } X Matrix->RowsLinked = YES; X return; } X X X X X X X X /* X * ENLARGE MATRIX X * X * Increases the size of the matrix. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * NewSize (int) X * The new size of the matrix. X * X * >>> Local variables: X * OldAllocatedSize (int) X * The allocated size of the matrix before it is expanded. X */ X static void EnlargeMatrix( X MatrixPtr Matrix, X register int NewSize ) { register int I, OldAllocatedSize = Matrix->AllocatedSize; X /* Begin `EnlargeMatrix'. */ X Matrix->Size = NewSize; X X if (NewSize <= OldAllocatedSize) X return; X /* Expand the matrix frame. */ X NewSize = MAX( NewSize, (int)(EXPANSION_FACTOR * OldAllocatedSize) ); X Matrix->AllocatedSize = NewSize; X X if (( REALLOC(Matrix->IntToExtColMap, int, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X if (( REALLOC(Matrix->IntToExtRowMap, int, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X if (( REALLOC(Matrix->Diag, ElementPtr, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X if (( REALLOC(Matrix->FirstInCol, ElementPtr, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X if (( REALLOC(Matrix->FirstInRow, ElementPtr, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X /* X * Destroy the Markowitz and Intermediate vectors, they will be recreated X * in spOrderAndFactor(). X */ X FREE( Matrix->MarkowitzRow ); X FREE( Matrix->MarkowitzCol ); X FREE( Matrix->MarkowitzProd ); X FREE( Matrix->DoRealDirect ); X FREE( Matrix->DoCmplxDirect ); X FREE( Matrix->Intermediate ); X Matrix->InternalVectorsAllocated = NO; X /* Initialize the new portion of the vectors. */ X for (I = OldAllocatedSize+1; I <= NewSize; I++) X { Matrix->IntToExtColMap[I] = I; X Matrix->IntToExtRowMap[I] = I; X Matrix->Diag[I] = NULL; X Matrix->FirstInRow[I] = NULL; X Matrix->FirstInCol[I] = NULL; X } X X return; } X X X X X X X X #if TRANSLATE X /* X * EXPAND TRANSLATION ARRAYS X * X * Increases the size arrays that are used to translate external to internal X * row and column numbers. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * NewSize (int) X * The new size of the translation arrays. X * X * >>> Local variables: X * OldAllocatedSize (int) X * The allocated size of the translation arrays before being expanded. X */ X static void ExpandTranslationArrays( X MatrixPtr Matrix, X register int NewSize ) { register int I, OldAllocatedSize = Matrix->AllocatedExtSize; X /* Begin `ExpandTranslationArrays'. */ X Matrix->ExtSize = NewSize; X X if (NewSize <= OldAllocatedSize) X return; X /* Expand the translation arrays ExtToIntRowMap and ExtToIntColMap. */ X NewSize = MAX( NewSize, (int)(EXPANSION_FACTOR * OldAllocatedSize) ); X Matrix->AllocatedExtSize = NewSize; X X if (( REALLOC(Matrix->ExtToIntRowMap, int, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X if (( REALLOC(Matrix->ExtToIntColMap, int, NewSize+1)) == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X /* Initialize the new portion of the vectors. */ X for (I = OldAllocatedSize+1; I <= NewSize; I++) X { Matrix->ExtToIntRowMap[I] = -1; X Matrix->ExtToIntColMap[I] = -1; X } X X return; } #endif X X X X X X X X X #if INITIALIZE /*! X * Initialize the matrix. X * X * With the \a INITIALIZE compiler option (see spConfig.h) set true, X * Sparse allows the user to keep initialization information with each X * structurally nonzero matrix element. Each element has a pointer X * that is set and used by the user. The user can set this pointer X * using spInstallInitInfo() and may be read using spGetInitInfo(). Both X * may be used only after the element exists. The function X * spInitialize() is a user customizable way to initialize the matrix. X * Passed to this routine is a function pointer. spInitialize() sweeps X * through every element in the matrix and checks the \a pInitInfo X * pointer (the user supplied pointer). If the \a pInitInfo is \a NULL, X * which is true unless the user changes it (almost always true for X * fill-ins), then the element is zeroed. Otherwise, the function X * pointer is called and passed the \a pInitInfo pointer as well as the X * element pointer and the external row and column numbers. If the X * user sets the value of each element, then spInitialize() replaces X * spClear(). X * X * The user function is expected to return a nonzero integer if there X * is a fatal error and zero otherwise. Upon encountering a nonzero X * return code, spInitialize() terminates, sets the error state of X * the matrix to be \a spMANGLED, and returns the error code. X * X * \return X * Returns the return value of the \a pInit() function. X * \param eMatrix X * Pointer to matrix. X * \param pInit X * Pointer to a function that initializes an element. X X * \see spClear() X */ X int spInitialize( X spMatrix eMatrix, X int (*pInit)( X spElement *pElement, X spGenericPtr pInitInfo, X int Row, X int Col X ) ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; int J, Error, Col; X /* Begin `spInitialize'. */ X ASSERT_IS_SPARSE( Matrix ); X #if spCOMPLEX /* Clear imaginary part of matrix if matrix is real but was complex. */ X if (Matrix->PreviousMatrixWasComplex AND NOT Matrix->Complex) X { for (J = Matrix->Size; J > 0; J--) X { pElement = Matrix->FirstInCol[J]; X while (pElement != NULL) X { pElement->Imag = 0.0; X pElement = pElement->NextInCol; X } X } X } #endif /* spCOMPLEX */ X /* Initialize the matrix. */ X for (J = Matrix->Size; J > 0; J--) X { pElement = Matrix->FirstInCol[J]; X Col = Matrix->IntToExtColMap[J]; X while (pElement != NULL) X { if (pElement->pInitInfo == NULL) X { pElement->Real = 0.0; # if spCOMPLEX X pElement->Imag = 0.0; # endif X } X else X { Error = (*pInit)((RealNumber *)pElement, pElement->pInitInfo, X Matrix->IntToExtRowMap[pElement->Row], Col); X if (Error) X { Matrix->Error = spMANGLED; X return Error; X } X X } X pElement = pElement->NextInCol; X } X } X /* Empty the trash. */ X Matrix->TrashCan.Real = 0.0; #if spCOMPLEX X Matrix->TrashCan.Imag = 0.0; #endif X X Matrix->Error = spOKAY; X Matrix->Factored = NO; X Matrix->SingularCol = 0; X Matrix->SingularRow = 0; X Matrix->PreviousMatrixWasComplex = Matrix->Complex; X return 0; } X X X X /*! X * This function installs a pointer to a data structure that is used X * to contain initialization information to a matrix element. It is X * is then used by spInitialize() to initialize the matrix. X * X * \param pElement X * Pointer to matrix element. X * \param pInitInfo X * Pointer to the data structure that will contain initialiation X * information. X * \see spInitialize() X */ X void spInstallInitInfo( X spElement *pElement, X spGenericPtr pInitInfo ) { /* Begin `spInstallInitInfo'. */ X vASSERT( pElement != NULL, "Invalid element pointer" ); X X ((ElementPtr)pElement)->pInitInfo = pInitInfo; } X X /*! X * This function returns a pointer to a data structure that is used X * to contain initialization information to a matrix element. X * X * \return X * The pointer to the initialiation information data structure X * that is associated with a particular matrix element. X * X * \param pElement X * Pointer to the matrix element. X * X * \see spInitialize() X */ spGenericPtr spGetInitInfo( X spElement *pElement ) { /* Begin `spGetInitInfo'. */ X vASSERT( pElement != NULL, "Invalid element pointer" ); X X return (spGenericPtr)((ElementPtr)pElement)->pInitInfo; } #endif /* INITIALIZE */ SHAR_EOF (set 20 16 10 12 10 44 29 'sparse/spBuild.c'; eval "$shar_touch") && chmod 0600 'sparse/spBuild.c' if test $? -ne 0 then ${echo} 'restore of sparse/spBuild.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spBuild.c: MD5 check failed' ) << \SHAR_EOF 52e89651613f1f96d78e8ca19733608d sparse/spBuild.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spBuild.c'` -ne 32504 && \ ${echo} 'restoration warning: size of sparse/spBuild.c is not 32504' fi fi # ============= sparse/spConfig.h ============== if test -f 'sparse/spConfig.h' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spConfig.h (file already exists)' else ${echo} 'x - extracting sparse/spConfig.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spConfig.h' && /* CONFIGURATION MACRO DEFINITIONS for sparse matrix routines */ /*! X * \file X * X * This file contains macros for the sparse matrix routines that are used X * to define the personality of the routines. The user is expected to X * modify this file to maximize the performance of the routines with X * his/her matrices. X * X * Macros are distinguished by using solely capital letters in their X * identifiers. This contrasts with C defined identifiers which are X * strictly lower case, and program variable and procedure names which use X * both upper and lower case. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X * X * $Date: 2003/06/30 19:41:29 $ X * $Revision: 1.5 $ X */ X X #ifndef spCONFIG_DEFS #define spCONFIG_DEFS X X X X #ifdef spINSIDE_SPARSE /* X * OPTIONS X * X * These are compiler options. Set each option to one to compile that X * section of the code. If a feature is not desired, set the macro X * to NO. X */ X /* Begin options. */ X /* Arithmetic Precision X * X * The precision of the arithmetic used by Sparse can be set by X * changing changing the spREAL macro. This macro is X * contained in the file spMatrix.h. It is strongly suggested to X * used double precision with circuit simulators. Note that X * because C always performs arithmetic operations in double X * precision, the only benefit to using single precision is that X * less storage is required. There is often a noticeable speed X * penalty when using single precision. Sparse internally refers X * to a spREAL as a RealNumber. X */ X /*! X * This specifies that the routines are expected to handle real X * systems of equations. The routines can be compiled to handle X * both real and complex systems at the same time, but there is a X * slight speed and memory advantage if the routines are complied X * to handle only real systems of equations. X */ #define REAL YES X /*! X * Setting this compiler flag true (1) makes the matrix X * expandable before it has been factored. If the matrix is X * expandable, then if an element is added that would be X * considered out of bounds in the current matrix, the size of X * the matrix is increased to hold that element. As a result, X * the size of the matrix need not be known before the matrix is X * built. The matrix can be allocated with size zero and expanded. X */ #define EXPANDABLE YES X /*! X * This option allows the set of external row and column numbers X * to be non-packed. In other words, the row and column numbers X * do not have to be contiguous. The priced paid for this X * flexibility is that when \a TRANSLATE is set true, the time X * required to initially build the matrix will be greater because X * the external row and column number must be translated into X * internal equivalents. This translation brings about other X * benefits though. First, the spGetElement() and X * spGetAdmittance() routines may be used after the matrix has X * been factored. Further, elements, and even rows and columns, X * may be added to the matrix, and row and columns may be deleted X * from the matrix, after it has been factored. Note that when X * the set of row and column number is not a packed set, neither X * are the \a RHS and \a Solution vectors. Thus the size of these X * vectors must be at least as large as the external size, which X * is the value of the largest given row or column numbers. X */ #define TRANSLATE YES X /*! X * Causes the spInitialize(), spGetInitInfo(), and X * spInstallInitInfo() routines to be compiled. These routines X * allow the user to store and read one pointer in each nonzero X * element in the matrix. spInitialize() then calls a user X * specified function for each structural nonzero in the matrix, X * and includes this pointer as well as the external row and X * column numbers as arguments. This allows the user to write X * custom matrix initialization routines. X */ #define INITIALIZE YES X /*! X * Many matrices, and in particular node- and modified-node X * admittance matrices, tend to be nearly symmetric and nearly X * diagonally dominant. For these matrices, it is a good idea to X * select pivots from the diagonal. With this option enabled, X * this is exactly what happens, though if no satisfactory pivot X * can be found on the diagonal, an off-diagonal pivot will be X * used. If this option is disabled, Sparse does not X * preferentially search the diagonal. Because of this, Sparse X * has a wider variety of pivot candidates available, and so X * presumably fewer fill-ins will be created. However, the X * initial pivot selection process will take considerably longer. X * If working with node admittance matrices, or other matrices X * with a strong diagonal, it is probably best to use X * \a DIAGONAL_PIVOTING for two reasons. First, accuracy will be X * better because pivots will be chosen from the large diagonal X * elements, thus reducing the chance of growth. Second, a near X * optimal ordering will be chosen quickly. If the class of X * matrices you are working with does not have a strong diagonal, X * do not use \a DIAGONAL_PIVOTING, but consider using a larger X * threshold. When \a DIAGONAL_PIVOTING is turned off, the following X * options and constants are not used: \a MODIFIED_MARKOWITZ, X * \a MAX_MARKOWITZ_TIES, and \a TIES_MULTIPLIER. X */ #define DIAGONAL_PIVOTING YES X /*! X * This determines whether arrays start at an index of zero or one. X * This option is necessitated by the fact that standard C X * convention dictates that arrays begin with an index of zero but X * the standard mathematic convention states that arrays begin with X * an index of one. So if you prefer to start your arrays with X * zero, or your calling Sparse from FORTRAN, set ARRAY_OFFSET to X * NO or 0. Otherwise, set ARRAY_OFFSET to YES or 1. Note that if X * you use an offset of one, the arrays that you pass to Sparse X * must have an allocated length of one plus the size of the X * matrix. ARRAY_OFFSET must be either 0 or 1, no other offsets X * are valid. X */ #define ARRAY_OFFSET NOT FORTRAN X /*! X * This specifies that the modified Markowitz method of pivot X * selection is to be used. The modified Markowitz method differs X * from standard Markowitz in two ways. First, under modified X * Markowitz, the search for a pivot can be terminated early if a X * adequate (in terms of sparsity) pivot candidate is found. X * Thus, when using modified Markowitz, the initial factorization X * can be faster, but at the expense of a suboptimal pivoting X * order that may slow subsequent factorizations. The second X * difference is in the way modified Markowitz breaks Markowitz X * ties. When two or more elements are pivot candidates and they X * all have the same Markowitz product, then the tie is broken by X * choosing the element that is best numerically. The numerically X * best element is the one with the largest ratio of its magnitude X * to the magnitude of the largest element in the same column, X * excluding itself. The modified Markowitz method results in X * marginally better accuracy. This option is most appropriate X * for use when working with very large matrices where the initial X * factor time represents an unacceptable burden. \a NO is recommended. X */ #define MODIFIED_MARKOWITZ NO X /*! X * This specifies that the spDeleteRowAndCol() routine X * should be compiled. Note that for this routine to be X * compiled, both \a DELETE and \a TRANSLATE should be set true. X */ #define DELETE YES X /*! X * This specifies that the spStripFills() routine should be compiled. X */ #define STRIP YES X /*! X * This specifies that the routine that preorders modified node X * admittance matrices should be compiled. This routine results X * in greater speed and accuracy if used with this type of X * matrix. X */ #define MODIFIED_NODAL YES X /*! X * This specifies that the routines that allow four related X * elements to be entered into the matrix at once should be X * compiled. These elements are usually related to an X * admittance. The routines affected by \a QUAD_ELEMENT are the X * spGetAdmittance(), spGetQuad() and spGetOnes() routines. X */ #define QUAD_ELEMENT YES X /*! X * This specifies that the routines that solve the matrix as if X * it was transposed should be compiled. These routines are X * useful when performing sensitivity analysis using the adjoint X * method. X */ #define TRANSPOSE YES X /*! X * This specifies that the routine that performs scaling on the X * matrix should be complied. Scaling is not strongly X * supported. The routine to scale the matrix is provided, but X * no routines are provided to scale and descale the RHS and X * Solution vectors. It is suggested that if scaling is desired, X * it only be preformed when the pivot order is being chosen [in X * spOrderAndFactor()]. This is the only time scaling has X * an effect. The scaling may then either be removed from the X * solution by the user or the scaled factors may simply be X * thrown away. \a NO is recommended. X */ #define SCALING YES X /*! X * This specifies that routines that are used to document the X * matrix, such as spPrint() and spFileMatrix(), should be X * compiled. X */ #define DOCUMENTATION YES X /*! X * This specifies that routines that are used to multily the X * matrix by a vector, such as spMultiply() and spMultTransposed(), should be X * compiled. X */ #define MULTIPLICATION YES X /*! X * This specifies that the routine spDeterminant() should be complied. X */ #define DETERMINANT YES X /*! X * This specifies that spLargestElement() and spRoundoff() should X * be compiled. These routines are used to check the stability (and X * hence the quality of the pivoting) of the factorization by X * computing a bound on the size of the element is the matrix X * \f$ E = A - LU \f$. If this bound is very high after applying X * spOrderAndFactor(), then the pivot threshold should be raised. X * If the bound increases greatly after using spFactor(), then the X * matrix should probably be reordered. Recomend \a NO. X */ #define STABILITY YES X /*! X * This specifies that spCondition() and spNorm(), the code that X * computes a good estimate of the condition number of the matrix, X * should be compiled. Recomend \a NO. X */ #define CONDITION YES X /*! X * This specifies that spPseudoCondition(), the code that computes X * a crude and easily fooled indicator of ill-conditioning in the X * matrix, should be compiled. Recomend \a NO. X */ #define PSEUDOCONDITION YES X /*! X * This specifies that the \a FORTRAN interface routines should be X * compiled. When interfacing to \a FORTRAN programs, the \a ARRAY_OFFSET X * options should be set to NO. X */ #define FORTRAN NO X /*! X * This specifies that additional error checking will be compiled. X * The type of error checked are those that are common when the X * matrix routines are first integrated into a user's program. Once X * the routines have been integrated in and are running smoothly, this X * option should be turned off. \a YES is recommended. X */ #define DEBUG YES X #endif /* spINSIDE_SPARSE */ X /* X * The following options affect Sparse exports and so are exported as a X * side effect. For this reason they use the `sp' prefix. The boolean X * constants YES an NO are not defined in spMatrix.h to avoid conflicts X * with user code, so use 0 for NO and 1 for YES. X */ X /*! X * This specifies that the routines will be complied to handle X * complex systems of equations. X */ #define spCOMPLEX 1 X /*! X * This specifies the format for complex vectors. If this is set X * false then a complex vector is made up of one double sized X * array of RealNumber's in which the real and imaginary numbers X * are placed alternately in the array. In other X * words, the first entry would be Complex[1].Real, then comes X * Complex[1].Imag, then Complex[2].Real, etc. If X * \a spSEPARATED_COMPLEX_VECTORS is set true, then each complex X * vector is represented by two arrays of \a spREALs, one with X * the real terms, the other with the imaginary. \a NO is recommended. X */ #define spSEPARATED_COMPLEX_VECTORS 0 X #ifdef spINSIDE_SPARSE X X X X X X X /* X * MATRIX CONSTANTS X * X * These constants are used throughout the sparse matrix routines. They X * should be set to suit the type of matrix being solved. X */ X /* Begin constants. */ X /*! X * The relative threshold used if the user enters an invalid X * threshold. Also the threshold used by spFactor() when X * calling spOrderAndFactor(). The default threshold should X * not be less than or equal to zero nor larger than one. X * 0.001 is recommended. X */ #define DEFAULT_THRESHOLD 1.0e-3 X /*! X * This indicates whether spOrderAndFactor() should use diagonal X * pivoting as default. This issue only arises when X * spOrderAndFactor() is called from spFactor(). \a YES is recommended. X */ #define DIAG_PIVOTING_AS_DEFAULT YES X /*! X * This number multiplied by the size of the matrix equals the number X * of elements for which memory is initially allocated in spCreate(). X * 6 is recommended. X */ #define SPACE_FOR_ELEMENTS 6 X /*! X * This number multiplied by the size of the matrix equals the number X * of elements for which memory is initially allocated and specifically X * reserved for fill-ins in spCreate(). 4 is recommended. X */ #define SPACE_FOR_FILL_INS 4 X /*! X * The number of matrix elements requested from the malloc utility on X * each call to it. Setting this value greater than 1 reduces the X * amount of overhead spent in this system call. On a virtual memory X * machine, its good to allocate slightly less than a page worth of X * elements at a time (or some multiple thereof). X * 31 is recommended. X */ #define ELEMENTS_PER_ALLOCATION 31 X /*! X * The minimum allocated size of a matrix. Note that this does not X * limit the minimum size of a matrix. This just prevents having to X * resize a matrix many times if the matrix is expandable, large and X * allocated with an estimated size of zero. This number should not X * be less than one. X */ #define MINIMUM_ALLOCATED_SIZE 6 X /*! X * The amount the allocated size of the matrix is increased when it X * is expanded. X */ #define EXPANSION_FACTOR 1.5 X /*! X * Some terminology should be defined. The Markowitz row count is the number X * of non-zero elements in a row excluding the one being considered as pivot. X * There is one Markowitz row count for every row. The Markowitz column X * is defined similarly for columns. The Markowitz product for an element X * is the product of its row and column counts. It is a measure of how much X * work would be required on the next step of the factorization if that X * element were chosen to be pivot. A small Markowitz product is desirable. X * X * This number is used for two slightly different things, both of which X * relate to the search for the best pivot. First, it is the maximum X * number of elements that are Markowitz tied that will be sifted X * through when trying to find the one that is numerically the best. X * Second, it creates an upper bound on how large a Markowitz product X * can be before it eliminates the possibility of early termination X * of the pivot search. In other words, if the product of the smallest X * Markowitz product yet found and \a TIES_MULTIPLIER is greater than X * \a MAX_MARKOWITZ_TIES, then no early termination takes place. X * Set \a MAX_MARKOWITZ_TIES to some small value if no early termination of X * the pivot search is desired. An array of RealNumbers is allocated X * of size \a MAX_MARKOWITZ_TIES so it must be positive and shouldn't X * be too large. Active when MODIFIED_MARKOWITZ is 1 (YES). X * 100 is recommended. X * \see TIES_MULTIPLIER X */ #define MAX_MARKOWITZ_TIES 100 X /*! X * Specifies the number of Markowitz ties that are allowed to occur X * before the search for the pivot is terminated early. Set to some X * large value if no early termination of the pivot search is desired. X * This number is multiplied times the Markowitz product to determine X * how many ties are required for early termination. This means that X * more elements will be searched before early termination if a large X * number of fill-ins could be created by accepting what is currently X * considered the best choice for the pivot. Active when X * \a MODIFIED_MARKOWITZ is 1 (YES). Setting this number to zero X * effectively eliminates all pivoting, which should be avoided. X * This number must be positive. \a TIES_MULTIPLIER is also used when X * diagonal pivoting breaks down. 5 is recommended. X * \see MAX_MARKOWITZ_TIES X */ #define TIES_MULTIPLIER 5 X /*! X * Which partition mode is used by spPartition() as default. X * Possibilities include \a spDIRECT_PARTITION (each row used direct X * addressing, best for a few relatively dense matrices), X * \a spINDIRECT_PARTITION (each row used indirect addressing, best X * for a few very sparse matrices), and \a spAUTO_PARTITION (direct or X * indirect addressing is chosen on a row-by-row basis, carries a large X * overhead, but speeds up both dense and sparse matrices, best if there X * is a large number of matrices that can use the same ordering. X */ #define DEFAULT_PARTITION spAUTO_PARTITION X /*! X * The number of characters per page width. Set to 80 for terminal, X * 132 for line printer. Controls how many columns printed by X * spPrint() per page width. X */ #define PRINTER_WIDTH 80 X X X X X X X X X X #endif /* spINSIDE_SPARSE */ /* X * PORTABILITY MACROS X */ X #ifdef __STDC__ # define spcCONCAT(prefix,suffix) prefix ## suffix # define spcQUOTE(x) # x # define spcFUNC_NEEDS_FILE(func,file) \ X func ## _requires_ ## file ## _to_be_included_ #else # define spcCONCAT(prefix,suffix) prefix/**/suffix # define spcQUOTE(x) "x" # define spcFUNC_NEEDS_FILE(func,file) \ X func/**/_requires_/**/file/**/_to_be_included_ #endif X #if defined(__cplusplus) || defined(c_plusplus) X /* X * Definitions for C++ X */ # define spcEXTERN extern "C" # define spcNO_ARGS # define spcCONST const X typedef void *spGenericPtr; #else #ifdef __STDC__ X /* X * Definitions for ANSI C X */ # define spcEXTERN extern # define spcNO_ARGS void # define spcCONST const X typedef void *spGenericPtr; # else X /* X * Definitions for K&R C -- ignore function prototypes X */ # define spcEXTERN extern # define spcNO_ARGS # define spcCONST X typedef char *spGenericPtr; #endif #endif X #ifdef spINSIDE_SPARSE X X X X X X X /* X * MACHINE CONSTANTS X * X * These numbers must be updated when the program is ported to a new machine. X */ X /* Begin machine constants. */ #include #include X /*! The resolution of spREAL. */ #define MACHINE_RESOLUTION DBL_EPSILON X /*! The largest possible value of spREAL. */ #define LARGEST_REAL DBL_MAX X /*! The smalles possible positive value of spREAL. */ #define SMALLEST_REAL DBL_MIN X /*! The largest possible value of shorts. */ #define LARGEST_SHORT_INTEGER SHRT_MAX X /*! The largest possible value of longs. */ #define LARGEST_LONG_INTEGER LONG_MAX X X X X X X /* ANNOTATION */ /*! X * This macro changes the amount of annotation produced by the matrix X * routines. The annotation is used as a debugging aid. Change the number X * associated with \a ANNOTATE to change the amount of annotation produced by X * the program. Possible values include \a NONE, \a ON_STRANGE_BEHAVIOR, and X * \a FULL. \a NONE is recommended. X */ #define ANNOTATE NONE X /*! X * A possible value for \a ANNOTATE. Disables all annotation. X */ #define NONE 0 X /*! X * A possible value for \a ANNOTATE. Causes annotation to be produce X * upon unusual occurances only. X */ #define ON_STRANGE_BEHAVIOR 1 X /*! X * A possible value for \a ANNOTATE. Enables full annotation. X */ #define FULL 2 X #endif /* spINSIDE_SPARSE */ #endif /* spCONFIG_DEFS */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spConfig.h'; eval "$shar_touch") && chmod 0600 'sparse/spConfig.h' if test $? -ne 0 then ${echo} 'restore of sparse/spConfig.h failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spConfig.h: MD5 check failed' ) << \SHAR_EOF 6d3a958b3b6d12db4938a5d61837e099 sparse/spConfig.h SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spConfig.h'` -ne 20494 && \ ${echo} 'restoration warning: size of sparse/spConfig.h is not 20494' fi fi # ============= sparse/spDefs.h ============== if test -f 'sparse/spDefs.h' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spDefs.h (file already exists)' else ${echo} 'x - extracting sparse/spDefs.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spDefs.h' && /* X * DATA STRUCTURE AND MACRO DEFINITIONS for Sparse. X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X * X * This file contains common type definitions and macros for the sparse X * matrix routines. These definitions are of no interest to the user. X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X * X * $Date: 2003/06/29 04:19:52 $ X * $Revision: 1.2 $ X */ X X X X /* X * If running lint, change some of the compiler options to get a more X * complete inspection. X */ X #ifdef lint #undef REAL #undef spCOMPLEX #undef EXPANDABLE #undef TRANSLATE #undef INITIALIZE #undef DELETE #undef STRIP #undef MODIFIED_NODAL #undef QUAD_ELEMENT #undef TRANSPOSE #undef SCALING #undef DOCUMENTATION #undef MULTIPLICATION #undef DETERMINANT #undef CONDITION #undef PSEUDOCONDITION #undef FORTRAN #undef DEBUG X #define REAL YES #define spCOMPLEX YES #define EXPANDABLE YES #define TRANSLATE YES #define INITIALIZE YES #define DELETE YES #define STRIP YES #define MODIFIED_NODAL YES #define QUAD_ELEMENT YES #define TRANSPOSE YES #define SCALING YES #define DOCUMENTATION YES #define MULTIPLICATION YES #define DETERMINANT YES #define CONDITION YES #define PSEUDOCONDITION YES #define FORTRAN YES #define DEBUG YES X #define LINT YES #else /* not lint */ #define LINT NO #endif /* not lint */ X X X X X X X /* X * MACRO DEFINITIONS X * X * Macros are distinguished by using solely capital letters in their X * identifiers. This contrasts with C defined identifiers which are strictly X * lower case, and program variable and procedure names which use both upper X * and lower case. X */ X /* Begin macros. */ X /* Boolean data type */ #define BOOLEAN int #define NO 0 #define YES 1 #define NOT ! #define AND && #define OR || X /* NULL pointer */ #ifndef NULL #define NULL 0 #endif X /* Define macros for validating matrix. */ #define SPARSE_ID 0xDeadBeef /* Arbitrary. */ #define IS_SPARSE(matrix) (((matrix) != NULL) AND \ X ((matrix)->ID == SPARSE_ID)) #define NO_ERRORS(matrix) (((matrix)->Error >= spOKAY) AND \ X ((matrix)->Error < spFATAL)) #define IS_FACTORED(matrix) ((matrix)->Factored AND \ X NOT (matrix)->NeedsOrdering) X #define ASSERT_IS_SPARSE(matrix) vASSERT( IS_SPARSE(matrix), \ X spcMatrixIsNotValid ) #define ASSERT_NO_ERRORS(matrix) vASSERT( NO_ERRORS(matrix), \ X spcErrorsMustBeCleared ) #define ASSERT_IS_FACTORED(matrix) vASSERT( IS_FACTORED(matrix), \ X spcMatrixMustBeFactored ) #define ASSERT_IS_NOT_FACTORED(matrix) vASSERT( NOT (matrix)->Factored, \ X spcMatrixMustNotBeFactored ) X /* Macro commands */ /* Macro functions that return the maximum or minimum independent of type. */ #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) X /* Macro function that returns the absolute value of a floating point number. */ #define ABS(a) ((a) < 0 ? -(a) : (a)) X /* Macro function that returns the square of a number. */ #define SQR(a) ((a)*(a)) X /* Macro procedure that swaps two entities. */ #define SWAP(type, a, b) {type swapx; swapx = a; a = b; b = swapx;} X X /* X * COMPLEX OPERATION MACROS X */ X /* Macro function that returns the approx absolute value of a complex number. */ #if spCOMPLEX #define ELEMENT_MAG(ptr) (ABS((ptr)->Real) + ABS((ptr)->Imag)) #else #define ELEMENT_MAG(ptr) ((ptr)->Real < 0.0 ? -(ptr)->Real : (ptr)->Real) #endif X /* Complex assignment statements. */ #define CMPLX_ASSIGN(to,from) \ { (to).Real = (from).Real; \ X (to).Imag = (from).Imag; \ } #define CMPLX_CONJ_ASSIGN(to,from) \ { (to).Real = (from).Real; \ X (to).Imag = -(from).Imag; \ } #define CMPLX_NEGATE_ASSIGN(to,from) \ { (to).Real = -(from).Real; \ X (to).Imag = -(from).Imag; \ } #define CMPLX_CONJ_NEGATE_ASSIGN(to,from) \ { (to).Real = -(from).Real; \ X (to).Imag = (from).Imag; \ } #define CMPLX_CONJ(a) (a).Imag = -(a).Imag #define CMPLX_NEGATE(a) \ { (a).Real = -(a).Real; \ X (a).Imag = -(a).Imag; \ } X /* Macro that returns the approx magnitude (L-1 norm) of a complex number. */ #define CMPLX_1_NORM(a) (ABS((a).Real) + ABS((a).Imag)) X /* Macro that returns the approx magnitude (L-infinity norm) of a complex. */ #define CMPLX_INF_NORM(a) (MAX (ABS((a).Real),ABS((a).Imag))) X /* Macro function that returns the magnitude (L-2 norm) of a complex number. */ #define CMPLX_2_NORM(a) (sqrt((a).Real*(a).Real + (a).Imag*(a).Imag)) X /* Macro function that performs complex addition. */ #define CMPLX_ADD(to,from_a,from_b) \ { (to).Real = (from_a).Real + (from_b).Real; \ X (to).Imag = (from_a).Imag + (from_b).Imag; \ } X /* Macro function that performs complex subtraction. */ #define CMPLX_SUBT(to,from_a,from_b) \ { (to).Real = (from_a).Real - (from_b).Real; \ X (to).Imag = (from_a).Imag - (from_b).Imag; \ } X /* Macro function that is equivalent to += operator for complex numbers. */ #define CMPLX_ADD_ASSIGN(to,from) \ { (to).Real += (from).Real; \ X (to).Imag += (from).Imag; \ } X /* Macro function that is equivalent to -= operator for complex numbers. */ #define CMPLX_SUBT_ASSIGN(to,from) \ { (to).Real -= (from).Real; \ X (to).Imag -= (from).Imag; \ } X /* Macro function that multiplies a complex number by a scalar. */ #define SCLR_MULT(to,sclr,cmplx) \ { (to).Real = (sclr) * (cmplx).Real; \ X (to).Imag = (sclr) * (cmplx).Imag; \ } X /* Macro function that multiply-assigns a complex number by a scalar. */ #define SCLR_MULT_ASSIGN(to,sclr) \ { (to).Real *= (sclr); \ X (to).Imag *= (sclr); \ } X /* Macro function that multiplies two complex numbers. */ #define CMPLX_MULT(to,from_a,from_b) \ { (to).Real = (from_a).Real * (from_b).Real - \ X (from_a).Imag * (from_b).Imag; \ X (to).Imag = (from_a).Real * (from_b).Imag + \ X (from_a).Imag * (from_b).Real; \ } X /* Macro function that implements to *= from for complex numbers. */ #define CMPLX_MULT_ASSIGN(to,from) \ { RealNumber to_real_ = (to).Real; \ X (to).Real = to_real_ * (from).Real - \ X (to).Imag * (from).Imag; \ X (to).Imag = to_real_ * (from).Imag + \ X (to).Imag * (from).Real; \ } X /* Macro function that multiplies two complex numbers, the first of which is X * conjugated. */ #define CMPLX_CONJ_MULT(to,from_a,from_b) \ { (to).Real = (from_a).Real * (from_b).Real + \ X (from_a).Imag * (from_b).Imag; \ X (to).Imag = (from_a).Real * (from_b).Imag - \ X (from_a).Imag * (from_b).Real; \ } X /* Macro function that multiplies two complex numbers and then adds them X * to another. to = add + mult_a * mult_b */ #define CMPLX_MULT_ADD(to,mult_a,mult_b,add) \ { (to).Real = (mult_a).Real * (mult_b).Real - \ X (mult_a).Imag * (mult_b).Imag + (add).Real; \ X (to).Imag = (mult_a).Real * (mult_b).Imag + \ X (mult_a).Imag * (mult_b).Real + (add).Imag; \ } X /* Macro function that subtracts the product of two complex numbers from X * another. to = subt - mult_a * mult_b */ #define CMPLX_MULT_SUBT(to,mult_a,mult_b,subt) \ { (to).Real = (subt).Real - (mult_a).Real * (mult_b).Real + \ X (mult_a).Imag * (mult_b).Imag; \ X (to).Imag = (subt).Imag - (mult_a).Real * (mult_b).Imag - \ X (mult_a).Imag * (mult_b).Real; \ } X /* Macro function that multiplies two complex numbers and then adds them X * to another. to = add + mult_a* * mult_b where mult_a* represents mult_a X * conjugate. */ #define CMPLX_CONJ_MULT_ADD(to,mult_a,mult_b,add) \ { (to).Real = (mult_a).Real * (mult_b).Real + \ X (mult_a).Imag * (mult_b).Imag + (add).Real; \ X (to).Imag = (mult_a).Real * (mult_b).Imag - \ X (mult_a).Imag * (mult_b).Real + (add).Imag; \ } X /* Macro function that multiplies two complex numbers and then adds them X * to another. to += mult_a * mult_b */ #define CMPLX_MULT_ADD_ASSIGN(to,from_a,from_b) \ { (to).Real += (from_a).Real * (from_b).Real - \ X (from_a).Imag * (from_b).Imag; \ X (to).Imag += (from_a).Real * (from_b).Imag + \ X (from_a).Imag * (from_b).Real; \ } X /* Macro function that multiplies two complex numbers and then subtracts them X * from another. */ #define CMPLX_MULT_SUBT_ASSIGN(to,from_a,from_b) \ { (to).Real -= (from_a).Real * (from_b).Real - \ X (from_a).Imag * (from_b).Imag; \ X (to).Imag -= (from_a).Real * (from_b).Imag + \ X (from_a).Imag * (from_b).Real; \ } X /* Macro function that multiplies two complex numbers and then adds them X * to the destination. to += from_a* * from_b where from_a* represents from_a X * conjugate. */ #define CMPLX_CONJ_MULT_ADD_ASSIGN(to,from_a,from_b) \ { (to).Real += (from_a).Real * (from_b).Real + \ X (from_a).Imag * (from_b).Imag; \ X (to).Imag += (from_a).Real * (from_b).Imag - \ X (from_a).Imag * (from_b).Real; \ } X /* Macro function that multiplies two complex numbers and then subtracts them X * from the destination. to -= from_a* * from_b where from_a* represents from_a X * conjugate. */ #define CMPLX_CONJ_MULT_SUBT_ASSIGN(to,from_a,from_b) \ { (to).Real -= (from_a).Real * (from_b).Real + \ X (from_a).Imag * (from_b).Imag; \ X (to).Imag -= (from_a).Real * (from_b).Imag - \ X (from_a).Imag * (from_b).Real; \ } X /* X * Macro functions that provide complex division. X */ X /* Complex division: to = num / den */ #define CMPLX_DIV(to,num,den) \ { RealNumber r_, s_; \ X if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \ X ((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \ X { r_ = (den).Imag / (den).Real; \ X s_ = (den).Real + r_*(den).Imag; \ X (to).Real = ((num).Real + r_*(num).Imag)/s_; \ X (to).Imag = ((num).Imag - r_*(num).Real)/s_; \ X } \ X else \ X { r_ = (den).Real / (den).Imag; \ X s_ = (den).Imag + r_*(den).Real; \ X (to).Real = (r_*(num).Real + (num).Imag)/s_; \ X (to).Imag = (r_*(num).Imag - (num).Real)/s_; \ X } \ } X /* Complex division and assignment: num /= den */ #define CMPLX_DIV_ASSIGN(num,den) \ { RealNumber r_, s_, t_; \ X if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \ X ((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \ X { r_ = (den).Imag / (den).Real; \ X s_ = (den).Real + r_*(den).Imag; \ X t_ = ((num).Real + r_*(num).Imag)/s_; \ X (num).Imag = ((num).Imag - r_*(num).Real)/s_; \ X (num).Real = t_; \ X } \ X else \ X { r_ = (den).Real / (den).Imag; \ X s_ = (den).Imag + r_*(den).Real; \ X t_ = (r_*(num).Real + (num).Imag)/s_; \ X (num).Imag = (r_*(num).Imag - (num).Real)/s_; \ X (num).Real = t_; \ X } \ } X /* Complex reciprocation: to = 1.0 / den */ #define CMPLX_RECIPROCAL(to,den) \ { RealNumber r_; \ X if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \ X ((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \ X { r_ = (den).Imag / (den).Real; \ X (to).Imag = -r_*((to).Real = 1.0/((den).Real + r_*(den).Imag)); \ X } \ X else \ X { r_ = (den).Real / (den).Imag; \ X (to).Real = -r_*((to).Imag = -1.0/((den).Imag + r_*(den).Real));\ X } \ } X X X X X X /* X * ASSERT and ABORT X * X * Macro used to assert that if the code is working correctly, then X * a condition must be true. If not, then execution is terminated X * and an error message is issued stating that there is an internal X * error and giving the file and line number. These assertions are X * not evaluated unless the DEBUG flag is true. X */ X #if DEBUG #define ASSERT(condition) \ { if (NOT(condition)) \ X { (void)fflush(stdout); \ X (void)fprintf(stderr, "sparse: internal error detected in file `%s' at line %d.\n assertion `%s' failed.\n",\ X __FILE__, __LINE__, spcQUOTE(condition) ); \ X (void)fflush(stderr); \ X abort(); \ X } \ } #else #define ASSERT(condition) #endif X #if DEBUG #define vASSERT(condition,message) \ { if (NOT(condition)) \ X vABORT(message); \ } #else #define vASSERT(condition,message) #endif X #if DEBUG #define vABORT(message) \ { (void)fflush(stdout); \ X (void)fprintf(stderr, "sparse: internal error detected in file `%s' at line %d.\n %s.\n", __FILE__, __LINE__, message );\ X (void)fflush(stderr); \ X abort(); \ } X #define ABORT() \ { (void)fflush(stdout); \ X (void)fprintf(stderr, "sparse: internal error detected in file `%s' at line %d.\n", __FILE__, __LINE__ ); \ X (void)fflush(stderr); \ X abort(); \ } #else #define vABORT(message) abort() #define ABORT() abort() #endif X X X X X X /* X * IMAGINARY VECTORS X * X * The imaginary vectors iRHS and iSolution are only needed when the X * options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set. The following X * macro makes it easy to include or exclude these vectors as needed. X */ X #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS #define IMAG_VECTORS , iRHS, iSolution #define IMAG_RHS , iRHS #define IMAG_RHS_DECL , RealVector iRHS #define IMAG_VECT_DECL , RealVector iRHS, RealVector iSolution #else #define IMAG_VECTORS #define IMAG_RHS #define IMAG_RHS_DECL #define IMAG_VECT_DECL #endif X X X X X X /* X * MEMORY ALLOCATION X */ X spcEXTERN void *malloc(size_t size); spcEXTERN void *calloc(size_t nmemb, size_t size); spcEXTERN void *realloc(void *ptr, size_t size); spcEXTERN void free(void *ptr); spcEXTERN void abort(void); X #define ALLOC(type,number) ((type *)malloc((unsigned)(sizeof(type)*(number)))) #define REALLOC(ptr,type,number) \ X ptr = (type *)realloc((char *)ptr,(unsigned)(sizeof(type)*(number))) #define FREE(ptr) { if ((ptr) != NULL) free((char *)(ptr)); (ptr) = NULL; } X X /* Calloc that properly handles allocating a cleared vector. */ #define CALLOC(ptr,type,number) \ { int i; ptr = ALLOC(type, number); \ X if (ptr != (type *)NULL) \ X for(i=(number)-1;i>=0; i--) ptr[i] = (type) 0; \ } X X X X X X X /* X * Utility Functions X */ /* X * Compute the product of two intergers while avoiding overflow. X * Used when computing Markowitz products. X */ X #define spcMarkoProd(product, op1, op2) \ X if (( (op1) > LARGEST_SHORT_INTEGER AND (op2) != 0) OR \ X ( (op2) > LARGEST_SHORT_INTEGER AND (op1) != 0)) \ X { double fProduct = (double)(op1) * (double)(op2); \ X if (fProduct >= LARGEST_LONG_INTEGER) \ X (product) = LARGEST_LONG_INTEGER; \ X else \ X (product) = (long)fProduct; \ X } \ X else (product) = (op1)*(op2); X X X X X X /* X * REAL NUMBER X */ X /* Begin `RealNumber'. */ X typedef spREAL RealNumber, *RealVector; X X X X X X X X /* X * COMPLEX NUMBER DATA STRUCTURE X * X * >>> Structure fields: X * Real (RealNumber) X * The real portion of the number. Real must be the first X * field in this structure. X * Imag (RealNumber) X * The imaginary portion of the number. This field must follow X * immediately after Real. X */ X /* Begin `ComplexNumber'. */ X typedef struct { RealNumber Real; X RealNumber Imag; } ComplexNumber, *ComplexVector; X X X X X X X X /* X * MATRIX ELEMENT DATA STRUCTURE X * X * Every nonzero element in the matrix is stored in a dynamically allocated X * MatrixElement structure. These structures are linked together in an X * orthogonal linked list. Two different MatrixElement structures exist. X * One is used when only real matrices are expected, it is missing an entry X * for imaginary data. The other is used if complex matrices are expected. X * It contains an entry for imaginary data. X * X * >>> Structure fields: X * Real (RealNumber) X * The real portion of the value of the element. Real must be the first X * field in this structure. X * Imag (RealNumber) X * The imaginary portion of the value of the element. If the matrix X * routines are not compiled to handle complex matrices, then this X * field does not exist. If it exists, it must follow immediately after X * Real. X * Row (int) X * The row number of the element. X * Col (int) X * The column number of the element. X * NextInRow (struct MatrixElement *) X * NextInRow contains a pointer to the next element in the row to the X * right of this element. If this element is the last nonzero in the X * row then NextInRow contains NULL. X * NextInCol (struct MatrixElement *) X * NextInCol contains a pointer to the next element in the column below X * this element. If this element is the last nonzero in the column then X * NextInCol contains NULL. X * pInitInfo (spGenericPtr) X * Pointer to user data used for initialization of the matrix element. X * Initialized to NULL. X * X * >>> Type definitions: X * ElementPtr X * A pointer to a MatrixElement. X * ArrayOfElementPtrs X * An array of ElementPtrs. Used for FirstInRow, FirstInCol and X * Diag pointer arrays. X */ X /* Begin `MatrixElement'. */ X struct MatrixElement { RealNumber Real; #if spCOMPLEX X RealNumber Imag; #endif X int Row; X int Col; X struct MatrixElement *NextInRow; X struct MatrixElement *NextInCol; #if INITIALIZE X spGenericPtr pInitInfo; #endif }; X typedef struct MatrixElement *ElementPtr; typedef ElementPtr *ArrayOfElementPtrs; X X X X X X X X /* X * ALLOCATION DATA STRUCTURE X * X * The sparse matrix routines keep track of all memory that is allocated by X * the operating system so the memory can later be freed. This is done by X * saving the pointers to all the chunks of memory that are allocated to a X * particular matrix in an allocation list. That list is organized as a X * linked list so that it can grow without a priori bounds. X * X * >>> Structure fields: X * AllocatedPtr (void *) X * Pointer to chunk of memory that has been allocated for the matrix. X * NextRecord (struct AllocationRecord *) X * Pointer to the next allocation record. X */ X /* Begin `AllocationRecord'. */ struct AllocationRecord { void *AllocatedPtr; X struct AllocationRecord *NextRecord; }; X typedef struct AllocationRecord *AllocationListPtr; X X X X X X X X X /* X * FILL-IN LIST DATA STRUCTURE X * X * The sparse matrix routines keep track of all fill-ins separately from X * user specified elements so they may be removed by spStripFills(). Fill-ins X * are allocated in bunched in what is called a fill-in lists. The data X * structure defined below is used to organize these fill-in lists into a X * linked-list. X * X * >>> Structure fields: X * pFillinList (ElementPtr) X * Pointer to a fill-in list, or a bunch of fill-ins arranged contiguously X * in memory. X * NumberOfFillinsInList (int) X * Seems pretty self explanatory to me. X * Next (struct FillinListNodeStruct *) X * Pointer to the next fill-in list structures. X */ X /* Begin `FillinListNodeStruct'. */ struct FillinListNodeStruct { ElementPtr pFillinList; X int NumberOfFillinsInList; X struct FillinListNodeStruct *Next; }; X X X X X X X X X X /* X * MATRIX FRAME DATA STRUCTURE X * X * This structure contains all the pointers that support the orthogonal X * linked list that contains the matrix elements. Also included in this X * structure are other numbers and pointers that are used globally by the X * sparse matrix routines and are associated with one particular matrix. X * X * >>> Type definitions: X * MatrixPtr X * A pointer to MatrixFrame. Essentially, a pointer to the matrix. X * X * >>> Structure fields: X * AbsThreshold (RealNumber) X * The absolute magnitude an element must have to be considered as a X * pivot candidate, except as a last resort. X * AllocatedExtSize (int) X * The allocated size of the arrays used to translate external row and X * column numbers to their internal values. X * AllocatedSize (int) X * The currently allocated size of the matrix; the size the matrix can X * grow to when EXPANDABLE is set true and AllocatedSize is the largest X * the matrix can get without requiring that the matrix frame be X * reallocated. X * Complex (BOOLEAN) X * The flag which indicates whether the matrix is complex (true) or X * real. X * CurrentSize (int) X * This number is used during the building of the matrix when the X * TRANSLATE option is set true. It indicates the number of internal X * rows and columns that have elements in them. X * Diag (ArrayOfElementPtrs) X * Array of pointers that points to the diagonal elements. X * DoCmplxDirect (BOOLEAN *) X * Array of flags, one for each column in matrix. If a flag is true X * then corresponding column in a complex matrix should be eliminated X * in spFactor() using direct addressing (rather than indirect X * addressing). X * DoRealDirect (BOOLEAN *) X * Array of flags, one for each column in matrix. If a flag is true X * then corresponding column in a real matrix should be eliminated X * in spFactor() using direct addressing (rather than indirect X * addressing). X * Elements (int) X * The number of original elements (total elements minus fill ins) X * present in matrix. X * Error (int) X * The error status of the sparse matrix package. X * ExtSize (int) X * The value of the largest external row or column number encountered. X * ExtToIntColMap (int []) X * An array that is used to convert external columns number to internal X * external column numbers. Present only if TRANSLATE option is set true. X * ExtToIntRowMap (int []) X * An array that is used to convert external row numbers to internal X * external row numbers. Present only if TRANSLATE option is set true. X * Factored (BOOLEAN) X * Indicates if matrix has been factored. This flag is set true in X * spFactor() and spOrderAndFactor() and set false in spCreate() X * and spClear(). X * Fillins (int) X * The number of fill-ins created during the factorization the matrix. X * FirstInCol (ArrayOfElementPtrs) X * Array of pointers that point to the first nonzero element of the X * column corresponding to the index. X * FirstInRow (ArrayOfElementPtrs) X * Array of pointers that point to the first nonzero element of the row X * corresponding to the index. X * ID (unsigned long int) X * A constant that provides the sparse data structure with a signature. X * When DEBUG is true, all externally available sparse routines check X * this signature to assure they are operating on a valid matrix. X * Intermediate (RealVector) X * Temporary storage used in the spSolve routines. Intermediate is an X * array used during forward and backward substitution. It is X * commonly called y when the forward and backward substitution process is X * denoted Ax = b => Ly = b and Ux = y. X * InternalVectorsAllocated (BOOLEAN) X * A flag that indicates whether theMmarkowitz vectors and the X * Intermediate vector have been created. X * These vectors are created in spcCreateInternalVectors(). X * IntToExtColMap (int []) X * An array that is used to convert internal column numbers to external X * external column numbers. X * IntToExtRowMap (int []) X * An array that is used to convert internal row numbers to external X * external row numbers. X * MarkowitzCol (int []) X * An array that contains the count of the non-zero elements excluding X * the pivots for each column. Used to generate and update MarkowitzProd. X * MarkowitzProd (long []) X * The array of the products of the Markowitz row and column counts. The X * element with the smallest product is the best pivot to use to maintain X * sparsity. X * MarkowitzRow (int []) X * An array that contains the count of the non-zero elements excluding X * the pivots for each row. Used to generate and update MarkowitzProd. X * MaxRowCountInLowerTri (int) X * The maximum number of off-diagonal element in the rows of L, the X * lower triangular matrix. This quantity is used when computing an X * estimate of the roundoff error in the matrix. X * NeedsOrdering (BOOLEAN) X * This is a flag that signifies that the matrix needs to be ordered X * or reordered. NeedsOrdering is set true in spCreate() and X * spGetElement() or spGetAdmittance() if new elements are added to the X * matrix after it has been previously factored. It is set false in X * spOrderAndFactor(). X * NumberOfInterchangesIsOdd (BOOLEAN) X * Flag that indicates the sum of row and column interchange counts X * is an odd number. Used when determining the sign of the determinant. X * Partitioned (BOOLEAN) X * This flag indicates that the columns of the matrix have been X * partitioned into two groups. Those that will be addressed directly X * and those that will be addressed indirectly in spFactor(). X * PivotsOriginalCol (int) X * Column pivot was chosen from. X * PivotsOriginalRow (int) X * Row pivot was chosen from. X * PivotSelectionMethod (char) X * Character that indicates which pivot search method was successful. X * PreviousMatrixWasComplex (BOOLEAN) X * This flag in needed to determine how to clear the matrix. When X * dealing with real matrices, it is important that the imaginary terms X * in the matrix elements be zero. Thus, if the previous matrix was X * complex, then the current matrix will be cleared as if it were complex X * even if it is real. X * RelThreshold (RealNumber) X * The magnitude an element must have relative to others in its row X * to be considered as a pivot candidate, except as a last resort. X * Reordered (BOOLEAN) X * This flag signifies that the matrix has been reordered. It X * is cleared in spCreate(), set in spMNA_Preorder() and X * spOrderAndFactor() and is used in spPrint(). X * RowsLinked (BOOLEAN) X * A flag that indicates whether the row pointers exist. The AddByIndex X * routines do not generate the row pointers, which are needed by some X * of the other routines, such as spOrderAndFactor() and spScale(). X * The row pointers are generated in the function spcLinkRows(). X * SingularCol (int) X * Normally zero, but if matrix is found to be singular, SingularCol is X * assigned the external column number of pivot that was zero. X * SingularRow (int) X * Normally zero, but if matrix is found to be singular, SingularRow is X * assigned the external row number of pivot that was zero. X * Singletons (int) X * The number of singletons available for pivoting. Note that if row I X * and column I both contain singletons, only one of them is counted. X * Size (int) X * Number of rows and columns in the matrix. Does not change as matrix X * is factored. X * TrashCan (MatrixElement) X * This is a dummy MatrixElement that is used to by the user to stuff X * data related to the zero row or column. In other words, when the user X * adds an element in row zero or column zero, then the matrix returns X * a pointer to TrashCan. In this way the user can have a uniform way X * data into the matrix independent of whether a component is connected X * to ground. X * X * >>> The remaining fields are related to memory allocation. X * TopOfAllocationList (AllocationListPtr) X * Pointer which points to the top entry in a list. The list contains X * all the pointers to the segments of memory that have been allocated X * to this matrix. This is used when the memory is to be freed on X * deallocation of the matrix. X * RecordsRemaining (int) X * Number of slots left in the list of allocations. X * NextAvailElement (ElementPtr) X * Pointer to the next available element which has been allocated but as X * yet is unused. Matrix elements are allocated in groups of X * ELEMENTS_PER_ALLOCATION in order to speed element allocation and X * freeing. X * ElementsRemaining (int) X * Number of unused elements left in last block of elements allocated. X * NextAvailFillin (ElementPtr) X * Pointer to the next available fill-in which has been allocated but X * as yet is unused. Fill-ins are allocated in a group in order to keep X * them physically close in memory to the rest of the matrix. X * FillinsRemaining (int) X * Number of unused fill-ins left in the last block of fill-ins X * allocated. X * FirstFillinListNode (FillinListNodeStruct *) X * A pointer to the head of the linked-list that keeps track of the X * lists of fill-ins. X * LastFillinListNode (FillinListNodeStruct *) X * A pointer to the tail of the linked-list that keeps track of the X * lists of fill-ins. X */ X /* Begin `MatrixFrame'. */ struct MatrixFrame { RealNumber AbsThreshold; X int AllocatedSize; X int AllocatedExtSize; X BOOLEAN Complex; X int CurrentSize; X ArrayOfElementPtrs Diag; X BOOLEAN *DoCmplxDirect; X BOOLEAN *DoRealDirect; X int Elements; X int Error; X int ExtSize; X int *ExtToIntColMap; X int *ExtToIntRowMap; X BOOLEAN Factored; X int Fillins; X ArrayOfElementPtrs FirstInCol; X ArrayOfElementPtrs FirstInRow; X unsigned long ID; X RealVector Intermediate; X BOOLEAN InternalVectorsAllocated; X int *IntToExtColMap; X int *IntToExtRowMap; X int *MarkowitzRow; X int *MarkowitzCol; X long *MarkowitzProd; X int MaxRowCountInLowerTri; X BOOLEAN NeedsOrdering; X BOOLEAN NumberOfInterchangesIsOdd; X BOOLEAN Partitioned; X int PivotsOriginalCol; X int PivotsOriginalRow; X char PivotSelectionMethod; X BOOLEAN PreviousMatrixWasComplex; X RealNumber RelThreshold; X BOOLEAN Reordered; X BOOLEAN RowsLinked; X int SingularCol; X int SingularRow; X int Singletons; X int Size; X struct MatrixElement TrashCan; X X AllocationListPtr TopOfAllocationList; X int RecordsRemaining; X ElementPtr NextAvailElement; X int ElementsRemaining; X ElementPtr NextAvailFillin; X int FillinsRemaining; X struct FillinListNodeStruct *FirstFillinListNode; X struct FillinListNodeStruct *LastFillinListNode; }; typedef struct MatrixFrame *MatrixPtr; X X X X /* X * Declarations X */ X spcEXTERN ElementPtr spcGetElement( MatrixPtr ); spcEXTERN ElementPtr spcGetFillin( MatrixPtr ); spcEXTERN ElementPtr spcFindDiag( MatrixPtr, int ); spcEXTERN ElementPtr spcCreateElement( MatrixPtr, int, int, X ElementPtr*, ElementPtr*, int ); spcEXTERN void spcCreateInternalVectors( MatrixPtr ); spcEXTERN void spcLinkRows( MatrixPtr ); spcEXTERN void spcColExchange( MatrixPtr, int, int ); spcEXTERN void spcRowExchange( MatrixPtr, int, int ); X spcEXTERN char spcMatrixIsNotValid[]; spcEXTERN char spcErrorsMustBeCleared[]; spcEXTERN char spcMatrixMustBeFactored[]; spcEXTERN char spcMatrixMustNotBeFactored[]; SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spDefs.h'; eval "$shar_touch") && chmod 0600 'sparse/spDefs.h' if test $? -ne 0 then ${echo} 'restore of sparse/spDefs.h failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spDefs.h: MD5 check failed' ) << \SHAR_EOF 02248b07d06b7eada20321eb3d75d934 sparse/spDefs.h SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spDefs.h'` -ne 34667 && \ ${echo} 'restoration warning: size of sparse/spDefs.h is not 34667' fi fi # ============= sparse/spDoc.pdf ============== if test -f 'sparse/spDoc.pdf' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spDoc.pdf (file already exists)' else ${echo} 'x - extracting sparse/spDoc.pdf (binary)' sed 's/^X//' << 'SHAR_EOF' | uudecode && begin 600 sparse/spDoc.pdf M)5!$1BTQ+C0-)>+CS],-"C,Y-R`P(&]B:CP\+TA;-3@W-B`V,3$U72],:6YE M87)I>F5D(#$O12`Y,3(U,R],(#(U-SM75#79[@/^7`2JQ M\J2V]\?;N5PRNJCM7-I_TL:30#HM#/8SOI@4XA=U+0V1,2:W18JH2#"(R=># MJC&E5DAUIG(5%LRB>A(A+'XP)2H@\IJ]%ZX]6)A7&Z/=&K0)_TD1ZB`6\]-`?+.$]L;J:H)ZT15`U;*XA3(U=90I7 M*SYDB5"[!"@8A4NQ';9YWOZ5)K#2V(/F^=[%GYOEXT6?F>;U"?=ZE3I6.'/Z MLS@DM^SQ$_&Z&HV'XG0T$JJBM&$.GJ5&;FJK/C%T;;/*I[MFZ&737!V@IVT0 M37=.+D8D#K58(Y9T-5K!'YS8;I!3HDS5S^'T;-7+P2D,H_;P.&61>[18GT94 M#=H6A%?MIN4(RIKOKO3`-Q\V=\/N#3BSTX M#5%+/7-M&1X"?V=4!M\?\NSC,+^.^]M"_2374D+\G$,V!_$ZCFT(Y$EJHP-X M3D7A_CS(#B^OTD@NT9N8K&WLAGN=,&*'M;NW-U%LYU+.Q:UEEY-`'ZTOZQ\/ M]>-VQ`?RN)(0'I?KY.7MRX6L7N?CD7&UU=K#X4*3E8?:J2\,SM:-INF?#0_> M;>P906QG7#`$RVW.WWCYY,:P):XE1M`']D6&T":S/#HTD2*A:;YXQ> MV4:=[!)IC'8VIU@3)&)CU8%,:CL#T:9LMH*W21O,-%BB8"T(B^F)![.`56AU MIMP*KL;,:#AD?BZ\O,'L'#U+:MI](ZG&I+L^L@K6QB\W@K91LZB:Y^J/'K5D M23?I:;)NB[]+K;V<\)>4VDK!Y>1:/\ZEI%HRZ\];:P8E?]A0O;ON8-R^RXU_ MV[:OLNYV^CZ_DK^F[=,:.1Z_E]?[U::]6HHO-U8/-NZ/TUF>)XT9OFU[/K&F MDG8VH<8/=T980P;]<P?%O-N\M+WI9MRH>+I-?I07:%58Y,+X]M+)BQ-09 MEQ2@[X)K[K7W&H[FED<.FS@51PS=LBM4_6PB/0V07_)P<@C9KCLA6.MA!\.C M;[1%Q]EG65+-E4^#@_OVQ`8-1%9'^@\82OM_`S>0'.\*.WWWZ M\:+4X8F=BU:<_F$'N'\G/2:@,[[:E>";[L0AVL%8;,+#IZO7$UZ#BZ2BOK[/ MP8"?6SA.= M0*O6X;(XV+78+!SP$2:S9^Q#=&:Q<@4JD]VSE-(&[##09,DWZ5BZMQT]8N&> M47/`W-VAL-[,'9@#AM?UG;^MWH#!N#O&I]O.WU,D6(SD1)?K. M;MZ.A=KX]C;4$L_>\CAKC]*'"MN@RY".Q.#+3@,I(9%7NYXN!AQ!FA? M`H@86`A()*3"U$0R!DI=-,;$@<4,-@DB%@IT77#]`61G?%*=N7MI<^/*=67- MXZO7ETV`6.QR.YR32QG'[N.P(0U(!G_(F90M&,HVS@\?ZK3[)&)8PZDTEJL"Q*5)Q<7-(UET5S# M]7+IKO56A8:N-U85&[G1/Z[V_C']\QV>:X\O=2GPYKIS^ MR`HC5\74)IEOB;P8_P[*4PSD6BI4'02P*9IJ0+$90AWHL<>`05A7`@3$]B=K MX)H3])U*D@2F[OCRPY8>A/*['ZXG]#_]B$TDP=9RB+[I=H0%E\B+502?.SQF MX07A(#]J`7<0U9C`')B%=*A[HO2P17Z3[)B&6";4UQ2/BCFV/OF43[`XOA5V'"ZW(ED0P*MH20CQJWCX7RC/A/+4VMYGZM:4P/?G M&1=%!O"B:F,#>?N/+0QE'RZ)&^#'(U7$!?F1!M+#_@O6WP>L;TZ7C.7=:]U)G3T++8XQ*X8TLPF=2]-83,'4X-)5?>'EQ;I(KH@.P9?"V@-:'6M>3)(#KSN\M/?Q%1*7#UG+HG&;3<>OM2X[SZ MM'KS_/H33=8%]4_:;`L_M^\#B_FQ!M`,_J'%R$P!U!F?->+*TY*,Y$;K9(]T M):M`^XL!/WV$2^HV!9.=1G;P*$LP;V!_ESQV(XOKZ#A@[L7WZ M;]IY>OW=<%G6(X544X?JG)841T&\7%X1W?6&1'YZ[SR0O MW/43>O3P@7DHD_*$!2R#FJ8#$5/MX2#1HL=/GV4J1Z(-+F6;TPAPFZ M+6%':CL+:F)UG.N.;M)U'CGUGBD,UU+#W'I*QJ*RZJG"RI,Z?K;H4 M82^R.V#`\75".;!]P<_6K/?I6H?/$D"7(S.4ATR@8F6L%EBD--,`S-SIA0WF M[N%SJC!_VN>=P*#BZ:L#!#"@2Y!K@5.+)R?FL7^)[!AULXJCM+,3I'$ZV8VR M>-WL!RH(,_WAJ<7GZT=/6)\/[V^V.D]OEUF>&[FWS3R96AU!>"(!RN8OEH0QNL`S MWK9G"`X],DMX1K'4%);!+C:$9F!%2X&Z M,]1/,*VQER`RI%5T3;'4'Z\NDE)`O^SZ;&@-NO!S2IM-0?U0DV5^?56]:5Z] M_RPD\K^TS&F\DVZ@N8!8*:5!O2DY=&BI68$AM,_^4R,8?FXS&=P^_>/.17[# MCS]>5'GW[SO"3-M6?&)8=>4#UYE*+GSH@'&N8"CR_@?KO-N/6;A[9]52G'HC MD_6<+SF.K`B^;-PA#+I$5-AXE/;':7M>HB,]\4-SA&)J2"6WSRCWAEDV-6?D M3HI>]LBA:(ID))9')$3X]RV=\]!^S_ZQB_]DY4S-_H"054=Q0&529PZW5V-I MSDI%S*11W1F1U2;=;4G[3,\!\V%-%$F#9C"*C6$90JDY/$,FLW[C`&V_/CJ9 M%(X3S853ZELRMN,S\OV?/@[+N?9H9YC+Q8U!6@,G*Z.U=\?MC='NSUNDVLJ! MS+%MGOL^S]->U(UWG<%DVIB?&N;V,0KHN:74'%JN-S;#QJ/MWM>+9W@4D\TY M_HK)Y"0HTPURFDX:"H:B25G\(1.(*.SRS8D=H9?WWTP+N1PUD.3":8]?MHZ0 M$E`1I3TI7R(ICUXM*A";30"+L4R,NJB'@5`3%5-?6A:5JI2Z+)("4<3.J676 MB)DS-I.O6#[9"_;G;`OI3[ZV+J"?.&*>3T^K-LZCV^>]\5\^7%=6WFCI7EI> M-V4B!*KLA>=;UP*>6+7?'+XDM\X,]B"MUA36%+O'!);H7V%;Z(,]PR@D`'^T M42&E52RY>7BB`W':VIF?)S[/[G.R>$;Y,Q/K2$,ZXAP%>JYXQWJP+[&#YL@F MM(_X\D@=XLA`K0Y%4BBY$Y053CG#Z+%#90J5*]&9LK'5LP:M;P6)M=ZG?,#6 MP[M\DF6X])DDU\R_U/N6WS^U)>C6,[DPV!35GA`.BW;%&1RL3D^*/K:B2@5?`HY6?1FIKN95%:?,"5JPC.,;K MN7`B0RAN?5E%9FZ]62%:*N#XF4JFU=>P/SV4K-(XZIZ':;7FK( MV?@K*2$Y(8/)(2Y>?4DAFJN\8JJUCAW9$)A=>V![Z-ECWV\+S:F]E1[J4O3U MYJ#L:ZWQ0]#3K.XIIH;6<'=IE1'GY6PT[!!R^?9US+>I92.FE9?'+'2%*P5D== MI#^I0^#["D@WL&)\5@G591YOR?\E!9OK'E$U%0M+-^^Y[M$TR+.3]#04]G`76M\DJ5SS)V-V[O\-NG<\`Q,G;=\")R^,.2*(">0,=R:%^@^`W M:)^G/[R7U-YRB MGQHPD*R_PJTW26_<;E]L8.OAPQMT4O;VZF&RR_>W*S;LIP M2[SN\N[&33J/3A[;J,,K^3RV6G/EQ:UZQ\V[$_52M3LV_Z5SXMLM5S4A;<*K MS)FL=`J8G9SB]TA#>;TC,A_]N3`OQ.]V.6"/49(?,X6K"*F.8 M&B.7!@6-I>EK@F2QQRSA38<./V<`\U\H,)E(-60F5FZ#R']3LK9-U29NDFD* M*,QO=\7A:2QMP?Y]+I).;M[;.GYZR]['((6P9@6N(Z$FE=:=6-.Z4QI;O;QD M_T:=P9-?;M+5ZOXJ7I!"ACRGCKF:-(5?-.[!R(/B>II5UOTWRNBZA&K)'+/1L6M4,J@9[[>HB'CQYD].7RWJ6!9PQQP=9I"GN$:H>N:6, MJ<99V(3N^14>SUO9DJ,J33*+M#F\52C.?C;\'!_N5=K=7V=RNGWW?)K1#%7U MT4R?)F>:`SZZS>#-J9B^AY292\^BY38PB MP]P'S!*CO-F^Z.G_+#'_JSIE!F-4W2D/%KP[>0FX;VTNJ(/;&!?6$CN-W<*# MJTSI\-K/+:Q^I&B<=/>Y;TM\7M3 M1DY.K?Q#<5=B6NYMNWYP_YWTZS$5HVG7)W?H''YP/.%::NVC]#I7^7_61ZYP MDX&G+Q"F[]5QE_[*$/Z;?.!M*OWO^JSJ\R7-\45PI^)W.)3:0C`SK3J$[U/GS1?D]P5SC@O%NP;D_QFHT\"S M[-TW\J]!L*.C$W>!\'O'1P8O5.[Y3CI/HT7'_-:G$[[+H?,;Q'M16Z]HU7R/0.+^NO!['_;-;P7V]ZHR*WZ3O\+];<+O'4+OK4%^G\KT MKV)2^$_\S]/DVP8!:DEMD^].G%P!:YX?A`H@-=B9!,8-W#A8(X)OF>"(+V. MZR6@0%!P>#TB#0U="\!@P-P M[%X,LPLG[X<*-'`T;X+M3F;^'24["I7?PBN?8'I:,?S_08]]29!UP1L-$-)V M-`=&$OV"+]'"R++PK`*,_.\8%DH+VXCI1;%LL(K'9#8/#3C"KKG9T2))5#LBV89`M(]!DP!(*6IU='1V%&X M['LT^QZF^"!6=@N0[J`9%V$):+A4`AL!X80/<*(^;(D^5'D>4P?! M*!_#@)N87BY6K(5D:6(9@PCJMTCE8ZQH$",O@_.+4#0G_(@-(<$!*^:A1NSP MS'X\CE/E8]C`! MA,7))U#%_X,=&\&`P$B:&9J#1A6/8GJ*<0H(6GH3Q?X"(VM`"MOATK,P>2U> M.@I5C@!26R.&/Z:7CNO5P]B"\`(,-@$.D_^$Q/E@&)_BV7NUV3=A$K^Q[/4H?: M6I"9MS#*9TCY#QCA)4/%ARCI9!%)`+`7CJW\#4>&!%`_^'B*O$V^UOSO``R^ M;?T-"F5N9'-T7!E+T-A=&%L;V7!E+U!A9V4O4&%R96YT(#,Y,B`P(%(O4F]T871E(#`O365D:6%";WA;,"`P M(#8Q,B`W.3)=+T-R;W!";WA;,"`P(#8Q,B`W.3)=+U)EGMK2-/3ZQ36G;RCY4ZEG5+0^YK6VSJEA; ME>=<9_;^2:U[5=S9NE7%->NJ6-]>V$H5_8U]?7E[5/VE0LP`3.*`40R0!8U9 M(T?L,"`X[!!8AP>\^UH0NCUV?S'^PO'>V!&32<@Y8,HXR@L^"?>!($8CRI'! M+-(!(M38)HTI;;WH))0#AY)1:'N`:$;A;'9[%Z2;V)@TQ/#QT0QL3/":")J- M\0?S?S!9PK`@IB_$O;V;:`_?T9E9O#,@1D1)G?S,DE5*35WU:J.^!1@`&W3E M&`T*96YDO56_B$1P[E!=3Z"@>P$<>?#\A@:1CTA3+ M3Q-*-.T-GC`UDG47X]#02(=Z*F]"NR/F!49;2@U&2^ECE*]G)*%8T>5\/6`Q M1ZNLPPQ2Z@U9`X&@AN`%ON#>^,]6"?Q6/:S+S3"S;L`.MUYI!YG`>B4+;FR[ M%257W*_["I=EY&_0.OW5`*H"MP;G!W!1X`8_`@P`_S_LWPT*96YD(W3?,S\U5B+)1[B@4$[Q_D" M[PP\@35\+/&(R\QPFY4+GF.\?8!E!<4-6@/%!7GR$MUB2_/K_=0G8%2 M)RH)*OO/(26O2;F04>^HI9"*7S2RFG[$(9J(&Z*$^IRZ/U=B(_IL\CO:UJP6 M/]/)+NI=EXY._$@FFW5@>]VL0BWJ-IKK=F`']HW8/>>CN\X_LH$UXD[=1U7= M[[,CFT9I$(G-MDTH1-<4\FW?:8QN:-0B_\;">077\"O``-@>Y*@-"F5N9'-T MH]0*)?=0IR720 MME`/A7;*`Z0=,Z2T\WDH]+4,>1&'O,!UNZ%$E=0Q!_=Q0MPO=4MJR]]@MI&ZU]'-/LW;>KN0L:;O#=8_-AKH%-H_2QV;]?$<>F_Z)/MX_W["_ M1P`W07WI&6(JF`?.3HPY,(\A1^8I)&9.]8_JCF*N#N)O]2V>2Z<6M!4,D`< MH;H4'WI\P3\!!@#B"Z?J#0IE;F1S=')E86T-96YD;V)J#30P-B`P(&]B:CP\ M+TQE;F=T:"`Y,"]&:6QT97(O1FQA=&5$96-O9&4^/G-T" M&9>^4X"S@B&7OJ>O0DE1:2J7IPL7`P,C&N)R]>0*Y`((,`!\YP_N#0IE;F1S M=')E86T-96YD;V)J#30P-R`P(&]B:CP\+TQE;F=T:"`R-#,O1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)9,^Q;L(P$(!A1QDBW<(C^%Z@)"%!2J9( MM)7(4*E,/``P=@"UOBWFUU[X$ZSS<>G2?GSKF_\8OW+TEV(].2 MDCE)V8N24C(G>8U$-C(5??ZXJN]Z@=BX7\)S"1/X$V``+40=``T*96YD(E0,LXD;=FM9P/MU\Y,IG__R7$I"K$4 M!V4MJDI41^*^Y$^\HF@AZD.Q7)247!0GN&IQ]\A7#<]O157R_!(+>+ZZ/A/X MTER)E^?7!]Z<E_^W2-01SSS!C4SA?9.0P-HS M!J\:-0QDV]0LVZ()KDULPPF#CGF`#2``0`?M]V.#0IE;F1S M=')E86T-96YD;V)J#30P.2`P(&]B:CP\+TQE;F=T:"`R,#`O1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)3,X]"L)`$(;A%0MAFAS!N8`F<16T$OP! M4PA:>0"UM%"TWAQ-L/`:$2]@N47(YS==%O:!9/=E=IQIIB-N/U$_TU,N5_%> M[2\_1\-[I>:2%EN]WQYG*592.>="BSX< MJH`.*C1=?%!W\47LX868D)_!2V\\YT8@I>%`.@`:@Q6L@E6P"JSJP,H(I#0< M:IM5!U8QL(J!%4D0K?K9+#+G2['(->(-4J8(/F=W11$[EZ_9><.;]_H##CCBGE*L8AY& MO`YI1RJ4:<"Q2%ZH12^(Y46\VE*:D;]D%9(_E07RT_F(Y9/-^+`_;B@;$U`" M9\#!PT6;H,E10W,';D!AQ*[1';2NIDE>U/F+2H#F8K@:3M^(4^6:KY?;D0(<@I:EK2EMP`#``F;XAH-"F5N9'-T M1<+B=/+OUP!1,S+GT/H#R7OE.`LX(AE[ZGKT))46DJ MEZ<+US_F_T#`@(/\`2;_@,E_-"?_,8+(#PSV0/(`@SR0;/[!#R3Y_[#__\=D M#U3VCZ$>0OX'D?__,_X'Z4$CF0<+^8<1Y$@&!O8&!F9,DLO5DRN0"R#``.HP M#.0-"F5N9'-T!$@IK(8**3'Z".#AJ=6_\,_\1/8'0PG%><68F7-*_7WB5M3D\H)25+ MYS2;TE'A!74F>>I2%2L:IW$ZE\CI<,:RPF1/.L-D+?>8E-L%*4RJ#=VN]Q-6 M2V1F,.]0:""J?=%"`)X[YI%?,W\"MJ%EEAJ(P'!3M%"(+_.S%HUH690ZZSJ? MS(]NR^P-(W1A>AWJ'?]B"VZP;II^[?[?)ZXJW.%7@`$`RM1XI0T*96YD_6W,,<0(AQS%&),1[CF<0,14##$1&/F<]OQP2FN"NR-D.01;%#$$2]J' M(%O/D$.0K_!ROAX@GX-R-"WKBW5KTZK^KIFCG-*29&$QTJE=K7J,#BBJWDFM M"ZUL4AI+:>Z6J;%B'WK&^E7W)U\Z=#W?IJ3/#%T>NTU(.2EM7VI*/F`-O8*Y ME4?>K%2KU+RQ^U=8Y+"!AP`#`+*-(60-"F5N9'-T1< M+B=/+OUP!1,S+GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+U___?QC^`\$#!D$@ MR<#`^/__/\;_0/('/Y#^_T'^#Y!\4`\B#_P'D0U@DA&DZC\$,8.Y5"49&$`N MP22I:PM]2&!P_6,`NI^]@8$9D^1R]>0*Y`((,`#NI/BI#0IE;F1S=')E86T- M96YD;V)J#30Q-R`P(&]B:CP\+TQE;F=T:"`Q-C,O1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB),K)0,%#0-570-;14`"(S(X440ZY"+B,3H+"!@H6A M@JZ)GB5(5L_`$@@L%))SN9P\N?3#%8Q,N/0]@"JX])T"G!6`E*>O0DE1:2J7 MIPO7___M__\S___'^/\/`P(!N4!!D!0QH.%_`P,"_6/X_Y\!)#@0Z.#_!I!K MZAL9ZAD9ZIE!Z!][XS_^@S_D'S#8?P"ZCOD_EZLG5R`70(`!`/E1RVH-"F5N M9'-T0JY#(V`8H:*%@:*ICJ&8(D]0PL@?HJE!25IG)YNG#]____7SV0^&,/(W[4HQ`?_J,0#U&)PZA$.RK! MCT+\DTE[@$FG`&<%0RY]3U^%DJ+2 M5"Y/%Z[_]?_W_[/_+_^G_I_]C_]_ZC_^__$?A!\#RK)U<@%T"` M`0"YY%R"#0IE;F1S=')E86T-96YD;V)J#30R,2`P(&]B:CP\+TQE;F=T:"`Q M-C8O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)9,DQ#L(P#$!19B\] M0GR"-FE:!%.D`A(9D&#B`,#(`((YN4&OE(UCT(H#4+8.58V+!`N6WK=D9SE* MU*ASU`K'&O<*3I!.^"B1EXX5ZBR>2IX4=T_HJE!25IG)YNG`U,("!/(1BX(?1,/'_=`:$W,/EZLD5R`40 M8`!'LX.*#0IE;F1S=')E86T-96YD;V)J#30R-"`P(&]B:CP\+TQE;F=T:"`Q M,#$O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),C)5,%"P5-`U,E8` M(C,+A11#KD(N0Q.@*%#<4,%2SQ`DJ6=@"006"LFY7$Z>7/KA"H8F7/H>0`5< M^DX!S@I`RM-7H:2H-)7+TX6+@1D"^?\/!0AS+9>K)U<@%T"``0!`3&EV#0IE M;F1S=')E86T-96YD;V)J#30R-2`P(&]B:CP\+TQE;F=T:"`R-#$O1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB)?-`]3L0P$`5@1RDL3>,C9"[`)B$A M@BIH`8D42%!MBP1;;@&".MF;^2CF!BZWL#P\A[`""BSKD_P[?F[/N.*63TZY MK;BK^;FF%VHZ3&+8<+NJL;:J+M#.^6E'ZX'*#3<=E;=8IW)]?\4UE<,=O[V^ M;VFX)@FYB#@%K.I%LD,A4494IB]V$*R M1`[BC,9V9'P-"F5N9'-T M\4X*Q@R*7OZ:M0 M4E2:RN7IPO7_\?__6/%A--P,Q'_ MS/_L_S,"<2,0-_RI_W^@`N@HQO__/X`,8/_/Y>K)%<@%$&``<'BQOPT*96YD M6E[RQ$B69$NI!9^N5#I*CVPS2K>24UKN5VPH=3M^W)\7Q4"K M(J!^!4"(!F.$MAHTNJ*/T<==@4&W@JBM)&L$H4"<:NA>A>!"U8A3)ZJ/;J!#HE(;_#&T<'>@MP`#0=YBT#0IE;F1S=')E86T- M96YD;V)J#30R."`P(&]B:CP\+TQE;F=T:"`Q,3(O1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB),C)3,%`P!F(C$P4S8X440ZY"+B-#(-\`Q#76,U30 M-=`SL`0""X7D7"XG3R[]<`4C0RY]#Z`\E[Y3@+,"D./IJU!25)K*Y>G"]8_Y M/P,2^L'\_P_S_W^#%?UA_,_`P`Y'7*Z>7(%<``$&`"M3@T`-"F5N9'-T,>9`#:F8&YHWZ\>->E#E_,N_4K3$5$^:#FAKESHN=FOW:Q_). M;8QDBX/:&J6A$[W*5NEE;6SYRRJA^XG9J(1N)Z::&T+3B8D/=HEF&N,AV,>: MJ\&C0Z291NB"'C33>A=DN/]C-+J)I^Z2>7+IAP,%N?0] MP*13@+."(9>^IZ]"25%I*I>G"]?#_P?^-]0SV$-@0_V!_P__<[EZ<@5R`008 M`$WB&C(-"F5N9'-T`!@9`"!Q$;>H*^4K:\1J2^0;AVB'/;Y9P-+ MSB7OF9NXBC)5]>N5US<6C+1=< MW(7O7*SOKZWGHM[8C_?/%ZYOF(B,-)3]IP2D)Y&F$CGFR<'`"N\E>8![N(-; M:))CE1R^DS$\>H![N(-;:)(:'M3PX!'K'G;8UTZ3#3S)U2N7J9=?R=4P4?U` MZNF8S(7T?%,A/:_1$N*C11V:(-C!/H<9ZH*:($JH'VH"%7W\G2WZ&Q-$.]BC M_S&!.H'AA_&>.JKBO8VDXT_YMN8'_A%@`+LZ4EL-"F5N9'-TSU3ZE3H2K80=#)!U!'047G M=O.U#GP1?8.Z=2A7+UDJJ*N!^R`D%T+B!'T,S8N'.(IP&\`1XLCD/J6A%^#` M]_RQB00W>\ARD&N,(Y!S4P>9+2<8@,P7>#Y==I!/0?=;$^*'-=NP^E\*47Q5 MV515*V M6^O-DE5%YSTES8A.A^Y6T?).*=Q/89;#"EX"#`#]$A+%#0IE;F1S=')E86T- M96YD;V)J#30S-"`P(&]B:CP\+TQE;F=T:"`Q-34O1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB),K)0,%`P`F)C$P4S$X440ZY"+F,0WP#$-=(S5-`U MT#.P!`(+A>1<+B=/+OUP!6,C+GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+U___ M'^3__V]@__^/@?G_'_;&_S_D&?]_L&/X_Z".X?\!$/['\+_A/^/_QO_-0/(_ M.9B!@0$%DVL.I1CHD_\-0/N1,9>K)U<@%T"``0`,O;S]#0IE;F1S=')E86T- M96YD;V)J#30S-2`P(&]B:CP\+TQE;F=T:"`R-3DO1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB)C)!-2L1`$(6[F46@-CE"Z@).?HRBJ\"H8!:"KMP. MJ$L7BJZ3P8MYE#Y"EA&:/%_U(+H9L)M\W715O7J5]D0K;?2HJ;0]UK;6QUI> MA&?%?5IKLZXMN*[.N<[TX5DVO93WS)3RF@E2;FXOE$=_HV^O[T_27PJX/H"8 M10\$-SM@=+H=X"@P_H]_J_8Z>YH^P%XY M[WXJ2. M+/$SCF:)KX?XF[E+M=1ABZ3)`>8<'(`_9_CZ$X"UDH5%DH\[#/[<6V)=?+LMCDX33;9!>\SL/C ML^UJ2Q_"MK3TAON6[NXN`Q_U;7A[?7^R^LH03P#TK@&<\YB=2S`EPQJQB2N, M?(%O;N&+-*2M#HPO^%:P)8\TKH;-.: M1+5&-8S*#HIQ+/`A/H5BLV*38E&)$>AT_$['[W`0'>!YP\\``0#Q[M7]#0IE;F1S=')E86T-96YD;V)J#30S."`P M(&]B:CP\+TQE;F=T:"`R-34O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB)9)`Q3L0P$$6]VB+2-#Z"YP)L$A(BJ"PM()$""2H.L%!2[`IJYVA&7,3< M(-NYB#+,3!:$A"4_R?XSW]_37F"%+9Z=8UMA5^-S#7MH.K[D8X/MIF9M4UWQ MNL3=*VQ[*)^PZ:"\8QW*[<,UUE#V]_AV>'^!_@8HKXDHFD!DC*.Y&"U-?EI3 M#O.*CGQ+7X(4:`C),Z*CP4?['TF$D4M\EF*V51@%;UKQGA7RYE0PLF6,3B)X MHB1E2<2D72IHG7:HP6*UV/L?1/<+JS@*8F"P?@)_[P_<`H[KV&SP@UA]2MP/ M_:\D'740Q5C09(R5"?G3K#*'@ML>'N%;@`$`BW;B`@T*96YD`G/)(S0OX+9U):PG856P!T%//H#K MT8.BY^:X![&OM(_21^@QAY)QFND$W,#P,0GD)QFWLXW=4KDKZR[MH84/<"WU MS=)N-ZV]:#;--:V=?7V'?0?UBW4MU`]T#O7^Z=92TSW:K\_O-^CN0*D;1*6T M4M6J^2=ZC1@4HB\1(]61"FGO6)`#XH]B?[-]B<>[0\2AZA8X:HYFSC4R7$R$71=\IO.[[?=]M@C2U> M7:-OT=_@>P-[\#7RXSVVZR9JZ_HNK@V^?<*VA^H5?0W58]2AVC[OL(&J?\*O MP_$#^GL@HE`2K]EEPLFY@1FZD85S1G/!<_?G,9( M+.?DD4M!BS#76E8SDLMQDG%2#I,T%8:QO.CE@MIT*$/'''_1*5?_Q?0_Z^,/ MIK[U7%TZGU%\<,-)2<99_,J2;XOXF;-$$O3C<_+=YF!S8\!//3P`M\"#`!Y((7�IE;F1S=')E86T-96YD;V)J#30T,2`P(&]B:CP\ M+TQE;F=T:"`R,#MJG MJ^V MO9B>;&^]WKA7U3.L.>SA)<``8XA21@T*96YD,0+0(<)X7=FQ`0.X#9?V-DLL_]DF4N<=X='+DM<=NPV*3V2][S)GYGS MJY1KJ^24UXF[>Z!U0?&M\Y[B2ZY3O+X^H<4JA*OL'"0!CY($8["+## M@=('PK9=L`^%W40]$?\_7[L#]Z??OG,*&! MO&D\>->P$&ET8Z1!CKG&VI<:Y5X8#'94T*8.3)>W014$W M]"/``"P];#X-"F5N9'-T^ M4X"S@B&7OJ>O0DE1:2J7IPL7`Q"P__\/HAB@E#R$`HG*_S_`_/]_@_W_'XQ@ MZA\#F/K/4`^F@!A$'9!'H?C!U`-4BIU4"D4[U$R8#?;(MD/<`G49U)T'@(YO M`/J!'T+9@RF@%%C#H*:`0?X?'`_RN"@N5T^N0"Z```,`7!HPX0T*96YDP.[,P;MMJ=647])3A*Q:Y]*EMY[.,SM-9NI18T.,+KBI,'JC(,;F1>TQ6=Y>485+=TOO; MQS-65PB@6**!>%JQE2U_DOP5"0RI:7V'HG[MT$\@TMLC;4!T<*JIF74):B0=L2F=ZD/N:Z=,P)I'8N"#1TV]\Z@MNQ-MU<:C?:Q.I,]*CWJH M/3+R^\>2\0:#3/@O#8-:60V(^)-;L/LRK6X07E=XC[\"#`!(J;#;#0IE;F1S M=')E86T-96YD;V)J#30T-2`P(&]B:CP\+TQE;F=T:"`R,S`/[/PBJ)]687D,:-9?C%T@(L MM\/(A@8>GN([L4M39\Q?A-_X/SR3@5B"T?TD<"TXWAU9].F;^`H,\<7`&V!_ M@(6];'-L?/)8G'4*_@&FC&^P+#GIY4(^!1@`B13USPT*96YD^IZ]"25%I*I>G"Q<#&-AC MI?XSR/__!Z0:[/__/P"A/LB#J1\0Z@\*]0^%^D\$U0^B'N.E_B-1AU&H9@C% M#*;^@"D&!MP45`E,`TP[JIF/B:;^TX@"!CF(9&!@QT5QN7IR!7(!!!@`XY?FT7P4'Z&C@WA>PX&2@?"37^R$I1ICAD#\[ MQF*`AQS.8"WO,[\=]G/L9?ULPFN,^Q/,2DAW:"VD*XY#.MO,,8>T7./UW0'E<'Y$3/4XG^0ZNTT49Z64XP MBS,3,Y2KH?8N+"O,5N1GF+[*/^>+MD2SFU2M]?NPW6#TA`)3<`=0WP)`R M,XR9&QEN:N:CXB33ZM9/Q$&F2^3V':%OSR/F00_T(,@">L4@7^_+@+."-5I[ M9-SIDYN,6P^I_D:XQ#\!!@#M+,6R#0IE;F1S=')E86T-96YD;V)J M#30T.2`P(&]B:CP\+TQE;F=T:"`R-#!4=$N!%W-`4:7`Z,H MN#+Q9O$F/4*7791^?NU84<$L?B0OX?U)>:*9EGITK&6F5:[WN3Q*43'(8Z'E M*N?=*COC.M7M3M:UI!LM*DFO>2_I^O9<GF0^D*`)@%@(J`W2Z"+ M/QAR'1#0.;QSCR6"'2+X`WU,NHD$P;4)&K1CCL;.A-]X]\V;(:_&PN\UAMDR MH[EB`1.S%/$P3#KA^"I8TDPD!]Q/XC_X_P@S2;`SS1?!MA-CR7$BC+.!4RXP M1`._9-'Q-R6*9+":R0W2+E%R#H;W4`$"W^8#W9G!L;ST4(?QPYZ+@8.[FPX@AO2IX6! MA[YA4\^P)I00MP>($S`WZ(9@+J@/9KR:H@UFLL3SZ;*'9`9/3BD$>8>B**,O MKH0JT'KTC/,FNG/.4DG-B8KK'PI!]H:B4;7TH"T)2XFZ0TR4@DKPZ$^Z]:B2 MMJ?1!K`A=8<^H/Q%_@]E)&%4,$]@#2\!!@`21/^'#0IE;F1S=')E86T-96YD M;V)J#30U,2`P(&]B:CP\+TQE;F=T:"`R,S8O1FEL=&5R+T9L871E1&5C;V1E M/CYS=')E86T-"DB)M)$];L)`$(7'4>>H^%Q(PE'Y(;\_AQV72^ZZ"[UTNH[D M61)1#IS!"7@"?5NN:A74/K8JR(*NA5#=^IW5!,/`5C5UT&NM!][&QS%*S[&Q M)/W5'HZU-^IYA]0S?Q8?]77W'.[]Q__ONKV[9VMS"5P^0VY#CD[;H!>-\U6``0!+4ANS#0IE;F1S=')E86T-96YD;V)J#30U,B`P M(&]B:CP\+TQE;F=T:"`Q,3`O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB),K90,%`P!6)C$P4+8X440ZY"+B-+(-\`Q#75,U30-=`SL`0""X7D7"XG M3R[]<`4C2RY]#Z`\E[Y3@+."(9>^IZ]"25%I*I>G"]?_!^S_&1BPXW]`_'\4 M8V`&8.!@PURNGER!7``!!@"I@:?6#0IE;F1S=')E86T-96YD;V)J#30U,R`P M(&]B:CP\+TQE;F=T:"`Q-#(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB),C-7,%`P-``2IN8*YA8**89I0\@D<-)JYT=]Z;L0+S M0TBG^\#+D@NNPE?]*JI7S+=47Y:SBG M?/7^PB7E[1M_GWZ.U*YI<-D0%W"?O3G\0WULX@X'-]7XR[.*LWB#OMI MUZGJ'M48%H7FE^M!GZH2%I2PH#.U8DS,!^A](#$Q>A;9W$'R)1TD?^JA]?3F M#9F96EU6_\5T&-4^K]"^.]/'AT6=B_P+,H_4YC.:V-P0R\P\9/U)VK3T0;\" M#`!(/HM$#0IE;F1S=')E86T-96YD;V)J#30U-2`P(&]B:CP\+TQE;F=T:"`Q M.#,O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)Y-&Q"L(P$`#0*QT" MM_03>C]@DT"D=1*J@AD$G?P`[>B@Z-Q^6C^EG]"QD^RAQUO5^11>UW=+\] M&O1K9.8Q$1B@99X`U? M*_-AFM+SM=WY>EWQYKKE*1[H@"GV3[>^,D7WZ+\^O]], M=V<0XPJ1[A'`)3AB`L@CM!@!5CTX[(&71Z*&33(S07<-H.C)U9!48%NQ!TY0$]%MN@.UO7UZ,WCV8S7!C&62&T!:(@) MH"(B0$$`-<]3/)D!`]0X5;2(.K\H,3F:+C#6O%BBJ")0;2"`@QRL>$S0_$7- M3%]PW3>Q$%(IT->9Q0F\\23[_H5#.<89SX05'E$.N++`^1+X\#AK`!@9`#!W#Y:'R6/T#%#56/'=[H#"8D,_B0G_IT_3MM112U=U=36U.WH MI<9W;!I)5M3MJ=W6LK>M]K)V]/R&AQ[+)VH:+.]D'\O#PS756/;W]/GQ]8K] M#3+/,+`L@%SB`@"L.0#//`D*YC&/N@G#"AFO3A*.%TF,P-'IJ2%"@DI-X&>I M#>`#;`2;_V,63`JO*M'$%I/FS,"I[07`&3*-:S(A+@I%%+UDQIM!BYFA,/@? M26MPK#N*J5M=D_4+=B98Y7QJ45P@6F$TU3/6C<+%/"&X!)45C")[AM[K-[(_ M,$C;?#PA74(U@R#C]+JY7MX'G:3,R*6G$(^B+!XAF0>;]&1SG^T7K/8G1$," MWO;XB-\"#`";RT9L#0IE;F1S=')E86T-96YD;V)J#30U.2`P(&]B:CP\+TQE M;F=T:"`S,30O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)=--/2L0P M&(?A#"XJ0:PW:"[@-)5QF%D-C`IV(>C*`ZA+0477[=%RE!ZARRY"XYAIM%:[&_7>R&^YW]('?MVL&W6MUWI/8Z?>/N6QE?6K MVF]E_4CKLCX^WZE&UNV3^OWY^Y#MO11"K)QSLP@#+=`=^`"NHMTD1$D;W$!S M\EBQ"_`9>`46V>?@"PX'7W(XN.1P=IG=@PV'@P<.!X\<3A;9$]AR.'CF<+#C M<':9W8,Y',WA:`Y'<^SD+MF".1S-X60?.[G(-F`?3O;A9!\&=\D6[,/)/IQ- M87"1;<`4SJ9P-L707\D63&&=3.&K[)[//=@L>`"/X`EL%SR#';H'FP4/X!$\ M@>V"9_Z?^(90N'+QKE$8;!8\@$?P='#RH94O\E^``0#*HW@:#0IE;F1S=')E M86T-96YD;V)J#30V,"`P(&]B:CP\+TQE;F=T:"`S,C@O1FEL=&5R+T9L871E M1&5C;V1E/CYS=')E86T-"DB)S).]3L,P$(`ORE#)2QXA?@&:6$5M.D4J()$! M"28>`!@90#"GCY9'\2-XS!#%G.\NQI6*$!N68G^Y7__I1-UM5W:)>58?[*VU4U=WIC_?/ M%]5=*X_#0>MY3``@:!%+1B3(HUXL'(J`'0=<'!0!CQ",5D@SN4#F%T%0^9', M!NA#@)+2M#*Q@%1L1@[L3&$XY#?.LBF43`N")"/O,6+/>2F\B]@F:".65LYM M3W"(6)S'H]RB@]5?$"+F_Q=/MKZ<^'?\Z9_EDW2OWV$H9#TCQQHM`JEM,/U!<% MWU88+>>,C9.T4]ID2>O)-KRZZ=2#^A)@`'Z<200-"F5N9'-TTB/P+@"Q"TADBD1;J1DJM1,'@(X= M0##3HW&4'"%C!H3KESK6'\DH`PN68GV6%=N)_V?TC#7/W9,O>/G,6T,[RHT; M:QG.IX8G>JISUY:\^:%52=F:BPBSL\COK\`+YUMON^-_X/5<^#]W+K3O'> M,0^8&U+G6MSRK=M+HYUT^5>E7-GC9+W5SH>E>1;U>V9(U^?%&'3A4/NMFK MXH;\JCC<7>I*%=VM?G_[>%;=E4*R";:T&$8+T&-@'`$@0\_H"#?H&($-!R-^ M`&,)/4="!H9#3V@Y9;2TR5&&3_5A3S`2=<:LX2&/4VN]H4S2XV#K" MO\6?6L]7\"]?QT&V@CZB^_XMK@^K>/RQ`R9X%,$G1FFDN,$HHVF#45SC%J/D M0H91B#[#*$^78Q0MJUZDC/-BN>-QSC[+WL\'9!CF-#(B/3,@I/45M>=NE=?`@P`]QN3TPT*96YDS130J"0!0'\!$7PMMX!-\%]V.IRC&2ET&=@T*I>02ZT?^Y2I MO9#69F]56GG2*C*[T/77![G,OA"L!.S@(<``$``#'`T*96YDD,/A,*I8`=!)]<# M=710=+6%OEC[)GF$C!E*8_*UBD(^B862_$HH_&D3690B%QMWR^),E(5XE/`" M4F[=D]Q[LY;B-%_G6W>5XN$9=C5D]\*M@.S:+8!L=WLA)&3UC7A[?7^"^A(8 M8P=VOEHW=60+V<(&-<6Q0H[X5O.#;N*8(HT(\8AQI-Z'>,P2I%(17+%#Y!`D M7X*[,-NY/PFS1T[\F\T?Q,&.:9@:^TT5IL%^W41QQ'YEPYRP?R!HL;^CB/U) M)'W_Q"GZ<4PI^GY31=+WZX:B[U>6HN\?2/K^+IJN/Z'9LP].4['SE*9F)U4T M#>Y#BB-N:8H3?O!(6OQW2+8)0>UG/5\XGQL]QW/BZQA1Z3^HJU\TS4*XJN$. M/@48`)+^_3D-"F5N9'-TX>RV"!G_V3E!!:W< MYH/9G>6?W9/&+WWCCRM?;WRS]G<5/5*]0A%E5!85]A;+#=;:WS[0MJ/RQMQSVIA2][/,@"0R$8X-F`]- M(5,,3TYIA5ZN'S]IO^'^H-@C`<%H$R%/X(&_("%_P?T@-;32%T">`@XV$31U M0-+!\(B(.#:BB*?869YUVHA@DVD#8L[Z?ZP?AUYY,][I[TQ&1Y;+FP&A"L>ACK`%[6`V)8_]B16?8PU@Q&S_AI?@J?P)$#L=M2RHRQD9--2EZAG7:&=/F!"6:Z M2XER@<<4?D&:<6*&V3S%63)/EKI)//S`JH!XCS*#^%M_AWBU76,*<;'!\]_E M!,4G"-MRI53/"=&\=@R)4Z+0DQL1*-4*4?94W3K+FX@<*Q/"LC9/RX9SZMAR M3CR\A?'I5 M2)S3O6Z#,!#`\:,,2+?P"/$+-'PLB2>DI)7*4*F=^@!MQ@ZMFID\ M&H_"(S!F0+@^WYUA;[O5$D8_8\E_":C*VI2&+FO-OC9O%7Z@W7F7Q'I;F=MR M6UH_]N;U'0\M%B_&[K!X\,^Q.#P=385%^VB^/L\G;.\0:'3.C73[,6AV-*X9 M38J@A73`$S*F`%J>P5^($ M'!?2-"[4:)R`XT*:QG&-Q"E"'*=)'`AZ0A*1:AK'38J1D$6`IG'C(N8UW#R*2CZCTFUZO,(C7G:V7%>:6Y0[7 MUCZ_ZGVKBR>[7NGB#O.ZV#]/]\T>V-9N:0L5Q$#>X#T0+T1#EP M1#))^4C,(QEVLJ)CWW'`"'[19QAOV.<8;]@I8!@E`ID)3[_(*C"<$0`>`TK. MC-3U@#..,/L_\`GN!V>$2;IUA#UT()"T0KWLB,@S*?#%/@,*Y9EBGQ%L2E:C MB821-,8C"XJ=S:@+2$+Y.>[?^,NH_DC\HW#*8F:("9J9,<4ZI1VE`^CD5*22 MRR4"B5".&.D@##EVK$*E]!4048I9"5*.]6VK'_6W``,`D^0MQ`T*96YD*KI-WLQZE1^BRB]*QZ4P7"3Q$\"?D@TP(,SE46&*%%P8/5U@; M?#7P`7M_6&)]B=7.++5=>;VDQI=W.+90/..^@N)^J4-Q?+Q!`T7[@%^?WV_0 MWD)O:4W?B%I4C;C&_F;'$IN(:60FYJ(6&]&2V_1]N741)>R<$IV\&3OE["@. MFO6S+%>ILR*Q)S'A$?US/E,FYNPH#CIT^Z(NTLD7JM!94>`4.6Y*.\,9^\@N M-@UU9U1_<_YG266A+I>^MKSGA._O7#7CI;OOC#WI8/R-OR\G3K:UMVC_[[Z^?==G>6>:*6 M\00R>"]$Q!(CB0Z`8XYFH1PG"HX9,_4\9C@HIWA&8*9^Q62D0[MB_`=3L6)8 MD6U`*WI%JW!2/D`Z033[D2"64$`=<`#<&E@2TV@_`ZA)*-)`J9\.%A1Q,]^8 M"E*%T=DSS9$6P!9=2<5"-P(8(#6`/@64Q0>4)P4D\RC[`IPTRD,2,8)9L9"! M.=A2!$7<8-@BWT*Z_,$E1">R'(LH2P[K=MN16%?7ISM"/2+PP2'3]>0ZIM.A M6UV!_!,I)=9Q+_:^L\_V5X`!`-<:)W@-"F5N9'-T_>2F=AAAZ[A07H>_"?9)G2;'C3:SNYRF< MQ'E<<)V%NT=9CM*N6-U)>\4":9W!QDO1(O3K5'5W*@"3C,& MG6`.V`.-;H%UQ0ZP6@!?L0%+4)^),F3LS<1F(NL&$T^.OSC+[G>OV?*[(P;A M:?C$S!.!J35H-N@.QZKED%IAOK`F6%]0H83[#J:B^1&VPO_C+\&9L9PD>$X2 M?@VY'.56/@08`(!">QT-"F5N9'-TPJL"O8@Z&D?0#UZ4/3<[)OE4?((/?90.LY, MHBN[WFP9^L&?0'^FNS2U:NZH^?*/+WJ M;:^KG>EJ7=U1KJOMP[5I=-7?FX_WSQ?=WV@`*&CL1/,?+Z!P@0)Q+!')"#G9 M'CE#=.1`=FS*PD!?B/;)/ME#/.>1/<0[Y,"F;)\,/[9B.H8C_Q-E2ERR%_8D MMDL>/1]R&>4E='CP6JT4'!U+DS.N2X7#N3P[<$E MN\P/3G%U+NRIL!=[=L9UV>Z7,PR9$P9M)KLASK%]&M2WO7[47P(,`!.*CZ@-"F5N9'-T6*G:PS06#*, MO1M8(]RQM02K-1BKP]Q-J#OE049Q/?8!NXH:!WDNY#D.^\`C_%Q^E]D"Y)$- MLJ4V$IOK+1+UX/T3.Z0`&10%&&R$+/?*'U2'()/LRUU&C[">^M5L[`SV7FED M12U"KK?%&Y#,_]F?XM[HAV+QWLRR57Z\"CK6M*-EJ6D-^R7='UWJ;FD M]:V^/.]W4E^)\])T18WBR:([1P1S`GX*>^!?X>/]F&&7U9Q6FEG]/X M",H,&1Z&5&/.8Q^XSSCQOU]8NC$!/!%D"/,!&P\#20`'32)Z-):[D^M:[N5- M@`$`R*WQ^0T*96YD^ M4X"S`I#R]%4H*2I-Y?)TX3K```/U9#/E_SK)U<@%T"``0"JA5JQ#0IE;F1S=')E86T-96YD M;V)J#30X,B`P(&]B:CP\+TQE;F=T:"`R,S4O1FEL=&5R+T9L871E1&5C;V1E M/CYS=')E86T-"DB)K)`Q3L-`$$7'6)W_L!!E$R6JEIYV_.W_GKTLI926W7D*P_>YYS\&+E<-: M5DL/;5G>86WD[9.W-1>O$CP7C]"YV#[?"P[UDQP/7Q]^!&-0&Y23%EJDVF'>Y`2!3MU0@'N>JI,G034)^AO<+ZQ09`EQF&7$^96?\- M^D_\U?S"9P$&`$]U#+P-"F5N9'-T!$%)&FX0C,!98DB)]0!?$CD0()*@ZPN^46H-T:[\URE!S! M90HK8BXDG6IY&E9]F>+SCAJ:SYA!=+_DKI0K-,YN0Q3LU:P7*D&O:%O,U!=I-9#.%(MK'*UO*FF]0W59J"Z"`[5&MJ16N5J M>?,UN'^H2IT:J0Y*7:?4=4J=I]1U&KQG"`--_JK/__`>+7/KA"I9F7/H>0'DN?:<` M9P5#+GU/7X62HM)4+D\7+@80D/_!``=DLO__8&#^#P0'&.3_C[)IRH8%/*7L MP>"7438:FUKYD^4X"S@B&7OJ>O0DE1:2J7IPL7`PC(XR?__V!@_O]_ ME!S,)#'QR.7JR17(!1!@`'SN*(<-"F5N9'-TXB/T78`FAE:*ITH%)#(@P<0!@)&!JLPU-XO$17J$ MC!FL&L>.[1>I51?J(?H4Q_;O]Q1955CA`J\E*H7U#;Y+^`*UQ.%U?8N+N71S M\TJY4>/;)ZP;*%]1+:%\=/-0KI_O4$+9/.%V\_T!S3TP/X0=1C#;$7-B1KTB M]JO[X5L=%GAW["K9A,7>-IP0K/U&P2WQGLV(Q5%W_GG.Q85]/L,T?[Y7.W&^ MNR;U(74SI)XA0G"J?^I+M,WFQ(*XL'$?D[[G,4)P&_?D,7[,D/O>D[X;DFWJ MG/]PTNS2WOV;?XE_DE?1]I2U*V)QQ"T3AV)LXIYQPT>['ZD7HX>N1;O#V"PV M7;L4T=W82C\TM_#0P`O\"3``KK19\`T*96YDHI4!7,0=!3KX)Z]*#H.0%?+(_2 M1\@QAY#/^;-M:;4I_""9GK*%[[RYZ5?5GYUX5]+]^&6A9=K=>FK1_-?G]YMK;ATP4(L1`%&"`9B( M"%NY351W0,^F!'3L&6'F*JF8J)82C+QZ%B-(#0:QYPXQNV432"_TXLBFD'>@ M$^<3<9#4[F#RGSTH#D9!VME"*RD+QD$>W+05O)DL2#JRW5`3BT$J1Y%[3FH= MS&3O$I#E(PNT41P:[!U%K0.F1/-M,_._EU;NKG%/[E>``0"\%8N,#0IE;F1S=')E86T- M96YD;V)J#30X."`P(&]B:CP\+TQE;F=T:"`R-CDO1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB)C-&Q3L,P$`;@"Y5:Z98\0NX%VB128L$4J2T2&9!@ MX@$*(P-5F6/Q9'X4/X+'#%',G>TB,H&CZ!M\MO^S54L5-;2M2374MO1:XP>J MBN1K%36[FN=VU1V/6SJ]X[['\H54A>4#SV.Y?SI0C67_2)?SYQOV1_3>@)2B!02SN#X;":G\6REHLG7L@/(I>` M]ST^X[<``P";HP%E#0IE;F1S=')E86T-96YD;V)J#30X.2`P(&]B:CP\+TQE M;F=T:"`Q-CDO1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)U,\Q"L)` M$`70+ULD#&*.D+F`V5W8)5H%HH);"%IY`+6T4+0V1\M1/(('"*R[01$K:X<_ M#SY3C;6LV(2UFJWAO:83F3)T%:LI-(]5H:9A)KP[4NU(;MF4))?A3K)>SUB3 M="N^G*\'(B7CSP:YLOD+P3",S^DA:,-/048`!5VMKP-"F5N9'-TM!#D;).QD'.5YX#<.`C**J#FM\B`6IE;(EK(O9-JN%ZC3A+M],_,#?`@P`2<;#*0T*96YD<"Q-XH&T%E*8"$ M"R2H.`"AI`!![3V:C^(C;.G"\O!GY10!E-%*;_7_:#3S]UYJV7J1<*6JP?X7!V>;L5SU3[*U^?W&[=WK#I1 MIZB>U@:B0DTC4Q/@S)JI5(TK[=&#[[#2&48JT-CH2'@+.AN73E"T-<,9^@QG M4'*Q^(MR`9TCUR6XW(G>"XC_(>_R&[:NSBYC*NVB$T9GUP*399,:2T*'QG+1 MOK.4-.8Q>_\N-?8?F.[=J\,8W-A]8J@KURJTC#53.K85+X=KM M."%;"5?T13]>0#J$`2.'#;4 MF;:0UBS&JW-H/CH_TP[,KXHK6R*F5Y(5^4>!>5HLU"EI=RNU&4VQ05 M12F?W\2N%OF3K)3([RDO\MW^1M)#_2`_/[Y>17TK#,4("2W(:#1`8P;+!P!( M36^Y(UZ;SC)PF!:]!``U<\]J2`&M?$7+B65-VRQ$^VK#QS$?6,)9XH%J4[3, M/5^&*R`OF;&5T2=9BOQ2XRZ(7-*X'F(>77G:0]*M'>N8!W>4X5Y"[B-.'5/O MW0*W[IJ&O]5?6$>,9HHEAG_"VO]CBBSF["BWO[*K%W('Z5'N(TYF'A9XFH&? M/,V,#F;)P-D7FCL8(/#.J67GJ3/+SFO7 M[DC+C?6=]>;*>Y-%B?QDG7X^^3W%7BT?Q+<``$PZZ(.FF1Z`7<`#CF&%%,FHB"Q-=>0!UZ4*C:^!F M'*5'Z)(%87QM7RF,1!:^!/(5$IJ?YMFES.0>K^):'J[D:\X_>)'C.C/+_2Z7 M%]DN*W`.\N6='RN>/LLBY^D]ON?I\?%&XJ)ZD%^?WV^\NN4G,^H4IJN#FW+= MD&QZ!+'J`>)-]\`VK2%:M0+8=`=0K[D!*+W;N9O@J`F1K)L\HH5W/+>:/*!] MY"#TY![MP_ID[A"LT3Y2E\-DA?9AJIY[G-P94V2'A^3=&%-8N_04'!E3)#-_ M*Z%<8T&)2_O@P3JFW.!>F!-CE+BT#]9@)OIE90U_NG.N*??DXQMSH\BY6_O` M.7).*-=[9.[#XMRQVUQ0KH\<1'`O7`0[M]W$!6NRV5Z7P*13%U)O\Q MOZOX$_\18``LP.*L#0IE;F1S=')E86T-96YD;V)J#30Y-B`P(&]B:CP\+TQE M;F=T:"`Q-3\4X*Q@R*7OZ:M04E2:RN7IPL4``LS___\GB?'_!X0:90PX@YD$QC]V M$AA_<#-^X&9\X$=G/(`Q#L`8#3`&`Y3Q#\;X`V-\@#$.0!E_&!CX&:!`GB0& MEZLG5R`70(`!`*(QZ+(-"F5N9'-T_0`B4?GN@XM0.C1F%O@<>=&T$,<<@MN M?S!\(4H$[E!DN'M>?*,6&`?^1/`=X1]O@#Q"O@+F)^#"X@8?`@P`0]T%'`T* M96YD\GR)9@ MZ7B,5B*Y94LI2+(@521!JDFR54XJ2&4K5ZL:%!^0;9:C)7Y1_2[9(T'BI(J4 M?Z3B;[55OBMS0P=]G3[UCXRRQTDV);N:="4&Q5LY-%6#2E)!RDF6EK#T]#FI MT%)[ZM+N#BLQ"589[.`EP`#&;<^M#0IE;F1S=')E86T-96YD;V)J#30Y.2`P M(&]B:CP\+TQE;F=T:"`Q.#)%]3N,:0H(A5(W@7AG&=&AS,F M!L6>M$*QYCV*9+L@B<)LZ':]G]`LT=K,NNFV]]WGAW7+JF7I+;RY]^$%9^TM M@<="I\W5WUK&/\EEBM3F\):+@58ZJY,C*:&QG.];/4$V^_J&XH7W-94+Z2 M!3QR[BW`(G\2<.`4,:T6?1=;:S7MZ9W-Z"=Z"7 MAM[I7X`!`(60??0-"F5N9'-T(_L?V>W4H++2N]7&I5J5OJ:RD?X@H-U5UQ:E%R3/ MZ@K)[[E`\O7CC9:2-P^ZW7R^27,KG3'9Q$8MM9[?X]R/X_`9T)\#O@:&[-=Z M=F3;M>RW:0WX/Y).9_20=N31^$X[7-!]VMY2F[9C2%.6]AAFRO_"W_T1]N%$ MAA'7'=V?&*[JY^O`M_'88-@>71VW15_C*SA8A,PP/S$#B&\8BC%UU)O(G"!` M[AIYDA\!!@#>Y9_>#0IE;F1S=')E86T-96YD;V)J#34P,B`P(&]B:CP\+TQE M;F=T:"`R.#0O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)A-%-3H0P M%`?P1S!I\F+"#6POX`"3&097F%$369CHR@.H2Q<:71=/)D?I$;KL@E#?:\$H M\:,)^97VM>5?=H4J5+E1QVNU6ZMJH^Y+?,)MK7B\JFAJ5=+DJCBA5JN[1]RW MF-^J;8WY)15@OK\^4R7F[95Z>7Y]P/8$'NZGL>]$U&940QH9[?D(JNGT=WDKDI=X+S[E M[5@W*]C,V]DL:H)RO@C$OY MVF8YLYFT,OR1T.^S:8V,>QW(\"W#(6<':8[8#D!S5@.)?R<=B*\)\:+%&_P0 M8`"RN9/[#0IE;F1S=')E86T-96YD;V)J#34P,R`P(&]B:CP\+TQE;F=T:"`R M,C`O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB);,Z_BL)`$`;P2`IA MFCQ"Y@4T"4;4*J)W8`I!JWN`.TL+1<'*52Q\K8"%KQ'Q!;;<8LGG1$_\@U/\ MEMF!;R;N<,@MKD7<##D.^2^B&<4-^90VXE8]DED][$BU^7=*O92"'XX;%`QD M3D%OU&=YTB$OYLL)I5^$':0*!SC".DI:LTI0@3:E)^.+9^,7+@ZZ]'AU?]=6 MX>7)PTR)?J:,!]S<"9K-;F%@M^`.F$+3R`&HIJ&@GKF"1 MTBL)7B3B!=9NBY!Q9,7:QU<,\YCW1F>88(H=A;J'785+!5O0FI<)=C6FL6(O M3GJL#!=KZ.<@YZ@UR#'[(/O3`2J0^03WN\,*\B$078G:5+>H"O`<$0GZ4Q>J M&U0UK-_8ES,/9Y[.W)THG"E*X>3D`T*96YDH'CTH[3F5 M@KZ6UA>);Y!C#B';V!>OXDT\>P:N;<@VG1E;=Q9L9?XM M;*Q2CA?Q2SS3U?HH!BS%.5*<(\4Y5H8MQ;MX%8_6C/^,K&U'E5&+T#8JE`K$ MT#;[DW8#BQQ6\"O``#,GH/H-"F5N9'-T/WP[$ILC>P0CJ9V=$FI8+ED M+A7J_Q5LA#<`ZPSV\!%@`+A?5[$-"F5N9'-T\,K2EO)!AQKGA4'IB^24SM[F+*U^Y:_=?DWU@GJ%MD$7]R,-'_F1@K/N*<'% M?B#&RL/=]OL3JCF0 MC5S%G:8WDWDB:D0MFN1JRWOV@3JY,?WK*>T\]C4ET:&D_44K$GEC9U-3$K2B M$;674Z0/M`,ZE<'V/SEF6U_DU9W:Q]34]QSB._TE?L1CSWV(WQG+8#J MZ*#HW&Q>*^!%Z@WJEJ'D,]8A+CY^TWOPL@03%#CEF*58S/#`X0QICI^Z$"AB M[K8XF;L(K$]026![3'-@:[<#J[8+Y,#D!J^7VQ'D$LB6U$=D(NI^M*6G&T_1 MEQKY4$!V[/43SX3>__N7;IZ#AR+G/@B5C;0M==^T'74!&?<<$JPD[.`MP`!) MS(,O#0IE;F1S=')E86T-96YD;V)J#34Q,"`P(&]B:CP\+TQE;F=T:"`R,#8O M1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)7,\];H-`$`5@$`72-!QA MYP(V((AE*B3_2*&PE%0Y0)(R12*[7G.S/@9("^?DMQ$6RQ;?2&VGG;;W1 M0AM=E5H_:5WH1RG?4H6PT+K49EURMBX:GJV^?\FND_Q-JXWDSYQ+OGO9*Z_N MI.>?RZ=T!P%\"R!*@2E*@#'K0^0!!V_18VB1D%M@2C&8*2-C8#"_9'ZF)>Z! MN5KX_T1\MOW#U<[$!"1>Z,%])&7*?6YA-*S$/H-U+&KB4)F];U'&'S@+R+&3 M5[D+,`!)P9AB#0IE;F1S=')E86T-96YD;V)J#34Q,2`P(&]B:CP\+TQE;F=T M:"`Q-C(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),C)6,%`P5]`U M5#`R5S`S5D@QY"KD,C)0`$$S$P5S/4.@G)Z!)1!8*"3G@!_8(=*`>@1K^ M(Q`C`OUC1J`_[`CT@Q^!B#+^3WW##RAB_/`?BAZ"4",0'?Y_\/C_`^S_'P`- MM/_/Y>K)%<@%$&``/6J3?@T*96YDAM6R",ME?AVJQ.0/:'-(;L/!Y"M-S=80%8]X.?'URM4MU"K&5&M3*U45*O8J]CSO%.>J)V' M&1']:M%-]&Q+8QOQ1]R)VZ@?#-]2/=@9]B_IV!AS;(R)[L5&W/6Z@S&&O@$*O$&=LFYQ,7HIYH M)CK1CX2["A[A7X`!`/9EI.@-"F5N9'-T\(0'>)BAAL_5^:Y_WI?2%D)(",0W)2T-;>DMP`#I^\.W#0IE;F1S=')E M86T-96YD;V)J#34Q-"`P(&]B:CP\+TQE;F=T:"`Y-R]&:6QT97(O1FQA=&5$ M96-O9&4^/G-TO0DE1:2J7IPO7A_\/Z@_8-]@S M@&%#?P;F`YHDDE>`#3"%HY0'4TD)1L#-BD6M%O,AZ M@]BED*P[HP1!LV[QL>P,P_RL%Z*#;1_;;H1^A-T`ERYL(/#4LX.A*GB=B*H= M)U(GQ,4:^C'8J`^S^=(`NV/$$=]O]"N(A2'7.1&F2A4'FAR/;4XJ< MS`2;D0E[RJCG)*C?O+.RLFSH?%@ZBR;O85ALX^-.Y;PE-?*6?^2]:TVD3E-G M;5QM:([[PZ]PM]='L(F@98VWLM*\D-:53%-.Q<,$CRDX/XQBF,%3@`$`5%FK M&PT*96YD(MV21[!? M@"9!1FJG2`4D,B#!Q`-`1P90F9N\6?HFEOH"'CU8_KDCT(':TF?)OI/_\]>N M==Y==1OGO?.M>^OH@_B4?<.7JTX>5^V&U]J]OM-VH.:%*ZEYX`)JMD^WKJ-F M>'3[SZ\=#7<$E`I`-,RL3\`4CX!)"J7."MEFC62+1NR9(,R'8A9&,$K0R-4/ MY4PV2/4_4H5H+PE]JO_(O\P'9ES@B(HI:DF&*8R2E@E&@_,QP)EB+LG5PDDX M*I-AK9,D%(C8IPCS?/PNR.#!?,>_-/^]-FMFY7=K3 MA4U7-DOL4Z)?=;K@X=QF9W8Y2ZC-YBNN<_OXHM>%CA]LNM#Q#74=K^\N;:+C MXM:^OWT\Z^)*`PBX,:F<')0AF^F$W"(D(VP`9X13C@882T^Z2@R>^Q]AWPO; M;S0Y=I[U$:,_EL[TJ/YQ1T[Y#HJ/?'H:%\`=>)%/(9PJ1Z92&".)-DA.=))9 MGN6J!(#'W=(['SXI>_OP5Z@/?W<+2T4IUD=9+546,HQ3#X MO^F,V#H&-FA4Q%1MI?B3]:!4B7I2BBZG`O%6H;#A;(.14_5UH>_UKP`#`%J6 M*O`-"F5N9'-T45A,^9O>0X&RE^679A MOMG)EYQPP:<9GR>\3/DQI1?*,]F4US,N%JF<+9(+>0I>/].JHOB!\XSB&SFG M>'5WR2G%U2V_O;X_475%`&HCC%XD#EX@MN9$_,*G&*(%I@A6/BEA#79&':#V M8HG^1^TVSNULLY5*?]UHM:Y!+2&AUK<1?(DUT$1,P>P8JH-VA;;4-CU=>KXS MF)WS5]!"C;#:NY08[:;ES MA;_USK`?_F&R^RG#W82N*[JG7P$&`)"P.!D-"F5N9'-T/0@=+V3U18YB>#$3W?#'TZKM4SDDBK/Y2J3;RGL(,_HG?!S MN4CE5;)(KNFLY>L';$J(7V2>0?Q`@)(6,(+^$X!JBC\U#^A(Z@ M"@//0L_1:(%CQ\<((^`0JS'D8T1^V`\^K"HG5P^(5/U5?\?Q:U""8(QO`'CN'DD0#W)3S# MCP`#`*7UMW`-"F5N9'-TSBY@!)9V/WEV M?M\T)9=&FOJ3&:=([6+&0^+26MW_9FX< MYY0!;4S_LT=;Q^QO:M$O^A_4HF[&W#@FKLQO2&SCN'Y.9S)@9LB3B)E!S`KY M,)NF#`>R)AMB(325 M3E^SO^&WBC[)+W5=8KE:5#PO%V6MXY9?/VC=4O'"?DG%@\:I6#]MN**B?>3= M]NN=VCN2T>EHQ,Q%3C";UDYTXTKWNJ#TC7FX-H\SD4%_&J+31`R.4TXR.USHT.).!JEZ!/UFM[CU30]O%YGAK!M"]VW]$S? M`@P`F5-J00T*96YD?`#UZ$&C9_#D:Y'X(O4-ZJT'0OVG95TUKK$) M^0J=3IEI5>I<%RN]O]0'2UVM]$U!]U36^)KKJL+2HL#B(C_$J/7U':U;RJYT M65-VA@#*UA?'NJ"L/=>/#T^WU)Z0QQ"=[X`5:FK@(-*1WX5('!Q%]VRA3;PQ MT$COCK#<-]X)#N,0[Z<]/#Q/XMRE42L1`XV*#GP$8OKNNYP*\A;OTT\Y'>LV MIJST=J.,FJ":G8_C8G[8-W\KNG_J?_%]]FV'KSM\V=K'W M8J!%J%4A?>@#TH7^(`WW;>)6X$I<*%TH$TKL1?QE(T)'<8?R:V5TVM(E?0@P M`"O[K[0-"F5N9'-TLP'&??=P(TU1KP[TK2@=,O>4;K4/*73]8PS2HL57\[7 M`Q5SDBK1N(G9%7G#=GUNB5ZT]G%NE0X\M0*V+H$3'TCH@Q8V+, MF!@?D9AE8IODWY"G14$;^@@P`"#>@HH-"F5N9'-T7!E+U1Y<&4S+T9O;G1"0F]X6S4@+3,S(#$Q M-"`Q,C1=+T9O;G1-871R:7A;,2`P(#`@+3$@,"`P72]7:61T:'-;,3`U(#DW M(#DP(#8T(#7!E+T9O;G0O4F5S;W5R8V5S(#8S M-2`P(%(O3F%M92]4,R]%;F-O9&EN9R`V,S8@,"!2+T9I7!E+U1Y<&4S+T9O;G1"0F]X6RTS("TR,B`Q,#$@ M-CA=+T9O;G1-871R:7A;,2`P(#`@+3$@,"`P72]7:61T:'-;-3$@-#8@-#8@ M,S@@,S<@-#(@-#8@,C@@-#8@-#8@.#(@,S`@,C@@-#(@-#8@,C,@-#D@-3$@ M-C8@-C@@-3D@-#(@-#(@-S`@-C(@-S0@-C4@-S`@-#8@-C@@,S,@-CD@-#0@ M-3<@-#(@-C@@-C(@-C`@-S`@,S4@,S<@,C@@-C@@-C4@-C@@-3$@-#8@,S<@ M,S<@.3$@-#8@-38@,C@@-#)=/CX-96YD;V)J#34S,2`P(&]B:CP\+U1Y<&4O M1F]N="]297-O=7)C97,@-3DP(#`@4B].86UE+U0X+T5N8V]D:6YG(#4Y,2`P M(%(O1FER%LE.`VBC/,%3^L:%I0=,\FIN@&=XJF M=]><4%3<\O/3RY**&0FFQ+,*5'^1GD\7^+1]GR;TJ?69'>C46DNC.)=ZHG*Q M`@Z.O91;"4#7!VT(D"[E"P3/6(T^-@?5#\@$_U&NCT<3GGK@^)M:O^^^L8Y* MAP=')1]#;;N+H:Y?)X.\46]JVRK\@9)[H7E!"_H48`"$@N4.#0IE;F1S=')E M86T-96YD;V)J#34S-2`P(&]B:CP\+TQE;F=T:"`Q.3@O1FEL=&5R+T9L871E M1&5C;V1E/CYS=')E86T-"DB)=-`Q#H(P%`9@B(/)6SB"[P("':@ZD:`F=C#1 MR0.HHX-&9W3B6A@N@C=@9"`\'U6)&/L/_?(G'?I7!NBC:(Z10"EP)^`(@>3N M-U4$KL"A[_H3SABW!X@4>!L,)'@+O@!>M)HBHY9X/EWVH&9`ED[]HOJ!\D%E MEWV&['+0<"M#2D.Z%W$'TN1$UYBR1X>LQ:*6Y)LDH=IFG`ZDX50],_PV(X5C MAI<8X25&>(D1BXR\E_SELT2WE+\]-@!S!6MX"C``^72D(NB]+G>X5D+!KH!TV;__5OL<8<"SRT)19'N+9X:^$!5B7OYGB\ MP6)IY>$RW_`J\>8>MC5DU[@J(;O@%R#;7IVBA:R^Q*?'YSNHSX!X?0JOPH*O M\1\&/<.;.2GC]E1,OZ=AN@D9T0HOD44EWPQU M2K^3XWOR*A7FG2._BD>ZYB]M_!E3A2\B.*]A!S\"#`#/$$1C#0IE;F1S=')E M86T-96YD;V)J#34S."`P(&]B:CP\+TQE;F=T:"`Q-SDO1FEL=&5R+T9L871E M1&5C;V1E/CYS=')E86T-"DB)S-"Q"L(P$(#A*P["+3Z"]P(V::%-G82J8`9! M)Q]`'1T4G=-'\U%\!$>'TIAXG41N-O#!?\F4*RO25`3&4)G3(<,SFCS,.HY% MFM%$IWH:3D7[$]86U8Y,CFH5WE'5FSEEJ.R:KI?;$>T"?0>01#$@\FV8VOZ& MGWS;\^V`_8C7D$GQ'#$I'F,FQ7W&I&@_IJU!25)K*Y>G"]?\?`P/C_S\,#`P0`LS]_P>(__]A M1B%^L&,2'_@QB0?RF,0!>TRBH1Z38/B/0?QCQ"307$8[YS$`B08&(,F`3/S_ MS^7JR17(!1!@`"Y0]KX-"F5N9'-TK!\W#]. M45651(I^\L>=[^RNXYI;OEAS=\G=FK<-O5';ZV2-F7;5Z-JJ[O6[XN=7V@Q4 M/7';4W6GZU1M'JZYH6JXYX_WSQ<:;DABEF569#+;B018S.-<%X*5O4G&+"D' M1PWPNNG+_6,ILB^6_=9_=.=%03+:(W&&.?'X&G#VDIH\'C1GU%K#DMJF-^(F;2Q/=.@QV.!P17#4 M1PS6[_"8T7B9'M7\0+T2-(2<>=(70K<#/=*O``,`?-=O]PT*96YDD-NNPB)/[MJ\W$"3SR\1+"'Y*5%)F0N3B;"YE) ML9R+1\E?>+Y`.Q/+8`-/UW>70O*TN!5O MK^]/O+CBGRRPC$4H!1K&0E3"!D9FZ(9_-*RT76#M/K0&4Q>B$F*$4JY;_J<& M6SMU#]@06T<<;^LC_H"58T3\!N.)9N0)F$S4:J`Y]8A@/?6%1P3KV7V,9`,; M8JN)EMD]6!,;QQU8$6LP(F[!F%CIT(R,P62B)IH$5!/[8*!6APSZ8&!7'M`$ M#;&U'FMBX[@#*V+MN`5C8H6KCHS!Y(AX-L1U4;@H0'/^DNEYS/26*,<#K':9QA,L=T@4\QO$"2&-P?O(WMN&*1M'^M^-@)G!.*_<#N*B5VQ.T_')( M0W<1V$&(W%*5EOHX)#[S",$)A\92\UV:]ARB*2VJQUV7P]]9/O?N)W'?4?V; M;37!30$/\"/``'43RPL-"F5N9'-T"5+19NF=P_ MTJRE\DXF#957VE,YNSD7?>VU+!?/#]1>$%X17MX%T_,^ M&%]\>!-XZTWDMQ\2<^]^V72)C6+PS1K_1(>3(U*.T!WA$8L#GP.[/_8#VP.I MP/L`(^4ZC%SG\!(Z8),!Z',E,D"7+=W2EP`#`%QDF(\-"F5N9'-TGG'?8W%'=8O%E:QCL;\YIPJ+_IK>7M^?L+]`9IZ, MES>/Z3(P>TB"Y241OM@'4L5GD3P4>%DQBEOIF#/I=8Q) MR(]A(GJ]`S)E3B)@(BG'2\L$O(!6/4`>J@!Z9(Q[IL@"L8&-N(@'A0\9_^)6 M!B7D]/$/D^;TFTR1U"1-3!#UY%=2CH(O>A-;3'GVE M++Y(?8,>>$:$<-!!U28%!#`GSO M=D.++[\--;;>!,M:]M/7TY4H4_2&6T.OHQ6Y??2J;P%_QV/!/ROQW8J[O$_)E,L7Q"%PW=T(\``P"3[@4*#0IE;F1S=')E86T-96YD;V)J#34T-B`P M(&]B:CP\+TQE;F=T:"`R,38O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB)9,ZQ:L)@$`?P3QR$6]+1H?C="V@2##6!0$!;:`:A3CZ`.CJTM.!D#!GT ML1)\D0]\@8P90OZ]*"VEWO`[N#]WG/?$#@<\=-F;L.?PVJ5W&OLR=-AS.1BY MDHV<0,KGU9:F,=E+'OMDOTI.]O1MQM+B.7]^?&TH?B8@!="H!*B4#Y3A0P(# M%:%HR7!(T!6:GE!;0B7@2JE_,!H=F$C(?Y&=[G\4ZMX]E77I".>_E/J8WC`M MN;:*EARG@39-?Z#+7?@856JODEK)#7DR`[W$M*!O`08`$\^,;0T*96YD.)PA MS=V28<:QB+GK8C9W*7%_@JJ!9(=I#LG*]9!4FQK=:-9XO=R.T"R@)S:T76@F MU@1Z(K521KQ5W=&/B@Q]*J+I2U%%'R-""=(*+4DKC778$7JW+C\$/GWHTT4^ MAOAH^@3X04L&]C"5X`!`%:BJ:0-"F5N9'-TGY497A9Z:M:]DYX?SZ"F76@M69]TISO[;#/`%'-\Z*,Q:/JXR&`-)G?!%,TC MYKW,Y7KILUM/.%_!L(#D#4T.R=3E(1G.1IA!4KS@=O.^A&(,RBU+1&PWJ,J@ MYH"F*G96EIJN>.Z(%R42%["_MA;KJ_$_U>U^MUVVSK_>YQSNV40BEU$E<[&< M9CM>>Y&XI=J*.QWJP]S'J-WW-O^/=_B[Y;V>@@?1'F3N;RK_-GO_:[46=_K3 MV\0?WJ/BB<@UT%ZJ2Y@4\`I?`@P`Q1M8&`T*96YD3"SW6J3Y/]62LKU+]G*@W ME66T2--+G8X2VAN-)_1D^NE5S7(5/^HL4_$=[:MX-K_6B8KS>_WQ_OFB\AL% M`%@"F`;@7UHPB!`BEF$+!;UH6AD\L"Z$$6)#W]'*FEC+IN.^8RB,CEQWW'4, MCBR%52C<#'CF,^M`N.JSG7I"7TB_-!TCIIV"T$.[`&R)MJ-A-@LHF$!9PE9H MBP--QST%.M*F(QU#CSNJ8I;&<3_@AJJ$D>/6<&IH4!F<^(8]4GWV0+3,LV`*#?]ARVSZ--S54QD8I^^JQ)^ MT?@><$EC*W19[AC4;:X>U*\``P`(W@8*#0IE;F1S=')E86T-96YD;V)J#34U M,R`P(&]B:CP\+TQE;F=T:"`R,3,O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E M86T-"DB)Y-`["L)`$`;@65($ILD1,A`#3"%HY0'4TD+1.BD\6,3" M:W@$2XNPOQ,?B(V=E0,+WS#[[[*;M"22NJZD(:VF+&)>I"DP5*RJI\7L$JC`(?<.YW&%A'CWRBRIY>P,^0 MZV,>]0V/S?8%T#O^ARCO7P'*B-R^4X"S@B&7OJ>O0DE1:2J7 MIPO7____&/\#P0,&>R#)P"`/$OC'____#_[_[?__?Y#_WPR4K/_'_/__`;#2 MAO]_@"0C$LG\_Q\2^M\/)O]30#("W<'`@(T$ROX!D_\8H2H'/PER,`/0_>P- M#,R8))>K)U<@%T"``0!"[029#0IE;F1S=')E86T-96YD;V)J#34U-2`P(&]B M:CP\+TQE;F=T:"`Q,S0O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) M,C)5,%`P4S`V53`T5S`S5D@QY"KD,C0$"AHH&%DHF.D9*AB;Z%D:`(&10G(N MEY,GEWZX@J$AE[X'4)Y+WRG`60'(\?15*"DJ3>7R=.'Z7__/_D_]C_\?@?#Q M_\/_CP-A/QC*PZ'`?X9Z!GL&^08P/&#_H/[#?RY73ZY`+H```P#0,33C#0IE M;F1S=')E86T-96YD;V)J#34U-B`P(&]B:CP\+TQE;F=T:"`Q,#(O1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB),C)5,%`P5-`U,E8P-%4PLU!(,>0J MY#(T`8H:*%@:*ACJ@27U#"R!P$(A.9?+R9-+/US!T(1+WP.H@$O?*K)%<@%$&``G6VCPPT*96YD8A#AWH<3Z("? M'FI.7)\SUXOX0MP=(4E!;5$'H):<@TK6,_1!I2N\G*\'2.<@A*R$_+>QJ!'4 MVD3V5X=:JY-8V0TVHX?1OK&M?3=: M/,[#TIC'OQ:9L:1?NUE8I+"!CP`#`'=@P"X-"F5N9'-T8@^3+#SN[^E+ED4LKIF12E5(4\Y?S*12;V5I64 MZQQKZ^P"S[D\OO"FX?1!BHS3&ZQSNKF[E)S3YE;>WSZ>N;EBC4Z_=*1VIQTE M_9XH]-%%Y^>@G9]J'?W8ZF3,?E"-QMYO?]$?5J`_@L#N'RWH:I1=8@0PV.!` MZ$=".1'*FP3`(+8;DQT4_]C;C4J?1K+0'1$6 MM@?\@K,#W M#0IE;F1S=')E86T-96YD;V)J#34U.2`P(&]B:CP\+TQE;F=T:"`R,3`O1FEL M=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)K,^Q#H(P$`;@U@Y-;N$1N!<0 MJ"D1)Q+41`83G7P`=730Z`R/QJ/P"(P,I/4*)ACC9.SP#;U>[[]8880SG*H$ M]1QUA"<%5]`QW488)S@+E"L&T8).@L<+9#F$!]0QA!MZ`&&V6Z*",-_B_?8X M0[X"(SIA+1,U*\B*^:1HI&43GPHMW7+;,6M+:SA9.*O"4$^=]OJCS:`WV@[* MG_0^?VO>9@W3J_25A_<)*26;*4=#L.^[K>?]AQ)V/RJ[#. M80]/`08`2YGW]`T*96YD;0\BGD#CQFL7'^;(C'` MV?HLZ\Z_[1,7O.19R=:R+?F[I!_"66#9!2_G)7KS8H5ZYJ\]U0WEGQBD_!U] MRNOM"^/2;/CP>]Q1\TKBIH)2&@2E6I'!."/BJR&#)J#MLG$2E8OZJC?28;Z* MAC:*>J!2P"?[I*KB*YT2IM%+FDMI?4J[W?BN"^U=U=7NWQ'V;5B?D#:XD_CL M[+0,:N(U>AF^D[X*1NBMH0_Z$V``&7;##PT*96YD^4X"S`I#CZ:M04E2:RN7IPO6/_8,]`_L!?@;V M!G8&=CYF!C8;QA\L-8Q_@/@?PQ\@_L?\C_$_.Q#W`_%_,&:F#?X#-)N!@1$% M<[EZ<@5R`008`,`1;VX-"F5N9'-T'J0ME`/@63J`=J.'5K:V8$,.4:N8LA%W!MX M]&">^C>!"#X0^L5?+C774N^]EH4NO+YY_N2BPC'71:'EW".;YQ5FI:\?O*XY M>]&BXNP9.6?K[8-ZSNJ-?G_]O'/]R-:&4R0G`TVE#ZW\6B]G&^4`1XOB+NPF M@0E08[9/`4M+SCI*K">R@:B)1`&!V`C6XFM(3S8ZL7B!PXV[2A.+_]`]HGL( M,^F:._0)>D,GQD\U[_A/@`$`4]QH*@T*96YDN->+$<)=X@ M98HES\E*P,89^(H9=N>E$4<<\TAS,N54\TG3E9()M^,TYGBL93>.YE(S/EYH MD5%XX&1"X4;V%"YV2]849EN^WQYGRE:$I@>@"@Q0!$,@=P.@+^V&>,(I%*A; M*H/2EAZ+TA16)IY<4#DZ`H_4+W(!?\D]MJ/H4'+-TYA*"1+#64GE;#-`#?3P MEB?F!91M\#I0WQ]IG=&>/@(,`'"%G?T-"F5N9'-T#]@V54*=A*I@!D$G/T`='12=6_#'!'^D M_D%'A]+STE#HXN!N(#QREUS@3J<4TYA&">D)Z80."L^HE01C&QF'2G)A/)65 MTOZ$F<%H1UIAM)(\1MEF3G(P:[I>;D7R=.%Z^/_`_X9Z M!GLPE&^0/R#_4/X_'/ZS!\$_]7_J?]3_^/\1"!__/_Z____]_URNGER!7``! M!@"&83/P#0IE;F1S=')E86T-96YD;V)J#34V-B`P(&]B:CP\+TQE;F=T:"`R M-S@W,YAMDPYMTPN^;K`]096!<2/Z!*( M;SD/\>K^"E.(BSM\?7E[AN(:B*BV],T?QW&4.HFPD M\^CX@P\M<]N1KJ1RTRV&N=IASGF8>Q1:\R7"30$/\"/``--!CM4-"F5N9'-T M\4X*P`I#Q]%4J* M2E.Y/%VX_C'__\_XGX'Y?P,V\@?S_S^,__\P___'^!^B0/ MAO__&_]_9*@'DH^!?CA0WPXD'S#P,X#DY('D?P:@.5RNGER!7``!!@",ZZE. M#0IE;F1S=')E86T-96YD;V)J#34V."`P(&]B:CP\+TQE;F=T:"`Q,S,O1FEL M=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),C)3,%`P!F(C8P4S(X440ZY" M+B,#!1`$\4X*Q@R*7O MZ:M04E2:RN7IPO5/_O\?_O\_V/]_8$8@(!"NV4!V@[9FA)UMB0 MH:^5T!=1WT"CAJ#KG5R#752!]'$(Q(_T'`N<\M8WJ&?X6L([Z)+G0L;II,3K M8E(L>,WQ90OK"O(-ZA+R![Z'?/UTBSQ4C[C[V+]!=0=$9&L*ZZ]F%?>4Q6W2 MN&H232&\\RQFDMW^?.=13%LY MW">M3MYJ1J3XUPT?_PGW%3S#CP`#`*H1<+B=/+OUP!1-++GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+%P,#`_/___^Q M4O\9Y$'D_P;[X4_]J2=(_4"E_F.A/F"C'J!0!R!4`X1B`%/_(-0/1G"0'V`$ M10`#`PZ*R]63*Y`+(,``US=,+0T*96YD^4X"S@B&7OJ>O0DE1:2J7IPO7/_9_C/__,[!_8+`'D@<8 MY(%D\P]^(,G_A_W_#S;[?\S__[#4`\E_+/7_@23#__^,_T%ZT$CFP4+^800Y MDH&!O8&!&9/D^4X"S@B&7OJ>O0DE1:2J7IPL7`QC(8Z7^`]$_(&ZP M____`83ZP`^F_D"H?RC4?ZQ4/T[J.!`2I/Z#J'9L%#N8^@&F&!AP4U`E,`W8 M#3O^_R,VZC$F]1^-.@RACJ-0[=@H=DA@L2.'&30@H<%Z@!\2Y/R0".#'&BM< MKIY<@5P``08`V%,P'3M8ZAJ%@,\5\S!*.98;\GM$*XH3B8:1?HX"B=R4EY^ M4EY0L.`XH>!%$BC(WZ8<45"\\O?7^H.*9P(\G(#69'O@:`;B=M/;:_A0X6)Q MJM#Y.%NT/;06S0"=Q=&9J76F:?5&W=X+U3RDJ[WOYOK?9EUUT]T^;C>QL;KM MC\7%P\[*7\HGL39]L3&EU;Y2BVO_?['S5&/\O]*LH#G]"C``,4L"0`T*96YD M^F?9FL9RPV. M;2+3M7Q+^"?/4NQCVVY6B;R*5_$6*Y.O'WQ7\.A99BF/[O&<1[O'&YGPJ'B0 MWU\_[[RXY6`8`RS%`LN2Z(-V/!QW]TC<)J)Z5F*)%Y;=-=)/M&C/P+&=5"Q`,D8FB$#,U(,Q)B0F"2_ M*_@3_Q-@`#HW^/D-"F5N9'-TPAJ>`@P`)C]]$0T*96YD)>U98N2MTRR'%JSM$-_]$"JN&!&].P6K!?`L-:P1J MO+?]B59>^@XM#\<(*?`/XO%DK_Z$UV/*R5TXLX6DC#CSLL:B5;N0E%D[Z-\( M9HTN&$FYK>O;+V`#O\%R!6_@6CCYFR3@%7K@'>@UZ*2A"_H08`#;`0+C#0IE M;F1S=')E86T-96YD;V)J#34W-R`P(&]B:CP\+TQE;F=T:"`R-#`O1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB);,^Q2@-!$`;@/;8XF&8?8><%S-U) M+M'&A:C@%8)6/H!:"BH*=GN0XDI?Z2"%KY&0%[ARBV5_YX*(D4SSP?S\##,K MN>0Y'U4\.^9IR0\5O5!=\[B>5CR?5))-RE.9$[Y_HD5#Q1W7-157DE.QN#EG MH;GFM]?W1VHN"$O(F*2`SY1'Y9%''3Y@Z(V^4C4;V,C1M)SQ9:5-RVDL:XANPJ^$=_@08 M`($(F9T-"F5N9'-T0JY#(R!`H:*)@I6.@9*AB:Z5DJ).=R.7ERZ8+EP-#.P,*(B?R]63*Y`+(,``D7X0K`T*96YDBX(P7?):77%QP MD?%33L\T+V4WX_.2%[/<%V=9*>N2'S>TK"E]X'E)Z:TT4+J\O^* MUE1?$SH`3@&[46ET]KU"8*SP91,$WS9QX:<1=I[M'V,4#]5$K\2ABTQ`JME.0+6F`+.=K))?B07@RJ`HR:@NBFIA7]"C``&Q#3@0T* M96YD*GA/P`_I+"_V1A?[`]I9`R#BSLRE4--`\FIW-[,RD M*E2ARG-U6BY45:C+"_5:BI685XH6%F>X-BMI=59J%'G]H#X_OMY$?2L`KTC3';HH]9HH]D9-1`P)F(:6,[`4B#R M[K<9:P\%0^G'_VWI6)CL;Q,6D_OC[DTQ`^HF,]9.2HXSDSJ4VQSJVS!(:I/! M"JAVHVU*-1N]V5*M5J]WU%(KUSVUU$G9TUA<)GMJ97>B>VK=<*Q]B\8C'5HD M_02@E3X56)SRX*?.0K1B80>]U^W=!;=>"QNV^6:O^;NQT606E`>Z\)TYWQ/* M[`$>+1Z5MHN[6CR)'P$&`&04_#L-"F5N9'-TL-DQC4U M^Y/."XUW)AMKO**O\7SS:7@4:_-U^3YJL5"@CSO:'D*X@&BDBU#)3X2'?.2X MBEC\.O$Q(D0-^B60XP9X`5X`VIQPEFB\7GN4'N_$.O];^P*+6&:=Y;VQ+/5@ MJ3P@)$#EI(>RE3[^.@DY(!$'KI;X!W19Z%:?`@P`4FBD^PT*96YD.=&4( M/?1Q$`0H)0Y#W/BP!QG0J8<1E=QGT?4B6B&N=Y"D(%8H`Q`S:@"1+,;H@TCG M>#R?Y#O[/ES&$\YX9-D]<_;$:TN_Y"R""#O.QA:Y\62*\\)?/S3/*?UD9RE= M(D_I_/V5<;$.OLCU#>(NBS`Q]W9P9:4+;Q;Y("?WYAQRDNE$ MSX_T/"UUENOL6-^EXE'DB<95A(/#18FGBZ0,5>C;![%J1'RM\T3$%T$AXM7E MJ4Y%W*SU\]/+O6C.A`]EI(<6H8M\IQ`8\TR&W7%XDSR`5;92&]36IE"S`-!" M<3!`GU0(_0_X_J;"6YT'6$J"+VBQ\SO*<$+T)W2X[V;3(/+TTC%PC"IX&`=O M);4:`6P5K*M)8-&PAWH2&(H[^)X"04]93H(I,?][WA28DWOX/53*R>X#^[1* MHM[W9LD).E-QAXZ9*;@=X)5;M,7`7 M?KB"H0F7O@=0CDO?*,!AT M!,T%8FDE82(("/P#41&PJQP@3IG"QH%TEO#%E)OX"%MN(?0RQ@EX![YF7C'S MRIPSKOC1PT35?JH2__`<>S4:KV&<@19G M%Z-S"3IYX:K]H^3DO:C%&(@PZ#LF@C<:/KYQB:C/4DR<.O3X02.@I)?\2NN&MO0KP`!,M;42#0IE;F1S=')E86T-96YD;V)J#34X.2`P(&]B M:CP\+T-3(#4S-"`P(%(O0T,@-3,U(#`@4B]#4B`U,S8@,"!2+T)'(#4S-R`P M(%(O0S`@-3,X(#`@4B]#,2`U,SD@,"!2+T-!(#4T,"`P(%(O0T8@-30Q(#`@ M4B]!0R`U-#(@,"!2+T1!(#4T,R`P(%(O0T(@-30T(#`@4B]$-"`U-#4@,"!2 M+T-0(#4T-B`P(%(O1#8@-30W(#`@4B]$-R`U-#@@,"!2+T-4(#4P,"`P(%(O M0T0@-3`Q(#`@4B]"6B`U,#(@,"!2+T0Y(#4P,R`P(%(O0U@@-3`T(#`@4B]" M5"`U,#4@,"!2+T,T(#4P-B`P(%(O1#(@-3`W(#`@4B]"6"`U,#@@,"!2+T0X M(#4P.2`P(%(O1#,@-3$P(#`@4B]$,"`U,3$@,"!2+T-%(#4Q,B`P(%(O0D0@ M-3$S(#`@4B]"02`U,30@,"!2+T-5(#4Q-2`P(%(O0U8@-3$V(#`@4B]"12`U M,3<@,"!2+T)&(#4Q."`P(%(O0S8@-3$Y(#`@4B]#-R`U,C`@,"!2+T)7(#4R M,2`P(%(O0E8@-3(R(#`@4B]#."`U,C,@,"!2+T,U(#4W-"`P(%(O0ED@-3#E?#Y#.@8BTS9#XXJ.VJM5_55M& M)29L(1PVSSJL17>;=)>4357/]#P&ID=-S:FR,3,6+?-&>BM^LGU#_O%*8]/` M/:9J\"I4EJDMA1G=19=R[;"%Z+-*W-CZO[!(80-/`08`CF,>N@T*96YD^4X"S`I#R]%4H*2I-Y?)T MX3K```;U.&G^_PSR_X!T_?\&^_\'ZAO^@^@']LU@^@><;@?3?S#H?C#]CR`M M#Z;_RQ.B_T/H_Z,TG&:0!Y$?@+'%B)OF*KEOH8J[5P8MD;M!E%R7/]Y(4F<44PH^0)E_X4F]UH2L>];6N M*_U6XB?6&YX7,JW6I;XJUL4-?UO]^H&[!O,776\P?^!US'=/M[K$O'G4WU\_ M[]C<80>0C3S.29TB!PF1Y=$9HG%%-%#PP$Z*Z%<$HIZ=6?Z5-[6++F4)C,O$ M+INCDQ&'=&I%FXQ1*W#`,7KP3JKWSK"/)EX'_G@.6,R"771(3[7)J<,JJN*^ MZ`4$+\'?RT7G#$S4!TPF!(UM"%BT,>#HJY*R@KVOC&COJY*RO%R25$9*3XTL1OTII;A MM:Q1H?(=M8![SWZ42A8K0\ M`]?GH7+55&K"NR/%"7E;#A5Y2YF3%Z]G[).7K/AROAXHF9,Q1DO,O><&!F^Z MGA8T/370H`0%R$`.;*%S0"2TH$D!EE9`@Q(4(`,YL($C=)'0ODB%!@=5H`8: M%.`!,F`#ZT=]_N,O6@N1!=_0(J$-/048`&FGYGT-"F5N9'-T1<+B=/+OUP!3-#+GT/H#R7OE.`LP*0X^FK4%)4FLKEZ<+%P,`@?X"! M@1T7_9]!_O__`^S__S?8____@'\(TD"?,."C!]I]5**A\40H/KE^IZ]"25%I*I>G"Q<#`P,[,O[?P/___P'YX86!?D+W)Y>K)U<@%T"` M`0`VT*ME#0IE;F1S=')E86T-96YD;V)J#34Y.2`P(&]B:CP\+TQE;F=T:"`R M-3$V+T9I;'1E1#]XBDJ(]KD"!`KNF;D],"62!`3CGL/\SO2A6K2%%6 M]P))9H`962*+Q:I7KUY]_=-?]/G'?SW\_O7AZZL^Z_/KWQ_:%VW.+?Q]]D]& MZY?QW+?=BSN__O.A/?\(*]JVA;4_P*_7GQZ:+!>7UW\\F+%_F>`/;'W]`W[Z MX:&YD_?T:>"W<6NSD?=WA<2OKG/XVJ0;M[N_MMKL_>ZOKX9<>]9VZLF"'BTN M;C)YM./U1J(\EGRSY/NC]K[="LFUWNI_0<<)L=LZU)#DL]!Q_) M<,>&[3#04FLU/KB;`'-:]&33JV9W0N9TUK.S$V5,@X_H1T$?MW>[_-XG8+>_ MNR]SO.L>=TQT$L>UN3_@)R/R#7GFV#-GQ[,96[KARBG*D%T&^OZ>;?0AU5// MSG5]1_><[^`MQ(Q@]"AY;N'=IMC>Y?>"[`[!MVZ@16;4*]_\'4UBA,[SV2FV M9&@D0]8PI*^X[]E,L*D1EV?;X@-LR#:`[F<(`_S,CE,2XLT0=\^N:0D?>FG?K@([I>?`CW$QECB&?I16H+B*"HO*D^Y8Q&?G,0*BOK_)//4&^3Q"5V MOWTBCICX2)]J?&'MBM-BNLW;M@3=4P/P;[NA7YD+U*BL);'#LBLOE`2[Y#\I%2[.`LK0CI M]I]WY6E;4"^P?9_&.8K!-5N\)XL[=A-@L$\&!->W;*4P0O&P*?`LTL,?_;> M#\&33%$7"O,`P;4MC90A@R$`GBK\RV13E-ZSB'^B??GA5\\HXR8J;$Y`I])):M35HO: MVX8@+XE3^:TM,W&@)'HA/.'2(,*3D+V,X%&B^*[75D? M,T0=(N49N`+NZ@`VQ&]0AR/K$@IT>C:M:('[`'\2E*B.I?B*5;M027%%MMH MWZ8<%3MGJ!V6?;%9FK9;WS8"=73IH=N*2VQ%;7R7C[[`(5=T=4'NZW:%=@4Y MIP-"T8'M?25.A'/7HS`S4:5`S@2M#VQ"K9WBI:EX./[`A\?PS$3%O])4^?!B M3JOR-&?.0[>;=$);U^9+(3^')7DL'M,GE[)<\(0(S",&H/=Y)`S+"WL4OWTF M.W:M)Z^>LNE$N-R6>+!B1%L[W:+ABMHA8`>4`M[K4(2:\6H#7Y75R=\_7%]L M0J$?5+@@%H>O&]./D6MFE,<:GJLW4WDB@,KB@.L^>2,1D^U93WI6BO^KKB-A M.BV1'7[N92TK413<+B.W8:4L>XIN%/`%Y;$SH3*6TB9VZ??X``)%+:)SBTQ$ M\H2H;P^@D+B$0R,Y9I7<*6HNJIGDKV%#F5DAWGD"BL;J;`#GLIBOKA M!68MF`,7[?D=/?/_2:&<&M5Z-'Q\`I'K*[B'!`3"Q:)!;D:'=`CW2=2LJ/B% MS]NX&!/\178L&8:?9^5Q->#FV2DKJ(_;-HZ!JCKLU*&22]+L.OL&M-Z;]]X@ MT6%=D\>2H``U"5?P$B;>OBZ)SOHAY>H*RBPCQ66U74N]LB)0#HM*R?/#B?8L MI`^TM1-`BN1C9_7J+@MUJ,?%>2ZI3I`=]DW9,4O=1=9"Z=C;C,3B@`J47HI4 M3$M<(T-JIB8)N:!BN"@`0A1S=]U"F852#D+_"M7IM4!8I/P,5,93DDP+AGJ2 M!:\2N^YM])<9AY%) M,;/C>HXXL_9<2>1*>>(V-<<.9B.MI37(RC]/,3Y\1H=]L`KLTC*D`7(@0N MD(T#J<<$UXVI=A_8B=T2LJ&3Q]@H)!M4!.CA[H9Q/X"`M+M_XM/5NQ1WAZ2--MY;_WF62-;KMNRA?9NVA6(^B78Y% MN\X.J'V,F]C)67F$,KMY\90L@=HM*)@3AQM%WXU:>-8NZ'"6#?J_D0T+9HZ" ML7GT&8FR``/IHX,3497M870I_*S$"'VL,J4@4A%X!*T04E!9`77:%W-`X"ZK M=H<,,^L('M"&*(R]C?P$ND#1B#/$_`0XH5S+0\DAO8E*93M4$L5N1H_QN/1`9HD)/A*$/]%&/E)&MC]D7>?PNL>5-@2N!18\G MB):H=[X?=*&7Q`;^B?BF3SO"G,UO>2";^I6@X+'3<^!B"DI:?^B4#=Q<4K\W M?>`V4H5*'LF#5.Y@VOS^E.JI(8RIM(DI1<,T&T0UD9U(1CEJFJN.38EF2=1' M2<27XZG3F-`NPD#B7[JY$\SR*?`+P&'OHV9L6@7+@+T3[S1T*]54E_4B^"OYJ,KZU[0Z=,,H<0E$;TPS,PJ'Q&L*)65KZ-[1 M9$H"DRAV!"9(X&_`=\)8%#'?OK"<\S`)?+]ZQR%DQ@S]@=>4%70#X9NB+[EC M]KVL`J.Z=CDZJ@RUBN],/DD$YNB"AL;>=H6U&X<"=\0?Q*+"_3+X^>%5-9=P&!@X9"[6^X% M16'=/$-FZ(OE*&X5J//?2N0]<\.OO*228922OGXC,X=ZB#OCF,!+3/.8J2>T MY.5!(@"XY>?B)(-ZR"&"\TQ`0LPQ43>_(RG;+C"Z:"0)0.7'%76D`)7531S- M>"LQS:JY+86R7H.[DA:@N5*#L3E=3II"VNF.2$AXHQSK\C]6`MO6&SE M$_`P`%:-*J"@T*96YD7!E+T5N8V]D:6YG+T1I9F9EQ?I+CC]C_9'%).55T45)=45'F]%+@.]8YR564)57S M@EOG^8)+3<]ON&PP>Z(ZQ^Q.'L!L^7!-!6;-/:U7'Z_8W.#&%0N)EX'(JX/9 MD0#@7[\B"UXC0.)D@?==9;AN55Q!*AI%B6@012(KFHEZ\$6[.G4[;0'C@2)M M^.3[2@<8=,;!B5,A+3Q%[-9L99T3<:]T*B/#':N=JOM)F^\%?UKGW^W\OIS: MR5,[WN_KX(S&W0F&\PUG;IWX^^>G-1NM!DEZ)9J\D*&0J[VL&:E#)D-.8]AF M5U,L0T.J?QI1JS(RL_'KB+E*=+VI_W)Z]UO"VP8?\4N``0#?X5`M#0IE;F1S M=')E86T-96YD;V)J#38P,R`P(&]B:CP\+TQE;F=T:"`T-3DO1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)E-2Q;MLP$`;@$S00X*)'$%^@ME4X@3P) M<%*@'@JT4Q^@R=BA03I;;Y!7TIM$CZ!L`B*8)>](_@P@H+`6?:;)._).4K.[ M,3O3FD][<]B;YO/>/#3ZCVYOW>C._6Y-NVGF;>7[Z^ZA/]]JZ:Z32RC43427L'8FU>%'M.3%Y]L"D/^E_TOQJ9A\6B&? ML%=^8\R1V?&YP1F\,,]<).4/(7P!7T%[+;O_L09#S_QV%%B"!1AS3)1"3)0F M3Y2&YU#\2!E>*`V'JOOAT!9.3I@1NN(S]I@%*8C!)#*IXQ*Q$GSXH8^K"`-F./[@P?&A4Y@3/E M+V7DLLK+E7P'W\`QXQFLP2HF5J,"B\2!XGX=.[`*9ZL&V<;(Y!J[6]5+A=RM M[KE-OGR>Q"^54*YNE>S4O4Z$0!`'\"$4)-/P",P+>.SF3(2*Y-1$"A.M?`"]TD*C M-3P:C\(C4%Y!6/>_BWPHA98FSN4N/[+L,C[BY%RMOK^Y'+*S:(CHR/ M@2C!96R_%!K3VI^&R"XW%!@+*DQ-00\EN.Z@"&JA$*HA?_=:-*N:5*Q5X)S$ M'=6/B@WVQ>[A]NE.%3*BR*5FL_Q4MU3H==I0OU3P5)2\]W'V7&;(0XSD])U=!RAC9F;58X*9JFT\TNSK"/ MI'&**^CDMD*#VP`AI\*KQ:+3@!3-6,E_N:A==1#-)+Q!?'04C^+KDN_Y0X`! M`.@F`4<-"F5N9'-T]% M<6WG17&\O9"5*/H;^?;Z_B3Z2V&,68B,&T24,&8K4M``Y18:H#1,TL9J9&&A M`W5^&1X)RJ+2E$\'CG`9FP.$](<^[0PKV`3MP?:,8R MUL3=\^J<1F[93W&NORJV^X\:5B(OMS,%:2_U39SD&!*!ES`*3V$0^@F=T$Z` M#PVX).QFP)Y82(1&8$\@A$(DQ$(RP4QHOL%_?M'ZQNT'>'I?O'T+USCCNL8] MO@48`,=7&Y$-"F5N9'-TP7X`F;6D)DZ4"$AF08.H#`",#".;DT?(H0;Q`-C)$/7SGOTAMED^M M?3[_YZNUK_S&7ZS\MO;URK\L[;O=RI^5KR_]9K&,:XOJ.GZU?WZSN\:6>[]= MV_(^KMMR]WCCE[9L'OSGQ]>K;6XM\X&HY?CU1(;U)U%@GL2">1`S72;9J9`[ M)(OYS`F.V'GG":-*?;`'!AS::61%@YG!Z3"`@4%IXTY M.N48O-QQA/]WGB$30O5PH.R7M2+[@=_B1-D`^Q8&Z&`!C6JZ3.M-)^-2=2A1 MHT.-=PGQ'GF']Z53W9D!^]H!\YE.YF40OT#\@/@I%)Y07\[>-?;)_@DP`-ND M*9D-"F5N9'-TW9N&WZ>*_6FZAI?EK:YL(=]A6O[\A)'8Y]>U;%3Q:.M:U7M`@X/+O`8`78B#R`O9P28N`B0DWI21@(>B$6$(9.HC0$< M/(AT#.4T7K3C)'KFA)S!HQ:>8ACMO^-4;N8P3.\F3H7S=N+TI)&UB%J6&7[* M\7&_I7L^^*8\:+^I3)1O"385QW^D-V16N;@;7^`T^9,%4?U?2@?NJ87B/'W8D_KTAU*OH<95TZHY M_;_4L71*S\A":M0\I#8V(;5VNJBR4/4[#0!"%7^0BTC0^0N8"Q-Z`(KNR M%$#"!1)4'``H*4!);1\M1_$17*:(8F9G=HT+"NC]),OS[?SLS!;CKAWG[+9\ M=@`@UZ,(]`HR&G33@%`I2`AU0PSS###/Z&=@-\N*X7V M[W"+3@%T0VJXN'4I=A$JZR;U M)?4B^+8\))KO>X3^8-$MHAI]B""K:5K8;:;$^C`MK4-3:H.85F%>(#YYK#T, M/^5TJEA!AX])]I0AR6R[-D19XTV$2\R@^YJ>Z5N``0!6KA@D#0IE;F1S=')E M86T-96YD;V)J#38Q,"`P(&]B:CP\+TQE;F=T:"`Q-3`O1FEL=&5R+T9L871E M1&5C;V1E/CYS=')E86T-"DB),C56,%`P-%4PMU0PME`P-#)02#'D*N0R`@D; M*)@8`N7T#!7,+?0L#8#`2"$YE\O)DTL_7,'(F$O?`ZB`2]\IP%D!2'GZ*I04 ME:9R>;IP-3`P,S`P4H*`)OP!(?8?#/Q`](%!_@.#/1`]8*A_P/`?B`XP_C_` M_+\!B-C_-_#_9P`B^?\-]O^Y7#VY`KD``@P`L7`KH0T*96YD M'D5U(2;7.L;J*;2>F09H+!+2`5VM1^K@GUE,1OEQ&E#.K_AX\&Q?&BAZBHX" MW[J`VJ\Q=1-PBQ]-&/,< M^;QXQ"%[Y<@P?=HA#0DOAY`5&XYBJ'N&NS&Z><)F+-'Y.6\C0N@5XFVT*R3? M18-K^W7,R?DGL&I8$X(@D*;C:?LHH&N@9V`)!!8*R;E<3IY<^N$*1F9<^AX@!5SZ3@'."D#*TU>AI*@TEL#`_/_`PR,_QL8&/XS,##40[$]F1BL'V06R,P'0+.I!7XPL/__`#839#8# MR+WUHQ@[!H4/*)Q`X04*-RY73ZY`+H```P""<[\�IE;F1S=')E86T-96YD M;V)J#38Q-"`P(&]B:CP\+T-3(#8P,B`P(%(O0T(@-C`S(#`@4B]$-"`V,#0@ M,"!2+T-0(#8P-2`P(%(O1#8@-C`V(#`@4B]$-R`V,#<@,"!2+T-4(#8P."`P M(%(O0T0@-C`Y(#`@4B]",R`V,3`@,"!2+T):(#8Q,2`P(%(O1#D@-C$R(#`@ M4B]#6"`V,3,@,"!2/CX-96YD;V)J#38Q-2`P(&]B:CP\+U!R;V-3971;+U!$ M1B]);6%G94)=/CX-96YD;V)J#38Q-B`P(&]B:CP\+U1Y<&4O16YC;V1I;F"VA;E:B3X`?80=#)!U!'!T7G]M'Z*'T$1X?2>)<*2:R!\"-< MR/TOTQ$E-.&MIJ04G5.\H1KS.9'C9)C2(!DF,MUC)?[ M%:489SMZW)\7S-98AII7$0&($`#D6C?B0NOZZ]NU;RS$%X36GK6"L&R-2H@\ M*Y&+?,7S)?)CUL#X%KFY%8PUVW!8Q[SY[I_:PM5YJ^_I]3:VF4S&R+6=PEO=C8LK)%OK$OA7DW94&M.;U?VG)=T-=U?D5/99_?S+8VV9,M M"Y/=^0XFVSY<6WJK[^WGQ]>KJ6\,(@Z0H']F`'#(#0"Q1T>`AN!_(46<&(ET M`8@0>P9UZ@0.6P4L,0QW-REF!;8$ M/LLW#63\*':*O0+/(SY!I`!!+]DPG")5R+`A!/B7Z0@A9H8<),0\:UJ,1QW?Z#"GO=U1F>5;&NZ$GUJ:&"AK2)-@ MU./."CR@_2?`2`-<8CB+O6*GZ`\((4>=4Z0R<]PEBE@!LHVXY5-3T5L^6NL1 M\RD(E!'%MVK]23J!Y`-+N!,TVEG_'U$H#LT5[GRB-5D=E7+4XJ#6C2=(0K;A MO@V\6W-;FT?S*\``6,SH2@T*96YDXU_%Z<,?8/BM&@>%ZN?%].75@OM^3I6?FJ(+>]ANKU^[5MHC M[/K.TTV(MG_^P/^>"=WQ6"G;7@-58G%@O=;WFVVV-L4!SP7_/@2CDW(3UR5(;_% M?LB/]U<,FCM^?_MX#LUU$)&.6KQE)(I5PH.%6Y?M)G^U>Z[-D?O8R9Z=1-W ML_,G,[*\^+7D6IYQU=)[#HH\E_U9QY#TGCL;T!1SJ$<9RJ=?A$) M-TUX")\"#`">I?Z9#0IE;F1S=')E86T-96YD;V)J#38R,2`P(&]B:CP\+TQE M;F=T:"`Q.#(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)W-`Q"L)` M$`70$0MAFEA:B)D+F$UB(FNU$!7<0M#*`ZBEA:+UYF@Y2HZ0TLIQ=ZHT7L`/ M\^`SW2\7E)+V5Z:TU'3)\(Y%[KM4G60T3Y-TY:/I?,/*HCI1D:/:^3^JZK"F M#)7=T_/QNJ+=(/MTK@7FVC3@/F!J,&\P`'$GMF(CUB($9^*TYZ3G&,`%8P8? M#@[%2(Q%(SJ1@P-QV'/TP^C/K,,DW(1)N`V3,&XM'O$KP``3I,*J#0IE;F1S M=')E86T-96YD;V)J#38R,B`P(&]B:CP\+TQE;F=T:"`R.3$O1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)5-&]3L,P$`?PJS)$\N)'L%^`)J%-2R=+ M!20R(,'$`P`C`PAF^]'"FUCB!;*1H>*XCQ2:+#_%=[;_)V\:7_NU/SOW[=9O M=OZI,:^F7=%B[;>-7R\;JBWK'7T7_O'%[#M3/?AV9:H;JIMJ?W?IB>[6O[]] M/)ONRB!F<(CX`[`@!@`(B#U1(B;@52K1%P]"&&<,2M:6#!:%0`?%'B(>&#IW M(L7$]"AD95#PGZ@$Q2H%0\&LPDV,Y0DDO(2(,H($D*+D+%'"%SH*M4I"3-BTAFR3C2>S#<]G-/7C']O2[^T9JX[8: M7YY?'Z"Y`"+JS(&?-!KC28?&B('=$DV,R8@&D0-ZT<8P"0SJ@12SG68=9US( MS3'9(#&M*FM]+_:BE=R^,TYTZBAZM8W:5O8PS8:?FME%VO,1TR;_<=FD2_J8 M1S+];?AE=M3V2__=CR[ZKO4@>M,Z$;GHZ+2.-#BM*W52:VY6L*,TB^LE1^9D M-AW9ICY9GHO&_KG85UUC8L-37S50OMVGVDWIO^`)'L)E`[?P*<``Y@T&>`T* M96YDS2L0K"0`P&X)0.A2SZ!LT+V/9* M.<])J`K>(.CD`ZBCH**S?;0^2A^A8X?2F*N"(#BZF>$^2+C_AIQ1E)"A44KC ME+2FO<(SZDR:"6E#)E(RBY*)E*'=$7.+\99TAO%2YACGZQDIC.V*KI?;`>T< M*[@S=Q"4,&5NP2\@9&[``Q@PUP!_?VCUQ?+#XFWA!&<@:WO:BJ58#YW`I=A' M)2]/`/V37/KA"D9&7/H>(`5<^DX!S@I`RM-7H:2H M-)7+TX7K`8-\`P,_`P,[&#$3@4`J@5H.,,C_)P5\8+`'VG6`@;\!I'V((:"S M@8X'>H'+U9,KD`L@P`#`06=A#0IE;F1S=')E86T-96YD;V)J#38R-B`P(&]B M:CP\+TQE;F=T:"`S-3DO1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) MA-/-3H-`$`?P(1PP>]D7,&%?P+(TL2D7-ZF:R,%$3SZ`>O2@T?/R:#P*C\"1 M0],ZLQ]0LFP[">17(%OFSVPI;X44&SRJK2BE%!\E^V;5&B](\WNS*L6-7,D* M:RO>O]BN9L6;J-:L>*('6+%[N1(].+WA`)Q?9CQM:U;S[F MP3CQQM<(W1N#ZLM74Z?M53M];*.J-U`S?6N?=R> MU'S$A]%9U/O1:=2#^YH4Q)(IE-Y-!04QMYTHFIINLIX[=1,%NIVLYL[?#&[V)\H/6[O@'%'FOVROX%&``F"9'<#0IE M;F1S=')E86T-96YD;V)J#38R-R`P(&]B:CP\+TQE;F=T:"`Q,SDO1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB)LC!6,%`P!!'F9@J&!@8**89!`PS\8+J!@1U,,S`PC]*C]."@/S#(,T``.Y1FAM*, MU*(;H.8>@-KS`$+S<[EZ<@5R`008`/XJO2@-"F5N9'-T!GN;`T#2DI(CP7`-L1,DMEB1\I+B)!Q0%(RA1! M4-M'\U%\!$H**Y.WAB:1TE$RVM&GW97F%;LNTU0=>_:BN=/W3+XDGW*?^JV; M9#I.)^F>2_)8K/43)+R38^'TX>4*S'6!96U,&L0L2L# M`JM1=``J(#[3@K8TYFKH@-8TZH]Z1]X0>+XYA!]$_5#Z%#/$6USM0WV9^01F M_C;\8_2/@X=WL$;L/T+0H##K$+9\'OM&).M2MO(CP``9G\W@#0IE;F1S=')E M86T-96YD;V)J#38R.2`P(&]B:CP\+TQE;F=T:"`Q-C0*Y`((,`#? MR6;O#0IE;F1S=')E86T-96YD;V)J#38S,"`P(&]B:CP\+TQE;F=T:"`R.#8O M1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)W-.Q3H1`$`;@_R219`KO M#61>P`,N.>`JDE,3*4RT\@'4TD*CK=RCW:/P")04%\89EEO.3COC%O#![BS+ MS&Z1/9ZJN2$00R0YZ;Q'(%K7H!0*4T@/XA'9WBG-#JSC#$`"< M&G0L3@P884'6)NP/L&ED1&A31*U%-@ZES1[I0VW?,XAH:+3#3&PIQP@9,.:P-70.]9#GSA+>.,RMBE:F8/^M7KZ"XN'+[3=` M<]@2?I/X;:.C9_V`!J%#C])!W`K_$88C,[3A$%G38T77%=W3EP`#`*_AB2H- M"F5N9'-TD"$R3(S@7,/_! M325$!;<0M/(`:FFA:!V/IC>)-PC8I!#'V4TB5I8!7[$?S)O=6=ZD$@.,<1AA M&J%,<1O"`9*,BP'*$<9>R)X79"R)FSWD"OPU)AGX<_;!SY<3#,%7"SP=SSM0 M4Z!*%,0JQ5CC*@8:%^'VB5*P7![.#"0I4`\%VJD':#IV:)#.]M%\%!_!HX<@ZJ]`F'I-?2NJ(Z2:^@" M+,"`_D^@`/-_`K=*C<_Y#1=O]VVY`4-H)EKGVYN.H>'HLY>@GH(HJH7*1.W1 M"T=>=B=\U#2JW#M(+@8@SH:/M]:':2GGB8HS%J:.'QM^Y1\!!@!@7B+=#0IE M;F1S=')E86T-96YD;V)J#38S,R`P(&]B:CP\+TQE;F=T:"`Q,3,O1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB),C97,%"P`&(C"P5#`P.%%$.N0BXC M`P40!/$M]`P5=`WT#"R!P$(A.9?+R9-+/US!R(!+WP.D@$O?*0*Y`((,`"7/!TJ M#0IE;F1S=')E86T-96YD;V)J#38S-"`P(&]B:CP\+T1!(#8Q-R`P(%(O0T(@ M-C$X(#`@4B]$-"`V,3D@,"!2+T-0(#8R,"`P(%(O1#8@-C(Q(#`@4B]$-R`V M,C(@,"!2+T-4(#8R,R`P(%(O1#D@-C(T(#`@4B]#6"`V,C4@,"!2+T)4(#8R M-B`P(%(O0S0@-C(W(#`@4B]$,B`V,C@@,"!2+T)8(#8R.2`P(%(O1#4@-C,P M(#`@4B]$."`V,S$@,"!2+T0S(#8S,B`P(%(O1#`@-C,S(#`@4CX^#65N9&]B M:@TV,S4@,"!O8FH\/"]07!E+T5N8V]D:6YG+T1I9F9E"]@F])6ZB3X`780=/(!U%%0T=D^6A^ECY"Q0VG,)=)V M=/`/^<%QR0V7-"5)L3EI1+,YG2.\8Y*96G(9!Q%-92#G)AF=KKC,,3Q2DF&X M-7T,E_L511CF.WH^7A?,UZBU!F%HP3,V`)WU0#6P&E@ZQVQA!=G9?KT!CW0J MX)%6CU4#*]%;^FSA'+'@G!C;KPMC`^_.&G2G\GHKP99.GRV<(Q:<'&'UX(<4 M]F9I7]EUZ#^J^"]T+31N1<+B=/+OUP!2-++GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+UP'Y_^P-_'_8 M&=@_`/$#(#X`Q`U`#(1L4,P"Q0P,_QD8V(%8'HCM@;@>B/__9V!$PLRTQPWL M__\?X/__G\O5DRN0"R#``'.B78H-"F5N9'-T+E63B9)&+JZ2H9)_S@5UIFYD4:W'+W&;+;&VK MDJ\?WC2;CZWDG#;OO[EY8>U(50/-=22:Z4!$VAMU!QZ@ MG9@-Y-M8@P]S?8`.]&``HX882=$4EZBV%`&J@=TABJW,.BP^>A99)7F=F`YV MP&X;/2)&2G[!7962&[B""P@>)"/5P9Y6$QX)_#_(C)Z5`\JMR#+Q7WYM>,=_ M`@P`Y$M^<0T*96YD\HMCOV*_YI.C"WFOCQ7[#2\73K-%M=%:\_%,VYK*`WM/Y5YS*K>? M;^RHK#_X=KU_4_U.(C$3D1Y0.R"(M$`N`B"304TM1")/%=J2/]!T"$\T#V@4 MU`&A3=1B,J;Z?6X^B/SWWFTJ^F+?@08`,M7DI0-"F5N9'-THH&N@ M9V`)!!8*R;E<3IY<^N$*AJ9<^AY`>2Y]IP!G!4,N?4]?A9*BTE0N3Q>N`^P- MS`R,Z+"!^0#[?ZS@`?L!Y@9&ZL,#S`_8N5P]N0*Y``(,`'^Q0C(-"F5N9'-T M!7%`/_TB/TOX!-2F.U4Z`J MV$'0R0.HHX.BLYGS*ZDL[]5F$6 M:<93E:K29\''"U4UR0/KG.3&_R2KW9(SDO66[[?'F>H5M8FSQMEX@'`0/4;> M#I&W!;P6:"``&$P^CN,N:'HD_GH&W=?HQ]$_:84;8-K8O=#0NJ8]O048`(AO MRI)R*FCH MJ'0TKFCO\(3RYG+&$RI&3FJC?")1T>Z(TP;M5AK1+J6.=KJ>D4/;K.AROAZP MF2.S3YFY`ZB9`X"1!("D0(*?RENGK!_*P1VR]ZT[R`+.BP0V^!!@`#3*0W@T*96YDM['6Y+??4;NWI$X\-%J_6.2P> MZ3D6Q^<[6V'1/-F?[]\/;.XQ9!.U$3QL&`/48!A]"EBA@SK,\`$\H07?SJ!> MIZ"9G<(0\A2T?J_8$*X2C(1!D1$R161$&HZ<=0%/'!4YP220Y144@=,OD%!! MX!E>P66T,*U0*_B-8-;(%?P=V3].OP:EX^R,;(%4PO5$*9O*4%`9@^Q(`J.@ M,B1["I@1"%[1G:&%-_E5A(.B@X.9`6?H00_!!0P@D2\A@F1/,()&_L>DYP0? M&GS!/P$&`&L\BUH-"F5N9'-T7!E+T5N8V]D:6YG+T1I9F9E9X5$BPL_:68\GOG_LG>MZ]U%Y_JC.[3NI=/O MNM\CV+H#@KL.N5U[Q+ERSV_Z-.CFR?5[W=PAKYO3P[4#AGOW^?'UJH<;S*5*&,F\CE&9(")2$F*:N8%J'*%22A--"XT(9U6I^%<7-TWQUI\;Z-M! M/^H?`08`7T(>M0T*96YD0%(HHL"5)$"2*1`@HH!@)("!'6R":MD$VZ$*RDB/KXB%%#AXA6V M_/V<6HG%RB@1.Y,TDT/"9[:9-F-))V+'B<[&\4QK*OL3%R5'.[$91RN=KD=N5PP4!N@I1[@B2HT1`8UT1"D>`6T5#GS[*$UOH^7\4/@[A1P MNHDF[U!7OR#\B^^43WRCUQZN.V[\0(74R@>_6M'I!GL3_JC@2".?-`C9.7A9 M\I;?`@P`KT"H]@T*96YD^4X"S@B&7OJ>O0DE1:2J7IPO7__\/&/\S,/YA8/S!P/B`@?$``V,#`R,# M/R-#/0-0@OD_`_M_!O[_#/+_&>S!J!Z&_E,3<;EZ<@5R`008`(DN6:\-"F5N M9'-T(;Y`H^@DL*RY]/DH:57K$S7U\3IU9:M,=@ ML;+JR39!=QH#AZ554XN3P-VD?.:;V>=6%[46'Q:#%DONM5BL7HR?^MT.^^.7 MUJ^*-L$!&.]\TYGS$X8L,9-\EPGO('>3V-7]]C+>.NA;K6N]"C`` MSO-PHPT*96YDX9']_]1! M2PUZ46DX:MWHXG]2*C?PEWXC_C)LM`EO6"X_K0&]%5K]G%G:@MB[U-2M"6L/ MSP&(ME&6=LO3WRWEII,'^1)@`!MGD)L-"F5N9'-T=X.\^ZL+D5Y3)OP?,538`YLO(PV]QA MFOV*%B;L^+@_6X0U)*6H_TW_I1L1E=>0T;=H)_+CI+U(U"C3.+:HPR2EA$W` M`1\!!@#0QXHG#0IE;F1S=')E86T-96YD;V)J#38U-R`P(&]B:CP\+TQE;F=T M:"`Q-3$O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)S,HQ"L)`$`70 M+RF$N43F`F9W399HM1`5W$+0R@,82PM%Z^1H.4J.82&.LUAY`P?FP9_YWK+E M6K>:<^6Y=72ETG.Z:JP+QS-;V*7.@D\7:B*9(Y>>S%;_9)K]BAV9N./[[7&F MN":1/@C>F`J>R`0C)H(!$/1`!R2R%T(J!.3:5H8%R3&/;,P`QN__,_SO_\]0 MQPYT&`0W,#"#\0$&_O\/&.K__V#_S^7JR17(!1!@`+5PGX(-"F5N9'-TD"$SC$9P+F!]=_ZH%?\`4@E8> M0"TM%*V3H^4H.8)EBN#XUD1!L'%9OF)F]LU[:Z82RT!ZB9BAF)$<$CZSZ:,8 MBQG+($S0"^,ISD3V)YZE'.W$]#E:H<_1;#.7A*-T+=?+[=>.;$ M"_LWG8+]H>EV9>U>>*"WG\!9;7PVGBOX#Y""D*))U*1K\Y;D??[AY9V7*6_Y M*<``UYZ^[`T*96YD(<`IP5C#DTO?T52@I*DWE M\G3A8JA'AURNGER!7``!!@!\A!.P#0IE;F1S=')E86T-96YD;V)J#38V,2`P M(&]B:CP\+TQE;F=T:"`R-S8O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB)3-'/2L-`$`;P*3D$!B$O(&9?P":1-J:GE:I@#H*>O`KJ45!1\"`FC]9' MZ2/TZ*%T_>9/H8'P8TF^W9V9]CS4H>0<=;ZLWV?'.^QP<'TX:X^'$?'[.VH:ZR?BZYWO^ M%V``:[@G1`T*96YDFV86 M&W>:VF)EUT0?.NDY2PMI^H(AP`%IR>5(_S' MA]0L7D]TE^M)OP(,`+E\ZAL-"F5N9'-T7/KA M"L:&7/H>0'DN?:<`9P4@Q]-7H:2H-)7+TX7K__\'_/__,S#^_P?$/X#X`Q`_ M`.(#(,S_\'^#_=__#?^!2H"8$8+_D8$9&!CJ<6%RS*,UYG+UY`KD`@@P`+M^ MRE(-"F5N9'-T M"R2VR1*2RE("$BZ02,4!@!()$+19'VV/XB-LZ2+:X>\ZD2C28%E/UM_Y_X_7 M;*26I%/-@9J+:N5+!=-/%S4&SQK>7GG;+)7JZ`KLB4"`BI!XS([@F"NICH7VJAZTT>?. M'-K_O7^]4]J4/+6@,0.)9E"(\L0"O7$S]1]DP0,.=<#7)?HVL4P\)\`VII]# M,B3H*!C`+)'4Q9O0OM5`%BOIQ+[%2B>Z2TPS:=+""Q=R`M)RU"%977;JBKV% MAK1#O&=8!I0K)`A\W_&>?P48``VC^_@-"F5N9'-T:6K33W3'TCHA!`]D=-DS"CSD@< M==*FH`.]!1@`>`CL%PT*96YD))@-L"]1`@F7*`I&.'!LDL%SU`K^2C^`CJYL'P M'UHMD&0(!7P#*9%483ACPX\YFXJ+)][G]$FFE&3&14 M;MF4E+Y)G=)Z_;7AO:T%F``0#%5JUH#0IE;F1S=')E86T-96YD;V)J M#38W,"`P(&]B:CP\+TQE;F=T:"`Q-C@O1FEL=&5R+T9L871E1&5C;V1E/CYS M=')E86T-"DB)S,XQ"L)`$`70">FF\0C.!E8 M9R2;/9>>S5I^IZ]"25%I M*I>G"]<_YG^,__\S,/]@L`>2'QCX@>3!'^Q`LO\?^_\?S/+_F?__8;;_S_C_ M'U,]B&3X_[\13#;\!^E$(YD'+_D'Y,]Z!@;V`PR,F"27JR=7(!=`@`$``W.Z M;`T*96YD[0F"*X!-RBNHNEC:N$&C/C$^]V,#1NV/!RQ MM3S)^)#2F6PW&IY*25)IB9G)9;P_4>Y)[]@:TBOII//-G%/2?LW7R^U(?D&H ME%+?H%$.@`I_S[NC#$+A!!7_`!6W@A.*T$8HT$1XH.GACKJ/%^H!JO",4<>E M0]M]#ZHGR`1:>MK21X`!`&/@\JH-"F5N9'-TS#YD\2U[H/N5VN\[L6;I. M]WH5]OY)#I4D=S8O)+G2YY(<;LYM)DEU;5]?WAZENA#X`,!@2J`Q$6!,"!]- M`5SI#2:]47_KHW($NOA3R[A7HG=@.A$>B0%<:&JXE;[H'TV]T&FWPD((SOX2 M\4=.Q$I7+NBDH@O!PLQYSRG'`<>!B>W`XH,<"8N9Q,`_370 MK(@H*TH?(CB=H!8D`7=/ M*@8&?_DU^1X-"F5N9'-T'0O26UA9V5"72]%>'1'4W1A=&4\/"]' M4S$@-#4:VQYY#C%`'C=G-]S78($AN0>8VSFF!+!#`"((<]A_N[TIU/9I-2>/L+C8V M+$MD/^KQU5=???[3W\S##_]9_^%I_?EI>C`/3_]8UX_&/M3P]XC?!OO0C]-C M-];P]NNZ?O@!_CU][SY^7%=1'!V>_KDVL**N804\_6Y=*?>L;1\G^$./GJM7 M^O55\FIU.-H15E;IE]I8^#'`CT=376?Z\/>GOZS;J>..5BJ,4G6ID8:I6 MZBI*H@)]:&WH'5XB1X8>96*R,7:!GJO#L2&DZ*B\QNS9K@W.)#O458+FV66X M++XL,HI5[4Y>!"S-M2KT/5I:=P&:*F>/2HLLQU/[Z=QD)1%:!"3.DCM\#(F? M;ZO0N+$.;XAOXT+O8XJ2!"^(0R-;WVQ4$65E?$_%U5#^SNPIMA$>=FR&SM(: M=XBW;*4P4:9I9A<11'#B',[G2EU#V336U2H:4U<0HTVN8WAJW--8WU$5V66A MZUPEO`;2E1U<$3C8Y)3">D9&Y`ZS$U5J<7"?6U6X9:Y81XC6T3TKMZI,M>R. M<%V!.VG9ID"8.5O,T$UG0&MK_O46ZZP)$O)*O*]>_Q M!JARR\5D.R@[,)_(:%%HDQCP.U\"UOH(W;+7SF<7((G5]N`^`5")]Y9Q/X=1 MM@)B@R@[$$ITL_TJ<11(/P5>/J@$+7KK_!18O:6BLA,#KB@CL$#[Y`S57N'JQO8!Z&LZ]M8Y0%;EE&H=1SM=THZ.B\$*4[DM MA3K@R55)_P.E[W#YL6E[ZY#NKND"1DB+B"K1YX2*T+Y`JJH(6&(*:]I3GU1I MK/;$-HW4=Q'M-:+,#E+?#R6=UYY?Q>2"!6U-'UX8UK/<>AK`X'ZQ:*^*/'I- M/-;[9T0D8U@3HQ"\2@J=E\!^U&K"XF@\=0+_Z@/R;\O9-W9!=S=461)@33X/ M-C#9TLER&D*EA[)!>)?R6ZB]"T]'K%,7"D@/8]2$''QL.MM?H#PNB;>L)*1^ M`1!F'']A]7+XQ%7N*68(6>?-AIJAZ4V04R3:A/IK-[R`OI.^L<`?IF@X:\1> M^!2Y*N.5$$:LJ3#:O@MM(S[`4#1==X:V=]P]K/2=VB.5&!$W&@$W+9[$IDVL MW)*,\W<`^7K>)W8T@&IG@MGKF2[7[4F41<1+D.4Q3@_2E0K_;$O?P!H0$/ANY]^]/6TJQK53O,)I+A^7RP!]KKX<]#[+;\4O MYRQH%79+)WK/;#KT_:*[%]+9H'F`0\5TFTC\C@GKHL=9SVO$.C33BQJ'VV*9=#"L1Q8:41 M*RF`G!K>CEN[+J3YZHZ$E]=<48(*EK<`N1`Z!DI[,X5-5(K/7*XRY>:@$#0@ MY1;=%S6#Y**@(,\9`"ZYUO/[91)$O(0[X#M7R52'9D*,]+43?S*2[53AZ[!@ MBK(A$S:L#IC*G!"\YQ[?05=;4/O-EJEVK!?XWL[5#(*Q\CIN$V/6I=!9?[&P MV6:)8!A2N3\$\&?M6>ZB5,=G16L'(S7[2_L)8:T)U*#ML'8)3S^E$5=PV\Z@ M2]R>5M3>IM"RB^#*;\!8I^SD>*:P>EP@<+-WYIQ,K(WM'H%.VV9XA%MA9IT' M,$=Q[C/G8[&`A65AJCQ#4T./?(VVM(G0US(KH\P/?LM!02O<"31 M,4@MIUH0%Q#VCISR%>YE[H+2<6N>$T72FY1ICI`A@IH88;2=,'V@AF^O/7WG MGMI1MM1GHO&YND'IC.W'(;$\+Q\YC$CODDYG\PT!PZV=F=V8=ERR.O>83E[R=8J)SR8<3EKWD4[2@D:IW9W5-A>:LU6*!1(TYD^<@/0]3R5 M"J!ZZG;84#M?-%K+:.3$OZ^,8%:,D>(B4,?1ASEJ*7?D:K=018L.-\^"^'"N M+M?U:=O$H;C.U?X,7+]*>X=]F9F;RW6%CN0ZRW?4:LBZO?`[.O-!!^Q>^N<> MCFH'HJ!092JUYJ0%G.!0FLXS[>CZLA1CN5VN5VFZR55ZZQ4GTASOBX/,G$6D ML?4##SB_75PTQN7U*LEFL1M3RE8J!_5,XK!;-L_;V>2(%L^L-1-1'EUO60YU M"Y@7'[?LMO#:'&."?&>\Y=I1T,%Y?V(]9'.W20/6VI2EAC3$*@_LH_[W`H'5 M(Q(8E]5O%%*J&8"YGI_M/42$`"*OPL'RA5M$ODR]KE!$"8D6QDCDL.3,_U3G MA6*196WG)T$$P6&'8W%R.7%]//P>,SS`OZKF5*HHC:NFI.0VD MFRXP=CX4::Y="V>7@U*9JJM-E'A"4K)A%OJ4_9NHV&:;8AX+(+BV9/(=S'+D MN0FB'17QG)X/+DHJ6=0'Q.Z3OM`2Z_I_\%;0,-[1T`KM)["#9*N:$5>'5E*# M&DVHT(!MW"FF"]>IW8YD[V(I#WSNV"$LZ;"-%?$][FSLJ5;!#/"AP54YWC\, MIV,&=:J;F$X;3UM5@O;95G6T5^](/`Y!Q1<-+8.-S_K\+9 M$2M#N@L]]V)6'9V=EKCWM+2H,,Z&H-YKPCS;^_6^[J@D,&YG16%JT_RPH*`O@8NM]UB[O7.+@4`AV=';PT M]VO4?$*\N8KUFXT7_;X0D$6.3=_XX4?`"7QW$9W?4O0>F)1_'(6"/-DSG3#, MJ<@$-]L#8BWQWH8=4JBZV"HO2;:*O;+]MX/:L`K6R\'+M`MVXL$KN(R0SS]2 M2H;'/[JPC^(X$.F1HB7(95$`5E2OL.KATI^,48/F11]'!F,4H6E&'O<02 MOTJ3#"?5(>I\4Y!-P9`U+GJF'[)"'V%^LF<91W?TR0* M+9@D-T&6-C7#%"3$CK[]P/;5`>L7U:D8E1UHOU/R\BWA;TVE,_\UOV(!.81' M6SZZ.(@96_[6PB7$5SSSV6;1B&7<"S#VS9G/2K.A[2N54OMCTD8!@JB5!S"N MO:4*[4SO,.%;PW/U7L51*AS+!`=2-BZBU'-V5LJ"(NP8GL'FN:OTM)W$OE-P M%W-\,\LFPHH0^42>F:YM"+B-&4Z4S'5&+=0.E^/T7"6K5_\=+`T8@YJ=H(8R MN"P#V>$:P@4`Z:W/\@H-"F5N9'-T%LP(#`@-C$R(#%LP(#`@-C$R(#D%;"`9*QN_WS%1U"\(:8M[E[6HF3D%8(\<"_3W555[L[ M"7`2>]H]Q^VNKJ[ZZJNO7O_PE_;XTS\WOWO;O+[-Q_;X]M=-&*\?@A`^[,VN2ULG MNX?X>ENO&N<\VE5I1U[?VH:NT0ZRT:8G\TT01NBI'/5.R0<^B$=C_"+KE7A\ MB`7\'F'CI:_7Z//V$<\<.K.I1O_`UN_?-O_8=,.D#3:JY:!UTW%L+_,$1EJ( M70\OID:-QR]?-Z]__-H>O__[YL\4<0COO9!/TT5!U/OVTIJHUP%=;J+O9QOH M;_>GLVJUMW-=">U8E,6%?D?!BV*(G=+^U=%)6Z_SH-@=1!$DYJ,Z/37UFCZJ M11X505J&^J[TBL[!?P"$Y>Q+"CR+- MHRTELQM[MA(*--//7HZJ-%OIM-#N=2Z2(`0O/Q*"6O/=N:%-@,WZFQW::3M: M0L""]1>*XF@0TBF+#;TPG-A8$,??)6E9!3K'`NME:%K7=+@/"W$(/Z(G<^LH+PR):;`0`KY<6(H31]1IWCO!!3I<"!.6^X8AE>!/Y4892Z)G9`*G M(E[Z*U.5EZAS.XT&C[*G5-,OG6].F^,I`4$G$O4+ MH8,]ZNA#8BKN(/J::RKMBP6UU9&2`FVJ%Q@XJUW'%^L4+/:AJ[I2[.60[`99XN=H&F M<8G/P4'P=N430$V'E.D3U`H MW*>F^AE/5>/D$5V^)^H?V8+.MGA:MJWS%+J`)B=4`+-;"6<'EHT^S`6Q5*,; MR#MLLXK7/EA5XU:.A@:UI9O3X**'-0028VUN:^,.SV5:H+OP8E*<*L?9PG!F M=U-$A\C&?.C=9*`OMF6BJ!EY;15LR5->%N4N*A-QH)[13>9*K@=(S3.G%;K3 M3N3(5>-PX]3BKMOFP^"PUF7%FB%C.5'I5H?:J?&H*"@^$OOPJ-^3'<9[5C?;G\F; MNSDZ1`5V'RS9?AB6DHT6765ZT@Y*F+$$E*UE&\*]XU`]+]=NO=.DQZ\K5G3` M-I0#0"`O'M)<++HQ+,"-**\^&ID[.H9X*++ M@4B[O-F%42'*2BSQ2$D?IG;O%+^?[)[YK(4$] MZ+!4N/'SK#I,["(1&]I3[%*M+6A^`>6KMU"\#9/!A$!'L:"#N>J];DO;CK1< M%@4WGKE.S7Q$%MD8?QV<]`[3,3__4AX_\$IT8B6/O^'Y@U+D0SM2Z9&0-Q)\(T.;!W_OEQU'*>#`MK" M9:9[`I4&R'KA-V/]@1JW4LS,,-A!15JQYEB_&LQ&&`J'8S>-%^B[,)9YX(C" M14%2BVM[%Y=RILSJ3F=T:(!P.!`_<$;-G(9YFLDJ';DU6U3T$E4!V1&U6Z+H0A!;B)O,L*][IM=T,)/Z?E MRI'9$MF,JS9#&3D9W$!I4GGI@._2DU:-,RH%D\NQL^QJ.I;'^%90YYG>A#I+ MTM#*+;381?3Z2A#9$2[T1SP8#T+JKK+G':3IK473-%W.AJ:9K==6]J8YR035 M>>VJ3,OON,!)O7NZ!*A]'<2".EW7\'D[XR+)!F-+.>6Q$B>-DTM7TT-M^+B= M;C*7Q"(RHZ+-D%NC:4:M7#;S31W:KN[)KZBB^?/%M,/.:8<%=1@"_2;\W*-)L84JQY*<4!9!U`V3G-H\.MK@E M"'\4]5;@:D_)3X0C5)*@.GM,Q+I@2Y)O1Y\4(N0E/N)U$>:4CUZY#=+M_-*_ MOUB=J)X%Q4&/)28BY0G_EQ@)T'F=6B2DJ_@$;JB+CC&B96I&)TLI2[T9JB%$ MZF+BP^JQJW#C/5\RB6R$K3;A%P;(U4)Q*T,[S>P5C67.B,U""V,RXW(@RCHO MF]NYQ=MT00E9VY.X52X?O'@4UM]70/]KX+V>(5J75I:B=.<;4!+HD4F\"0S$ M42#XA<'[Y)*7)X$J9#?9]??E:D05J!JO%%*CSS5,@+$^H6_=+I`&3IW.O MG?H!-&1!,540&8=_>N2DV<5HFID#/9XR;@SSM9>F=7XB&7RE)H@0/'A8P:T)QB&U/LF^P6AV"=ELD";),:C.5XN\X8AZ;H&GH\J)J'I0IQEXUJW63#8F'$:=XK[!-`#@ M:BV494^_#5N&+_SUDQ:>9EG7PA(-O0*ZA`]9+A1\2B*2'N:8B15Q;IUPJ!&# MFA_T^%#Q0+6&5I^3RE!*#1S7YZ`2/%>9'DCFBAW3D?[:A(@N$\>D"X?1HY.G MH-PNV(*)D^12:*-$OM+R012_,MV.S4"S,_ROK?G)LG,AD(DA*^(*/6)ZN4BP M_Z2V'U74CZP48\0V['86VCOG.*]&F&8&J*4AR%2PU7UXR4$"%8+F&ND5P!Z^ MW)&XE`X`6),,H/1*Y38[Q#N9"S>/25/O*5*JI",FM M!NI!>^4D1%+R6S\KBLSD`ILZU4*&N99^HY`=1L)I,6AVZIG228)V3C9DYPP6 MH"?1!+8K>NIJD]S9FP+^6VYI8V]S2_73CR/-?EQ$F&E=0)/7$C#I^G7O322F M^R,*<"AI7(U'L\Z"";QE>U\%:IK"8]TYUSW$CISJYHC_7/@$'!Q'N>TM\$%_ ME`F@YD^\`3=3_[Q_-UXNS6WC,!P7N:QE-QX].O8VGIUTW)MSV%:4J-?>^PUR MRWUO>]ZO7Y`@*,"2.[TELD00KS]^2)$\+^QP*"X1)6'?XF16:=+P=B2SX-]X M#":AY?V+S?7O\)<;8!UIK^T`^;^^_2<#&Y8F1-%XS@7HI,QTKN*B%_2O729Q M^,Y79%A!1.>CG`8&Z%)X*JP7VW.L3KZ[)6BIHP)E]#T'/'[GKR:C6X,Z;Z+I MU]N+2^#S8RQ0[TB!HSN'O1,)Y+HGXT4@-,Q/\=43&)] MK#&IP&SBXZVK!*\4GB&(?^T0'0"\#5>`HHQ>^$(J8`Z7"N/K5DY>-!XT"1_] MLRFZW85'.R.65'RO4+%`EB[`0K)I=:W/8-P(Z[<8`AL'MR^_R['"EX9.2&?0 MRBB#H):5POZ9R&\(;VD=_'>@XH%+4W<&ZM$;AB_LR6W#+WCZ5 MGI#JY0&=,]\,T)-2H1/]88V'D^#_7]'_5DS/#:>ISX6_R%)T6>(EVF86@R(SL#M$X&#O9_4A+\XP>\./3JQY MS.<=-HX=^4SWA(*A%,G/,]@9XE6M<*PZOJPCRV88?_=7#J]\M/IS7`K6,-#XKV`JA5AP+8_,B]^K MD\[S!.:)R3W#^14-6;IW5(`/,)I!.N75H93=QGH!_T+H?_[-RDQ<7_ALG1:*T?TRKI=QR+22G-H7JA M<&3U\L#B@P/(.GX_CW(/JW1.'T+PU))_F&WX4SC"?##09WZO"HO.,`J$?DG1 M>?5K;?SF@.L8[`GFSR*:3RD!RYE?;GWI!730%_UD%/GPC>D-[IGS<(=-26K< MEM2,C5QVDM3Q4E54%8I8C%GN_/=$RRD`+0XNLU0XT\PC;*!C*E,^P&JI_@CJ. M:_'+CRKRU6]1.:]P($N,TRS@^ZPKC?/#K29U/!8OV7*\Y@=#M+]4\(\4Q--QF7H!=!UG MH-N+F)Z?3B:M?SV9Q(A:NR;K3>X"/=1>D+AE0?&@EX=PL>]OD-OKV[\?01G: MJ,5=-\5P"$M/Q[CQC-N5]'[+]W]`RTR^<[M;%CI7A;CXMOGQ]O$GG>,ED0H- M"F5N9'-T%LP(#`@ M-C$R(#%LP(#`@-C$R(#'0O26UA9V5"72]%>'1'4W1A=&4\ M/"]'4S$@-#S#_L/^KE[>2U*D M[72+PKW/NN:]__*LX_OC3^O=OZ]>W^2B.;_]8=R<%?6A[' M:3X-4P=OOZZ[XX_P]^T'\\_/ZW9SA"7]X6_][W8^3.:U3PGG43TN?OHKC]_]:_X7",7T8CD&)-7E6[EBS>7X1LW%R;IOGKN6;C&UX MDW/[(<2B\`<4]997]RSAK^Z&M&()GB$%?8G5Y&R=G#J3V:4!H7I#%X:@M(RN"]VSJ;VS+Q/\-/P*1ZRQ)K M;)O@NQIM%)W4-J)HUXB6#M8LBB[:5O$G\_Y%:<@-?B*$_=8[P!_,)Y#Q,"D< M`J,P2*)EUJ@90[A[@(CNX&T7&$P/Z)/J*#TK6H7]19(PJ@6T70Q=&"=-J3=5 MU+J+Z(2DS'&'ZF1@V\OBWWN[M5?#WAT=D1[2#7_$?:]O(Y7JBQB4IEUJ[!?/ M2'[;Y^-(MJ'NWB9WZ;[;_% M-3V:<,_V1?4$4-WG]8JNT,X^R"=])H?S'#+DD3D,@2T\0P%XC$6D\-?D14/E M*H/+W]LZ94UB60/JUO[(`4=%EO&\9D7C%ETA8G:^<:M)>;>JBDV5'LPQ(^4\ MK?U):1-P$J6F-;4]$@!*+!SW5)N$29-ZJJSD&1^S!X*A&N?S>GYO/&L`/M)C"HFJS#;^K4I9;^^$S<_;8[I.:H^5X1&^=-+>9)0784`$L/I6I MB4B"N>U[#^[/6VX`Z]"1N%-=(0+H%A0`I[411.!YZW=+?%=4AU_9@A6]3:4B M@A9'V6''&);2ET%9++GOA[`V?F-+V!XW#V%'2>.=&DAM(:_V].M72QE3;VXG ME@$FY?&9/'T$5I8LU6')-CA.#$&MDA1 M(5/4:@]I7;';IWBG$G.(SCBDD[BL/W+!UB\R^$P/MF@O80HUF]>F)V'&EWJ( MO5?R`Q^B?K.00MD8LFH*Y*)>1??=1Q@WP6YVY]<-^FHMC'/4QGS"'+.;G\[3 M[JIEB[L(`!U@++A>=<$]4:Y_P>^*;;;(;,-P?@+0VW?;M-EYKF([3MA6/;AK M^]YT=F#-JP,A7$[39>X^;ZNBA"LMV5$[MQ'!Z"`_KE)J5)/283F9%"%WN+J% MSA37GQB[ZQ@:HQ18PMSQIJBYRZ7M';T*U13>F6YN*.KAD7*,..`,"$,?4D", M`SU'QJ#''YER@?J6E56]*5JO]?H^1\KHT4WG?_ M31QS(#S1.XVD@AHX$4+$(6)]%;XVAM=[;V2"!FBQN&2.=NVX:II<2)Q M#5!'HP+@:N?:F30J!%7L0(7QV]\MQ&J,30T$V1=L7T\+825%!.>((O*Z3#,/ MG2(KK1B@^JX*V\QSGGA+P6:$EQAF19:ZN"W9OZL*AP*A=4R+S=(E&74#,8\? M]8ZZ6#JNU9&]NI!$@1AV($2*U).+(@1]E3PY-J[/6S%JBC!\>-5\-K$L_&9` MF1>'%34"H_$31I$C-2`7@1PT#?=PH-C;SF1M6CG=5%EM@(FW;>:.7-=Z"L&\ MC]2D#DF\Y0D1INH#&("*WY0QXTHA+R!R>K73`PQUUY"!%6\C2>H;O<-1BXR_ M)PQT/OYIY4>'8/>=)=!>J^L%@+Q&(YCH+G2".=2\&F/$@%I-:E:7%@9TJQM+ M9!Q@>FG#VPE]/;)1U'D"W/8IEN!RCCCK]/H8'2#Z*Z[Y#$4*[G]D*)2!)Q1K MHKN82]\7)DA<3NHE/1M&+#-H$55+XQ(^FUS;>[Q\PC[I8A^Q/.[3D>*J"\J7 M/,_7>UNNEBIW-4#E@,9E!=DF7'Q9L>#NG@FS<0)_/T@@P@I*WL<*;GSO$C0R)-Q6-#D:(%V=!67T M;N4,AA^<=]2NQF$^P_RN:+@#D^%B+PS-*+3T#>PCMI%-7LU$4ABG18>[95;$ M67`',ID;;0;SW6[I8/P_.;-I$7(4'S:+;&\/@F'0TSJQ-BW:\4-I6SER]FEM M]UC+HO9>^=;0$SUH]HO5[01O/#0C`%XFMCAA,2O1A_0;O*"E;C%:J7'UQAD2)&<>T*XR"^GGJ'BFV**[)S%P/T MZ6K`0'C&U*6[(%K)W0BNB+8YE.)B\M]*5 MKQ.[7YZI6Q?N'$ZR:3%4:WV=#`-S#=J21<.G-,6J:`9]P(3UD3D%@&_GS"9] M`+_=6\\S(/2J3=JP+V0ZJ6PP?&@O8H.ID/,X1!WV2C,-37/-5'^`F91(2ZD+ MR!05$,:F:$#>&^_&$'5MQAJOSWVE\MUW&4MJ;")*NM7/U)!$?WEYF3P1+L1Y MO01V!XA):K[?AHB%HR\YI/,M' M1B$OIG0V$SM/$MUXS@+.'1/!AOQT^JZ];VS[OO2DXI]*5M&I`,EI.NNP9D19 M41GU72"MJZ*$=N$7G=RFB=,DS(QZ`\'N?LMR"R_GD51T_)8]2VRV!@WTO[2= M*Z,WIE=2H8_]13G=/U(+].()6\:+M.+)A+]L7UH$*I!L)Z?[MYJ&X/M\2C7DD/@PZ&H6C@ M>73Z5UX;>(ZUPS;#^">.I41;T@)ZHH.T%8Y:8J+`.YJ@BIJ[D$(BMNB\U/N6YVF9\,!JD\#E2..&"U^;%X>#EXW`F<2&DEB7(TYU35X&Z-N/\#0$!R"1!:SY>` M:'WBQ:0OBQ4-:MQK8G.SJQ[F>[P`46`HI0Y` MY\M*M/\EO$IVX\:!J"0KO60$+4$WG+;3`P.#`.V#G19%;1\P?^";OV'^_SK% M6LAB2YU<$B>F6&3QU5M.^:FHE&1G;I2.`9,AS'`(#!:C[:-P:Z:0%VK"V56; M0KYFMBV#<\^8\WR--#GMD`8&*K_?YJ`M1S'JDY#AU72:UAZ)RYCSQM&SW5/U MA>`XZ;?HR1\\1`PX=[?OW3,!,O^9<8W_-L%70NN(O'.F;@F#(D'Z'?MA4,W[ M%,Z`Q3)>S`:\485DZC!\))L6>N$4MCO'TOG6V7%P+$MM M_H-\"LD27-JKE3)5#O36I,Q,[+*[68=)J>!NN"M>C0"`G0&/VVA6[1MKHTZV M;%7Z?L%SZYG@KI/\=/+4F:"Q?`FW(5Z#S+7O&Z?-8'LH1YCLEU>P<2#:-S#XIGAQQ+=QXTFP,-.]F:,=]ML+RP$B6C030DK6NG6$%KL%G-4!SKVE9R!'IT/#:C\()Z&;DKC5L! M]K`L249CE`M\HT!CS9V=G`W4HDV;[(MM>6K8!MD%Z)@"\0K=>K;R)QP728J= MZ&J0&J1#KD"VKPLG@G@*D;R$+N1;N0J"1]JL]P;JY,#K#EQWUM^HA>OUUG9S(\WECR5&3NSDMD4`23GRI'5HXWWS[-LG/#-.=4^`N MFAL/JWO,J_"QBA\ZS3=<,YH%AK**]A_M;R"M)?2RR6C"NF[YVF[ZJ53;KE2! M!X8LX*!.-9Q]+S=IG=2ENSY"I>_OS:J@YKH8U6R;%G32?HF1R=Z=J3[>9;\O MJVR/D.O&:1T=UZ7UUSZRRM-R2Y@WLS:OGMRBA]0BO*L*#F'CQ9D.&BQS_1UI MR-@SV=#)958/A8QJZG2`Y-9/^'?[BQ]B*'4)MQ-F"QO]D)!8Y1JP:\X,XXHC2CN>A`S92(J<_\W MM7H+K0'16N<1B+ATW%O90LU%D]/PSMV")W[F53$V1>/^IC$5R(>;NT/,XQ*O MI#3S(LLY3X)<,:ER)MCX#4V,F1EQ_Y60(%]?K\P"RE7^3'EX8H\<__E7&@=.T$13>?SW%TF^`6T&L"$JV'2)=0PEL%^8;F(=8I-4- M5>IXU9]X\8G:GV:LF5WD)FZ!HC\1V](OI)&%UO]"T5O#LB M^./?K$*D_;VW\&$$;GO<>I,L[>WTJ[#Y?]9V'8PP_)G5G$8H`8!KKA/DIIDC M`;,S+'6!5#YN"A^8#J\2]EHC/P-+'9T+GLC=<^T-/%'-Z@#"6R4;4+*P6=,H1-/>)9?'S.U M]:V=F&-,;[P;T.]0\/!KF+QIO=SN'H38.Q%LP M(#`@-C$R(#%LP(#`@-C$R(#'0O26UA9V5"72]%>'1' M4W1A=&4\/"]'4S$@-#\FG%[QOACZ+Y9[][<->0.W(3NE?SMEWQQ(21O(?,VEZ>%'`26$/*P_WY* M52I9&O=P'+=XW'))JJK?1WWZRS_T\9?_W?WI^>[3\W+4Q^=_W:F+;H\*_COC MT]0>QWFY#+."7[_,O\/_S9_//U[LF%_GI^=]W&E8H!2O@[4]W39$D*;[N M1WKSTB2G9.D'T__?/X;;*L5[=M.0W=9EF6V02(18XQV,J%; M#O1.WMA0E[:YE3?O$@E_3_#A96AVZ<]*M_$UO#D$V6P_-_GI M;&Y0B3PJXUO*O5D">1\RMW_;B/Q6R&]RV,WLH?6T!`ND,P;KO*P_5!R@ M:TKZY-R-4W?4&@\QN#*]<&8@1R8#BRU-D4L;`NNB:<7N9/XM(KC;$Q\K<@6D MHU*`G8N:X3="1)1B_`;.Y&+F0EKX3-25NA\&+\/GM0T;*3Z^(Y`.KW-)"[Z] M>?>$"Q8&3/7MGO"[>,W9Q(?['",Y++20F36ES>43?J5Z+C-W!3=^N9=\[[!! MQL4_&_8'?V-B!)WQ9-/0/$3EGI,W6%"98O10L8[B5%D9I=(VZDBY$SMN:WF` MMX-YW'GU-/]6<5)BO$7;%``NX&Z]LG]]H#S8[(_VQEZN+?-MD^N79LVS3Y]7 M\^P5XL4DFD&<51'D1MB+<([;@6!ALSL-G-B\ED64QH:.>RP#H#A*HM(RI@JJ M\,CIR0B@%G(V=DDO_>U>U\J"2$VS*8FYXT`WK`Z4)<5;/41)8NI*0*@S6Z)# M9$(:2FH15?8^VN7[)A)E5-V;:)TK#G(AOQT[/[<`(K.Q'K;%J..HHDKUUW%2 MN@7*ZWK"ALLC_-Y.D]\SW>++&C8.UZ/14*WB(1(2>Z0?U8:A6H(>;=RI`(\8 MRA$WMW4F2HELPUFK92SR@@AC`@%$+ELO-V"P#(L+`<_=W(ZT1FO]FOBHNPR2 M;&_I90GDPVNPX4J#M8$,>`VFN+LP-O:6IMYR^A)T%BRDGDOCBK_R]6HD-*00 M.*MW>2D/3^LWL54QAW][+?JUCLLBNED154AL*&(+6A*%@,!TR%0*$1>/I*JJ M:_V*0U8GX*6E=2#P&NLC[.[326BG_!X%M8"%B_92;GO!^**`,I3#?)'E@!$I M'(J2A!71;M&U5-T'<6((RA,NAB*\MVXEV$\64&(?PI0U2`&!;O)!YWI+*Z;% M0_0;R"AV=;LPFA$]HZ,?`8*:EM"Y@IEA9F$>>NV!"E4QF MT^BCD7;T'683E\00@1G-JE1B':'6?OBU,FIO-X!H'L!6H$('##WCRLH3=YJU M<-1.MDCF+96B(C#(!Q8^EYC5JI'[L:6KTBRWZ.Q;YYQ+T\)/'!]\I"5O3BUX MBH*#`*RL)TL2$3J-67=O&HT/5ORT?_L;ZHCKG!K?AVVK6Z;&RZNRR1DIX#V72Y,AG%2G9ZV*@;\Q^%:+7O M`CNW.QPZP^'IT@/S=L3*[O0;N+^^![,F_UDR5+5_XF87"R$/Q$C3YJ2DDK&P M:;@NHB33@'35,=);CP:*>U#'BKR*WLQ#/$40\#)NY_+DE!`GB\Q8AA"2PSCZ MG62O&S;!:WUXK=NK2'C3!AX@J@X@Z*4[P(/U?19#R^Q?9:TJ3)M&O*VA8TF= M/;K"WR;Z#%G6+ZV8]BV!+E57) M(S\G\%E41^4Z$=WB06`[DR]U/.,3Y.$RC`YK?V-Q%41MSQ^V%--KK`-C.=TQLQ(CO M`UM&$JS4=35SFC=<(7Y+)L.&VZLL&$F:)*ON*7WM-/N&JI+R8(R'.4*`TE<% MN2(,MBNPT`-,.B$$$5#DB$(X89<5UC6E21S5IJ^)S:BOI]X?/?)5#)-)<"!R/K*7DN0KQU#D3('9Q(3SO)QYA:*S)Z8UZ M_!7S+K_)<0@3-+5V?4"]M2SW5CR:@V?=K7'"DVBG?")+WINLVT)"2]1/M&9< M-GG$^3,J[.++"'I09,S1_8Q6SK9N@-M:IGB#H0MLY#ZN(E&+IY5@8QP)%T=H ME,4XN8?!L]S78M64G-I+2/>!]5/K%->/&W\88*I]H^NOS'+J-?RX^VU/KW/O MM";;&HF\\H_H&/=W\%X6Y>^-Z-#C;_]@9P85X/>/V+J+"HK\`X\>+?BFHHS2 M/(E=(0"E;L21]6X/?EMX(B963=PE<;IB,Z.<5JL?6(65*J[-+)JA7KGSFR2M@Y*>I\&N`KG%T.!8+-:GWL=Z7%B2]-T+ M#:Z=]A<^X"S+(HGL/"\^DH6$4L66-:8K+.5@TFXDR$*%1CE_VU#"8II?U.*? M-25;K*=I(R)I#)IK4^Q_@K@65M=\R3CDA:D]R=D;^'&RLMW/-$N<0O$YR-*] M!4*6^B`-P%:Y83[\=ABN3P&.,6$0:$<>!%;EL@H]!+5!`J:B7I'IE82UWM3& MT3'Y@W'K6S[:C"RC_]UWA;`-.P63@U;*/UJ4)-^G&0P!9@^;M[%C1$15X+36 MY`_!S6@"'%DWK=9-VYMZB5"+7[^5/KUD=%?S0#9I]!O*WY:_$2@I\J&1?)X_J]@$8UDF3'0S7XO=0QQ7^&D^#S>'S^"M&/R`_T MR>2;4DRDHD7N"-;7N&*#%@98D;Z7J-8/6Q8YX-8#U MP(:J/W8PRL+<`Q.8[XTBE[_)Y:\=MQA[L2+Z?\ZKI#=N&PI+,CWRV(*D,69@ M:]JT!HH`8P0S(RX2I5.:-#WT6"`W'WL.BO[)_*X\\I$4*6J,I$F[B"[!#N`U6$N"?Y,D2A M_YSBI@$:F8O/K(/E>R0>;^U@.3U4A=*&HK%U$/&])V](8_T8P?HT4QH-6,#A M2%GVZ:8JH-;-&^/,.AK,,)F)L]EG;3U>Z+G6:)!&J-!VA2H).0E@DL+).QXZ0PR=3/WG;W64VVLYS*@S&C=M+8I-`!; M`=P+G8?5[EIOO3^G*JQYCAN2I25*;VY%]:+(["Y>)M)*GG"QC:(-SC7*'1]+ MP'J8Y4+D=$YG]-Y;Y#<#L9/V\1S5RI` MH">C9C1[5P2TP\81;>:(;(00ZJ11AD+S&!\"V3A=MNTOIZ\;8F2JR#J!64;I M`7RYSO))2J#ATR=V+/9@;M^HHIZXL[LA`0MA`:1L@A%B3B6\J&DUT:#+=2D] M+@K3MHNP8C&RP'VY5CARU3.!93B%G,`#\[`*JMT%KG"^,/"6(8'VH=0ZG]^G M=95M[MY]-*48#Q\@_WN3?#FU$($?!OQC_7S4@^=2#<#M4MUS=@$?;.IYW`XK M`]M$[%WV.10-"!T-0/J MC=6F5B5/.\),[>S)Q<83=M/Q1`>DG*O]G+C[6SUVI$(:E>%,Q^0?]"K('@Y_ M=(9N_2+/4>6/@=X!`>VM29Z5)`8_E`.@ZW+UO@0X/+R_@T-7[W[7R!,(]\.' MO6D9AFUQ'.U@T]%PL%,$4V-)ZC$H,_/]_^!RQVZGIS%:UT M!72D!+1CPQ-CT+&00QC*?[[?ND-P0EPR`!5ZA2OT!Y+!H:_8:RWJ5^PP MB[$7D2"?FD2:^(]TH%-ZTB!Y@Y+I(X(%!Z0PL+5-[K:DL@@.2%?M]..(CS5B M)JF:U/I5Z@=R^N.,G"7!@C"!G7@/.Y+P;JSWY\7,E]^35JR$YM'7Y+XG6BRG MW8+VP>K*,*\5E4Z[M9;X-VM2U1EZ.BX7R+E.2!HK7"/ZT,&VX]P+SI1B&XM, M1_EL'.-#G14)CJN@7GB?SD)__>M6_6LD6IKLD&R$77AXU/[6X@Y1KE:?RA>N MZDYM7TUBEJ9E@RX$Y&??._F9-EF>:PYI@3&F;K1"P4B)%(4"4:H,W@H5>4/6 MJ!-8:Z\&%TO2!G[@R(7@78ZLT]2=`;FG=NE40MVD5'`?G6OT:%Z!7@X/9BDT MMZ++U(;F6EY'R_'5JDQR%6:+C^7-]#>J/A-&I71"\9N-ER@UL#?ZN/?U^\NA M7_C5AM1I==,I=:-5`Z>]&E[FZ-'A217*\).*)'S(9- MCBZL=*1D1>PP3.."Q6>6+:P1#%1H6GY=)P1'B$H_\?U";]]5:+T8BX#WOL;^ M'MQI.&"TO:"8L_(-4'(^AI=CF'!QH?17+2X!V9;&,'4*>!X\$C M=F5EO!P;OSMZ_4G%EJEA3=YF2B'N<=JX=&;/V^>3R:A8R+9-::A9DM2:%>\E MS*-^RUD$PW4%XA-'GCO\U\)'.&&I^\)^6P-J:$N9XYK!WW**[@8[4O5"5!XE MS/>X6LZKBSE_,F976I49%]FT*UUNC^R*7(?\T7-_]1L]'^(BL-M&6+Z'7P9] M&]T(0BQ?)BA)<(>P+FXV@Y+XO+Q4EPL)F"YR(>Z*Y-!_^G(@`@-$P<2-F#C` MQ\'86S9*A_S^V!=X5%"D`,SRFRMKQ+@"9H"S5,^HPC.08]\&`'!HA^P*#0IE M;F1S=')E86T-96YD;V)J#3$S(#`@;V)J/#PO4')O8U-E=%LO4$1&+TEM86=E M0ET^/@UE;F1O8FH-,30@,"!O8FH\/"]4>7!E+T5N8V]D:6YG+T1I9F9E?8H<+#&Q-E-]R]G>. M_7EN,$:3X*-9S'"6X`3?#'S"-$9ZS2*QAV/#QV,3VV>.NP]89A!M<1I#]$HQ M$"W73V@@RE:X_SJ\0_8,C7T*0D[X)?P0`OO5H44U(J06Y9%BC_U8+T&R2D*1 M2A;AW+3X%@Q;U$&+2I1*51*Y/!4Y!8F($JZ*,,^RA"N5N0>Y"B MSC"!9V,:/$*\3)U=HM7C9>.\W@1>24Y MST_I1,-+!AOX$V``O/M(J0T*96YDP&:.*05G2(5D,B`!!,/`(P,13"'B@?H*Y4WR2-DS!"E MY,[RF1AWZ(8C?4M\O^W[_UMJS##'<[U:8)'C!3YKV$"1(7UZ=8GY7//?N<[& MM<2G5UA7D#YBD4%Z2UL@7=]?H8:TNL/WMX\7J*YA=QA73/@B?!,:0EN/Z,H1 M/6%(""JPVRMI2UO744FO;-W`=9%@*]@+FMJ"95J1,5!6L(^MX!!9&-69@*4_ MI]C7$S3E7[1)`,J>_@OQ!+U#=`3NJL/L!)C7G/U+G/(.`^G!T3:Y3GHM]@Q@ M4T)NA5SUC/=BL94V,SRSG+/N`HF%RZG+KLNSR[@DU*16B1PKB0B/$`^3FRT> M-:GB0>21[`(%LO>P(\!-!0_P(\``*-%&-`T*96YDF595D$/PPYOKJ.$[5I M`#J%;.XN/Y_:7J0HVKKM]?1L/^O#VTVW]HLVAAG^.^*LWAVX87]JAAMW/M_7A M9_CW[4?WGU]NJRR^?W[[QZV&$W4-)V#U^]M3]>7UT^-73\77ST?3PWJU*J_@ M9S>.X\M8[=5J_71X_OO;7^!27=.M9G![XSB0B2J*$S1L>F?8>,-7:@6&!EAZ M,=6U6EVME;O#&6ZKF\W?:FVV*S3=-?Q153A#?WR[_==MTPW.6FVU?V`S''K] M,@Y@0<,[6U@8:ML??OQ\^_KGS_KP_3]O_TK1&=Z-3JM?-$>G*G:YBG?I&J/2 MU(V(2G63Y,E>%2K_B)N#=N\=>?-0XG.U?_\FI05KI8D/^&4C/U1X;O#?%5%: M)FNW9#HCSM5D!A)709#:;(UG=->(\&)>JWU2Y-$*+8R#3$FA-N2C#^RNC+[( M5.P6+<3L_)YBI^@.O;CC)OI`V=7B@IJ.N?VTH)N6WD5XW=A*Q]`M[T"U2V.* MB6D'\7E5I&2SI;\-67`VKY6'FG#F_2<I.ML7R*:[*@O)[O>^;L'<3=9NW,;G65K M:>ZJ)RJWE'J).^G<753L\(0./(L!W3$&RPP^$XBD#0.D[X+RI@37S2*N1J09 M]L4=IRK:WT"UNV(?P-TB*;'2;=<&$>TN8XKS;ZQTFZ%S.;LGP-+ST3JRL16@ M"F\S72,/VA#/'FQI00]H1WFX%8X<[0`1U-J*RHO*:!]]JRBA[3L)F#!I%O#( MU7VV<0R`-*$]T#R>C$0?)HS2`$S!BP]TM>07A-=@Y=(C>T`8,>?U='(%A5GJ MJOC9A0\2@Z$+X7>1!*;2[R_5KD=6KHJ$7@5-Y;\^B+Q?(H*Y9'BGME-'E`67 MC^W88LD.=I>)POI.=4W(J1P[!(]?@$5B(V4J&4SML,$;(:UR+.RBGF54O+O) M9I/ER882:[M%9OM&A(4N>-PQ^4K;6?R$JTT;@"C=4\T;B9D3,-/^)D_W$=3, MT1H7ACT&H87B%)]GQ)RZ?9^3N[`UW:N/Q#'-A93^`C=#'^4;YU9\M-HMW#S7 M%82>Q$%].>FD:M(":/")`ED'_!L'H0FX(HO5/=_]HEW-0?ZI"(H=(,U=>[2- M;2&O^JQ%W"0;;O8<&,?$6,,RR:YHT&02;#1$NS#>YTC_&(; M9BAU!]E70-31-.Y[1A#M3!7EON^-";Y_\(>BYZ/38C'\C5&F,-!>5JY5/.WP MR6+^LJ;/B-!CVMXGZ[7*_3?%+BG]BXQ\<:[62<%OT]VX[".<%26>Z2[0U0P+ MO,+E)]HKGR`#.0@*C\S@S82K-;C-79^!Q1W>ATEF#,^0['7/G4`Q3MXH;\5W M<\S6$/0-\VY=GK`PV4+B+QN"\#3]1$IJ/E%0N$4T1O2"EM=I+,^F\^^T%!N* MP(_-%3T8;1UD)=L[R!/)I-]FE'V&BT)TDH<00C+?A`F) M79$.7"IKN@/$;.U?=!?/@Q'5R4A_H+LN)46:1]\F3AX0R(:&^=,,''U(RV^H M-S5!+9'(4?D#I:SONV6-5+&K7.3,)AA[/!11D0?3#%+\V4"E@9E!X+9M]V*' M2R-5T$#4C#RWV2^'D#UW1#TV2W6ORJ3$(]8X%&"I+]-!^YHRJH MIQ=,V:KI"K5CQPT$@AK-M9-B:JA0C@,4+X;J7U9D44R@]8^98]8*U MA[FTI\#/IAMA@B6\G#'] M?%&_,Z^&

\,)Y0C`74C/)+*1X=MFMVPD[IP0$#<]OP9`$Z<9&88+J5NEZ1 MI-?=8E#4MN^!R((]9)GAUY2J-VNHL&UQ^PVZ@J'X^#_Q(?X=?/K!"TD`9E4DVW,\T";)::FGW(^S2+-^*=*9_H2L\:\[ MDNP:SMYDY)N,\66%BMR;F*8F$F)J%E@S=2]E$WS3:-]6AS,J?.1^/09C8T&E M+4F.V4F6'G)N/\A32%3-@J*[2%F$81*D9SR M>D026!8[[,'NZUM_@*6?H%?H)(K?,ZXX.5?K]3V0[[: MK_PE`UW2#/8B'UK^@-K433EZNK)T)GT)'++^G!30"]Y'Z;704),OI"MWMY M)9.V72`$XE^2F@O:^R1I):;.I@3Y_'X2)>$@(-%2P7I&&V:TYWIABOBP`&-4 M;G*5Q%0-=GZ\;_+ZW&FN:\\V[JNZGMZP2W_4RW<9%G&^H"U&)0#K+$ M<(2N)D&D[HEEQG[PC*5+G'";F0+QY\1W6/,P!O'/L!6IJ_S<%>O[ M'?O@=^\B)TT*[RQ,GMDWBILEP?QYZJSN_Z8EV8&DV_LYJ,HVJ`-]LG:,ZF9& M]52/DA$ESJ`J$6BF'I>Z@]H)=R?72:8!I0D,>@J>)Q5*03BK&&JSEI6*GU06 M>F=FBDG]L[0W0G"P4O":@X09PY6[(?.VZUGS].#`!*EB.&@[-I(]Z^ZF";CCFOJA)(Z'@`@'F27@ MP=V2G/YO9FL6B1<4)AD348=S5!NH>V2Q6M+5/)WI6AZ]W):"*B#$+9CL%.I/ MUIXJIBX4C#I5'&W+Z*M-@L[:;GE-L7Z@CB"U'2FNG*IPU.'\XIE>K%"$+LQ? M,W4"3=K63[!&O&6JCT;6AT9EW3!M.K(5Q-K-;-;2X5RE$]>U!%R2-DQ3!85I M)^1ZG.S%N.%[$@EVG^=3)8YX&[XS"=[$SR9@=A75M824%<=^,X<5`'D`$_)%)/(*>B M*=!C`=]\[#DH^O\O'M%J1',[C>]A]QT%'4XW)F/A"W1CB M#9&:J*)#VR^U'Y:608Z\F*CL]"IRU][E%%F2*O]W=IXESC4\/8.HPTH\M%/G M9)G1K3<1T)ND7WK')O6GT\O#S66)A>IZ5D$G%;4,Z\_Z2IUL+C]$Q7Z8&W,# M34[M>H/(35.34+)3YID9+`:^R<5'E;+)SXI+(6;=4[H-L[A+;&=<,GM8*8"Y M=KNDR1?.IL,[)+*AE;HOXNX#9MS)&EJX`C5OU6C)ZO_SI*IHQN>=4+5:#6*D'XUF(T#0@"VH:-/F`MCXF" M)AM2\\8KW7QK*16[87(NH;[_]CM!1;-)1%@NXXJJ9(R MR39$3:8[-OF^.TWH3CRM68WZJA32X6'KVC_'590]?A#XNYGI[7KG!S-"05WY MHS"]`Z=+N\0-%TTMCAB_L_+IIWUF472@-[BQ^S3=/`49MM=QN$M_4/W?._XD_WN2ZU# M3T"90(C8HN*MC>?8:Q_<.W_%*%4)\SW6G4>,FJF:9E&`FL&CQD9??;L M5/M.H?"\X*IG+FMNCPI^*B6, M4F>B^U]1RE'QV6JH#.#-MVB"Z4MY[ZV-O2B3W1[.Q/KCNGVJ[-(]3P!@$3_M MDF*W!S#T+7F@,*9!2OAH_&%O\$U\2[B"T+>[#,X2"2FYOYN!SH2#J_HZS$T]:'Z66I.U.W]^&CQ?4[;F($#I7N MBH18[$$[<^OK`,_;K$BY9+H?/31\SDN^OL>@K.33\FHGM?<[\<-JA[[V(=NO MWV7D?F"[=F"7T=ISC*C&J;KSYL(%6CG6C_@BN`DKQQJI/ITM':5(19::EG:H MY[^+?%\FE4HC\VIZ_EZE4!3]"PK=;\]G_T#$QFJ0Y@92`DH%TO$X=N,,OD&# M/YTFD/'#S5_?SY[^^-[>_/;WV9]GOSX#$J+HQT7PI(<.O^Z`Y+Z#/D`=.7>2 MP4](AZQ76.^F*4 M/(LBG5ZI`.I.^.P1B:L-.(^CU+\C-&"]S(C`&(/'7@VWG_776QK?KF?I?'7/ M>LQ$,/!2>S:&@#+0I@Z10$O,+4VSXQ!/&%=P37'*H'7,-OJ)-X\N99ZGS7!X&CBZQ*@7F8J2$<]U#EU:=M MML.XIE&.@*4&3?:37NB9ME5W+$LND(WUQ/7UMHL>;*,6[CEGC;?4XL!:0N>$ MMMOEF_VAXI8A5!Y,--O[`_DQ9XI,C.E]7>!#[8C`C"8,2DBE\QRZDR7)R/!T MC03Z!%^R3W-R7Q56&UNW-#+VV70BPIE7$ED3CP2T3!592R#F%3[=4"@LH!WF MP70@[.F^C5K)OAOX7."L%+%-=XW,FUVLZ'UDX;QBEA\CE\U&I-AF&,./*1+S M.(YR0^KLMAM6G.+:N*)$3*NN<.7']6:(`CHD8"5!QMA_.`1JM1S[0>LYW'/E MQ6IU$FIF^F@0+X1P04Z/RB]MWX+9A]9GD$P/FM1A54N4HUAGO11O4I[:U6TC M$[DM7)?.,A,_)Q#JA*/%J=LJVQ&E]]+%."/S,Q8ZZ+"M\]29FN:7'@P30>`R]S"5H M>#>432\2+5N/AAE*N83-.BV+_<[6`:_>M5)G0_?LU!:OK*=)+@T[VPR1)FD[ M(S?&Z9J:5P+"^K^ERPQKD.2NMVX`BE;<>4AM9)^1AG!S9]8=Z"J<_7Z\+,K.06,[A-N_DO_F:&M29U]-#.G:&C3=LN0C_/E6/8 MXRSU4F^V;UC8F3JYL_(N11RT\P<.[=\!`!JK+D$*#0IE;F1S=')E86T-96YD M;V)J#3(Q(#`@;V)J/#PO0V]N=&5N=',@,C,@,"!2+U1Y<&4O4&%G92]087)E M;G0@,SDR(#`@4B]2;W1A=&4@,"]-961I84)O>%LP(#`@-C$R(#%LP(#`@-C$R(#'0O26UA9V5"72]%>'1'4W1A=&4\ M/"]'4S$@-#T]&IM M"_I8V-CS[FWC0^X:+7#VBJ0^@?2E2%*T;T7WS=>G``U0X%`4?>B_W^$,29&F MW01!DI5,#>?C-[_YS>N?_L;WO_QG]<>WU>O;M.?[MW^LFB,7^P;^.>!?@]CW MXW3LQ@9^_;IJ]K_`OV\_J__\=U7G\9>&=R]O_UQQ.-0T<`A^^&%UJM^5SXMG M]O3\X;G\YN4@>OCMR.M%=0,/(SS4.[9(G_.SJNR7X)-8+--VW^J.Z4H9^?%O] M>]7VH[+62&[B;,?]P(_3"!8XA-O!B[&1P_[GKZO7/W_E^Q_^M?HK)6F\FJ2. M'[E.4HU.C\))")S#!\AFP!NFNVSQ*2[I(AGXFU98J*\Z= M/-79W5!CK\6R.C>N\S2?%?%=&WCVHMB-^.G>@Y8.945 M.QWU0;93M^<<;7?XZ:WZLAV]?GB@SYNZ?%'EV`!@)5?/REI>+3%X:H:!,-9- M+CAJ%G^S*!;KQ3WZ.KG@JO-/SQB@`O$H(8RFIPM-Q`."K%VF:82A=WSJ7'%Q34"W*QU'6)29# M/^2QR0P8HOJ?'=2_.7F+_<3QQJ\QBQ\6ZW?%8K>HUEX2NFZ@HLV12IV)9SJH M+1[DV.CZJ@7S[Y-!<\-->!$/H>236`@`+"725NM^ MYC*CN`QERW>3C`3V`MRSFAIS_.N8%FQ907;(M[;WK3\TX;%Z-9!]OV@ M2JWNZ*B!/;P=!/8NP+U>+EFL7K38W`JA0E!WZ&C@+#W?HB]@M;E<.20)@223 MQQ%@E6X!K!?43W+P2?C>'%=EA6O5GT1H\-10`J)J&X&'S\:M47VE4K2)EN1/ MT_=D56@O-IL7$QX>@"X^Q\@[3%SG#65FDP!9R>;[;@UQ:A?A;L6!HBZ2*DYQ M>N@PRLS\%5'=QE$&``!454EJJ@3HY-PR+SFG0R88M5>Z)*N66&K!O>EV`:'B M.D(OF]:3HP^R=H:[;@C8D?KJ:4-]Y?5&'E-;MV%/W&\8#D[1A@/NJ?_X07S_ M$2^40]#"3Y]U&P8M?"$*1A]1JP_OSC3S3$V\#6QP\'-.4%<*H_OO_^,V`/VR+#P72GW'HV`\7V M5=_[VA6'$Y\;:Z+31BOH%P+`@)\/HS_E/=@C&>GY.")A;+<*\5H,9/:F^4O+ M&Z/^R)"I/@I:4'^NR%#_.7<97EF9]P[F4*T`X_<4+)\$.?VQRB#MA5'4D*87 M)SFMZ4'0]!&Z;L2\=)J%5/W)5+V, MBF6NHJ+3<;0CXAF:WNE-T)+`%ED1F\\B(C)["[$*,K!>#9+;*`6[6*1V6S[_=M7[5:'EV\SDL*- MZS%I?"`]/6E@W30]ZNI,K),WG!R,T>/CR4*%6Z/ARZB^J&^LA3*4]LD3[XF\J!-Y!=T>F$ M<26\#.&:*;6C1C.M/MGA-H>N\X/YP-_1;;"QA)G`MF:JXW1TYH\>U153,CTI M'F;"F=F?/ER^$!DFA9W.&^OIC@Y[#MY2QL&4)E)J`OO-[,DRJ>;J^,)`$U>: M)31;\3SVJ*I>2XHM202F^\)IC"!CJ^DJ"7 MW17[]VI%PRQ/@7FLVCB$'\6LU+/'==L3U]P6(P0-(62@C6KVN/-'W4&.HJ;VK.0AZ_>!Q;%1Y'4DJV`@I>B(A?:Y^ M;!&G1OJX,#!);MC"*Q0L$4ZR3Y"4V&KT.RW2)+<21TGBV-7PW&$+Y`*:#C?` M5&P1W::6'>Y)GW'NHI:Z@[OXIA9IKJR*!L@N%\`8-2SF2"0W:UHA29!(AF#J M/\2ZOVS2Q%RM^+LJ6V=)D7VI_72U9J;H?"A0ZY`CXG2[K$0X_!Q.O[,C,HMC MG9IQ+B82IPYD7L;T5D,)1MJKH\5?E.8G,YG+U+BG(F88(R&88?+^#* MC(G(#AQ0)/,7<(^/"](B2D21U3/W9'1&1[:(J*D&H1)]0VZ%$:-VP MX(!6A/.!1IYS\;D@-1ZRA'A/B(#"\*0A4S7!\B4LH)"?![K#>%QEE:(!%)U= M$^9+5QFYK)>=MZ2YY#0T5Q).PDB1]\4A^5F'T+:_$L+_)\(A'%!*%F$M/-V5 MN//@9.4DS$S$Z%RYDHK/.^Y]Z3)F9E!?N5.!G/1/+RZ;L')H#,(,.T0).)K$YT$+9U3Z::1D MP7YASFB,V>JH"3'7YYP43E@J+?.G.@="0X;V*$<.;L.?D*C96E%F1S2IV%5Q M]W?5EY=#?_9:\??,DLJX8EDN[@?O4;E+;/^)9Q+9^C68#L-=8[4Y"-0Z:/1+;&UY_0P=%EBB0V MC=^>.W)!R9H.XF/(H466EU%%S@J/WH67ZS#-)A\N#)05$]$E[A3G"L"9:<)IGJ,M;:5AP$=$V-YLKO<0*)HPY/=I0V&I'H; MF#=ZO6^O>.:-2$]NX-AJ+:XH`%MN@Y_6Q<4\C*5E7#LTW.VH7A(\0W\V3$N& MH#^%65H%H4F$:-)>S2M?_S_2JV6Y;1N*D@AB2@V'#U<:6^ZX27;T(AX1("ER M)EWV#[K3__]'@?L`<$EIG&DW&4N[S:[8I,!P<<[D673;>/=A`>0M?(D4YI[S3R&^]T MP9%LJH%#+.%WKRYG$726LQ___M?/GZ\0XA":&?;;^2L5?M%YXA#?B-K1''>5?&("`[>/*YL M*46@+#R1!YC[&[P@R,ZGMFE[AR8:$217O#^+XFO6YK!NT=[K4B<4"%FXR=]B M9F\,,\[QM#6VQ*QW!.+:@>M&>V,[?<05R5HA>+T+HW[,T40\\6X`:] MI*QZ?Y8&8>T^MO3#MH2T*XCIQ[2$5Y#,7B!/SK@TSI3AJQ=I3 MG4BEUG;CZO8CL.;OETX[>#NCG2.CK@RPXT9S6=ZM][YB#2L*-%MV@X9K]^C- M.%EX8!)B5-P[8!%9D(A!KYG0DJCK^8#HZ=/1>Q)S M>;&I=OX'OT;KDR^#D^1'3'7:F+=K5U:%#WC$%[FO2Z?CCY6G$K<_H<9#WL'9 M/R-MSO3>#^4O]&5WB'9>D/HE$ET%$'9)SG-C&W>XAL*X/?XCJ711N M7:E5`XD,_KL'2&0>EZ2!9D*EQ)EB+4XL^]`+J#SQ3EJB)P#_XLI&.RO^V'+I M-`C!9W7:%02+:1;3]WO9>LC42-OCZ&G;8,?@.A5;LG[=0^H\'(!O6S7QFZ`!?`L1"PMB>:4"K:PF*81:;Z&//GUO>M8HXA(S)"BW1`C M`^D_(TFGZ"$@JZ(*EWN+BE<6I3H"5*DRI$"3">+DEE7Z*HD^D!P-`70:"Z[V M<,)EF@1A'6*_R-=F)QWOS1N]DWB`\,@*`K/9?@KV(AUP14M0OS'LP'YS6JMK M]UT_-F"?X/S,Z:#R?@K'Y,)^D_K8WR$5]=*>)"`3X+L@MZ:"^X7:4[.ZL#]PO^!%F'#ANU^B+U#B>G#CF`F>0^WDM7'MY**$VY-T3?/ MTV8^V.&CZH#@#B8="R1OZ>UT&&_N"8'3)<5M@4FPK!@@$1YIH2CFC!JB"##N M!:!&)^'LDPU)K'/'3)LFHAG_)"B"E"(03:++)MFRZ"T:$S.:J,CZ"\;L/T!0 MG]R5NCF4)'5S=PPP$^S^!QP,2;6!^A#S=K0K;MZ7I^`76V:N6-6&F^:YNZ0P M;9SF.GNH&LW(?,;/AXF?>]3E]-2#IES9$OQ9>!RH#*`&L8(<=3:]F#M8,Z.Q M*0&Y;)+>.)+A8B,\GK!N:\K`(`RHD+L:VK4068(,'MG3S7':SS>F/;@'QTSN M7C)-V[7/K)VCLPQ:NB,'BM1&):Z3TWGDJA*,X,\J-9FAU*IIF$3G,>%/G$K% MKQU5$_YVEJ/:-U6MMZ1Y*^L.#CC%2HFE@S4\40:RA[9BK"MK*49TUJB MFU)\4EJ_@?D210V3/V@PKW!FKI\)X]:L&,VM5'3B+M/[8'@D_02WB'`E@P+` M?`UW5#1'_<1;7<+KAQBFVCN\GREU2)1)J(_U@X^WGE[8(E(BJ( M=YC5'4B-Z0TFO2$R[(]^$=^S/I\C`...Y<+2$J]1+2E ME!KB2GV!CSM_(7%=!*)0&>855WAA*O(RTMRN+LGF7_C+9^?OPXQ%GJ@>ZK)J M7/X\`DTIN7!FO'V\"X9I\SL2&A48&CQO(6[MYZG'M\UT61'LEU*C=[LDYPD' M4^P^,>58ST">``)P__[GMW\'`&Y\W],*#0IE;F1S=')E86T-96YD;V)J#3(T M(#`@;V)J/#PO0V]N=&5N=',@,C8@,"!2+U1Y<&4O4&%G92]087)E;G0@,SDR M(#`@4B]2;W1A=&4@,"]-961I84)O>%LP(#`@-C$R(#%LP M(#`@-C$R(#'0O26UA9V5"72]%>'1'4W1A=&4\/"]'4S$@ M-#J-N$I0&:IDSP\^GHD$6Q?9M4;UY^Q1@%RA@+!9]Z-_O_9@[G+%D!,5B`VLX MG^?><^ZYCW__I]K_^I_5Y^/J\3CMU?[XRZHY*+UOX+\'^FO0^WZ<#MW8P-?G M5;/_%?X_?L5__KNJU_'W]\=_KQ3,:!J8`:-?5O7;\KN[ZS<+_/)X5`WOJ\=I M.DS3--I)49S04CW@4LVC3_6;].+^08\P=-#U97KQ)DOA]P`+#UV]6/[<*'UU MXT0_'U>^KMA]QM\8H>4([[@=UF$;80<%+.A@8&S/LOSZO M'G]\5OLOOZU^XO>/K[Z_4P=EW_\$]X;[3'!&O4RR+-W:7P=5P]VZ^Z;.B_L' M@Y>HTV3)-VTF>=X&OBG\MH:9<5J\7Q9I4N(N=DF6[M**5HV]0KS8.["3D/KGZWYD)P1U6=/MO>1G((Q/8V<^#P=-S*T!L--01LZ MPS.4,OBYF_?=T2V,!QO`G971(KN1-)OJ2)2AOJ2GM*8)%MQ($DYUG.^$>#:O M3!>J3)Z5R54JX83'VMG?47*.O$TRI_5R710B6.,8G.L1IN3DWR3E21A`8^;K M.R;-],)8S_K(RH>G39/3-+IE,[.:+EP(TUJM3TG&QYRGV8MHG6'9T+N`S;%J M\2.@>3[%H2I0VI@3FIWDSC)9),L(CHN)>FTWO$I>/]^G0`^=MHO*LKJ;(8R1 MA9]#7,0E3YI:^9XL*-6<\#GXS=B*LJ:3,34#80$T<]R!4)HS2DZ?=6#FI;G(Z3%[*C">ZS"LP M8;BN/N`<*[6NJG:VH"T616ZK6:<G+P24F]29,XK^);RX0A"'X;0<3OY%8&U(8BJ_S*Q#FZ M7N*1<@&Q;'0DQ%,/3%4R0);=I1UMX=GT%W`:?]DTG$R0K7F!QD[>F]\S`BAQ M_/X'L3@\L$W?1LM4+@Y2XN64#&->`II>)=8]N\@8M^OJB)\+!K0]2226^G66 ME,Q;2"3`.TRD98(':$Z=2\X0P!T/,O85\//#>Q$PCFQ>V7(S^>K%W_B6M'@' M$(@MH8&%>,FA\ZTVP88B<9H-->CX[6E5?:IU5!%XPK0BM1<`))/"76$=K\'@ M8IVQ(=TEE5!/!ZR^F==O2=0I//'=_"YBAQ(;Z>];%VD,%2T6J]@[OM94:%O? M,X!(XU#KR1V8+MB-"BT[S.E$NJ,S'HC3FCJ0,=@NCM#S4-"-=H,W<9GN"$Q( M!#NHTR5X"'9'X95$E_O^E<0*)=.6@5`W7THFA63+Y&E]4?4>BAE)7LX_UX8* MOU_RM4QP[B;:;E,^M.G/[XM<+'+R(EIW+Q]YJA:`(1_4L9WY/XGEV&UE>9D[ MK*&^74C70>E[8O\SED/'!NEORBS@NB10:WI1Y,/)371^F7[1S6 MK70KO)H&A[)G((ABCT?XMC_^`F<&[$CC/U?Y59X4^<_U!4\<>6)K3I/D\$A4 M4'H,KLST]AK'R#6NKE]C(>X=W<)J0X63W:"B<&1I4E''X7=CO.4N+[R6"VI? MQ+CS[UUB\T!I,X56%;ZD7I7G&[72_-F$O640`"5"`6:W_=YHQ5/B[XD@G1#D M;;F_N7ZS"(![P,]@$_C4/][(B<@P;%%LXHHOL79[E\?.XB_SW2YW$*9%`3;1 MFYG,AO::/!3_O8XQ`OBF.=C3Y'O5FOM`P!A3KMD_T%_PN,/4[UL]'D`UCL]^ MP),W:9@5NE'G'=D3EQ6.H8&4`#1=BP@#5-]5G<]A3HHBX3JIU4MYL0T-YWQ4 M;;'4W(9O:]7I3:SRTBLGGU9G'R*@E'&:D17A3DTU9UBT9,U6)O0T4+TOUV4: MST)&?8I474E^L(J76;J32%U:01$J&GGSYIX3@'%1@PH.>\NRP.EK:S!+GE9! MIF.YG!FRC3ZDTKZU@PJJNM>J4=>U32^8QH-MGNY%_KKFM%_:I#L,"Y85U9PJ MV3NV'8']]ZE;;HK4B:Q_RZE57H,&EZ3`C'TK'9G4.^IN%JCP8K,AKTKJ0-E$ M)A^@IW.JQ"F(CQ3_*%J?KZLM;F$XB&A."[L'[D@N;[:7HW6XJM>][PF2V?C2PUI)WT86UJ@-6I(K=I6_^7='8>N:UQE`LF*N/BY MYI*J^VA\HATN"150M0[SHET%YPC@@&5DB/J5%SBK5NCPH>*D)]$U6 M-3-;*VL:VRYPZ7_*RY0O%]1!@45-KD.+N!EE-#\*7TP[?BL)C#&'J1TP-@:Z MS`ZHH]MO98$QP\%H6J!.DJ`Y4UPI'Q3+D@B[BF>W02@:.6$QH':V+\@I*00"5,8W_"@'( M>%2TV-BVPJ'3!471@2*_EWFVWG%39DY#CC"0/&G3GC1?+7[H!#"XM24OV(OS M[JRNA`C*A"DC^B`87-LD@J9MM*LK2Z*C=R;LZRH/U>3&8Z`ZOU8%R]=Q>UO1EL9.T.5^9\ MWL".P)Z!L^U[$6OK9-5YKIYX6C7I5\)OVT:KW]U(I+6.V9$\WN3K;#N;5";[ M[.#*68*@/:-V15G3:YL94>3T`KQ+12:4EL;1)[>-191_>![94E4<<[:.I7G0 MS`5"V@Q]:(`LN#NX`79^UDVNX6<99='_.*^VY;9M($HRC"6/-!+E6G55-YDD M=6>DF3@A"!(D.WEMOZ!O[O]_1W>QNR"6H'+I2^N(!`CLGCV7BE*GB@K-R.[1 M6^[MA;9P9SJ7__K31?9UX;$+OSVQ2-ED+J^;*N@,FRK4BP%,%1#G-TU5A\$, M%[3-3"6@B1;'TP0G''6;83,N3[J-63):Y!^/UTBVP+DAXEM>?Y?38_750U'M M,(P@R37BYQ>,A3=G3D55R2*X5,8K_EPB=FT7?_D'1P=&:)S9*]:9\79S@0G_Z"XU*Y.188HQI=YZ\LW'4:6&I1_?T7DGLLB=/(4-^ M,BABS4`C4P"R%SM_BD#*B)JFCFL4.=&`M$JW0ZDKF0R;M/T.8VN1DQE6Q:0> M2W&:9/Q_(C=COUJ%Y'L,_J61DTJH^BQ6PEPM`NW8*2>N3HUVDF'5&[O@YA[O M&:110U@R=B&F5*QB3=]'2'KA;$KOE`=^15I6[1^H*LT5X'*MZS&I@/J,!FBT M?@956C_A-4XD/]8ND[@_MA1;;RG0`K(Z1L)/6A3;N+ST;5JYIJPJ9FUP MD*8/4"R7L%E*)=$R81)5ND52/>^+(&K# M0LX!8W*@9*+]"GD&*3C^RREB)48CYC(VIJ59/"+Q>[:#&02-+VP\/2OM<3S- MN?0\)6GB@C]Y1TA>+U967(UYA*SF,;0]OQ#H7>;X[$1V.K9L6%8$) M_2#XG_!6 ML0E[;=<5@(3F8(P/,5ES$W^[^+V\K7)ZOY]<<79+,<[6RB#Q[89TP#[^];<_ MX:!LQ-/[Y^B;TK^GW[Y\]'>VI/8R?S6[[::7[X5=E4\-N\9!U._*]]:G= M8"BH\N!&%`U-*QA^"`\]-?AE`VTBR96&7UH$NQ+<,X+[#)?C5?'(MO^>9Z#L M^CD[OYS?\(%@:^]8]?;6I(1_=?NQ53L3.]#6A5Q[7<#8E140.?\`M'TLC].; MU5:?P-2-H`)BRRL"2QV9"SJ%%6@7F]FIE.I8?_9/G]FH#@3;3D*0B8[OLJ`M(Q[$F(=_Q8=**`6_+V)8!M.2,#\E0IB M9#C?01O+XQMQ;/4[3C9C(I0Y"67V1_AEZQ4R2@#\ITS$I#8BK$>$T"D[('+H MZ0V=N3-UU!#+K.0;^E!=2#K<^?["28*"A%6&957L-ZCBGG4%9&3F>N6:[X@J MEP?D!0T#W0,N>[M=[4[BIG'NJ7I#G=BEW8F<6*ND;VI?2>U+YH>J4%VFB!!& MJ!L;S9?RSP79$GM(SEQ>?`S$V-JO$:-CJ/D?F12=D*+'-+67_@Q(&WNK@*;Y MSU')>&P8[>7/.^#OA4W?'HH5@]ZS)?[70XT>'^&D$79H\T;0,XQ=+)T/5)Q@ M`1+[&GL!I2_7#,$0E]==X=87J'<6W3R?SH]0*H26`,A<:_2=;',:I\/2BYH5 MJBW=MFMLHDKH?#!W:1%P+C#PP^M-ROQVK!/Q%8XUT#$P/69.KX_3F+\E*82^ MX6?MV"I*9;\3V($]9#LHWQ*[G'"M@"$9.T\A?A],-,T7,L^##7/V"+9%WF#/ MFLEZ=/-G.@RNJ_E0F,\A+S/,^!$:Z`@1'X=W'VT)@_6\`HIO(V@H-"F5N M9'-TF]*G``U0P"B*/O3?[^S,[')7E.(ZL$.1^S$?O_G- M;YX__DWN?__/ZD\OJ^>7<2_W+_]8U4]2[6OX[XA/O=IWP_C4#C5\_;*J][_# MWY??S#__757K^,?#RS]7$E;4-:R`M[^L3M7#LGCW_6WZ6'Q_.*H.OE1WF3C\ M_>4OJ[;53R/\H94U[8+CJG3QRIST_")KLD/IIN-5N""*$[Q*]6:3LE>]$C=P MQP"OGE1U*VY>I0)^]W#%4ULMEK_64MW=X-5=PYNJ;\Q!?WY9_7O5=(,YK=;2 MNMP,^UX^C0.<(,'S%EX,M>[WOWU9/7_^(O>__&OU5XK7<#5>K7R2'*]3M1'Q M,H\61925AZ.6)AA@5'NHJRPW+XRE8Y60BGZG5V4!UE*E\>5&^LRNC_ MD(B4WZS=HIW[5A[(67*ATW[`UCM"=$,O-=UU*PZ*KP(D\Z,XZ.D8U33>,:<* MXF6"149$M$;7.BC)(K,+3*CXT:(,;8,:]M/6);`P?YR*JSB8?Z?-B)$C>3Q@ M7E5KWOOYH^*G_$EUENCM!8N*K$A2^X,\:0;&[O5XF=A`N>>X_JC[7D)Z]5EF MY[`]2[8/E]]]_FK]+R@W1A%]VU?O/CQ]^#FBG2G*N3]]JCU/`+&/\A4P0N,VM MNO4NX::`VWIK=I%1Z'3("@81C4&$V=_ZK/1H*<:`6R*XF::X8-O615+DI8&F MP9H%/ZTE8#DJ-Y>:O0#AH`)#'KREDZ"BQ#+;"<>*YJQLG5LDO;Z/3$.C2XO# MD4N0ZJD9[=GBQIDGS\V;6H:J8O&&;-/C>1XDYP%1*CN'H+<8E)I\BP[$:F;D'_(`LJ9F M@J:3*#_$3CF1A82O2).Z#>KX=6S)QT#;V;I$O['&8\MJ`!$XDRIK8C`(\@Z) MDK--AW$:(6/>E,;W1?J;",9 MDA\0.TM^(:OI4^K>K>G)=#C^5AZD;774XWPM>+R0-E\!:L\42^2*4GBQ\879 MXL9'U>$:7Q?TO(&,H["-LZXW*L\:S]I3]4-6X!:+3OBH*Z.GZ#[X$9EWTL*+ MH9+P-?"89PQA7[(J5D<4_>B@!G-!5\4')FE^,UB?^UD!3!T?5_:3ZXU?U;PT MLP:I>1!L>S=?2NZ>LJD#<72JHL*!?HFZ-1?Q@OD+4%)NHO+.@)MKA5LAU1OJ M@H9%;AIGMF9&VR2E[FN/8\7&V405V)'I;[`2HJ68BHZHK&M]H5.)/$ZHT2H9 MZ!GD,;LU(B8%RY`A*',]Z8U8U;=!LQ++(\MA.0B,DQ4N: M29E-*.G"J7,QRS!T9=VZ'O/:,2-@H-Q,B70)YU1%+JR$`+/+>548PSPM%@81B00))&&W8$ MH+U)BH2KI&W#*F%[L-ERLQ0W4Y%`/@!^+G#FHABL,R\,> MF1720)IQ[I>)&46=WO@`V7EDG=U[AE'0/SZ2+O3!J:5'8)^2@U67)3]UU8;" M_-E]HN3JWB\&W^ZCB2O6[#`K+!5Z-M^*@ MJ7V9.=VCHET$'3?%Z5.W-AJ4HT5;\>UZ&*>!SM??4#X`/Y- ML.K]$9_4`%8W^Q9DB.KV+U\FE-EMG_CW)5MXX!A\.ZK/#X$@]#>\I1%`CE>( M>$)/HRZZ!D`^<\XG:C?7=+.34V'XB&+7Z7-D5NMREZ6@WG@N/.I&M79J-=T* M>8O%2WQOU:K3-BA41VI/3I/X#3X5Y5UQ;W_QC&)WN)$BR7-B-"G[+N@@V]B* MGR'L'A"0[$#=TZF'+#1@".GV;'2B3OZ3MQ#.JP(#IQY!6DN%76)LZ\M, MW251^6A-[\"@*&;*;CIM`^PW3FT]3M*UNQ!D3%).5,[36F6C:WH!C@0"?-M% M)0H'_G#+G;-F'JBV02WVHU4J\?;F+LI^K6["$AR"6IWP]/3\0`N!'T@/R!9J M$M=JI3QDQ3\BH.WL6GVW+-X]W*;;`@_H)'MZNGLEPK`I6Y(G)P,LO6\I(4-K%V3KW/JU2/(B6JZ!UR>5G"P6:40U MSWBK!Z>"N##'J8>8ITU6"GMARPAHN];K!H2"HQY:Z1H`OE\2D]OS[Y/RCN2T M'5PP\M/0Q_',K=Z26(V0$.0H)VI@XERROAX:=[B3<+D[#Z*E>-!0H81<%%-, M_D#$X2D6#B"_,ZCUB(;2H=872G0[#4O?&1E&>9<=)S*8\2#&)M6WZ2+(=C-8 M(!"DFW.IGXO05>:]7`@F`S4,>R>4,`]I$L<"4:;&V'S$K[/&-'VKFH7`52`TH*_1.6W14393'!0FE]KP!M9=9HZ#^-]55 M)Z](K+#"_)X[K[7.]SZST#.V-N.%JJ8O65*LX34/QY M%`.[F**TO!>OJ2K[NO.+N_[NR![;:I%8Y0[WY)6@/B,72 MS)O<@ULYB1PL.5C&,Q(2JYQ&T:`?--J6QM&"1E=0ZAL4.(EZ[2P:Q>4A8AFRK[I`TK-[+-]:"Z,R*J(D M3;>6;5-MJ:&'PA#GAX3I9T`6-;I49L<&5`"67YG+8` M&:2<^Z"B"3$X)`T^73(FAJ#&+3%A=,+B>_R'(2KLI!&=?V\]ZTYEBUOO^8JNJ6 M8J!F$3Y5:\NRC1D]A:W0MF_L<.IIRM?W(APIE24[?^KDRISUQPL=1`:5."DI MIRM)2.:%D];$1)*PF90TN+&,,_DTYH]#>P5P)JTT:*%JQO>`/F_@>Q<[R5!Z M3!MMK.P9_U=XE22W#0-!;I*H4$50CE2VL\HW5:IL$P0W/2`_\"UOR/^O&C9B!K--Y1*OKM4#>6ZERB@5.?_9>43EK0'&.Y"UL1 M_*%"11IY4/$6]`UG@/4_#M%S@%FB'&J'7?V3B->-NH\<&8PU$B4BQ@DQI;`* MGRPD[>49/^D6CMI=C!Y>8%R\_15'U8=X%F'S\!R#[N@()QIHX=V6\DC)HP/- MPKY?\/LA.5L1M!T_=Q&Q40>)M!U:^8[N@K*/@N$LUT"GI'#!BIZ5&48KXG@X MVV5%12MF9^26_`JNVL%6%4>X*.PBVI/"2:>G\8-X&LY0JU!J8R-"5N7.`CO. M#5H'$0?9.7!699_439%/XG$`;(@\Z31%^_TBL98"-Z&WNIZK.HZL>I077UZI MPT(`,$XK_;O;826F M#XM`EV=_ELS&FK&0B\A6;+U==IJQ#%X"NY[-LIG+P-LAL2P8!_5(RYDY4M;W MM6,FK1LVD?#$:LAB^SJU)+5X]/V*5:\MU5%7PX`2_+]4$`92V(*H-7@GR:Q2 M27-Z+,!P(PN&UH07]_64L'$8PZFP)S%WW6#\?BR=9MGN9/:C=<`(5P7":SJ& M'JXEO#HDWAQFQS@TX!*,D!E6KL_YF6&)7O'#NCJ"IM=AFKH&72OMIHUKM[N' M[T\1@8T@$6YMEU"'1WI1CTSU+J+N/K=CHEH."=/+=B#@T0JZ$R3J7#7O^DCJ M_Q+5#B-E\JEKK?[1A;#$A2F+C_<13L5)?Z6:08NA2]*C:NX^GW9E/.W$$4I- M_,=G;69],2/+C7U!0/#'2&%4D"4.';K)4)QN\[H9SC?4-NV"B7F- MD!HCV]B^[<@,KMPC)%)\OV_G!8>PNML84F>SR],,,F8(`GBAZ7;I9DT87XX^ MVS8GCWB*:EWLD93!&T[-%.F>^P,F3M.2Y%;PM/1#S=W<5O)&&XGLO9<^!-Y^ MJ(^\-R]_E$7@V99&7%+D]XXA+,U[E033E(/MZ]M-"-M>QEDSH-$=50=VVM.Z M8?MSA8L6#V:`@Q:/U-G`WV^?_@&(H'GM"@T*96YD7!E+U!A9V4O4&%R96YT(#,Y M,B`P(%(O4F]T871E(#`O365D:6%";WA;,"`P(#8Q,B`W.3)=+T-R;W!";WA; M,"`P(#8Q,B`W.3)=+U)EI9]W99Z6-@[T"DI?D_GP-$@3M6U&]*7T*T``%C*+H0_[] M#&=(+E?*&88/*_X8#K^9[YOAYY_^J0Z__G_]_7G]^3P>U.'\[W5]4OI0P[\C M?O7ZT`WCJ1UJF/VRK@^_PO_S+_;/;^MJE["7\W_6"E;4-:R`T1_6E^HQ6_#E M!_GQY:A[&*^6Y0(^!_@\J6K/E]F'P\N_SG^'0U5-I^JA'6DW'(NFX(R*)2F: MU]UI',>!%N#QEPI.\.;?O-0G7?'E(N/NF&J5_UPK?;?$8[I&6YN:]E?;G37Z MXWG]OW73#7:F-LI?N1D.O3J-0S^."F[>PL!0F_[PRY?UY[]]48JD'&`>(MVV[OAG"\Z#?+2C36?W?W9]HT^!\/"AE*#;?9ED!*,&^TUCEJ>0)8F3& M/CHX#D):(EZJQ\/&61!8R21+,_8)K*!-:U1N.9ILN]Y=8Y=*#*16M4^11(I- M+C<"(F:4W3=4UN]"T%;31(=5N+D?XB#N4RG8$D$T9HS2H_J&YSQ)4L&R!X2X M;^)]UCE[FW:(]SR+1!+\'GE1;"0K<:U1760A@N92B4T)_FM*[,A_HV:K6P>D MM39JBMD$8X5^'LT`:8-3(5:K5$B6;[)46'0=?=S%'7^Z$.!1N:,P&1ZL-TS> M8U8UM8JSZMU5=AS#O#NY.G*(3T%Y._B0/23NK@CBT5CB4&@AA/8[CBE..D3< MK[".KA"&(;^B]2]'2V;!;4;A*/QRYU&:[/F^$`]X?ZV-3[''*Y(`-UJ+B8-C M;TW+1R1(0=(I*>S7]@J@\U^!BEN>8#KHOHO!=(F28*9T=9RVLKB[ MRVC/4,>I/`RO<&V1WI&>-M=<`ZHA"1IOFXLRSIL@&7<](PLH=(\&P5QUO^1!MG+GN.=>-MPQ&6_40V]).^W%/ MW<26.`-;@F[M+AP=+S@A`N4HWE0(QR\UAI#GQ7Z5<:F800ECW._H$-\ M]K,=+UWN3QZ#2*3>*]B26!?F5(31E+26?G`LFC69^9[Y-\'O.N3_8,)T]6>W?): ML>S45-*2!&X:5R``P*:BTYM(KI)5J'T\9\]\-X679WSOL%5&FX"MMP*054N6 MR&2*%H.ATB>`I(A.9\UD#F:^*I61"@H.$V5(P70*_HJ6V*Q@/@M\I;E820B^ M%/X@)J]+Q=&TQDIC3=R.`_"FSZXCALNENY_&# M7^2FQ>S4!6"LAU#0AS&*F?6+`$&-K6?"B"!T&)$D*9S>Y(\<#T8K)>>`O_.5BSTK4M39DP9Y)F98YO\)W;&)/"=KO9/I^ MCEG3O@::ZZU0[:]\MRT7NE-8LM*0J>YXR07Y1K'W_:)#65$M=#?JK&6$6[?- M'.Y+7%:NNR8G3*@R=GLSUA'967DW+%@GBBRS\7&Z`X&G\J*#3H26 M#F/4=[<==L#9F":&^*E<\CG$1M]6!>D'EK45;+604I7@-340H%E$QV)/:.)GV[U-*OJE\V!48^M&5 M=]?J2UYZ0[S,BXTO'1'V7/"@ZO=4'H@?O2NZD_;T=1]Q@[A&&RF17+==^*^R MD%/!WZ;TSNWCATKUCNJRMQBUT"GY$M@T531!,WP%]=1Y-O3A[91*5I3)=:L- M7:^.6FU\>:`2XTOT$;N!5OL&^,_//$\7Q75!MPM"C.(.#+E1YO;DT'9%M]N5D MV;]A#$C"?NHN)(UM^63LZB7G>R[TPF@]*?(NVA\5*O/":N&S$R M]DHWYO;!LXG2D&*98AS%_56T;*BT.\C&2_,I7?D[:DQZW[MQ@>];U7IA^4J! MF5JX>6MU"UO4[!63"G[BHKCI=_K:N-?MJ.++_86_E6]%\?;;J=N9W=!<-P'= MIDQB$J%0J7:F:*#FPFOJ2E!>@=>60O7AB%^J&4X=G-/J4],=SE]@USMHZX-& MLY))EF;L4TI)[[I"7V#\^4[PE=:S]Q]/]9N\<79Q.([Y<@L%K,W&Q;G MFPT7^PX-;;.=7^/<5=Z*. MHJ,T+=Q$]&S#!E-W"JZ0N>D M<:0E0^_(9(8FL"6R>%=P"E7<',ZD)ULM_`6,I805NG"-'\_KWP<`F_&EU@H- M"F5N9'-T?[<2_W]_]85D>I]A7\N<.O7NV[83RV0P6[7Y?5_E?X>_^+_N?;LBR" M+Y5L#_?_7$HX5%5P"#9^6CZ4K]_R]8UX=[A3/2R7'_@EM]\WPGY=YA?ZK@^R"[9$&(,E5WA+L#'4"U'LH+?FF? MOSI41U7RRXM(RQ[TTB(&A=7J$L5TC=)O*KI?;C;ZT3_=+_^];+I![U2UM*YH MAGTOC^,`RDKP2`L+0U7W^U^^+C__Y:O<__2OY<_D1]D]Z,]?P=\/%\5K+1W,EDH;U:)*+'C/F_;UMB\UL^-@Q+;)$ M*U@KZ0?:4_":%1P>42/ET((^CWV9L)SDR7&FEGA<>V4]HA5G@H41^Q`*EN3Z M0:U_`4=#04KVC?3S1H=W4/V0$AT=LM/1=:<;)<@)W;LW37+5DP[K,,EN"A"P6UP M!H`C>EI*$T@):(M7#5P"HA06!ZCGK3H=(&=-:%M'#TGFQ"AGI@3 MBXQ?LR0-\&6TVIA*(8(@GD2G)I#@+M"00<26L#:TTCJ[]O2^?7SAO&+F-G;E#8 MSV-4((^2UDEFU2J`D@1N&*.3`Q87VF7PP_C$$BK6"\/H-MK3-D8;`4N"*&GD MT-C(0JI0:(&UH1">UOTBR=%0I9K3\)1`U;HEJ+%2^)Q?8J\PS$J]@:8N+97R M+H`!H4`)5>U72!MDHZ"CL!*%-O97N@/S8J"A+47(;XG*URD3I'W=^J$KV'N` M`#ZDQL;J@?&IO(%LJT&VQT%>UKZK>]Y5&!C:5/J%99K17 MFEGD*,JS\T]&>4?MH^S]1;1V>`3WP=15<%X80WU_E$%Z%?#7*7FTJ>9A,Y'T MO._\8GL,.-'6W5D<%#6[ML4>I2_31;PY#]TLO+-+1!/GX/'\6G>]_V)K.FX# MS'JT`71]'E)>QI+LQM;,-1##!I):,S\QQ^@JR&Z![#]0FPHI-)$"5G%=2Q@6 M;K=N?*F/0^,-+KHA55VOSS;U<)0=3"YG M@\O)N*+!`?\U=7_L<5YY'W^:>/-[]_4@I45-GT M>E)]@92A/0Z#+^7$2C>\W(VV>KI^^H>B"&:3!Q'+-76')[UO&*7<]O2ZV39L M8#\6Q$:S@8R>5U7.V)8(^']!Z.1C=Q\IAM#)$^KS(U7US;"&) MH--Y7N3T!?D2I+Y``D'527``^;8L]W^XW3X"6&/PMQ<8C#TNJ-,]U^`.DNBW M!NP+M"#`3EK,7/%_8+5N^F,#*%+CV"A;1M`P#4\*\'ZP;@<-[_!A;7TN)Z M:,U^K>245T7PNWE_<"T^7ET(VR*T_?A$0^#*_EF'3=WL?.KCZ\LXVG[/+[>Q M."6<:ACWJJU,ON5?RMNG$ZX]]@.D0K-770NP'JKF&25R/';Z`LRFLNLHV`Q13/$8)).=<9)^@['3+:] M;VJ^NC)R:]&A)3C8K<)M63C-FDN:&D>_Z:])E@A7W/;9RF!(/W!7=U#*#8JG M1'_0,P?+3/,."`P.^E^1I;%(,TX]=-CB,"NW>E+H*1*8$\,\)_`G MA(_;/5!/(&@3"_*,W,DA2Q#";3,;YJYQ$FN]R#R4CE,^X%,\2X*)$[3Q6;)S MPC?IG8$Z2:SBDCH+"GT9M*U=/4NV"HFE324_>A3`1. M0'0]/NBYQ?ZZ,NENU0<_I@&W4PW`)'.&!6D,&8\32ZH')B*WJO?GT;-FI566 MMD=I`/"))LO>IY]L+5Z)5Q?<@KVW][M^7O4GK!@DJ;-*?`8B-45IH9-$L"0/ M+(&/%`TB_QF7P_#IBDKNZH00?+?0L5!3`;&V5SA<\,ZA\A:AYL:'1K M9U84J_1B;H8\IX'R^!GO5F10>'8M*?,I?F7V\K;D)O?&;@W8^4U;8AT"("7D8Q*KL4+U6**3<:C M4#!BGKZ6LT+.(0?-#8X$<&FUY7E@[C1#/4."C32<3@Y$'3;_`?6A5LSU!@0Y M##,+IGJ?)YZ_,&>7!%3FX"\,;-K_SAA:E$"K#G!,6,+*9$LC"T@*!+9K$)4X MPB7F MK^#KUV6U_P7^/?^,__UG61;A-X?G7Y<25E05K("W7Y9EA._J^C3`'_-*K.<+ M6EFWWNN7,G[,/A^.JH%7IZ&Z?:ML.C M%!L`'^E<<*@,0CI7*CJAYP7DZTLY$W,PW,':\N90G50IYK-8F+/*V\5K)=7= MG,YJ:^4=4&X2-/K#\_)?R[KM\4NEI\[>1KZ;A@DI*F!%WVEN_W/7Y=/ M?_DJ]U_^N?PK)[>_FMQ&GJ1);IFDA4CO\4#529NBATT0@J=X17=YD*T*<4_G M#E7OH=B!B'($(61![JISZR`@N(PI6D[2G=L0+J(XR._(,'114T,---M-<@86 M95Y;M(VYO[?]*TT#[+4<3'&_./Y03>-31RM]-D'JB+<9M46M._\3\@6F;M!> MZLH'(([M1_P`Y[9\[G&PQ*,:7A6$OUMCWS?K3V+Q'1VKE&\\_9S-UL5[MF/Z M]^C6N`R8S!K2&G,[8%(6%#Y@V]9A8QH1EB:A@(W:PI8?;@%C`6.Q[AN[*\FX M1IAC%\Z.4B2Q!97+Z^##)K+N#FE$W7 M3-HB2ZR5[_]D7>F-*SLN>0.9-ZOWV;AF8X[,BUC8M`$E,SDEV<;:198@N&FI M]9CL`#`>1/&8;XJBXA"";Z,L($!R3C%6U7.:0H+DZB84[U>&GHDAPJ1W(` M5<*G;-K?@B9^AD2-S57C]V;22Z;4V'+MX,]GZKNNF^3#@H<<1Z[;1!F&RBG: M1#8?V.N4WC@54;&UV;!5%1RQ(PK*$0XA"](;"S+I=_&6H^LXNFZX,O5%^,`< MT+<>]6%4XH6[`D_ MOO^#\H\1(1^N'>U=/Q=.H[55?>['"\L!@FP-@B`,*:L5_XY,_]2=ZQ\S5"9U M=57%YUR((O2H!G!N`/WH*"&S&]=![#5BM,H2,!S`J,"QRIA1Q&?@F#O,\A/] MF@),(:UP0YBDJ4[]7RI^!4J_57R_[N,T@^RVDB8:G<@3[;6 MVO/)55\,+=W9%D@?;\1\N\C.1IE42N^5#8,)8C=+CG3L4%T$CF`\)PM<9U#A MIT`E1HRT/A58,1+Z[&P@,P43L"MSL"&VFH^3;:VM;NO/FR4()Z[)NO%S_D<3 MO+$T-#Y'1-.=G=:^=#_]^,32L>MA=UN-^;H+C.1J)X0W>\L*21_$V#,() MESKSX61$\_^;T\ M\$N)5]2)-:5]H7K6DK*2?J>@@"&J1HIN:N\8GT`!C"BD M?&+@/$,!J66N;82.)XG77>PT#$##3=6^97]_MF$>E7WE)]3>1]!I:6XAY2+* M.7)C1Y.%&UHE5?OV^%R%'+N2\HKJL2'J\WLL@X_"\Q438Z^KWPJ&/@%N<6FU M/])3C;OKO>KEJ8<3OUK>R=(H#V_M'0+=)&9M[3$A=3[#IU7#R#Y()9"?L4DV MS/6\7SM*^##2#\K@9*20;_`JYCXX<O[]Y-XNW:XZ]J:W42.-X$1,B7'NEM_/MNS>U M!NI.U!J$.IX.T4PLWF7O9N),5%0TXKF(L/`/819`#()@66M;#5-;4!CFQ?>? M@_P3W.O((V6=)/EO%1ZD,B\"$A9HJQK\)N=W_24>5SL&M(]'[3Z_(.!I5M5E MRG3:^:W#R$VLDD?07,.I;-L+R'/U6/4Y)#Z8)AV)Q3A_I:-L!+77I\YOZW4[ M80O#`I6\T$G]A&EW(@I7*1-#:Y/N15M?*Q>H_L^&BD&\&E$W3*N$N$;(N:(S M+4K'0,ZJ]="`G4#I2T^G*"F3=7VA[!$A2)K'J;8;<15:J8!AZ8E5,FF%RIU3G0&NNW$_.DV8LIRG[I71] MENU^FFVH6%=;]D-.7KG$CAU?V>$O3;>_EK/UPQN=;CBT%=DJS<.10S/BT%$_ MN_9&L/:J];41BILR]^3M*?,)=!?-@QV+N^[*W'S!'C%GZS*ZH:C$R,DI7RF; MP1<]P/SV$%!QJ]W3*-,F/!TY;;K*IRJ>7MH69TX>+"4K,^.`-<\`8$=M= M4VWQ[\%LP(#`@-C$R(#%LP(#`@-C$R(#'0O26UA9V5"72]%>'1'4W1A=&4\ M/"]'4S$@-#;M4X`&*+`(@C[T[_=^DD/)VA9! M%M9P9N[,O>><>^;USW\WAY_^O?SC>?EZ[@_FFJY_ MJ;L"OGY9%H>?X/_SC_C/?Y;YSO_-\?ROI8$910$S8/3[9=GAM[=\'][-GP_? MQ<=3"1_SV3R\>XZRQ?$?Y[]".%-PO+*K>UX'`6F387?/#VCWLGGI^[[C613] M+9^%=[!OBQO?'XN7,@_O(``,=3BTB#X7IES=4:RF*G'CDM?G6_A4X[X_G)>_ M+*NFPX^%-7KIJCNTYJ7OVKXW?DWSIAI;J:L M-B]&4@;7^*V',?%(T2R8A=%#]C`+=[_"0[R>.]GD8$R)]ZSIE$V8)>G:QRFV M+ISL.EG*LVU(":I[7-@["O%8+VZ*5J9>;]48)Q9O-0_25>AG MM%G?.I'RA?>!"S2$H%G&6/<\G'P*$O/GVJE'<1%>=O+WP7PN46U=.@!PHKIE MU="UFR8.ZUS\"DDY0:PKW*UP>A!E<1KN.+SLF>^#+/7N/M)@5?'@6_ZT]?SC MR1+(X2^$G-4,\_:M)BR-D]461TZV`9A`HG@+;[WSH@`B(H#AA"]]'A\+.@CL M;&CG^?Q3%,,\J.O:6Z]@9LTS/9BYAI\]3LN.",(=DSI>L)_Z&:M&U]"WX7$#X-4^5BG.'#F'Y-Y"+P?4EN46FFUC2G M*N1WLKZH0]6TDSH\TWK;WR2'1_0Q_01I4(+,6]/-;*F02AVVE:T+-@5F:UR$ M!/=^/$\RWJ75&Q`L*WM]E&">T-2R:-RCC+@T_T=,AX:VT*GA/-R'E#C00EW. M`.N::JC#>!2/LVPN8S43]7"!WMQ*L%-7ZRYVBUNX[HEICWRNQUMUC MCA<3.;BHG5LEOJL=DN5G'NPK57.EP"U%XVYQF9YI%9K_585V3',@96FM'7D/ MO1%RA(2-@,11DF(>3J418B.K2_X.=T$&UW6CLO,('W$Q;H(S6"S*DM=FL!^* M@/Z&C9.(-IACQ$Q2IGODI2EI-IMZ(?NX7'7MIK`:H/Y:ZQ3G;)B&@FD6W=VLN<> M(=RT?*3[\$CYKO.4_LA9KK$J]42TWO(7!YKM1(M>1Y'V./68P8;55\67KM"Z[;)ML^BUC!WL_4B;AE=XQ#C+=_JGA[9FR%$`H7UJ9`-URRC[[%: MGQU\#[,PW:/(Z1:2[=JM:LO+%S`?&JJ[L)4;^<-AQ)P M]M=.#@H1`LB@F3\R_L%-H=,9*^4#,OPAO32KJEI7)#YRE]'>=1H^H[1S^_HX M0W<51K.7=VR5D$TM%>1!.#4ZR),@+T[6F-J*+KP>RCHFS(`RK=9>ENQPJ))5 MM_([R,50+9$*8@UP6X#<,N8R[\C+UMJN*[Z%::HKRS2?W+-7C?R#)$M6]K4K MQ;E`R[#>Q=_H,@[/WUS-;*7ZK)(4661T0/=$-2$O1D%JU@\K)0F#70:U!UFR",>(L-_-D\/6!I4 M4S5;7S-U.$F>)IVCC^QZ)9'BJ>2G[^T7\T<5).!^E,RA[?I:BH`XQBL'3O:B M9D/%,+6CEDG_*VWY'OHR+8_#C#[?%Y]UTZ?GV(I^UUA8!"2GT%56,9)Q4=?6D3>P%T8S4>VXE M^C:Q7-+'X`.[S^):GD/,`;\3._=MY"07=9"1K(22'ZD'CQDZ%)OP267DQ4?: M#7?$;\7A1']9>*^"];8@:N"=SU\N(-4Q:>A11;=*_!#+E.>=7PZAYE9OP*WE5"QA,%H%G'GVO2!*5 M/N;^*>>+]7@[IV/W3L=6@`2Z)&(5$T8I='-ZC7CQ$,;-8Q2#JXRR*SRC&`.< MYY#U:&G.#+/R]GVS-'FH@`*YO:O>,#'N*6AGW MD@X?,(=5<165[?/E)86,7V>,K;M^9(QPHA7P=X>3M6,[\Z:4J85/[I0IY?AI MUM]BG$O'*>W:8J*-`^VP+8AQ[]O"),EK5]GTS!DTU$KUW5DM9'V>#U_V$ ME'_">TIKL6[Z<@PV(VB<3&7%<=NBDCBD&L^;S6R#Q\)=;C>_DV;7'UVB8Y+% M4/$//PNRQ%?SH,ZZE3+P*+Y9@C3SL!"I^'G1"WF0%H`N5X&+K3^C\F3 M=]PY)D%4J5[/`F=\&JJ)(GZ6-](>S"[M\D7NI>_[XSEVL89E(Z0.5]P.H*YQ MTNBM=_"2$JC)80/'^@2#_FM&Z<<6LS4TA#Y77B.8HP20N7M6?/OQ?F#`+M;1 M=9SI8`!K!O>&$(^GR1K]&=CJ#[,9"][D#9+,O%6PFF:JU(:3O[R2&!7VRIBB M(.&G\MIKB%'YX.U"1+4KN"0K%15:QFW!DYA$ M\9$\$-L0KE1;X;O6#NQP3IZL(SYZ5[E!N#%-[5PH;XE^((GO&"XJJW_M3#DE M@XST+CNQL=>>E]Y_C:+YGR`N';$UKF:X%MM!4#`MNFE=378I(8PQESITS952 M*_HV>5_E^]#W@U5X=>>F*AWW\KJ@APW^LW\([\G+I)OW;CNJ5R-/#]+9XL8% MAN9X?06&H[['`$R$VZ$Q`4NHY#H009?8RYM6P)VOV<[\"&FD%5(S[ M\"B.+*7$F&:"K,U6'GAEH\<;+_*.O3+N'1I[[4P"GVU-?65-T+E&V`QQHZ:[ MX6QT?7.UW@>\DXJ31W&IG8NKF:J'>S5WLA@_-[2E`-^&7*C77!E+U!A9V4O M4&%R96YT(#,Y,R`P(%(O4F]T871E(#`O365D:6%";WA;,"`P(#8Q,B`W.3)= M+T-R;W!";WA;,"`P(#8Q,B`W.3)=+U)E'0O26UA9V5"72]%>'1'4W1A=&4\/"]'4S$@-#3(0`P$609"'_/U45U4?)+7(&KL>DGW4\=577WWZT]_4^9?_ M;/_XNOWT.I[5^?4?V^I%Z7,%_SWCKUZ?NV%\:8<*OG[95N=?X._KS^:?_V[+ MW=5-?'G]YU8-9L?KC]OR M]0`+R[O??;Y\_^'W^>[R]]>_@#FJ(GMTUVK:``;AB7![&<4)W=*9$P=:@(:] ME5=B!8?VYM#K2_6B2[&Z2H6]9[T!$_5NA==TC39G\@7E86\._>EU^^]MTPWF M2U4K&XQF./=@_]"/HX*8M/!BJ.K^_/.7[:<_?U'G'_^U_2M%QO$%S55\'065WD_CR7"MX_:(P^MF^./[:!K$C M4^"'P2?4\;1MR8CH_F=X9'.5>E7G1*91RMR1@V# M"IQ_*W^(8&7.AT"6)>;:;8;?48$[VVH(0/)62I$726H@8BX/3%ZF!@-T4&[K7]1C&8\ZWHT<<448EPY3@+2!<%O*OT5(-R; MPW5#X8DN)I+DF6Z[)1Z10&`C/K*+5PF>A#>YKP[!V[6BBRKG%W5C[ M'P@V8V?SGJ3[12AZ`J_;E";Y3L32PCK/\)!!54&<=4^!OLDNN,D`?D,_2WY5 MIA?=4;[V;M&)OQ473:XR/+LZY#>-_+8_,8E.4GM-&"M%SC4QUM8YP$B,WG': M"!]$N(Q\(!$XLZL#QM,]?3=70C$3'?U?:[3;!":)BZF!EP[J1W.A6.R=[*<$ MK0%VF'AS9X-^C'Z+A8R9B3@SW30S$*Y[!%/W]!O]AR<\<.Q[]NS^!V/@<]WW MM<="MB:&HK)$4&,][F.Q(,@J(,8'!,>]^4=@8=8,H5(F5Z9YX%L]Z'FEEI)H M]+E7@S6CXG;UW6V6W8I@?^M:IIR1[[/YQ-O+!M=.F8^Y-[84-EKZ14>)5*;8 M'@F!,I/$:/1(^%.ZK1>>S#)>NV1C8G-;@QD]ARFC)9"HU.*!-YWL!V$_,/[U M&/(>DYAK'H.A<8*Q,9Q3+:Q/:-<:J8]ZF-B!=RP1CL"^+[FUN"_-B' MFFZ!6_>=Q<-@73:U5VLBU+V,"F$?X6F]ET3D0]*4AW27)@%@X2A">@W/%UF M?/ILC:&H1:'YURPT,2/R76*T"9.NL:L-JJ><->=7["VW2Q;IX(@V%33">'^$!QH MI0,5Q28IPKOX*%\3ZYSEWUCWDS.E.+J>0,JO")1?R:K2ZKS$G$@\M,E.ZUS$ M,>[G6UP5^E3Q,Y$'K0'+;<.NNB#8;^6YL.L]B02"9@:`;IZ_!V*7*DSUP+`N M$0S-$);L_@0,L2'0-$%4R@_4Z.PY1BD9)+GOE/BQ?[_0=CS*J"[X1+`6K"J& M!>QI*U.96=.T[Q>62#9T1CBK47T;H6JV=W."D[5KGB$%2*-84%\&>`&$]*\GT7? MDK(BO;.P&QTPZL[>*E9`**+8"$)%R'XEQU*%-&,\G^QIZW9")8A5]':9P(03 M."Z"'X2NFP0:^:-[9Y#!/H!N]1"Z8[1)I)5BK6V1E9UYHMB7V:QMF!FH+5G_ M0Z\*1%^125>1U$?WD21BHN[J>F?%K=4W8R\6<0R83*'O=$FC&1F25"*@\Y(@"&D@Z-^BBO;N8=U2V;=]2)B+!BOPV/I!:G)]27N39\ MP=X+(76J(\:Y4A2.'!/[8Y.MJ4$WCK;N;(PSBC&1G65C22])A4W'/C[:WQ*A MTN-5>;0[X%6PW=Q5G9_Q5]WT+WHXUP"S'D+PQ;#XQP,I/C(^.KH$T]S7JK#C MO95B(XFK>3[*K`>)_?%(;&U;44MB9DE9%ILHQ6"X>4R#5)BN1*AL5#5KD5#( M#F#7Y#0$R\',=%#_&4T8U"+@\N`V2!Q4*:'K/-OER:B.E-NC\* MIY5OH-\QKGL"G>KJ+@024[^3$U=B-0&ILCJJ1*W;]4'<)V%32&MN%CEDL6O7 MM2438TE?UW.NH8&`16P(MH$#HERAKCC^.AP%[7Q$(4^M^41D8C^?=DP(<2[*H.OE M13`8)3/`./Y!5N`K/"_XV#CB!XWF)99V?38'HS2V2YL,*%)2'$UE.S1VX-%^ M7Z57<4)-V]/^`"4%?A%WFU4-=FTUP5+F9M6CD$F48DTWA,RH<.(B,ET-)U;N M<+IS/(+;X34K95!IK5?)QIDVV9AE'8>']:Q_H^G-.LE-+=,M1C-#\1SY)DR` M^<;\@WS?!36BK.Q\@\*,LGWL#S)`R8K''=0E^]&YI@.WFG?Q@XU"+*Q'3C(Y M3H/`]'YB\=+2P+'8V5=AKS9Y:I(;;CTUQ!&'IMTAR(8V^LTQX*EC1Z78)%9%R.8P,[*;CR:MW^;P#)(.IF"@T*96YD7!E+U!A9V4O4&%R96YT M(#,Y,R`P(%(O4F]T871E(#`O365D:6%";WA;,"`P(#8Q,B`W.3)=+T-R;W!" M;WA;,"`P(#8Q,B`W.3)=+U)E'0O26UA9V5"72]%>'1'4W1A=&4\/"]'4S$@-#V3P5:8$`P#'O8WQ\O(D7J@B9#BR`WDN?P.]\YYSOT\CT^GNP_U6Z_(,/.)&P"K@#E]77M_ M>UE!V$Z2(AE71J(2'2I25A6BX>5T@21I&7V\\XZ_W:'HU5_>[X(4E(ZRDJ,# MDJRTT9S.V;T9$2L(8194KD**^7:5L=6&"TZB'RR6D&VE:=8LP[-0+1*LDSN= M<&)1E73=+$)@<8L]HL?%+J(QL'U^<\+^9;[R=LU$LUK[/*$X-1+*F(86^RDV M2]WFW/:7&TO`SW6KL._?TD)-M0V-PV6XV4#0L*$E53#H0H;BNPGXOJ&F7U*A MNQ$$(=V0F:VAVP37P%E`0.D,./@,&^6)&U_<58:[U1EO+\1.JM==Z(;+1Y:@ M<)^BJJ2$(U4#`X1KL!G`2>-3O\B9!`N;)\"(.!:WJ,/<'H2N-17D96G2;#CK M$(0.]P!\GD3?X;9ED6GITBF\A,"R+2U-1NBT"W^PH>L\FO$X,1:E2`:J*S&: M8*CBMI9E!]`:*'2CI%5GZ#`E!5AGM"'-G?HG-H"\%'(\PKGJ)=0K-$;[$IZ9 MG`ME=B_RAO,R--.G1S$_Y&2EIF.JT MB?:Y[SI+>P/-FON)U]G&G\S6+X2#-%%:-&(BR%W?WV)O+6Z:A)S-UGME14&* MB%W)=^LI"'WWP$6`%+UR:`TZXA&"$^*:3 M62*34*X`$+W1:4H(@+!1ZTWED"_@&NPGS@`M.O'?9F*?=^<@@#(?(U*E&_6E MR@]T4M5!1\S-F M.G;G0=TA@WM1FBBLQ<_;U?998CPO1-'ORP*;+[2FYVZ@984G@@EUIHM^R\/M M/$N9-A12:-9R-3K/0V-[I22Z]DJ2@JR1)N"0#\ M+68#M_^>BRMAJ2:OC)*W7OVZYHE@L[+0+Y!QEJA4<8I]D9EO`95\$6IU3ZCI5T1)'DPH>0C*>W96 MTPOJFE8<2E3(&(S9D0A=Z30B-A]16C>JX2JZD0:(NG0^M!T5:=^D[]*OT'=[ M-4;O.6Z>`/\2FN\>;6Q4ZA.&#QS;5S-?C7?UWFTET9V(5]RH,%>S?FF.SIKF M":*-YRJ?`=@?6_\-`.X#3[P*#0IE;F1S=')E86T-96YD;V)J#30X(#`@;V)J M/#PO0V]N=&5N=',@-3`@,"!2+U1Y<&4O4&%G92]087)E;G0@,SDS(#`@4B]2 M;W1A=&4@,"]-961I84)O>%LP(#`@-C$R(#%LP(#`@-C$R M(#?J'%MX2X1N9R=^:7?TX^[B>[?;TAF_W? M)VE"Z":%?[?B;R7=%%6=Y%4*;Y\GZ>87^&__C?_O7Y-XZI[9C_M_3$C%O]A_ MFL1WXB?-DC2%+_"9PY]E65+#/_CH$+\]?W?^-7[_N*4EG(S7;R]?//YM_Y=) MD:;ZIU]C\6W)OZWDLW<7UQ&HKS8"KL64[Z%6A MF1%1'&+7&R@?AH]I0F-OX/H>/*KXH]'X:TKH=("N952[(%Y&W.CG_>2W2594 M_$W*B$(NJS8E2>JJK&L"`.;PH$I9N?GV/-G]]$PVGWZ=_(RP$W82=U8F!96X MM]%,`Q%,QK0XU&M(R<9W![8;\C.LHAJ6&B*8O/45&J):8H2U>#%>S*^MF26P M9RJU]HWO\B>457J*%OQ9G6M&8G`G/RD)X%BKZQBN$!2M&M/AK' M#_;BO(NZGE5>CRL?74T[6(<+_MZ=G8U.\=_@3UY*+A!$+F\M6='5W%^XB4@: MK70$UCIPFFLIAII3"86>A>,JM:+UW=WKJ_LWFX?;MZ__\,?U78)EE'VO8B&X MG`>X""/VE.\4H8Z:6EQ//P)'=B9':O_'3T'#A1RR<,PWZ3]EK%0TZJ+"W7YU MNWYH?:Z(01G=QUKYJ#=><*_B')89.N4&*8JZR^;_/8[5_PW'DJ;`]N*();V\ MYP[9\QO9,\L6RI;K\EG#<.$5RW7HH7F*MS-[;&FX-[P]Z_8_O?6I_+PXP>_C MAJJ%C/4F35,],EEII"2]3=IWW5.UC)Y)>"'1OUMX/#A;UF19]I6C`K_*JZ8( MC^^AAM?2CXNU0+2M&VITMJ-IMXNGA;$>\E9D1!^F)G#ACSF$[5(#W3IA;M8JF0 M*(M"^8U71^Y,:+M!HIE5%VY-<7)$Q<^#7<^H7'5E4%=6:*A\'OQIO-W)/)SP M7EYD]@KKIG1C6J'FZRI8510()$?M$0ZS='E(DE#"\ESX+CP49F@`6Z+$24)[9 M'ETODMN&T1E'@REW6+Q)C:8H1'PC$5U[::]L]P7Z4Z$_6Y*18D/A3C.#5TL/ MSY5XCIR2V["`X"V='N?`^C'T1\9UC';+V/PF`:.I?\,/_R++2BR)NKGQ88 MXC\R_B,3MQ/80'5JDP(!2?6)H^6FN'^U1BX7H!STTC_$]_YE8$=J[?,NQ`6L MKHSPO/`<3M3BQ"-G;CR`B.<0R+GZTL&UD!B$NL"E2?YR+-\9^B9/8"E2@P1Y MH:C=SX;<<$NF2KFP=.S6&[FTCL4>&\PP;:6Q]8JTC9Q01(^I6`'8WLNYMU)\ MH?&9%2V;I"Z]]JSM7'KJFCG_.[\!\E0UPSWV!F-O)*J*UL:N(R@E2MD8KA%6 M$2F,.?K%'MV)QZ5Q>!1Z+T5OID5E8$A2A3BV)./FI3.?>J(_,ZJ@CU"&4:9P M^6)S-C]N"XST;HT50XV&VBGYQC//OKF]Y>\^[R>_35C""M7RL@KZ&3S(`$.Z MH06%)U7*RLVWY\GNIV>R^?3KY.?)Q_UQGQ1-4ISGC?`9F/7J@A?[Q>OW!D,R MI9./15F"TX/0CBASYJNCSE4>F\`R^FY4&8PV!IG_[T/*@!%%IL6D#[N'#S>W M1F"T,A00AK.%1;+1,X?XTAF'`90`2WG:ZM@)@9^,B'KP[8"_$`W.&_G0J%;J MMR1XK0C.4]VB`>.GB\37QQMD3-5IP=?70\-QDJ?=62ZS80QHS7XG)67925OK MPXET75^/^AWI\:,KU3G_9_-Q9`5S>RU7P33;--JL< M2WQ1=L6?-C]X9::ZFI*S3'3KH?=(Y5#"24#SSAP5&J%0+<#ST,O"[#_B&D/G MF9\%&(9:IJ!IS&"*VCA<]=FG^K:QU4X#/N!X:\JJ$W).]AQ2ZJXO'9P!NBV< M(89N$R"IG><0CY#)<1!&UM#R+82&Y89+YX94@I$+D^C2OL&`E.%H*3MHI3,B M#N;8EDMJQJRVKCG&PAK_>-N_E1,W;Y<$SJ8UPF(HLBO^+"_U;"#^.E%BS[$] MG!EI)W(86Q`\8"XBIYUY8AC*Q'5%5YI)'4%KW1.N'I"4W6)7?%;7';&8=J]X MB?.?Y-EI2M3=SU;6:R]$)M/:Z(2"R65_(SG$P8AW-BB8$C2,TR"4D:Q3\?:Y M'2E&,,/@$8P]4?'Q+RA!*U&/>LO,Q8E`#%3LV2A"A!"3)II@^O*U[6D@W^\= M6O;T/H:*7VT'8P<;@S+&VEN&Z`4S<@WTLWQ11337F0#S9(62B=_>U$1B>TXH M]`=MZ+@459-JK@4A+G6Y#O>70NHZD0F]@<2%!U!#%H4>RHI.VJF&TFY/Y!)% M>M)MV0](1.-J]RPZ"SW?G+U&!0;1X8$]2I<-! M$57QG1Z9&:B_(MPF0@J%IS24!WPYI/)<1G6"0:G-Y;401>M$L;+@C-U%JU)[CSSRLOI)51]6']!"2 MUGGD!1A.9Y<>NI*3=N=3%_B6V'LK%,L![K6X]_"1(12T.LNW1&L\\YU0D=G" M\VB^4K+PN-";4J-Q*#=1WMENE!6>JZ88(@1OJ3WBX]+,)4E-;=+TL9YY?JJ; MZ<4NFUE*8)&HVS(ZQ/<23`C2%G!:ER/?4XY[H#@&#CY!S''"-0F0P&&#J4NE M)/LD1M/"B-;"E*5HZ40M'F/'![J9&TIJC&6ES1K&`-_-3:HY*0:0[V-%%44_ M5\]0_N_V-7Z^K?)RP[):ZEQ/[2"]PN`0^R.7`('< MV_0*#0IE;F1S=')E86T-96YD;V)J#34Q(#`@;V)J/#PO0V]N=&5N=',@-3,@ M,"!2+U1Y<&4O4&%G92]087)E;G0@,SDS(#`@4B]2;W1A=&4@,"]-961I84)O M>%LP(#`@-C$R(#%LP(#`@-C$R(#4J0FMN,DMB@OHY!M;XB\#9XF,0EI0HB'_?OX5QHG MS;IV99J8QOGNN^\^.^=S4GY#X?5?[[3RDHJ%**Q^>C!&.(3\9R;_*G"84Q9G M%/+5&P^&U_Q?=25^W7K1.]N:5K\\Q#T@Y![/TOZOI3!<()8,)$E9ET&DO^ M8`](OY36W(\"87?&3Y*V'3B.[2A;F@H8K69D#4\<((.`7"9%86IA#3\"L?C& M!T=`YI1>>;Z,/_X0N,)N2R8SC`D)$:>]CB58HL5L#F6<6*S#)O;@N0\46:)# MCOGFC:VQO:](ZKV.G&=67,*Z6/,86;8_$!EX"/>0.1"A^O`@OA'"R15VN$PL M_\LR$\:MU=%T3.V68FMT6;@(SE:7FTH*F21?U3\NDWDB-TK1+5`NB#:;WK!M MDO10=H`%G->*,$4FX49W2U%F:%F[N2[0*]G?;X+ M=+D6^N+AT.4]K'5#RXK4_":W2E#*2PU!;-QJD4J*84;;[0JVOX6^%N:#0#9@ M3(O>SL7RNBMJW,"75R!.6QWKX=3%06:O0H M>7MW^!%Z/[67DPE["P(_;O4Q>3$@N!213O1U@]*TV+#H/F2Z@OSE$^O%;KK( M5@G(I@F0L?7;)"A7$I#_I,WI8VNSV#0!5N=JZP1GCZ;-^<;:;(]]\8C8O*DI M<#[DF$W)T(7JL5+O*UJ3I&X"MR)=Z(R['5!$%'G:[@K<>30,?`N,U)R*6[1; M2RFLK1)Q`$::),UQS]D0,Z2Z^CNC.B)YS&>Z',:9'-7;39R4D[EJ3VDS00HL M=6'>-?#-VU&-5B*4;!*Z?&PAF.EYE.^N*2SOH`>'KGH:F>.\T7XO(S!R]&LH M3B.+/Y%09'.],ODDXJ^??P(,`(H":>,*#0IE;F1S=')E86T-96YD;V)J#34T M(#`@;V)J/#PO0V]N=&5N=',@-38@,"!2+U1Y<&4O4&%G92]087)E;G0@,SDS M(#`@4B]2;W1A=&4@,"]-961I84)O>%LP(#`@-C$R(#%LP M(#`@-C$R(#C?[R$/29.2IRTV M00)9O)SS?><[%SU__#<]_/B_U=_.J^>S/-##^8=54U-V:.#O23\-[-`+67>B M@=7/J^;P(_P[?Z_^^V55K6?W\?'\TXH*=>+\?E5]K7^RMFX:.('OUJ_42TEK M"7_PW:7Z5#VN\^.)"=A8?9NOOX7G7CV_>ORF?OQ4[8__.?\3W*(-^L7ZCN%9 M<$Q?#EY449R@P5Y=+G"#=O!2S<@<[AS4G7?'IF85F<]28DTNEI\:RM9S;:9O MF;K3&*BV]^K2#^?5?U=M+]1*PZDEI16'`:"(04H*W'3P0C1\.'S_>?7\C\_T M\/[GU;^04WZG?IX'V M!TJ9@MOI!:)>`TE@S?V4G<___($HK/',W._%H#=QP-XK++]/09NBE['%[2,-# M6>I#7`:7S>%DIVZ\NO>L]@T]]WF``#SC\<%_#;XDZM0F7B(HD':#&P(8KV<; MM2UTV]-I_5[?3B7U)%K)=XUY.\)O;=:RT>=ZUMQ8%&:12D_\P8[6'J>^O/T= M*#I(O8$'?'J^2RT\VG+?]7D\0SY#S=9SW&M_RW<+@1;:0024>0;2Y:/:\GSN M,5\@0_L#[[NK-;W.'(#<&J:=38^"Y)EZTS8NHEF.@@_4Y12& M_.),U;M$%[J6^WX`:W&!!%CQ+?)LG2=[ M31R6/KM2;),"PQ!0R7R0K5^%/9!W*?J(FQCP+L\T.$:E[P5'+\@QV5)[6MZ]JI@R:!J&US1O2>"`=+P#(I#?U]Z^CW-);&61`$O&I-2I(G!4%! M^RGB`DI%0/\2F;]F7)%$I8'**;/Y%0.SBP5,$W"REA790:)1E6BO,=&:H,V2 M/+;JY`WW>*QZMYH-I-ED>5$1Z7MNA9=-TDE M@.%Y!?`V4:'3UI0/#*>&=N)M*VT$+M4BS8I"\0-E&[G8Q/A3JH,@LG*7Y(J> M1L]=D^TFVO&7EL%XL]2&(##^',DD,JJ'NVUR5,RK\U#"\'&HMD=MI*8P5YCQ MT915WHK;C,$$.0L+<,O'9GM]P`W?D*B6-M"2Y._ MVS7\E=-:4"3S)\1(W2"@I$IE@Z'S M6FI^']F\<91PZANN_AIPS"S(3;S.-B4)R92-GZS?Z3EU-+2#%&&J@:FD9DR/ M["_K+HM4=`YZ<%,'TS[ M\55+A;Q$7-95-D1`YT> M73%P:X59B]Q:YM9*[W%RQ:-&V_5RXMQ8+T*U!B$<"S[4>[/7?.=2VD]'M9`- MFR:^2/\2CW)WZ,:5-KA$\"!-$Y>+.4FA-V*SX+T+,K%)7$"1V>8DWF;I;A)N MH>H*5(T1Q'P$D?'?AWA[+)W";"?DW\+J)[[&JK$D=WJF(19[\2*P5LB#F\9' MZ,@('0^`Q2&PL!9X(G8%\]J`WN10.]$1E\W!F%R&,$4`$[X\]C'.`:AI;):N MF1U5`:GL3&<**#5CPM#ZC;PBJDF/14T[6^M!VF-2'D:DA&/8A!3K=$\6&`P0 M',3'M8Z[393N!C'5ZP5G3-!'0SB8MXFD-Y';:OI$:Z_];/!>=#"Z^2^5PNMF7H6)::-I_ M5#&9KP\S@CE=N*R!YSQ:;[&G]KTC\.U6#7FVH48[8I\Q4!V5OR4FF!L:2])$ M3",:N!B7CTOU`-]C.I&I&=[T](,.Z(':A$R$]`Q!H5%MKDSVX43#F:N23TX% MR-2@GN65E^0Z?8WLM&'BOF#'G[^?$F6CD).LDUWOB#(70(I73L9:>LO'AO/IU`+"\'5\*#0IE;F1S=')E86T-96YD;V)J M#34W(#`@;V)J/#PO4')O8U-E=%LO4$1&+TEM86=E0ET^/@UE;F1O8FH--3@@ M,"!O8FH\/"]4>7!E+T5N8V]D:6YG+T1I9F9E`3G`O(*0:Q,4!,I M3+3R`&IIH=$:K+P6B877P!M04A#&&:"@L;!WDYUOL__N-#/QR2&?QAX%(04> M'5P\8Z"7#@F^Y4IF.5-9(>U/&,5H[R1!>]74:#,G%^UX3=?+[8CQ`@%@Q!G` M-UG@6DHZ8^8T:PINS1E#I)5G;4A9^TQD#>) MSD;^,C>-[VIN5H9:`IAJI1/5E4+26&B,RQBW^!%@`),2G'`-"F5N9'-T^4X"S`I#CZ:M04E2:RN7IPL7` MP`Y!?QC__V,>``2T%^X&+E=/KD`N@``#`'8C8U$-"F5N9'-TH=Q\O&`%9;O%\^GG`.TKJ)2"4I,)&Q31D`=E MR17LV`1LR-N`!:O%.F?7V=7GE&V2&SNQMV+##J(KQ)P=19^)*1M$2L3NQO[" M#NNK;L:.]^(=Z\7X]K_T9R?VD[Z('>V8$SGK,E9Q4]%/PEL+G_`KP``F7K^8 M#0IE;F1S=')E86T-96YD;V)J#38R(#`@;V)J/#PO3&5N9W1H(#(U,B]&:6QT M97(O1FQA=&5$96-O9&4^/G-TP7X#& M44B!*5(+$AF08.(!6D:&(IAMQ-"15XK4%TG%"X0M@^7C[KQ06]8GZZ3_SFY7 MUMF&3MO8RRN[JV$/K;.\Z=HL:WOAENZ&UK7=OL*ZA^K9M@ZJ>ZI#M7[FD"CPGJ0Z5050>`R(&CY^9+V+H\$",!C4Q:31"8N9%[(A8 M1B_,3"HFY*5.0MAF-L*P$$;]GRDSYUK,S&6N%<))"3^98^!&>!BX.WYG],@C M)2-IL9.TZ(791TZ;:#;B-T/C<=I17LI/Y9A22(R1WD&C4O1#JCL'[GIX@C\! M!@!.J0"L#0IE;F1S=')E86T-96YD;V)J#38S(#`@;V)J/#PO0T0@-3D@,"!2 M+T-8(#8P(#`@4B]$,B`V,2`P(%(O1$,@-C(@,"!2/CX-96YD;V)J#38T(#`@ M;V)J/#PO4')O8U-E=%LO4$1&+TEM86=E0ET^/@UE;F1O8FH--C4@,"!O8FH\ M/"]4>7!E+T5N8V]D:6YG+T1I9F9E0&WC;2N>RJL"O8@Z,D'4(\+J^C5 MNOAB]4WZ"#GF4#K^L7M0UA#X8"9A_JE74LI23KR<>:F\/'I^YKI"L93J5)8+ MC]ZB7.& M:=]1]7!E+U!A9V4O4&%R96YT(#,Y,R`P(%(O4F]T871E(#`O365D:6%" M;WA;,"`P(#8Q,B`W.3)=+T-R;W!";WA;,"`P(#8Q,B`W.3)=+U)E)#+V!.F=D,DEL0WWIS6B`++#!8!#GD[X>L8E&D9,], M@L4.VA19K,?W?55\^?1WOO_EWXL_G1$OUJQ;[K^ M5'>5_OIY4>U_T?^??S;__&=1_CZ.#N=?%USOJ"J]0Z]^7(@.ONW8;?J\3YZ? M;F9FS\N95WB#Z.H>=^HKX)BS%\4)V!/-J>_[#G?!?:_E#;L]'$6K#Y1WA^HD M2G9[DS*]U)FEV?RGBHOE[>$?Y[\N&B6,88'GR\T;8_3'\^)?"]5TYDLE.<6H MNGW+3WW7]CW7H=9ZH:MDN__Y\^+E+Y_Y_N-OB[]A@GAS-4,U/W'*D`NI%`4& MWN$Y+O@D[%)DZ7<0V)AH\N?]HPL-I7_F6SG,5L/8=/LJ&Z#Z9&@G[("S%ADE=FLB+)U#%^$\*)[U2#37@OC]9;-,,.GKF3K+5N#^ZKQT5Q" M$^578)VA*$Q5HXS6LQ442*AFRI^W*7.U3G(&44E54<#$)XUE-H_>LNVS M99%-NNR5!RG_UJ`&MG+30M1^$#E+MC%Z>D$&3.!YMH/OG+)%."&YQ(OB(EEO MDWP+F>V$7VS(QB,6NZ4R.,)YU?>OCA\LCH-PBN06X4'^3REXO`#IP-576QY; M`5T-BZM:ZAQQ>3FKO4]C"U+>3_3)N).E:69![!^ZCQ\Q9#ZIG,M&/3&X2XH\ MND4ID9XY1PP#$Q"^9:2%Q40BN@N0O(^*#=8QJ'^VQIM;XD<:V94N$)`AS]QQ M/+^+M/[G#^!<7U](/[LM,*Y)X]2E29`AU86#R;R([4$O5ZAO-O=BJE).P)1O M46+-TX3R)4T#,PE+D5A.&\;('^H8J/L]**^T66"`^*/LJI[`HRNCEU07R/N# MZ4G8-!."'N3:]5*HD&U<)A2[*3I4)2I?VU8!``JVU))NM\71.S9E#4#'[ MM<"(!ZL65GB=GEHT>>ENR(XH(V@8YEZNJBIH/1O;!:BM\[KUZAN@#A5DI$!B M4KHL'UGDE-\?;IYB`-\F,*(J_\J29@=5^\V$/=PC_1W:\RV24,@+%/F#N:;` M+NDV!-#2O:=5KLP;K9UV=T!;'*DY2LKK/U.Y9GJ$E<\B$[ZP%WQ1B2.*LUE?0+J?4QM@5H M-!FI`!@$B!K-(1N8U'OIXQ4F6>4WS1*,M6T(:.Y(#>S/=\&L(VA.+F]`\&CO MZAV<522@V`$#Y5_EQB0E;)LD4I"&5W\>[0"`BFHJ%R[7MB)KJC:/ZJYF3 M4B/5/G&%4--$$FG5E'-W=JKO3<,&SAIS%FST"R^"!XV^"#@Q!Z;:%=C9#8V$ MTZ$VVB5Z!`"!L8_/)'*;.]O![5,)`^!]VU_!#44Q#L`)@;XTMA=9L7$QZ-]S M_3W;S5(]?-K^UKE8D.&\ZGSHC]%ZU5<#MV(M=.SR8?UU/M$*%.9/<%Y>8S>DI(R^,,J_F!1!ZHEQR0@&XA@P5X3P)%`?IY MM-S8OB-DT)J+'S8PZ^"^.-HR^OL[.%#SEK;JRAI,(VP`TS`H#90*&B,M.MP) M_&T>I\9P7U=CKEF$/$&X-G;T0LC)>.OY@X;-8!C3P;IRXX,'T2?BU$N@YN:X.WZRK&%PR]$;PHV.]6&G#&IJ)9P`T25U^U M9DKWWUM'C\U3QT?OFHK\M).28;%RR$8!11FD2DL7R/".%#V4U`](^C.`)G$M M<8>%B,5?-B@D)YQR,68+/7)R-BAN8FB/[")_@E?4*^HS"/%(G8GF;X'FJ8:6 M;6'(XKYRKR4KU.A8%ZBT]>[9=0FO%^HC2R)-/1FCG(@_#P^UG0V]#]AID.(L M@GWS1T:12SZ(W;!O\#P8?X>'T%$T7->AYY"TQ^$Y)50@K,^;W+Z3N'U)23)` M&T5C!2B$XWC:6',?/GRPKZ#Q&`.AZ9:Q3K$GT=B$9FY6]S/`4%?; MDZ0RGV#ZJJ\HPDWZ!NAYI!WZF"4&W=M/#-BE9FJNFT$\1YVWP(NKECK?4OFA MGYX7WK[K3EP\*J\=[:[YK[[H_P5+ZNO^HZX.F__'^.6UH^K_"^*"I4_?Z,04 M2M\0_]6CL#1Z[O3ZL2@TZW1#-8^=;X_J_=3^^VOY>7_-DG?9)]QS_B/9Q]\5 M%!2W?0Q6I;_P", M5/UX7OQW`,YK[9H*#0IE;F1S=')E86T-96YD;V)J#3%LP(#`@-C$R(#%LP(#`@-C$R(#<\WWZSN?OI-;K_\O7A[6-P=AJW<'GY?B+U46P&_.WKJU;;3 MP[[5`DZ_+L3V"_P=/N._?Q95.COW;P]_+*1&CBEK8*C=[V$+ONAT%",5IXH47=;S]_7=S]_%5NW_^Y^)5+*+MG:]C* MO30UK(+92S^#8-H5>FT:6[N;56X.)!Z`O9KM[5!&=1WE>JQ%?H$9O459I3NG M%-6[=U2DP79@%7LH2I*U:4'E1R34U&ZO0C\ORAB%8PI.#4[7JF)YX^/9,CFS MIZ[N17'Q8EDN4>+5'`5VLI>:1:14:*DER08/:ZV>>CXGM?%(2HJ.M8836IK? MN=BR/ZGPLTZ;TZ*G^G#/B#4#<-2INX8'%84V!,!QZG*+2=EV%F!AGA&RA$WR MPE]G.1O7QGC?]5L%:1./O%S!A-<*)QPGYU9461QG-.1UVTXHY]PK4Y"5(+N7 MP"0LQ#VT,1&[]/H)\3Q4L><7QA-HTYB2`=4)QPO/7P%!45ZC[]=QZ!]])UEI MG/=N$8O`*\,US[-P^]U]TQR4!LO9_SVR*.,74<`>^!F9SH7\EME#ZI?ROTKMY,<3F?:=&#,M\^+\8O4HU5U=#YWMRD-5AA@4AD@9 MU#PF(>>"5?8V99TA8%#U0-L@6GU![[87Z+__,LS8.-!5.Q M"@H[F5!62CJ(8W\Z65),-^&WCI;L)C"[-@O>\J42&AS!%8G,F>:_SK+96(LW M\1L3D;$H[$K5E@@)+KT!HGE$LJ&D#>,TNAZK!&SCFS(:%--CGBT+X*'1F*55 MNJV9\46*I>XMJ2:CF]EMEBSZPN_'# M^(KG0G7->#VYYOV*KZ:=^I'&@M6#!&SYWEGL8!<+N"P39#!;;T2:Z0@M;*A.% M*2FJP>4D!^/V";X&++?6AA6V='O\JA#Z5S,V*#MWYRUG7NH&T4VNB]J$(U5D MHFE=Y0A*^EV1![/1?SN]JH3^991ZVE-)M4W63)?RH/KDQ78MC M:F/OVR:<"*]RJ--5*;V>$(0 M`B%GB*?&-7R1)4`(+L.((^A?.F!A*J#'!*?8TD#_B`9JN\Y`:.6`UV&#P;(! M9L/3D\1AD/-T&5@'1*#ER.9Q%JR/`I:.>;J!@TD%)C"ZML,[\J#\/T_7L;\X M8GG$-UMM43,"4N@I%F_X=J==Q+W:9&F*S(G`:R97_,@"83!K1#;FM@E?!\A]77G$AOYXG_IP6/P[`'YUQ:T*#0IE;F1S=')E M86T-96YD;V)J#3%LP(#`@-C$R(#%LP(#`@-C$R(#'0O M26UA9V5"72]%>'1'4W1A=&4\/"]'4S$@-# MY#X%:(`"AZ+H0__]SH\=:BG*0?M6&S9([N[L[+??S'SS_(>_Z-VO_UK^_KA\ M/O8[O3O^;5D\:;,KX/=`3ZW9-5W_5'<%C'Y9%KM?X>_X"_[[]S)?W=SY^^/? ME[K#%<>?EODSO9KJJ2A@!7];O<&/O7[JX8>_G?*7_'Z5[`^F@XEY\/;[9+7_ MZ_%/X(#*YFMW, ME>RQ"%\*;58SVJ:I#-JT&^31!HU^/B[_N:R:#D>*4@L*5;=KP?>N[7L-8-3P MH2O*=O?+E^7S'[_HW4__6/[,$+:O0ECK)VTAS)5_/UN]36;9;+UZ+G!C.+$V M>)Z:O*$)G]81_+WLX_.4\CSZN)XI9P`]=M9^EP;OSJ/:M8QFHR")7O+9M?4G MFJ)6`5Q-C:@EKA_V@AI[S)WN#9F%2P'L;^<+-/A\[(9Q=OF4/VX\'^X!+3[I M_#U=2L,OX1[_1VLR7@F/3GD:>&M?[B[=*+G9:)%Z$8W0:[I'D/--D,J7(!FN M//;5UNX$$V&M'0!JA$20V$^CS/N6W`&0GCJV9_+-F39!2*Z9HFH<`N=()CQN M54Y)&JS4`[&T;QR6$4OS"@=J/5ETRB,,AQ;=Z(&Y0;:8JZ_%X3+W4L$,GN7X M8R00?;N'>3U7F?';-&4(V9M!'.#F.?^%<"#+(H9ST:W$[99 M2J4XFR,GPL@I<^+RP*Y,95%R+YLUP`@O]8*Y#2=`OATRI;T2&(P&GH9!"A>$ M4\N^=B!V,<,-(?;PD*,L96EL$35R$1NNQ_8M"]+$HXLI!VHXP*ZW7.%K.QWA MH:"H)AO-@V2E_)1-F>MX<;X@B]7D@M=TN]TK<1UG5EQ8=MJ(5ON2*A5AI)O6 M-9BH1:)\178AYN0(:NL<05+0+6]NW^[QK7,O_7QRET?.R3*+<]U-*+CQUEP2 M#`PZ>SY:DWRSU\[LJX33S.C::R>^#F57]A<,4C-;%R!,(3&D%.)$Z322IRP: M:L!62H#4'ZR65+,Y+[SGBD#%I1]907X',X^+*$_F^*''P8F$&9H%ZU#QKIA" M(ALH^F)9$L4I`.;+;G=>NI$Q\*JELL3E.0-'D;^A\A^NZ)."P6:1M= MDRD6<$>K2/`Z(,5T?@E+*UF::I"T(EE*GA\R:%&6P=1!_&PIO0=I@'6:)_JD M1=(D#M,XD2WRK7J/B8*U#E>^]=86=SYVF$3#=IWD`%O]C=5,).+\19#X@^]F M`&TFY@1PAO"`9W*TF\*UNK,9P)-,87FS*X#GC4B<*3JD0Y M<=*#1PQD6TO>B(/Y`[-8_-%=*Q@[F_?YAWF*)C=C'ZK.+25$9JJYY0672X%L M+ZK8Y@Z1&<+^446,21$6S`6/";.V1X3''ZPO1CJ]3C.(!UV?T>0,-PI53'/G M&?DC2VF.75,,,9Q_3P/0?SD?W:3Z^0(["!.Z2SW,%OWR'2=TJX?RY$-Z6ETV@G23N9U[OXR%TJ8\SW@6Q"F4>*M5Y(A M,YL/Q6:'&I1K8]Q[#R00=O<0/.Z M81Z/V!UL5_$-=[N'8;N_0N4=?Y5DZBCEDGDG;44X2K2`>V5EFU[ MJ:_,J)V8J"ML+&XORGI9%J]WAN5$>08XT+K"[90OI*9(RW.7>&FJ^#0`*TZ$ M)$-/%>CNIMJ9IG]J=\.)B,;*>2"K8HBP9Q50PSR)N44S? M7-?QWI8!TDT]H5KJA=R^F**ZSE+AV%31#WAX+.E-,:'A5PQE,5VFWL3>6]YX MVEX$<^LQ=Q=#JR;]@>GYLRU3/?-;S4*U2,_Z&P734(8V2EI6EG:<\/W82X/; M9E?_;99HLOWYR+('2= M[V>C9HF2X18""@"*/:A3!],P["IS:Z\5L\.WE%,WD69HG+X:$+>)/>:D9$QY M[G$34+6I3XGE0`.VSOR_EG!;*5_V7'V-:-UW[VX?XLQ/9NB6E'$8_I;T:)J1VRB+550A)W@^BJK<7_[.YBZN[K<_\K3Y^//:.K M*TG,IJD'#)S$LHHX0YKV>L(ZY?,%MA/8ASV56,_AGIGV>(V?C\O_#`".2W)) M"@T*96YDP'Y)T2<2%`3&4QT\@#JZ*#1&28=N1*37H,CX/8&0BTD/(4F_25?FO3SIVBC MR^O;Z,WPZ,`%O(!S&UW3P8EMVG.>``]GB&*P]N@%8*WY#E:T7:`#5KS!V_5^ M@G@)1%5(U"G'0TMC:*$/?8U^/K3&[$]#-(Y391WF2=\J*<).F11<+*E4EFWQ MAZ2R$$+OS(7@XC?5RB?57)P1*?E)VK-F\Z1J;9ZGH=!("O$OK&+8P5>``0#; MO^"K#0IE;F1S=')E86T-96YD;V)J#3@Q(#`@;V)J/#PO0E0@.#`@,"!2/CX- M96YD;V)J#3@R(#`@;V)J/#PO0V]N=&5N=',@.#0@,"!2+U1Y<&4O4&%G92]0 M87)E;G0@,SDT(#`@4B]2;W1A=&4@,"]-961I84)O>%LP(#`@-C$R(#%LP(#`@-C$R(#'0O26UA9V5"72]%>'1'4W1A M=&4\/"]'4S$@-#VLRSO(XM M8Z,,&6]2`G2"`#' MJ?K;6]/]AVDSP@##H]4/M@!#V`&V*6;H`7C+>H_[Z+S4NU[/#4D+3ZB>'(=R MA?"M!:&AQ^TFQ1@4:X/5L1F^0Q)J)((,E2-7MJ"JP>:;`L$3?:]VOLO!=I:Y M6HO2)P_D%K\YQ;HIY2##\.Y4W\X"4844O@NDA]6U>'\[B47;M"*0"49%(X32 M"5ZVDC9"Z40W'NG$"WE@=&RDL>?J.TPZGJJ>3TDUM=0]B_)J@WL?IR5:\ARC? M9O\GQ>?^8@KU(2^V6>L;BHNC`8_,J+"^]52324?4FFN_<;)&2MPV"Z,)_8D0 MU+Z)@GJ0[JL?KW=+/52=2 M,K<(I`?.14A+V#=]J&J0M;6T6+@N3G7U\LSZR1 M=/?T`?O-1;3P;X>WTYNA8=6JIE5:F"`ZV?G'2PZB0MN/UF@-WQ91KD+1:T+[ MR"]`[O85Z<:?XJ215;:`NG;9TJ&I5TJ>UKN6VE/0'UJ9!PQ8+J,"'TS/2(H) MV-R8>#AG4T!E,3US:HV81ZML"&5G0;UZ@EZQC[24K+4/S<88$*3\/+J$"6TO/&VG:670^*#78,<^,^1:R'&IA8*L&W=#:\;D),(W;JWL'N3 M*RV03L&X=/,W9QRGT$[8$#QK9_$OSV+?`&T5U89H3:=!4I?9?P,`S1P0I@H- M"F5N9'-T]_?W/Y0_'Y>.QW^KM M\;>E.NABJ^#O'G^UQ;;I^D/=*?CZO%3;W^'?\8/Y[Z]E'D]>1;OC'TO=F17' M=\O\$1^+ZJ`4K*!W\6?F9:\//?RA=T_Y^_PFEKM]T8%ASF^_D_'NU^-_(0ZM M*)"BJ0LRADC0&VR;LXC3#HWQUI$!1O243\04'+;&X=5.'8I<3")3GJU-7+V)ZUI\OF(92U8N=@H&HH8# M'OJ<[_:E0L,=)"KSCQ$\9C)=9*DTOEJRSM8\@P%RN,&]%K92K8"8D6\76RFR`=M<;EF5\M1#1\(71XJZJ M_=K$>1\R<:CS#1Y1LNE;7+`O2UBB=6%J76.E/1*>,D6QOT6O?.>1PR9(4>^#QV#K@ MM@'2`;&(_A!O(:"Y02TNM]`IE!Y#&PN2KA88X1U9ZX;,][I7'>VG`9RTXQ<& MZEF$_\\>3MQ#=^G2;(_YS-^L*;Y"NSP-H7T.S6-ZK2[#6!:`5/.VM&_SC+,5 M^2A=QYN$&9O&V21I-D^SARQYL$[KI@B2%.8#JHJ,4@4)HYT')]%""H%A&'(* MLC7XD6#-5@(#*^O&QF&1;3:HF@L5\<%7X>8;Q"5Z4\7+6\[YPF;F+'13"?.M M"I?F/!9WE/SN+)8"U[U9BQMCL2^;WG1"&4"98]];EECP.;\:R,YS"#Y@WC)J MUO:TZ=RY]D5#;<,\#PD&9@-%NP7HV71U(C?6%!ZDX-=A`%>6+AQ*H//L)S!V M/=CE(IGC&NLE@?Z7`SMQYXW*MG$TUUW83NNF#A(Y[(D'IO%BF]0DM/,)#>?+ MT*TZK.`+?5H,3!AECB#FAE$UP"^6?'-'-%;T=A[:#GW*OX],9AU-`UE&C$YC MZ2QL^(%'3>O1.0,FA>X3UW9WV!CK9YF51;[*">UF"#J=B7^-DH$.;3)R$:T3 M.36(Y?$$@?2HS,F/W[IO+`;DCE]^BTCN7:=1J[3VZ3I.)[0`.+T,MOIZDJQB M^C)P&G!4KZA1B,_N3!3[U^SU1)R3&>7AS3I(WYJ*P*(LD3=#DQAX65ZWV4IQ M;,PI.3!A<1E,PBA$>R)?&`3SL,*GH6O;OQCZ9I+L*3,JX*`1*9O-PJ.\4#B- MA7,]I#T6FD:'4(A]1R>>TAN/(_K\P(7J!MU89M$-*2`<'I&3HV7:(;OO:#;&A\O_8/AD*+"*6RL^F MA&`5`!M?_QN;H*I#-3S!=^'T@_33G-)M?6$FI40^6O6&24UKB3OQR?64L1K* MBT*THIK0W*H(1=&A*CU.F<_N7Q#<-AOV9$@))E2<46UN MYTPY",TR-_*UH0-(M(=Y--AL=D0CDI$,QM<5GD8F&SL3FEZ?0&FX5CT9Y64: MN1AZ$W]&Q$_)C%1HK9N3%",WN6N`L-145DT(M7NQX&DD2!4Y1O+7'ZV:<9Z> M'.E1,F:6(F5,`[7L3G0<@@R%675)TJ0;>T6D3U80X4Y78H0LHZG9E?U(_S%_66$; M-\$2`J]C2=(^B4O$FN/96M<-<)LQE7*!("K:\@13R2I$%17?=*R%EC.S_3;, MR03>Q.O9S9!805?-J9`+%HW4F`9B#87Q_WMG\A<-*@"*ZM8=\LV:J+-RP:;1 MV^&:*#;S-8]8@`KF9ZB9630OBEQ0X$9$4_^VC3K7CX"[:#CRAA.8BDZ?#,@; MEZ6,/96/7L"U5J)W%/E-MWV>ODI1OHWZAT;D])P>@/ MEJ11(/N0(/C,'S,S^<95G6X#^CJYI$@Z^4BF5%ZFV//^DV6*E2)WZ>H^B9-T M%4KVAZF!?.M5TT&8[M=WC+]A;-S`#@&Z?C9LO,**R/I.BC@NP MV;NS*?3E5,2W?GA>/O[TK+?O/BY_7OYP',>/$Z'#!3!-S1F^QA`]W]N;T87SI)?. M",)`D;JZ605:J`N!>PG: M>TYOB0D`J2.Y&B>"G+4O,_Q3/IM/7"N63I)Y`0"U^M\`-DK1Q0H-"F5N9'-T M$5Z'JW09)%V[1%4;W9?5J@"Q0(BJ(/^_<[<\X,.13EHJT#.])%3Q=.R;@Y%GT\7HPMT=_SYGM;IQ_#"G&7SE^2'+Y`U8DX+@B[Q%US/89_S0R!_58/M3BNAV:4 M2DW=4S/RQT;/9)<=./7'H\"72[5:FLZ4ZL48=)C'/!#DS' MF?KNVW"T?H!P2E\Q8`78X;MW_CB^#C=AK5T7)VP`Z,WF#UI MUZ&ASDZ%DZKM!K?#'2SX*[U$G<1\FI'DF8;)+'9(TEVX<.)E)SU>2NE"Y5J6 MRK>OTX_JH-)5TZG2Q6E$%I&W/O&;\]R4S14ZRIK,*D8Z<+&JK%I[HC&1:AFJ MG9'UU.EJ%$W2>^M=1KTX6^/23*'(YM>CFEBZOKK5H11:E7W)O;J^OT]9NW M5S?]#!=%G?*:P:&]'WO28:A%466V'4ZB(D>BM0=9DP=RE?@63"@N,M%#F*[L M5/#4?FGW1E=[NAI*\(`?L;6RX1),\!;=VGGEF$G7W*44?3'QG'J:NTDX)D#`!$_:BSBH# M@W4_9Q(!(9J\%"],'0<%,_$_UT3D*PU^Y"E4HZ$78$M( MY+1*Z9I(C%'+8A#0$Y.*04@')M7-?S#)1%L:`@M%W-+H.1LNCW$O=&3F7.%7=LK\_NOM\_T>;=-MX#J9:[3 M3-]\TT=YEC'6PCP0B[HW9A"N;.RFP&5*89)!PF"E@TT#!SR%<97I=(G.6$E1 MXIU)"Z:`U?P%C/"`F=9LT,)*14(PSL;,"SF<%MA]1M83M)">RXHW>`D8)TW% M(NF-[7A)9$4IBVIA!^!)L0IELHQ$)F=!8)4=H/Y><$U=D**RJF4,D3,)8]_8 M0)V5K%I;/=QF^.T"=>>EN2WQI+V8S$ATBX1!MEG1$09MFW2/`%>(/-27:5ZE MKN0L>R'",>D/DZS7`WJ$#.,_&"',%(/NTY,B_5D:FK8<;ZKX410[LS#PHFW' MZSY[,S(Q'F=+)_:WQI%'=;XM+":JD[$@WSW^)11+;K6ET"7G/>]V)N8>TH#^ M2(&KN8VD\I*D5EN3VSU"1F+L04Y=B"',R[1-)FU.?,5'!04O&Z3#5]@;PJ8) MLH761O0?0B\*#%W9BYQWCXA[HK''3M))+WK1RA180Q^1>I-NQ<.5I*7>U[+* M7.1O;9E^`MU-82>>*JOB^`2FY/0O%-%L*0#UM[?;Z^W-W;:/,EDJGX4=F'VW M"M^^^_XWR!4-_TY!>*.[A_(->FM&QCY\"Q3PY)6F)F`M>:K$2I:K=YI=WW>; M,\P3(62'6WEM!_-7D>/*&8H$N2G/,ZO/VC`VH&BX_ZB5GAG;I>^L-KV6X_E` M"QZ])3AI1#88PF,DX=KFU4(SJ>X9P\<+'L),:.'Q&DRO][X:^_W753.8?Z41P\:F)(%8,)ZW3?!MXGRE'V]F0$6N$R.5 M:?G!I:GO3XB*ONMN37E_\\=WWV^PO6HY3EE=875`82Y($$Y.2U/R&NC2#SM> M8!]M0.S]3KHH)G^B][:OV(EB4FZ]_ZCY`"YKT:93P M%(;"@5\3+]EH!J,K252L-[5,*N!9VY*?#2!9N';CA;>0--W70G12ZMSF-4_R MN([CE0(.3M7`Y#E"Z6>R3!2MRM7^/``OCWZ2^,CQ154,^LW` M38^W/:*L#>*!L9%/OH3/P=KK,DDMQRR MCB.H%(BF1%FKH[WD&M/%B\JD"[OT=NR3-TG_F6D>)4_4I%&VQ!TT4C`Q%$O1 M=JRX,O27@=/1@X._T^"0-(\X!%8N&VD\SMO$8>OWTB/,)A1ET8;G M;B9?>&MGUC%ETXI(:2#@[TV,<,:J&?-X40VLR>P0NB!U(UF**:1P73D4F^ MH`Z4"1`54/3Q./_W`/:/%X$*#0IE;F1S=')E86T-96YD;V)J#3DQ(#`@;V)J M/#PO3&5N9W1H(#$U-B]&:6QT97(O1FQA=&5$96-O9&4^/G-T1R]>0*Y`((,``_A9*B3GN!@808`>3#,Q0"BK(Y>K)%<@% M$&``,2L1#0T*96YDCG:'H4C4%(0QMT-%I863O&*^?O>0E%* MBN:2U)*RG"X2[ZA<,Z6L()5(.TO2PE9.YQM6-8H3*85B9^\2W``%5JU?\-"F5N M9'-T0'`N8';S(S$@ M"%'!+0):>0"UM%"T$TSAP5)XD!PA98J0=79PTUGY8#YXS`M]E&@NB'$:XM&# M"P01=6FJ[WHXD:Z,*3,\G"%1(/881"`V]`>1;)?H@5`IWJ[W$Z@5:%TZ^DMA MR`UOPZNE3S0#2\V,B*JEG!,%\R!RIB8R0]?2]"RU8ZF&1,F,B8)9$/F3R!A: MZ@Y#<7ZBS>0?8*U@!Q\!!@"J?K>7#0IE;F1S=')E86T-96YD;V)J#3DU(#`@ M;V)J/#PO3&5N9W1H(#(P,"]&:6QT97(O1FQA=&5$96-O9&4^/G-TQ"$SC$3(7T"2ND5@%_`%3"%IY`+6T4+1.O)E'R1&VM`@^ MQTVAB*W+\A4SL/N^-QQ(+%9Z`[&9I"/9)7QDF^HPEC03VT]TUX_'>C+9'GA2 M<+01FW*TT#U'D]54$HZ*I9Q/EST7,T83`*A)45$.$(5X$'71$`6X&]>!"QH# M%\*@#D&H_VV"%_)JQR>%[SFIP`#`"%1R$X-"F5N9'-T(%R(N`0@,2#XD42%`Q`%!2@*!.1LLH-P)EBBB' M?>%50%Q\LL[6R7\\H(!&U`\I3FB8T"'$,\81/P8T'-/("WGF!6.NA/8GG*7H M[RB.T%_Q'/W99DXA^NF:KI?;$=,%&BX]_6V1M9F;WZHVZ\Y;$"N8L'?HL1J@ MRY]80:S!S46C'5,IR4;Z%E[S0^&RR MVEJ)TU*6'2T"*#E<*0G184L;I8G51,9EBEM\"#``*AP.JPT*96YD7!E+T9O;G0O4F5S;W5R8V5S(#$R,2`P(%(O3F%M92]4 M.2]%;F-O9&EN9R`Q,C(@,"!2+T9I7!E M+UA/8FIE8W0O4W5B='EP92]);6%G93X^7!E+T5N8V]D:6YG+T1I9F9E`E"D203V..`!'X"I[%!]A2Q>6AS=&T,$6GW9^WLS;K6HII)*+2ZE*J0MY M*?G(ZPV2A=1(KDK45L46YTJ>#[QK.7^2]8;S.]0YWSU<2\EY>R^OI[<]MS=L M(YE93ZE91XE-1*0CT`R.F$S4]*D%#9E%#6J#=FJ3=O8-L^1?^'";^R`#&B"D MCL6\%G$D7+%-884R=["$SI,!AKSB0I1Q=3EZ$#MZ'Q1_,2Q_,/Z-3[2D,SX< M[]!F9XS*L"`TF$U*BY&TP_/A#PZB&X*K^8=@8(`IOFWYD;\$&`"`+MEF#0IE M;F1S=')E86T-96YD;V)J#3$Q,2`P(&]B:CP\+TQE;F=T:"`Q.#YRQ62$/BEEU M8%DYJ%@!1`P`H2!@"-V,H>H\;*\89%OQ`![\03O!>KA?,-V._P;)D"#[^N:& M+AB%(O'SDJWH/L&+]P'+[;^`ZP;W^!9@`/.RM)X-"F5N9'-T07EV'3=QB\\T#.AAI M=ZP;,_2!"*TJVM%;@`$`:FY@\@T*96YDC.K_,_R'(D:*$9>K)U<@%T"``0#CCU#"#0IE M;F1S=')E86T-96YD;V)J#3$Q-"`P(&]B:CP\+TQE;F=T:"`Q.38O1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB)5,T];L)`$`7@02DL3<,1F`O@GP6) MN%K)@(2+2*3*`9*4%(E"O4Z58W`4?)0]@DL7B)=G(R&QTM>\-S.[<):;LWEA MKK1%:1^%?JE;,M:LW>S*TTV['7K-JOK="L?K&?[^.G MUAM%,\-5GM"+A$[$1VH'O@UMZ$.#*P$/`M!ZFI($1)FBDX0W)KB(`#*&G!)_ MFXPSH+L[TXG^F/\B>O&-%^X,&N['\4["CSQT6^NK_@LP`/V.;6\-"F5N9'-T M%@F0;CL!>0/EY)$I%XD\B MA8E6'D`M+31:@S?3F[PC8$>!C(NQ=9*OV9F=L1.))951(C83F\LAX3/;5(^Q M9.J,$_7&<:Z:RO[$LY*CG=B4HY7Z',TV\\5\!X5[@,U\!RX`P_Z(P_`@/FA6=0_M..+=KY=B%?C MX]F:WNBNT7V#X;VF`(X*=`:\+'G+'P$&`*,892(-"F5N9'-TKD=N5@P4`T`4!]HB8":R*(BRN$\ MRMSE(-2VZZ&UK4&'QNA\XY?J0%&]D7_@[`"GC]0P:HL#;ZA?R)YPJ\++@+;\$&`#+C*&D#0IE;F1S=')E86T-96YD;V)J M#3$Q-R`P(&]B:CP\+TQE;F=T:"`R,3$O1FEL=&5R+T9L871E1&5C;V1E/CYS M=')E86T-"DB)=,X]"L)`$`7@A!21:=+:92Y@LC$)Q"K@#YA"T,H#J*6@HG6T ML_!0>Y0<(66*D/6-"C:ZRWSL,,SRDB$K3E%)S&G"VXB.%&?HE;1I$/%`!6J$ MD_%F3^."PC7'&85SS"D<+R<<45@L^'RZ[*B8DC&F<4$M5((6'L+=`SW0]4$K M-,K_4!]`)>@:7*O4-"F5N9'-T M_IJU!25)K* MY>G"]?\#/P(]D$=!!^P1J*$>!3'\1T;_&!'H#S,*^L&.@I!M0[,0V39<%G*Y M>G(%<@$$&``>A(3�IE;F1S=')E86T-96YD;V)J#3$Q.2`P(&]B:CP\+TQE M;F=T:"`R,CDO1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB);-!-JL)` M#`#@*5T4LND1S`6T+;38K@K^@%T(NO(`^I9OH>A:C]:C]`A==E$:,YE8*CC, M\)$))#/)4HPQYY,M,_V%5073"M(!HQWF( M5H[S?'G]0;8!:8P(BBWDZ9HZ0J#&2;&;4^$+G"8,1B*E+HM=S2CW" M%[]Y*5+A@Y$]>$+O"YTO%VT@U*%E,/8!O$KJ+21XCL!1.KA'%]BF0CFE4>QG M0VJ5<$JGN`DHO>*/<$%YW`@I9F3ZYR\XK]@)*%P7MA4`3G`CD:"KH2 MK"`70:TZ0+5L4=3:N4E7\29Y!)O!?%,I`)7HRD\AS2 MC:(E>M,F?B@7@1^DJ$2>KI07I(XR6I+:XI]4OE_)D%2QDX_[\T+%FI@;CU&5 M6V7,VB@&>V,G/%URZ[Y@X[T',X9U-@S5I1G]4?,_G6^U$/UL5`BGFT^(M*Y5 M!&?81+"O3:0J)T0T6]5F=YL.XY11VA1TH(\``P#9`@DF#0IE;F1S=')E86T- M96YD;V)J#3$R,2`P(&]B:CP\+U!R;V-3971;+U!$1B]);6%G94)=/CX-96YD M;V)J#3$R,B`P(&]B:CP\+U1Y<&4O16YC;V1I;F7!E+U1Y<&4S+T9O;G1"0F]X6S(@+3$V(#0Y(#8P M72]&;VYT36%T7!E+T9O;G0O4F5S M;W5R8V5S(#$P-R`P(%(O3F%M92]4,3(O16YC;V1I;F<@,3`X(#`@4B]&:7)S M=$-H87(@,"],87-T0VAA8G,4I,>9,Y+C(\1W"#V-[2GFD0X4P&D*A8+PB'$* MX9;_$!;[%480JAT^[L\+J#50[1,-J?(_&/U+27]P/K0N4PM-CA%YYPKA=Q.+ M>.,;KQ6Z8JCV6L?B4F.IO6;*23W>%QQ:Y6-PJ-$#.+2D,4I;.(9#G=L#&P4' M>`DP`$"PUT$-"F5N9'-T[-T7B,W,&6*(;__J,AB8#[" MS&/R_]FL++>M':]L4UIY8@^%ONBZXF9N967;9<&S97[*I[+[)]TUFMW9NM+L MBN>:[6[.K="LN;:WU_=';2X4F%(`?4)$6LPB-8*(QRCB,*2#0^\YT[6!@"28 M%_."\/V7R0$'C/Z'^8"A_D_X)B73'V.$'_7H'>G: M#/4L\8[.]VWL$.FXL(_$1HQ)8L$0B>'`-)_H:WRP$O9!8N\DPMF.E=&S/`;^ M"R;DO%XV>JM?`@P`+P?!6`T*96YD2, M/0K"0!"%)U@$IO$(S@7,9L/&)%4@*KB%H)4'4$L+1>OLT7*4/8)E"G%\8.<5 M'(:/X?U,64DNEJB3ZG^@93?4UTQ.)(-$*D M-B!`1`@/1*A'2O!VI!34`)G7GO?\$6``CN(%5`T*96YD0JY+($B@$%+!3,]`P5C"WU M+`V`P$@A.9?+R9-+/US!DDO?`RC-I>\4X*Q@R*7OZ:M04E2:RN7IPM50CPH_ MU#_X_^#_P_\'_Q\&PG8@9/_/Y>K)%<@%$&``+9LI6`T*96YD@YC(EQX6H`\GDQ4;^HG>BCR!R\5&OB`\/JIRS))G3;&Z.L_[;H?]; MD'\Z`OU4O_9;"61_OYN;TRR9Y]G,M(T"V78JF+9UH%2`K(:^;64H5:)4C&[U MM"3K)%<@%$&``?$)68@T*96YD M^4X"S@B&7OJ>O0DE1:2J7IPO7___V____`^(? M0/P`B!GL_S-@PP^@\H,4-S`P\&/#7*Z>7(%<``$&`#W^I6,-"F5N9'-T5H[R`#"T<#1F=3B!.V=%?'%X&KP"Q0Q7I'_ M(7T@L]!4/PUU0]U0W`PJ M\EV``0"_*])6#0IE;F1S=')E86T-96YD;V)J#3$S-2`P(&]B:CP\+TQE;F=T M:"`R-#$O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)=-`Q3H5`$`;@ M(5N03/..P%S`!SSD!:M-GII(8:*5!U!+"U^T9F_@$3R*'(4C8$7DG-:W\K;\?V9ZRO&$`-H22'R&(D2#$0;]$0Q.M<[M,D8H?.3 M`D3XULR9'R#HO3_02J1[6AF=,AB]/1Y<+S0+K5_H M3B3_\F5\6O*'H66#=U9%*U(3_$1HXU['(G+SO*--/ME'(%A#UC)?UWS/OP(, M`/QDV1$-"F5N9'-TPP\\_& MD01BI!6)Z4DGEG7(.S8Q'@/I=,6T0]3:P0"G+ZLMIQG[2S$Q^U/4V4_G(PG9 MSV9RV!\WG(VY)'+T.XGJ?PP;("`@E-BA38MG<;5TG@TMW1>"O`HTE6Q;7G-" M>TU1D_S*W;;=+%?+13'TK!7A0D:>(++P2O>SP(7,!-LF&K$(5:0&)%$A0<0"@I&`%M;TW\U'"#5(:*FY6%E;6?-IF9M4UUQ=?;\IOM!RR=K6BWO M6-=R_W!MM9;#O7T/LH:?F1['R3'T9-,V%GR6<`:*>`=KP8G#J$G8R_XQ51D_W%, M^#\(Z6UP)Q@MN#']P*64-'`.Z.V@C_HC MP`#Z,\>:#0IE;F1S=')E86T-96YD;V)J#3$S."`P(&]B:CP\+TQE;F=T:"`Q M-S(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),C%2,%`P5M`U5#"V M4#`U44@QY"KD,C8%"AHHF)HJ&.L9`N7T#"R!P$(A.9?+R9-+/US!V)1+WP,H MSZ7O%."L8,BE[^FK4%)4FLKEZ<+%``3R.(G___]_`!$/[('$@7H0`<3_&T$$ M(Q#_8P81[$#B#XCXP0_2P(^B"Z&U`4R`M#+`]2,(D$E_F.$F(0B$F7#B@3P6 M`FP/3@+A`(($EZLG5R`70(`!`"])V*$-"F5N9'-TUA:5I?(3,!8CM;!R9:J40)%P@0<4!("4%$=0V-_-1 M?(0MM[#\,EDC:$!BBZ_8F?B8X"J80M+(5U-)"T3KKS7*4/4+*%4)^9\&@%J;X-NR&/__L MR5YK;?5XKVVOW:D^-OS";6>;M7:]MKO&SG;UF3V]/CSS8>#J3MN.JRL[Y^IP M9RS1$$&8+#!Z81/C'9 M8NM&&#/#_6'^0?XAB/_-)-Z2)?U#)@>2(*M11)*9Z$@B4>V,>V?]@ELIMTY> MC-0`J:\?%"521WT`.YC;':+3F0BUN^WQYGBBN2+A61-E$T MJ.4#3.0-C*4#4GD!(VF!I,<30(]&41NTL?@#P\>@,BB;D5F:N<6P0'VT7TA: M1]K35X`!`"E>2SD-"F5N9'-TT)$!81S?0%1=&OMR MODOI.&DXBO*F)H_X@*NL;$+2A`#8A_LL.R?*1\YYR=^*-A47%;\7M`W&:.7.3]K9UYH M;YY76@M^^Z153=DK&T/95ON4K?9K+BBK=WS\^?V@>D.B-2"<'6XB;L3?@W86 MZ!$%//"EM$"JV"0$'+`^3&^4X"S@B&7OJ>O0DE1:2J7IPL7`Q#P8R/^/V"H M_S]*X"%P!AV7JR=7(!=`@`$`?U[4[PT*96YDS0L0K"0`P`T$B'0A8_H?D!>]="K5TL5`5O$'3R`]310=&Y]<_\E'Z" MHT-IS+5U$(2"LX'PR.5"N!N'I,EFE%"L:1_@":-8:FW+T`]HI'V=2$QH=\3, MH-I2%*-:2A]5MIY1@,JLZ'*^'M#,D9D?P#9J`,]:`@RM!8#;'+N5(SXAY5M[ MN[8#I<,5Y,QW5UIBT2G#GWJ-]1*KEOQ8O4F/4*7793&[SW$ M*0Q,2/@1$OC2I40RPZ1S21+YB?G$:8(]LNML&LLDFD9+U$*^C[S*.?R2-.%P MASN'JX^UQ!SF>[F(7L!7Q)Y+YKJP5/!%()6'D`M+12M M$WB%UPIXD8`7"-AL$?([LPN)`:?YDIF=F?^?\LSEKG2GA=OM7%FYI\*\FFW% MR=R=/9U%<&H!0_ M@*<$WT!'U'P!+5%ZQ$1$5F&DIC]1\`E_*0R:6Q1ZRX\9*=`K=(PAPBNT>V!< M85*@!5B0+CB"[`HZ(H%$2TV$C@@#6*V*D-^>P=6!0E9`XHNC8>7AC3B290(; M&Y>.D>9QLG.,D+&"*8)7(V:#GB'"4_,9H4/G7TPSU#]`LH(,'Y,93;@@Q;.* M8J\1S%F(*[YW0GIB8ZH+-G4?3%L?G*0(+O>B"S#7M;DWOP(,`/%#+@T-"F5N M9'-T2E.-J*Y^' MKS>NUJQ-V!I5HO!()9@1!6!"9`"%C>?0EO2$R(GJCGQ0=CC648@]HA0,^#W' MWYAKFC/>+3%_?#3B7UME$-CZ,_HMP-->XAC,`9,">G.C:>_0U0[ M6E^U?^P^8/+E/_"FXF?^$6``HS#]$`T*96YDE"T75SM`=> MI$>HNRQ*QYF4QW,A3@@_F"0S2=J&2Z[YK.*VYN:/]X_7ZB_(5G@16,$C`!THCE8=LXD M.)'H9,Q%IERF3&Q&V'YE,.8#G;$<.+6=1LP3LTLL/B'R)S,22,25/4X,9*D* M?*J)(758&XUKH_W:*+C?P/_+?$0/Q"-:[!O.2G_IETP87$"G-_**W7+4=P8, M4=&/T:#;GA[I1X`!`'UQQ!P-"F5N9'-T`7M@A,DR-D/(#9'U9900Q$!;<0M/(`:FFA:*U'\TB6*8+KKJ2T M=X;WP?"*L6-6;&*L83OB@Z8S6<5IXVDJS4-5J4DHI.1$*1 M"#WE7P#B]U_0TM.6/@(,`.:*?+P-"F5N9'-TVS'J&N->M9JM]5XKD^MO$FW89#A"XVKEKG5>LMSJ?L7 MV0W2/&JWD>:6>6EV]U?:2C/D,=F8 M#K:0%V`V_9+^Z,T,M<.W'5`ZO(Y`<"K+8'`""O,9]NV83_-,[V_VF%C-:_CB M]U!Q[@*[Y7\4I#^!$[XD?Y-@,)1SX8U+J\"5ZI'1<&2YW`SR(#\"#`"31(K- M#0IE;F1S=')E86T-96YD;V)J#3$U-2`P(&]B:CP\+TQE;F=T:"`R,C$O1FEL M=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)=)`[#L(P#(9=,53RTB/4%X`V M(KRF2CPD.B#!Q`&`D0$$(P4E M@+2XUC6W/]!.PE6*.WP(,`!*U;%)#0IE;F1S=')E86T-96YD;V)J#3$U-B`P M(&]B:CP\+TQE;F=T:"`Q-S8O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB),C-2,%`P5M`U5#`S4#`Q54@QY"KD,C4'"@*Y9@K&>H9`.3T#2R"P4$C. MY7+RY-(/5S`UY]+W`,ISZ3L%."L8^IZ]" M25%I*I>G"]?_YO___S'\__^'H?[_#P9[,/[`((^!87(@=2#U('U$@P/R_QL8 M,/$?(/XW"#`#`T,].N9R]>0*Y`((,`#B][,P#0IE;F1S=')E86T-96YD;V)J M#3$U."`P(&]B:CP\+TQE;F=T:"`Q-3(O1FEL=&5R+T9L871E1&5C;V1E/CYS M=')E86T-"DB),C-7,%`P`6(S0P4S"X440ZY"+E.0F`&(:Z)GJ*!KH&=@"006 M"LFY7$Z>7/KA"J;F7/H>0'DN?:<`9P5#+GU/7X62HM)4+D\7+@8@8/___S\A M^O\!AO__1VD<](=Z_/0#&/T?/WT`!]V`1C.BTO^@]!\H_0-*?X#2#Z!T`X3^ MP\`(CE<&!F:"-)>K)U<@%T"``0!]DYOH#0IE;F1S=')E86T-96YD;V)J#3$U M.2`P(&]B:CP\+TQE;F=T:"`Q-C@O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E M86T-"DB),C-2,%`P!F(S`P434X440ZY"+E-S(!_,-=8S5-`UT#.P!`(+A>1< M+B=/+OUP!5-S+GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+U_\&^S\,____9V"P M?\`@#Z8/,+"#:08&9A!MQ_Z`$43;V'\`*OS#8%'_`TC_8S#\_X.A'D@K`L5` MM#*0M@?2[&@T/PY:?EC0#,```N)Z0C27JR=7(!=`@`$`H<"]H@T*96YDH#JV*&HL_9F]B;["!X]+&Y;.0K^9`,+W[#LS"X[_A0YNGKY'+T`CPY

A_L<+M`!^QH@[?K_031$M@W3#6,D5*" MQ4H25$(0A)P@"05!53`(CWX\&S`[((90F+I`-R0A)Z0EW@_^&]6INH[L;ZHA MQ^4-V\CG0U`ULC;B)D0+:0L)@=%_$20A(Z2,!H#12%0P?@%6$>S@)<``0:Z` MKPT*96YD=1\NCS"-TF47P.#,-0D!S'&3TM=UEKG5;C,R\:?E6[?9=U)\:+&2/'@`U*LGS9: M2=$]ZO'P\2;=G9!]1D>.2-F39X#6SX#68]-S!A+9>`U^HN45?/!$"PX(*9^/ MN(1T%_RDGT@C]D<6$?P)=L[LHW[&M.!$6-Y]=Q@NQ4(_A+;9B-!]X6[B/78Q MC9`>D`9HVTA\[)\8@.0WY+Z39_D28``G>S/I#0IE;F1S=')E86T-96YD;V)J M#3$V,B`P(&]B:CP\+TQE;F=T:"`Q-S8O1FEL=&5R+T9L871E1&5C;V1E/CYS M=')E86T-"DB)M,\Q#H)0#(#A&@>3+AR!7D`>CP>H$PEJXAM,=/(`ZNB@T1F. MQE$X`B,#H?8M)"0Z.-CA&]HN?QQ32!'--9D%I88N&N]H$EF&E,84!5IN0;B2 M6=+YAKE%=2*3H-K)'55^6)-&9??T?+RN:#?(G<_\G79,,Z8>J!RE`X3>T3EJ M`"C@`_WP]W\Z_RKB/!><,6XM'O$MP`#F M'="A#0IE;F1S=')E86T-96YD;V)J#3$V,R`P(&]B:CP\+TQE;F=T:"`R,30O M1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)I)`]"L)P#,4C#H4L/4)S M`6VKK=I)\`/L(.CD`=310='9'JU'Z1$Z=A#CB_X=!'$Q$'[P$I*\I`.)I"^= M6-*>)`/9Q7SD)(,823*4?C=&K1MEB)%L#SS).=Q(DG&X0)W#R6HJ,8?Y4LZG MRY[S&:M6;47Y0&.@[Z(GKG_BUH;236F]\WND\.$>-^?-A$VX#F`YN]ACWB9((71AH M*]\_>WV0YSFO^2'``"="V4P-"F5N9'-T1<+B=/+OUP M!2,S+GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+U_\#\O\9&##Q'R#^-P(P`P.# M/3KF^4X"S@B&7OJ>O0DE1:2J7IPO7____Y/____\#1'P`$0>`Q#\& M(/$`2#`P8"%D0+)_1B#1`/0_(S:"R]63*Y`+(,``.,C'@`T*96YDO0DE1:2J7IPO7#_L'_`?8&Y@; MF!D881#$.\#^@/^'/9>K)U<@%T"``0#O:Q?Z#0IE;F1S=')E86T-96YD;V)J M#3$V-R`P(&]B:CP\+TQE;F=T:"`Q-S(O1FEL=&5R+T9L871E1&5C;V1E/CYS M=')E86T-"DB)Q,ZQ"L)`#`#0E!L*6?R$RP_8NW('ZEH5O$'0R0]0QPZ*@IO] M-#_E/N'&#M*8=N[2R80\"($DSI,E)^4=+2Q=2KRAM]2GM*XH:6X+NY)8TKG& M*J`YD;=H=C)'4QW65*()>WKU:/@-N`1?P(,`+M_RX4- M"F5N9'-T]#!LY&^2IYU<>.K%G'&6S[NWGNKO0/V1ANXW=Y=2L_M M="OO;Q_//%TQ0"M4P$+NBX"9:$@=W&7.O8E;XEBS=/-<_I:ULFV@=?3WS/WP(,`"B:'\,-"F5N M9'-TBC,)8^0O(#;%C:+ MG@JK@CT(>O(!=(\>E-US`[Y8?),\0HXYA(XSX[H*%O*#S/SS5;]UO?,R>K?U M[F7`-]S(7*=^/;B+?MU?\7?IGE]Q-V'WY#8>NSON8[=[N'8#=M.].[P?]SC= M(!%8(LJP8@/`S`7@4F5;*FV%AK(E;J>9(E`D2C!'RRO&8*B`#:W(X0J&P]\N M)]N_.WS#O9>@_/;D=YV<[=I+^0[T](K'`9M' MVGML;N4O]XP>$:F:%FY@R5F``"\PC@)!S!\F)X,GQVG"K. M-<_`V8AQKHH@AK09_[%DKODQ5](G+E9D[:F->5HUZFA5G8$7G4=M2J+2@!O!GS`3P$&`/H=\Y,-"F5N9'-T&U4D2:)D=@+T!LBXC85:0`$BZ0 MH.(`0$D!2NKX:,M-?`27+BPO,[.3EQ1>R=:WLO3^QE7E"[_AI[KS]Y7_+.F' MMC7?"[ENUJ6_+=9%S:?R']^T;RA_]]N:\F?^3OG^]<&7E#,?/@5W`+*(&?ZNZB5B"I(1-7)M*J7:=/2-$"C1D23 M1D11IU4Z;=J9`J01E494.JW2:5-FFB"-)$DD2::39/JLE:F'))(DD2293I)I MZ&1J(8Z8.&+B:1-/0PO3"''$Q--GM5"840?UT`"-T#2C>%$+!:B#^FNEORX. MT`A-4+P6/37T1O\"#``34]6R#0IE;F1S=')E86T-96YD;V)J#3$W,B`P(&]B M:CP\+TQE;F=T:"`R.#0O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) MO)*]:L-`#(!E/`2TY!&L%VCL@Y)+)D/:0CT4VJD/T';LT)+,]J/Y4?P('F\( MN4HZ<2FDE$X],/XDG73ZVUQ30VNZV5;/ELZ&7 M=]QU6#^3=UC?LQWKW>,-L=`]T/[S\(;=+<88`[11#T"A_P"05!/#4F`H(I3\ M/[$X@?FHWPRBK?BN^,KM42X.B_2II#'8?DH`\2@.[-H?]9T`?3!HF1E8/QM4 M&?1A"9)A-%A#2C6FSIU9!, MZB4V]VK%*3_JQ`<'E;:IRC)5DS$54LVHLLER_J[I_UP8W?B6V;6Q[NK>Z\DH MZ;GIJ3[5I/7T52+:/DXJ.42%32HS4Z>3*(Z&-8,]O`48`)+"Q!P-"F5N9'-T M2+NM;@#DB=B M^2+)K;0%<1.XYL3U1J$F-TC*2*.GT%*OU=*(+!0+L-1-X!J?0"_`(S1G,,Q0 M_T&P!+G1!/"/L)X^P^KR:VAGJ!;01WY1?63/P!9^O;;P"^^J:?.5%Z5E+],W M>^&^>)22-^S%#7F46XTPJ%>:@KTE-$]-XFZGMG&]U$ANC,8JOP@=AS7/KA"F;F7/H>0'DN?:<` M9P5#+GU/7X62HM)4+D\7+@8P^/__/X3!#V,PPA@,]3"&_/__#X"\!R!%(,8? M!G8(XS\#,Y31P`AE'&"`,B`Z4!GV$,8'+`QY:C/0K<#F'G0WPWT!\Q?S_0[7_'[X,:%HACL'EZLD5R`408`#+#Y=S#0IE;F1S=')E M86T-96YD;V)J#3$W-B`P(&]B:CP\+TQE;F=T:"`R-S$O1FEL=&5R+T9L871E M1&5C;V1E/CYS=')E86T-"DB)C-,Q;H,P&(9A1QDL_0M'P!=HL*4H"1-5VDIE MJ-1./4#;L4.K=@:)B\%-.()'!LNN/Q15BO)3%X'Q`].+C=$[I=4V7D8;M3NH M5T,?5.[C`PUN-T9=Z8TNXW%0+^]TK*EX5N6>BOOXGHKCXXTR5-0/ZNOS^XWJ M6Q)"A/F(DP87YOZ$"G!_(_0B!R:QBN,9!I$!]OH2K03&/H%^'>;;)4:,H6-@ MYR#)8$*=SU)PJ',Y`X^ZJ6(04&>;)%`W!@ZH&UB@KDL#J9)%K/,9BUCG`>9=C_/WESE# M&Y8PG$!W-3W1CP`#`)Y59NP-"F5N9'-T8'+04=5O3DV/+X>\+`'0>^Z#ET/`1^Z>F==D$7]7?@L.UU;]M=ZMCS\QL= M1FJ?>!>HO=-]:@\/U]Q3.][SY\?7*XTW5$2DR-*(9%G<^NF`&UC!&GK8P`C% MK*"'T=0%673V'[/5N4+E/6[A]=Z8-<-&9LV#R>*E.$M>W*1GBXME,K7(22/^ MZO]8KQ9XC&9NS.1A;;K5ZJS_L3[;),O0),L0$S)D>_YTG*R36GG6P%YR98'U M2$$/!=V39&^4&HH&N@9V`)!!8* MR;E<3IY<^N$*9@9<^AY`>2Y]IP!G!4,N?4]?A9*BTE0N3Q0*Y`((,`#!+Y#EX!TP2L#;T'OPLMT2`_#R+=YOCS/D M*Q#?(:5L83,,AG@R'"E+6F[41PIO8;60PNQ0&!U*H:.B0F/4`])?,*U<3?[> M!RLX*H=_<3LU-ZA:IITT3-G!8H@>CGX(?>72T%N>=MK0_C%4#AW6GZ!7;@8X M',S1$E:,@L,+P1?`X"MA,6Q&JM\?.B=8Y["'CP`#`)LI<1H-"F5N9'-T*AT2Q[!?@&:1*HCF"(5D,B` M!!,/0!D90##;CY9'\2-D]%#EN#.E:IF(K$_R6='=_;YWK?/NLG.^=;UWNX[> M:>.E*-?>^74G;^OV6KXK]_)&VY&:9[?QU-S+.S7;QQO743,^N,^/KU<:;XEY M06#FA%J,6&D!%7-&`/-L.`5.-<^!)\MQT`,Y88%=BGOP'C:?.%<'%PSI5_EE M$B-/1AH=C&=FHR:9)YH)EF&B3`53)\.X&'+%0)!VQ0#HZ,4!VL6>6)]ICJY. MC?]P^JL.A9"+A!`E$XIIU\5I7GJ`))VC:&1*DIEWRD`+= MC?1$WP(,`#%2V!@-"F5N9'-T&:(J6:(N1F'9FK,KHQ9R.4H:Z MD";;6FPCUC8EJHHHX3%8OJ6N,E@N&J+)M&+3:;&U6-E/RQ>+SHC-7M3>;>_R M3S1?K']:_66MJ8>-_E@49@FLX";``!=0(!P-"F5N9'-T(.@DP^@C@Z*SM=' MZZ/T$3IV$&,N]`SD@_!#$E.BQHJ[,&B6>,WA`:;@6?NQRG)[M!8 M4&SK'RX26`G(@9M9"0C2LA)0IA3FCZS^.:$0/OS41>>(`["R M"Q#;R(Y"92F`A`LDJ#@`H:0`A=KF9%BBX!JY`5NFL/R9G8!`,BZ>O#/S=_[^ M:B6%U')R*G4AJT*V)3]Q56M1CUI7^D3MWLTC^(14J:0_,ZH8.>R?`L[N_],F4DF4QGG4E2V]SF?N"C%KDNM3'*M M3;)*UX_LCCQK.-U*47*ZU#JGL_6OY)PV*[F<_P[T,?Z9( MEA:*ULG1/$J.8)DB[#B+:4(^O.+_GULT6+)BA87%:P8/R,-F0BW3#)'B@;>.&M5__1,%"%`"C-5)H)1'U3A68.OS&NAL+P]'\(:MP- M-\,%18@S\E`K:DDR']7-JJ+Z\UGI[`^/,Y[S1X`!`%V^E3,-"F5N9'-T M0"TM%&W-?[1T7B.=;4J+D'$$12Q(Q9V3;@TXRC3;FG$8+ZAI-5E-+-,J6=CY=]IK-%"ARH)$.\)`V4$J+ M)PD`(1JY"NH`'G4;!1XA4750YF7Z`>U57H#P(O(%_3_@6_Q'\`L/W.%SW.#3 MAJ%A%;*9O%&)I*B9PWXO=\'R.L]TK4\!!@#EKXJ>#0IE;F1S=')E86T-96YD M;V)J#3$X.2`P(&]B:CP\+TQE;F=T:"`R,3(O1FEL=&5R+T9L871E1&5C;V1E M/CYS=')E86T-"DB)?-`]"L)`$`7@#1:!:7*$S`4T62$Q5H(_8`I!*P^@EA:* MULG1 MSM*EK((O=UJ7E)PY2RG92P$EZ^.&+27E@5_/]XW*+<$`:%>C]('05#[<1*@Q M2A<*@9=/!/03+VT,N'"<9@5TD9>Z`CZQ%\\$!IIJA!K_!`.2]H=\+?D4R:!9 MI`^:>$C51C#:.7#ZM#-&1^F,T9'TQDBA/-0*J=$ST*ZD$WT%&`"I1T"N#0IE M;F1S=')E86T-96YD;V)J#3$Y,"`P(&]B:CP\+TQE;F=T:"`R-3$O1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB)?-'/2L0P$`;P+'LHS&4?(?,";I-# M%O=46!7L0="3#Z`>/2AZ;A\MC])'Z+%"[?C-M&!!W+3D1R=_2+ZFQ(%CX(O( MA\#IR,^1WB@%UN>@0_N(P7TXHEWRTRN=:BH?.04J;S&!RM/]%4/]\ MH?J:1&1PHJUU._23,9$S&[QUN(?&$^MOA& MW<\[5H8N5:R?-L:X73,4:_K=FLZOR=7_M,U9,NZWH$?_2X,(O"WHS]!5=B^0 M%[RA!\R-)0`TCU^&C:&1.4W.]\Z2MHS;F3P'C\3UWJ-;?HI^T$U-#_0CP`"X M3T#:#0IE;F1S=')E86T-96YD;V)J#3$Y,2`P(&]B:CP\+TQE;F=T:"`Q-S,O M1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB);,HQ"L)`$`70C2D6!B%' MR%S`;$("B54@*KB%H)4'4$L+13LA>[0<)4?8,D7(=U6TD,R']XL_6P(=IRC+9$4X_1U##XPP#>E^%'[T'`^A"U<0CAPWR8 MO)A*-.(A824`6FG:T5.``0",QX3?#0IE;F1S=')E86T-96YD;V)J#3$Y,B`P M(&]B:CP\+TQE;F=T:"`Q,S(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB),C)0,%`P`V(C4P53Q.3F@T* M96YDQ#TY'5A]>A!T7/;-^NC["/DF$/I.#.)V`3"1PCD3](TMK)[FLVM M;8[VK89/.+2TKGBYW]7VIMI5+8VCO7S`J8?RU1Y:*!]I'\K3\YVMH>R?[/?7 MSSOT]X`T%.(_JQJ8)<$GN/.6Z[ECYNN6*<'\47`@$Y9(+OA((;C.17(.#,(< M06$,3`$=,.@R"ACT&0<"N>`CA>`B'7I#`:44,2>,"9H.,AS`17,`5\V!#100 M"H'OORH.,/)\12_8,H8_GP):&$P`'GIX@5\!!@!QB#0IE;F1S=')E86T- M96YD;V)J#3$Y-"`P(&]B:CP\+TQE;F=T:"`Q-#0O1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB),C%5,%`P!V(3(P53"X440ZY"+F.0F`&(:ZYGJ*!K MH&=@"006"LFY7$Z>7/KA"L:F7/H>0'DN?:<`9P5#+GU/7X62HM)4+D\7KO\? M^/__1R<>R.,@#MAC$@WU.`B&_]B)?XR8Q!]F',0/=AP$-G?C=#PV=R,YGH&! M01Z-`&(&>S#!Y>K)%<@%$&``.%;C<0T*96YDH2, M'4J?B4Y*'_P?_/S+RS,T6(3D2\Q*O*1PARQV$VN1I+@PB5F%*_%\@]J"/F%6 M@-Z%'71]6&,*VN[Q^7A=P6Z`:!!$W#-'O&75*!A3XRS"/JA6#LQU`?)RX!%! M?<3+?DY>?9$_>$5=-4574>O^:!TU-$43_IJ$TRB^P-;"$=X"#`#C5H0G#0IE M;F1S=')E86T-96YD;V)J#3$Y-B`P(&]B:CP\+TQE;F=T:"`Q-3DO1FEL=&5R M+T9L871E1&5C;V1E/CYS=')E86T-"DB),C50,%"P`&)3,P53"X440ZY"+A,0 MWP#$M=`S5-`UT#.P!`(+A>1<+B=/+OUP!1,++GT/H#R7OE.`LX(AE[ZGKT)) M46DJEZ<+U_\/#$"`A7P`)!AAI/Q_($`F#]AC)QOJ\9$,__&0_QB12)`3ZB'D M'[!SL)#,()4_V/&1'_CQD9C>PN\YJ!?!08*+!#F-&4IRN7IR!7(!!!@`Z.CS M=0T*96YD0JY#(R50`)FU@H6.@9`N7T#"R!P$(A.9?+R9-+/US!R)1+WP,HSZ7O%."L M8,BE[^FK4%)4FLKEZ<+U_T#]?S!N^(^)&1&X@8&A'H09&!C^(^,_[/\Q\`]^ M3/Q!'A,_L$?%,*<@\'D@[O]_P([__P$&&);__X#!_O\#1HBY7*Z>7(%<``$& M`.,SE'0-"F5N9'-T*5[)/BY(08E+"B`D7PG9E@:%R.ZXO:->WHN" M&BV?]0-^F/F%?P48``3\B9\-"F5N9'-T7/KA M"H:67/H>0'DN?:<`9P5#+GU/7X62HM)4+D\7KO\?Y!'H@3T*.E"/0`W_41`C M"OK'C$!_V%'0#WX4A&P;FH7(MN&RD,O5DRN0"R#``#9JC@H-"F5N9'-T1<+B=/+OUP!2-++GT/H#R7OE.`LX(AE[ZGKT))46DJEZ<+ MU____Y\#$S?\1\6, MF/@?,R;^PXZ)?_"CX@_RF!CHC`8&!GX09D#%\ERNGER!7``!!@"CY*;;#0IE M;F1S=')E86T-96YD;V)J#3(P,2`P(&]B:CP\+TQE;F=T:"`Y-"]&:6QT97(O M1FQA=&5$96-O9&4^/G-T7/KA"H8&7/H>"A9<^DX!S@J&7/J>O@HE1:6I7)XN M7`?L&^IAD.$_PW\N5T^N0"Z```,`JZP7=0T*96YD0\(0283U,0WF.CD`=310:,S'(VC M>`3<&-#?XB+Q)3;YAC]MVL8I&\Y$DG.:\R&B,R6Q9-/'+(QX8D*32TUY?Z+" MDMYQ$I->29]TL9ES1-JN^7JY'GZ]Q8M+6WI+<``>42^;@T*96YD MQ10O7N\84?U^,!?G]]O M--Z22+*R$?$;#V=CH%'7;,K&TEIEPO>?]I^Y'A:Y"PW1:'/(>J/72R5+J:&AU-"8 M3:44QXQ6#58,_K(2#)P'F6W2%TS#RCD+\TO1K/VVV%,\#H3N1GJB7P$&`)XB MY00-"F5N9'-TKHH.CG`/7AUO MZD-]JOPUK^5&4'64RK>^:UO6EYU*^NN@L5-&T[TFW4TLXJ:Z4_=/K9_6=5-- M*=UV`/)+OPKR&;4X2W&%'P$&`-R?"FL-"F5N9'-TQ-SH-%6HQH^@2]`CX"-)B>,P)`"SM`#![/7SH)&"B:B!FE0 MIK_XN:*@;8`B1V^0]=G)Y0^#^V=_AIA]`._`&ZQ>+3R\P-E2J&G#2-JY:/-A MW(C!>[)_L,'#L15$\TW-#_PMP`"O*MD5#0IE;F1S=')E86T-96YD;V)J#3(P M-B`P(&]B:CP\+TQE;F=T:"`R,C(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E M86T-"DB)=-#-"H)`$`=PQ8,P%Z\=`N<%2@T-@TCH`_(0U*EK4!V#BKJFC^:C M^`@>/2Q.LV)J7\LN/_X,S`SKC=!&G]]P@)Z/!P+UZ@M\'!/?^"G;IGE_YU'+UY`XU5W=(X.KT<.T"5^V] M^WC_/'-[PR(RY*+6D7XGH@88B4H@$>46HAU`10-,#+V50D5J;#5,61?VNJK'O";2#GD1?).5$#XUS4W+7_6M*-FQ;J* MQW99\WVUK!YPM?SQ39N.RG=N5E1N4:=R\_K(-97="Q_VQR_JGDC&$.Y$E'!R M"FAT_G'#15QLQ(:)DP MU=R)CK;RW^!K0L\=O=&/``,`/%0=R@T*96YD?.,A%S6BDJ9Z39"_+ MY8QI]8&3$M-W&N28ODD!II/%E!RFY9QVVZ\-EJ_(S"?82^0#Q!*O`)%IH&HE ML)0!X@:THN!O9OW\D<(.\R]S+6__I+[+UBHQ^,(0VC-8O$:&IM/&*6ZC[K91 M)6T]+K0]]M#2GI.\61XEC^`Q!W$Z&B,+JXC?(+\,NK_5E6YI[1M]U^J/&KZA M"745RG97ZYMJ5]W3:/7[%QPZ*(^Z::%\IG,H#Z\/NH:R>]&_/W^?T#T"6H8T M)IDP;"C.,!>T^T*=01/<=<*242F<3@2K#:$GG!+FC-G@,!.FJ5\Q7H!'F!6, MX,6`R*Q!ER$3E@PUH.=6H54LP23,&Z:>T14+P?,5PX9QA42V@1,6N4C/G8AP M(L*%N6+)4`2KG+2*D@1A3<*@J(S7DQ;F`;.,9VO1!4$O`J0R84 M&2&"]`Q%B$28A*&_``;$KPW.@*<.WN!?@`$`O;ID%PT*96YD#E2_DO3&&"?= M%5LHTDY$Q@S5+;T;M^E2U2>JME==%'48B-XF]6H5\*IQK2IP>)#N489$Q;I? M[?#?,)'?Z`]R5?(=O2"%D[-.PX-ITE0=^IZ]"25%I*I>G"]?_#PQ`@(5\`"088:3\?R!`)@_88R<; MZO&1#/_QD/\8,4BPY7^`)#-.\@<[2"4N\@,_/A+36_@]1ZH7N5P]N0*Y``(, M`,Y[#>X-"F5N9'-T M>`3N!1)`"DV8D))4*D.E=NH#-!T[-$KG\F@\"H_`>,,ISF\;J6J5$^C3^8PY MNVE\Y;=XFP??[/RIIB_:M-A7LMVN:[^JUE6+M?/OG[3OJ7SSFY;*)YQ3N7\Y M^)K*_ME?SM\?U!^)9^0>J3;[E[B0:;/!0+8@\Y*Q=(K39F5R,B?IQJXKY(9D.9>P@AC38T^O M=!-@`"4'#X4-"F5N9'-T8-%2MI MI=0T;22MY$ASQ0Q*XLU&AM#92%K@-'\9."T4F9YBT2/C*`62R](JN[3`:1Y_ M62U(FLJ%58RD:3)N(:Z8]=?T>-+F:W3(TN0'=LWT*WHJE?_01G(I?Z,UY<*^ M@;L>GN!+@`$`3/Y)?0T*96YD&/WF4 M`BY.R%X/!2'*9//#5"E:ME-&FRLA&BXG`%R.`4(Y$C(`^`P$!BIB`)DR^6DY6F$DB:2C+Y M`9#T^O@)[.%K\&K`6_P28``4:,?A#0IE;F1S=')E86T-96YD;V)J#3(Q-R`P M(&]B:CP\+TQE;F=T:"`Q-S4O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T- M"DB),C%2,%`P-%+0-50PL50P-5%(,>0JY#(V!XH:*)B:`J7T#(&2>@:60&"A MD)S+Y>3)I1^N8&S.I>\!5,"E[Q3@K&#(I>_IJU!25)K*Y>G"=8"!@8$=&]$` M)/C_____`T1\D`<2#^R!Q(%Z(-$`Q/\9@?@?,Y#XPXZI#)M:-`UHNA`V@(B& M>AC!@$4_G/@!-^D#/YQ`&"GW=RH?L!QZVVAVUWVQ9V[0#QU'/[W*:I'G1_2#-`^O2G)YN=2O-]*A?G]]O M,MT)L)8`@G'`;$;`F!JK,15BQE+%$L$F8D0!#V(&?CBYBHSR"F7*X$S5!6O- M4K3LY3O>,BT8`RR&-U_# M9@T*96YD@TK#)_I>?F4R:DB+Y":TS MNL;XP"0W7=F:AC&M5*AR\S*ZW+&L,#I3DF.T-W.,RN.&8HRJ`[V>[QM66V3N M7?YXS(Z&N@-C"T4#]>`"+`&*80:PL`+`7`SL0M#+=FMD[7`7]`YKURK7M%5[ MIMOB,^F;X0PENZRDEN.%_2K`SJ<3@3BK1>Z.XJ_"$7P$&`"@.R^`-"F5N M9'-TK"ST=&7>WI&P%]DRJ0ATB)Y1N`BBV1++9)] MXE)X)J5LS"'[3D[R+\``-4FOT`T*96YD^IZ]"25%I*I>G"]?_!@8&9@C!P,#`CDKP____ M_X$\D#A@#R0:ZH$$`Q#_8P02?YB!Q`]V(/&!5LJ`+@"Y#)D`8D8(P>7JR17( M!1!@`-<>A#0-"F5N9'-TA!T7/Z:'V4/$*./92._Z2++-C#1S.9 M?_Y_XGNW=]Y==<[O7>_=J>-//G@4<>R=WW6XV^UO\%V[MP\^#MR^NH/G]A'W MW!Z?[US'[?#DOK]^WGFX9Y&Q$9&%",Q$060B0FD,R8A4,M>R&EDK65`DF3=: M-,3\QREL3!N;"YK"NA`>4TP4H4ID54L&TS)5,((Y7&:BVUJ)#`B%KI5**C#" M3BOGA(F@5EE=*E7I(?3C$)<:UV$VF&1S@QD6>2";+'J5F9HQP,*`*ZC#C5I, MA>F"^3^;C=">F>QHE9-RVSWJ[MBQPGQL%,I/R5RVT\RAY,?+BQ;X8>`7_A5@ M`+W\YW8-"F5N9'-TH"T8X>69`LX1_-1?`2/'HR5WUT*'4(.$('0 M]R/0HF*AN8Y=.'U\T@\GW^(]L2LG>U7O)MMQ+MGI; MJY.L?-7#S_%3RHW8.?0PZR(S(#1(K48862.V,Y*1P&0`(_`<-U&?U!-R&5IT M51-9#U]U:*V-;("S#GMK04ZY63.2,S(FJX&CY]'_3,CT&E,RW/E'\!GL$W[K M9LI+*3NY"#``4@'U-PT*96YD5W\EM;WW3^(^:/[EMI*ZTW&UK?U-MJ[V<._]^YD//Y9MO M&RZ?I<_EX?7!UUSV+_[[Z^?$_2,3$8C<)8`L8@&,!G,'!(-)L4$8@(DP`IAI MV`@B=2;AW@H6NG:"E:X4HBH4(W49@R*0OJI!UOQ#S#^7/!?)Y9[+`S:/FR0+ MNAV_66,25ILTBTV:Z))F+M:$;E7--&@VA)1-&Q*)"FR1'8 M"P2OA2.1RA))I+A`(A4'`,H4B4B]/IISDSD")47$RYNAH6!E_[*]JT_CIYQR M6O!N6[_VC7YIVZ1\_;*8-^DQS_,SUS+M/G75:[U-;:/U._>U7FU>$E_Z=3I^ M_QRT?U43KG*G%4:!/6"L8#,,$U@'8 M@84#WBGC``)```@`#IR*`RP!;^4E8"4F",`"L`#L9H*10(0JY#(U!_+!7&,]0P5=`ST#2R"P4$C.Y7+RY-(/5S`U MY]+W`,ISZ3L%."L88%OS_@Z$>2#,"_02BF8&T M/9!F1Z/Y<=#RPX)F8&"P`^)Z0C27JR=7(!=`@`$`?PY/`PT*96YD^IZ]"25%I*I>G"]?_ M__\_,-0#R?]_&!@$[('T`P8&!A#=`*'_,;#_`=$_&.Q_@.@/#/_!]`-F(!-( M'V"'T`U0FH$?3H/5,93@$_280W&'3,G,&($&)@#0,<,97#\!<-D>09.!2>C9S(\ M&XY&@['0'\+>;MQ;;H7?NG^U?QT7YT/#8@(;L]*;J$TN2I.?8!FZ#W&$(S'` M,3C)G8..)W5E[D/+KOXO%SB&8G1RT8M:P=#(5/T:;CV/9X53*+CG9 M)%N=`4`?S]L&Z4:WK?62!O"JQSO\%F``2(S9ZPT*96YD7!E+T9O;G0O4F5S;W5R8V5S(#(S,"`P(%(O3F%M M92]4,3,O16YC;V1I;F<@,C,Q(#`@4B]&:7)S=$-H87(@,"],87-T0VAA7!E+U1Y<&4S+T9O;G1"0F]X6RTQ M("TR,B`T.2`V,UTO1F]N=$UA=')I>%LQ(#`@,"`M,2`P(#!=+U=I9'1H(X[?;TJ[%PR?;)]W]MIE) M+J6,"C%3F3S*MN`#&X/+7"93*<<%:N/\"6LF[WN>UYR]B3&B6?'\<=UTM6O5"JJGW047*S#UZ"ZNZVC[R63K"C(22*$_0E=.Z#CORL\T"] MB7:)VE0M-E4X-MZV\=U:_97^&/U3^NESOME\S_J::T,&GP0EI'KP_^`B1U3Y MS)0B?XPKHB%LZ1G?9_&,2MT`#^>7FC=\%6``9U/J30T*96YD+T+YS;=(#]@]NLCG>ZR.G*H,N#QH@::-!HDX:4 MZ-E`&AQ$`=\B`R-:Q8C@8E35JG10_2B'$'/H-;`$^-O1/_H?`08`0D:EG`T* M96YD^4X"S@B&7OJ>O M0DE1:2J7IPO7`08@L&\`DR""01Z39/Q_H/X#460SD/QA#R'_`\G__X<5^8"! M`<1D8*C'1H)EN5P]N0*Y``(,`&2I_D8-"F5N9'-TGFE2XY?:'*Q,S67BYSFV2)[ MDU70^1,W):8G,CFF'Q+'='/8DACEGGZ^?Z]8[M""@YAKN$$D+&`R4#TAZB14 ML1,G,T\8A"`W<^T\[Y'0WCQ7RF85*V?*QVRJ!&4+R<`.U@,=5`-]^7\XSAF_ M[48U6]^E[^B[]TJ\*AMTWKWFH+^?)K)T3QO<0C/@48`"0V'QS,@%.WP:C_"QS_=Q"LACVP M$79KIK1VM*>?``,`L&&X@@T*96YDQJ[A,+1?I*)/)>;.GVI-95"')1242NLG]R3@0!$$: M#8)\VDH$%3=Q>*D'BG](((L6[8./'?"QM_63X$?!;T-)=T=W&ZZDF:<5_008 M`*,/NJ0-"F5N9'-T\4X*P`I#Q]%4J*2E.Y/%VX_O__?X#___\_#.S__W\`$0]`Q`$0T<``E`#A M_PQ`_(\11##3GO@#(GZP@QP&=@D_R"7R0$+-S!>1H:]!:+ MS%*6F16=@.<;E!%TAT%/]?VW38U'BO>MKQ9UXBMJTN[:ZW MY2,WM2UO$;?E[OZ*<>GO^//CZ\7VUU:F3G""47VJNFBB$H$Y]:KY=1"93%#S M_^BCHKJ1\/<0"'F20,-\DBX:?'*%'L2B*H+S35@8W M$Z9W>\(^/&783:`5);"`(W78'[K271J9"K$WO7VP/P(,`,%HYQ(-"F5N9'-T M)1!8_4@K]OX2VHYI.=-51V]+UB2Z- M^31M@XL2^J<[/=X3;\$3?7S_O9K@W ML>?4\V(Y6@XE+R7/!]%K63K%P?(TB\EF'?ING&;(N\6J$E-6;^)]Q/$MXIN%C(#S,0):$$O\ M%,R`'X1YS,-@7LRO``,`WK)#\0T*96YD:-]G; M'7CCH7\A`EYDP+$?)$5*F M"%EW=L%)[8/]X,T,*PM,,3=/2ERN\)K!`V1F>DHUCS-JAN2, M,H-D;_:05,<-FE(?\/5\WZ#>@M:CKTT&0?;6;F9K5`'4*VC\BN M9-N&59H4[.238\`.(=M'9%>R;<,J30JK1YG;.)6=.+6]_%?EB9_N-R?/85?# M";X"#``/K03D#0IE;F1S=')E86T-96YD;V)J#3(T-2`P(&]B:CP\+TQE;F=T M:"`Q,3`O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),K%0,%`P!V(3 M`P53,X440ZY"+F-C(!_,-=1<+B=/+OUP!6-C+GT/H#R7 MOE.`LX(AE[ZGKT))46DJEZ<+5P,#`\-_(&:H1R/`$O]_\/\?K@1^GW.Y>G(% M<@$$&`!HI\OB#0IE;F1S=')E86T-96YD;V)J#3(T-B`P(&]B:CP\+TQE;F=T M:"`R,3DO1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)I)`Q;H-`$$5G M1;'2-#X"38DBDBV94/8+MTD2CI(L'1.`I'H*1`_AZ&(I';;/&* M'6G^?Y,5$DLF+XEDJ>2O).$H_I=OCZ_KUQO&7``1JJ`@4*@(P^T%`!$#G?Z(4P.U$P!VF;TZ*IA MA3X<0@QAKUCU%<8%78/)+P#NOE4$3YBSW']`NL4];];(X$^X=O&_K:SDTGF& M*9B,:9F@J2[2IF^'L)/,Q^%=S4=^"#``I>#"*PT*96YDRF+5[^KQ^O6/[C`-`Q]P#Q,P`$"E! M%3'$A9J=62`1\A![CLK)>,[*VXD\E[L,;]ROOR$G9&J^[@I[M8/3/L(5_]R] M;EB[\:`$4J&GSOVD\U]%?'*!S#U$/M0,#D"PWQJ(P).)R7[/,R/C2X@&_ M!1@`N0G/]`T*96YD0JY#(R!0H:*)@;*ACK&0+E]`PL@PGT^HTX.J8`=!)P^@C@Z*SGV]68_2(W3L4(QYU+;0 MD/S@GPQ1"088\:@UQAN\A?`$%7,.;(R\$%>!%VRX$KP^(,W`OZ"*P3_P'?ST MM,40_.R([]?G#MD.2J&-R(U5"R&'[C;=];NDUJ6_J,T-"F5N9'-TD6#2- MCS!S`>*8Q+)360H@X0()*@X`E!1$H?9R@ES)$A?Q$;9T$>WPUP(I!5N\7.-EEQT#WK8?[YQ=\L67&S-TTF,J)$3T:5,1"0A872!9)`(]$;B>_/BS08A MLU&R,[/;K$+%M7BL0M.$I@J?M>Z56[M=$D5MR:):,U;AXULWG9;OE+7< M4=!R\_H4:BV[EW`\G+ZT>U9@=$#TO9QQ\5%RB!/)((P'7$B/*.Y*2;(?(5M0 M-3LQ\@CT$W-0&^;,C-<_MTTSI)1DH]$/-J?-C#0_TEO@ M(EVXG@KP;&#]/P3<--`OT+N\N=25))+*6,CQ4P*(T?#%RZFV,P% MOW)B4)ODR+8$5G5%U&J MZHE&492?(S^`Z3L)]E'%`5^K0[]59[7#)@[_\!L*71!WP2[ZBGJRZ,0@T*51 M&Z28D/S7Q1Z(,9#CZC?$M`C%!T,^U;@K)]3H0;:G)+3B`:J\:GG''P$&`)>/ MJ&P-"F5N9'-T0"UM%"TWAQMC[)'L+0;-XF"C:4#\Y@_GX'YIF;-56QC MV&@^EG0ATVO=RZHH>:H+/8M5\^%,K26U9U.16D>?5+M=<$G*;OAVO9_(+DE$ MGIEX.)](!P<(,$<_U$#304=Z<.H;"<@'NN`D)/(0"6D\_S`;./EB_H/-G^B! M^$H')!)3Q"AOCIO1I96E';T$&`!\6;(:#0IE;F1S=')E86T-96YD;V)J#3(U M-"`P(&]B:CP\+TQE;F=T:"`R,3$Y?8+GM'Z92\YI\RR[[?Z#FP5K="?](5KKF8BT-T1#9VA' M9P!>$U52IWC<`>*`"]..@8Z`2%"H"E8ZMOHAM"TOA#R4Y2DR$)J^:BDGOI2 M\9U7:Q557DH]KW0W7WQJ;>1\XVW+Y4E6:RX/NN=R^_,E%9?MMSP?OU=N=PP0 M@$0.B&2`0#.5J,!(1!AH))]F"$UOT#71(;K8H'>=PG8>R88)>&,PE%%,D]9_ ML!/TY&V-\`>OT*>CU?>C5;?!]#;#()E4(-J!?'`YFM.0U*`C\AH\&^;T&A2\ M;_G(+P$&`!`W?AX-"F5N9'-TR$#X+>I`X@ULFZ]- M_[]+DTS%DG.CDJ5*%ZHT?&,9QV4J+3*B1?%*(E/G*Q>6]4F\K'>BLRX.:V58 MV[UZW)\7MANN*>_()PH=>;_\J/T,:*J_H@_4U3`24\*;U'?Y-C>AD\,V:`/` MR6^`CB@'>J(*&(@@4?LCG8B\M7SDEP`#`&Q5[L(-"F5N9'-T\6B.`V62"+%!5)2<0"@ MI`!!C6^0*_DH/H)+"LO#[([MU-GB23M;[/SO4DIH02^6G*/E*^TMGM!9&2:T M3&DQM_(V3][DI+0[XCK'>$O.8OPE[QBOO]])+OF&+N?K`?,/9`;^96X@,\PU MS,0*)F()(!9!6(DM<&&X>>+2\-UP)49<&Z[5YS!4,V^IWKCI+53N;",O^PW\ M&?U?`+$%N(TD0"^$21%>A[^&WV63^V#VMZULKBF&1)*NZI-J:FU`V^B:T98J M&(?VIL8WN8JZ3/B9XP\^!!@`\]CP@PT*96YD\!E.?2=PIP5C#DTO?T52@I*DWE\G3A.L#\'X@:P(B!$8$:P.@` M&/T?'.@?$OD'3C+\_P`D&^P;&-C_-S`P,`#]`R09_S\`DQ\8&($>^\'`WL`. M5&K__S^7JR=7(!=`@`$`HI">MPT*96YD^4X"S@B&7OJ>O0DE1:2J7IPO7_W_R_____\,/)'ZP`XD/ MS)@$6`*L!*R82/"``:CM`(AH8&"&$@=`Q`,0\?_?H"`.,#`PV#>`""!FD`<3 M8"Y8@LO5DRN0"R#```['X.H-"F5N9'-T12.0$E!6&*71E12$,^4M)H3#N!1Y2"^U"W0U_0(/3#"5=$VP.F&08;D@*# M!<\Q2%=3XB9;TOETV6,V0Z4*I0ORIRTD1AOPM*[9VNUI^]]6?>?;^&49VT:O MN?7'XO[H7T$5_`&+MSH!H+-E$X.-K4I/-8ZJ3-9L3]5O*4`'M#E\1-\\ICC/ M<(TW`08`,*[J:0T*96YD^IZ]"25%I*I>G"]NY7#VY`KD``@P`N!3]G`T*96YDOLT?8H.8*E5<;-IL@1_#,\AO]G8.J&;:IZR;7E:T4/BF-R(LJ* M%[:TJZB&+W=J'9ESW"6SCSF9]KCABHP[\.OYOI';DLB@1(+^H!.O`PJ!\L@% M49GXQ`#UU2+(AY'%>(!U8B:?.B#KD7GH'AI0\>V)DS.EM'-THI\``P"? M,))M#0IE;F1S=')E86T-96YD;V)J#3(V,R`P(&]B:CP\+TQE;F=T:"`Q-3,O M1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),K%0,%`P`F(34P53,X44 M0ZY"+A-C(-\`Q#72,U30-=`SL`0""X7D7"XG3R[]<`438RY]#Z`\E[Y3@+." M(9>^IZ]"25%I*I>G"U<#`Q#\9T"0]<@D6+;^#_O__P>()A_\1Y#_L9/-2"0C M&LG`0)C$U(5J)BY[H>0/>P3Y09YX$AP:\@S827NPK#V7JR=7(!=`@`$`O[C1 MB@T*96YD+E4HAIFTSAYB_OZ6[R].SVHQT M,^+^?Y0H&0SM][WW.K^5_,DW`08`<93X$PT*96YD0JY#(&"0*YA@I&>H9`.3T#2R"P M4$C.Y7+RY-(/5S"VX-+W`,ISZ3L%."L`*4]?A9*BTE0N3Q>N_S_J_____P%$ M/+`GGCC`P,`@WP`B@)B!'TPTP,5(,HID@AU&_&/&(.3_,/__(/^'_?\'_A]` M@OD!__\?(`?]86"P__^/@0'H3P8&H#$-S""S^/]SN7IR!7(!!!@`+IR[\`T* M96YD5EQ=I2\X&RK>\Z6^Y58SJJ=/.[/"U=KAB<` M/:5`2P9H*`:((HQ$5(_DJ1YB-&5OT*9=BCX):$L,IJTQFD81-X#_0:21T1_0 M:A_]_'U'A=!/O!:9+D&7A/)>-=)!A3G@?PYN*#_P28`"ZDHLH M#0IE;F1S=')E86T-96YD;V)J#3(V-R`P(&]B:CP\+TQE;F=T:"`Q,C`O1FEL M=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),K%0,%`P!6(3(P53,X440ZY" M+F-S(-\`Q#75,U30-=`SL`0""X7D7"XG3R[]<`5C^ MIZ]"25%I*I>G"U<#`_O__PP0@AF9:``1__\-7Z*!@8&!'X@9V-$(L`27JR=7 M(!=`@`$`&1_/-`T*96YDI-[^JW1<+B=/+OUP!6-#+GT/H#R7 MOE.`LP*0X^FK4%)4FLKEZ<+U_X_]__\_0%C^__\/4/P`B@\`<0,0,\C__P/$ M!X"X`8@90%@`B`OD_S=^0-(WP/@``P-[`P,#,P,#`R,(@]@@,2Y73ZY`+H`` M`P"(O:2^#0IE;F1S=')E86T-96YD;V)J#3(W,"`P(&]B:CP\+TQE;F=T:"`Q M,#`O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),K%0,%`P!!'&0-I( M(<60JY`+2!LH@+F&%GJ&"KH&0"(YE\O)DTL_'"C*I>\!)IT"G!4,N?0]?15* MBDI3N3Q=N#[\?U!_P+Y!GH$?`AOD#]@_J/_PG\O5DRN0"R#``,GR&GP-"F5N M9'-T#1+"JSZ06$S`6T#;2B M&PO^@%T(NO(`ZM*%HNOF:#U*C^#2A1CS4D0/8*`?22?S,ME84C'^RT:2C^1@ M^,Q9[L\ICF9H9)`.TXE?8]F?>%9RLI,LYV3EZYS,-G,QG)1KN5YN1RX7;"/G M5RNIK_9']P>?1)53#Z(BJ'\L/E77:-RO"V@K2.A]=4("DM0=DZHFAK6&MH"^ MW[DN88]_>]=='#)\1?/F1*&I(!V,B M55/4VOYIJ[PL>91]A"U3A!QGLA86ZD(^-K-GSIS=[B"5M'+2 M--+MI:ODL>87[EJM5K)OI-W5=KBK3G4=Y.&9CSV7=]*U7%ZI@,OCS;G47/;7 M\O;Z_L3]!6-)08"G#QHPD9##2$0%R+AN7)*9[HLY6Y/@0@Z:AU!@!"*]0+JG^J>*-N8ZZDE,#JE:#*$,Y4@Z/^OS(VQ=]'(D+W1PT*96YD!+?H$7POD-AN8IQ.AC2%>BBT4Q\@[=BA(9GM1W/?Q-`7 M\.C!Z/*?TI9N%>B#0__II,U6O*]2YE>6JD&6^RF^QMG)X MYUW#V8NL*\X><,[9[NE."LZ:1SD=SV_<[%FG1%5[JE6)O`8BIQ-1HB/10@A?YDB7NT"(P5#I+:'&!@# ML!43-"RL-?EIC5VQ(68Q[Y]$O(VNB>"1"!Z)V2$QNY[PX5M')32]O(X2AO-7]RTV.SDD,G[;Y&;5_U6)V\?O!IX/)%FI[+!]2Y M/#W=2LWE\"A?Y^]W'NY85U+50$YUHIUN1#FVB'0!8\PV4.CL@]/@0J$Q48#E M0G1@\6`=`<)BPET)"0_F$1!8R+X,#;@-[/[_,BM8RT;IA+OF+>C1-8%&W1+H M5KWP:_P8DV$1QI;(P9J3!4P6-7F$SAC+!0SHHXUJ\]KDB+,WT)EP[8J#?#_P M,_\),`#;"<=�IE;F1S=')E86T-96YD;V)J#3(W-B`P(&]B:CP\+TQE;F=T M:"`R,#C/V>3UVOYTW$C&EWQ"A&=TO!`-VEZ-&-UC/R MT8U7=#E?#QC/D=5Y!DE:XPQ"`)Y"1YE"(BX,$M$6D)06+YKAOZU.14=JFUI: M1UDW?'Z]&[Z^,L/,]*9]*Z?:7-G6%DI;6YHVI+SJHR*SQ(ND\C,6!PBSG^+R M=F2;.QP7,6[P(\``:27"?0T*96YDF"K\3C!6H/YH"9`[/A M'$R]6Z(%X[=XOSW.X%=`06EZ*S6E7BE%':-I&55@Z!`10U/0SX:Z>`!1/X9B M1!.BUQ]`N@C&37\*%;6Q:`V"7U61%GT90B:1<6#M80\?`08`.QVPKPT*96YD M-O`^.RT,+(O>BLV\]0^;^#_6\??W M^?=JT,^[P[YGGH(GXNF:L)_:)3'B>)*X,[?`DP``=0V]4-"F5N M9'-TPK?N6ZT\M2MJTTFTIGF[+3T\K3"^\&+AZE[KBXU3D7N_LKJ;@8[N3] M[>.9AVL&_/B%Z+)/S"XY(#@ZX.0B6L6#0-N20:=TDT>$RM0JIEYLQJ M958PMZJ]E1ZM/NPC8%]B"WKPS<`/_"W``%HDS-L-"F5N9'-T"]@F-4J=A*I@!D$G'T`='12= MFT?K(_D`P9B[+"(.+@;ZP7'A[D]-0XKJ^)DQ3:9TU'A!HV.MN*Q+32-5JED\ M#1W.V%JL]F0T5NO8QZK=+B@6=D.WZ_V$=HD.H`@!`#(1.G$H%B%V(0\^#[WX M&+`^8Y]B@#==LF/['TPWW>><-#EM21MY>THBJ;PD])+62W(OK^`)_]3!//!? M&GXU=7%E<8W!2Y835."E"SP=SSM(IZ`Y$9-9B]Y,L`:]&"_-G$(S,DP:)YDVF;'U\JP; MY]9.G.N:4EN[\E$IKC][XE[)6S\EYY7-PINHFQ5J63>/*PTYZ8]JMY(LEV%N M$U[ZW:IH89;""CX"#``9I-RG#0IE;F1S=')E86T-96YD;V)J#3(X,B`P(&]B M:CP\+TQE;F=T:"`R,C(O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) MI,^Q3L-`#`9@5QDJ>[7B32+S` ML66(^+$;"=BYX1ML_3Y[M9:%5#(K974AU:4\E?S*RUJ+6JZEFI?:FR^N]*WE M\84W+1<[6=9<;+7/Q>;^6DHNVCLY'D[/W-YP(B+?&5%I?O`C'L#_>2-E3PUT M:&Y,C8E!B)-!/\H1?'2(C28ZUP')?2JYQOM?ILKPAP_CW0@&^9'^3$XVP'#: M[5SPX_C01/>E1V>)D&R-GBC#8*N!R`%!TT@9P+^IZ]"25%I M*I>G"]S!LO;_F/__?T`T^:$>0?[_CX<\B$0VH)%` M:PF2F+I0S<1O.QGD`09V(-G``.(P()$0$8@LEZLG5R`70(`!`&&%[H<-"F5N M9'-T8\\EX?'&ZFY'!_D?/IX MY?&657VNJHY45Z))`]&@GLCJ0E3HG/M"W1!RI(7^V?G_;]5+;E4 MLBND*N5P(Z\%?W#9Z&4NAUJJ?:'>/F_TU/+RSL>.LV?L^'@K!6?= M@WQ]?K]Q=\<(!,"3`P:*`:(("Q'ULTH[4:#4)QB==_!N;#$G)D./)1Z`<%U6 M=U39AGV*R4VZ()UC#.U"9^J#KO]!&GRD9(.;,4=0N')3>[MQUWV_7'T=HDBS M_R\V,GO@@P`'*'LN$-"F5N9'-TI+H4(+0J$J^`9!)P^@ MCH**0K<^;]HC=.SP:'R%HH,7,(%\)/Q+LIR8DM2/K*",Z93B#>=,8_LUX5DQ M#BY\Y72\8&50'VC.J#<^@+K:+2E%;;;TN#_/:%8H$LI+Q"Y@@$8L0PVE``!# M/$(>ZP7EZ:!6TH=]X*(/D0N'+\I%O\1._0LVZ*"'Q@:M?Z8!:.$*Y<1TG"*X M-KC'MP`#`$:IH5(-"F5N9'-TP<_6,Y3@ MYRN\G*\'R.>@6%HS43*AF,N,O+5MVO4AB!1]=(E*>FJK_W*B`;VTMK8IC'5J M5.)7LLL1[[Q]:3G:N^6XG=Q8\6%/Y9:BIU"=M5`I45,T!<$BAPV\!1@`:%R, MJ@T*96YDT1G3L9#(U-(FG<28GI>,%%P5&![(&HXWX&"UV2Q)1;.E^>YRQ6&$- M3QAP!3/P&(3P8]_I7?:X8I92EIZP?DJ;F]E0^`Z4_EC80BC\P%S8*1E*I0._ ME!7_.YVZ__.!?7!>[Q*\``.&2;=0T*96YD0JY#(V!PH:*)@KF.H9*A@9ZUDJ).=R.7ERZ8WNTI["E0%]R#84Q^@]2C8HN`M69\LC[*/D.,>RHZ9 MI*32$+[#3#+S-PLL<8YW\PJ;>ZR7N*M@#TWMJR4^^-:LXN:L7/JSP.T'K%HH M-MC44+SX!U"LWAZQ@J)]Q:_#]SNT3V"%,D(;(8U00LAT8R5VQXR..44IH\$[ MH4$2=2>=UY!31/9D[^UUKY.*=5 MEW*0"_59&Z6S)LA!G.["GTF8Q4'L;?"&(]B?X">OM8)7]4(%>:03/&7@KS2& M9/# M\P^I:LY?L,!Y][:5DO/^5B!YV(2$\"#``(_=:]#0IE;F1S=')E M86T-96YD;V)J#3(Y,R`P(&]B:CP\+TQE;F=T:"`Q.38O1FEL=&5R+T9L871E M1&5C;V1E/CYS=')E86T-"DB)C(\Q#H)0$$0A%B3;<`3V`@H(*E8DJ(D4)EIY M`+6TT&@-1^,H>`-+"L(X/VBT]&>2G^S.[KR-$PTTG.@PT6BNTTB/H5QD'+,: MZ"QD:Q2R.0KF?(D>SI+EXN]U'(N_ID'\;+M0?OE&;]?[2?*E``^@`BQT%EH; MC8.GB]I#5:`L6(:-;H"6V^)K^S_]#M2%N]55?I>W@?U MH4PG`TG,F/5QDY.T9"8Y^4V#MSQDE(%C^'BATE)TX#2G:`.?HG*W9%QVR_?;XTQV16^1EX@C4CVE+N032.-+ MZTGG2C=10Z16KY`FZ-7ZO;[>('<8U>FQJO\T^H($C4*FAFN1EJ(=#""I>BJ= M!2=HP0P'_,ZP"S:BM:4]_008`#80JD`-"F5N9'-TP,#\P,&9@8&H&<8(21$!"++Y>K)%<@%$&``*-GA1`T* M96YD0$DN?:<`9P5#+GU/7X62 MHM)4+D\7KC_R'_@?L#]@/L`(A`P'&!X`X0>&/PS_&X"P\3_C/^8_S#_8#_`W MR#/8-]0?^/_X/Y>K)U<@%T"``0#VLRIG#0IE;F1S=')E86T-96YD;V)J#3(Y M-R`P(&]B:CP\+TQE;F=T:"`R,3,O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E M86T-"DB)I,^]"L(P$`?P*QV$6_H(S0O8#ZA:IT)5,(.@DP^@C@Z*SA9\L8`O MDD?HV*$8_TE1NXI)^37M'7>7+!<)]C`5629&8[%/^<0XVI^CB4BB%+$HF6+E M8G?D4G*\12['2\0Y+MU5.U$='7;S[ M%FX&%Z*@M?JU]YG9S=_=Y:Z0:QZ:[(5;!'DA><,O`08`%A[LM0T*96YDFK4%)4FLKEZ<+U M___C_R!P$$PVT(@\P,#`P/B_`4@R_`<1#/7()$01?+E=/KD`N M@``#`/QUU(4-"F5N9'-T\4X*P`I#Q]%4J*2E.Y/%VX_@.!/!#_XP<2?]AAQ`\P`1+[`"9`2AZ` M"7L@<0!,U,.(!C`!,HD!1#""C`,3S"#CF(DS$\W@068FS@#`8B:$:`81QT$$ MEZLG5R`70(`!`*Y<,G(-"F5N9'-T\4X*P`I#Q]%4J*2E.Y/%VXCO\'@F80P0@G&$!$`YBH!Q(' MP(0]D'@`)N2!Q`<$P0\D?H`)=B#Q!TPP`XE_8()H4]&,'H2FX@P&;*:"B'\@ ML?_R7*Z>7(%<``$&`&+$,G(-"F5N9'-TH9`6L_2``B,%))SN9P\ MN?3#%8S,N?0]@!)<^DX!S@J&7/J>O@HE1:6I7)XN7`?_?ZAO^/_`'H09Z@_( MX\,-]0?L86J)Q0?^/Z@'V?'P_X?_W____<_EZLD5R`408``$Y523#0IE;F1S M=')E86T-96YD;V)J#3,P,B`P(&]B:CP\+TQE;F=T:"`Q-C$O1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)?(L_"L(P%(>%#(&WY`AY%[#)LU7:*5`5 MS"#HY`'4T4'1V1ZMX$6RN6;,$!I3=/;[MM^?JD:-M,`IE5AJ+!L\$5R!JAQK MG,]R5]#8%KK)U'B\0&M!'9`J4)N\`-7NEDB@[!;OM\<9[`JB"=(+QWOVU7$O M@HPF_24:+]WX^OU].9R;-+K_1.L+:PAX\``P#JJE3_ M#0IE;F1S=')E86T-96YD;V)J#3,P,R`P(&]B:CP\+TQE;F=T:"`Q,3@O1FEL M=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),K%0,%`P5C`T5S`Q43"V4$@Q MY"KD,C$$"AHH&!DJ&.L9*AB:Z5D:`(&10G(NEY,GEWZX@HDAE[X'4)Y+WRG` M60%(>?HJE!25IG)YNG`=8&!@8/S?`"09_H,(AGID$B(.44,\(-Y,+E=/KD`N M@``#`(PO2Q4-"F5N9'-T7SG16BP<4K/-*?/W6C>ZWUHM2ZTL.E/I?NS54M/_=Z:+3>E9SM]BU?HT^O M[MBYXE&KUA6WG+OB>'^EI2NZ._UX_WQQW;7#*@`FR8%1/#"(Y9<8+"*"65;I M)XLA1(\88HZ8CP&3'WK,?L"))9.$(9:L_V'^D,2PT6,YP=#9,'XR7#0:KA0S MLX%A#6-8B"$B9X8ESRWK?EG@>R786+)?Y@V?;O)3CRF/(77.,89$3P;"J\

SY4A7=Z#^YDQ,LC==.[!_0@P`,\JMTT-"F5N9'-T(QO#?OS0M3\BFFE$)-84(7C7<,(LOY%&04>YH2 M+_/M+.E\PZ)$=:(@0K6S,JKBL":-JMS3\_&Z8KE!9LG,W<*"$2/P-<)4'R/: M'-ZBD;`2M83Y#&0]G4+>`$#5`DS8``CN`&Q@#3GS#WC8'.=4=^<0JY#(V!PH:*)@KF.H9`J6`1'(NEY,G MEWZX@K$YE[Z'`I!P"G!6,.32]_15*"DJ3>7R=.$ZP,#`(-\`)/B!F($=08#% MP+)\*CS?H#90G%!)*'9\ M#T5]6",'L\?GXW4%LP%RUEFB00^:J,_Z;(I=U@5UIW_8"B$B:EA!`6$_'<_' M'FIMRW]H;!/DFBZ/8",*C_T]U\<^IB]=[(*)2Z;H4Y_R](R+8&O@"&\!!@`8 ME=86#0IE;F1S=')E86T-96YD;V)J#3,P.2`P(&]B:CP\+U!R;V-3971;+U!$ M1B]);6%G94)=/CX-96YD;V)J#3,Q,"`P(&]B:CP\+U1Y<&4O16YC;V1I;F7!E+U1Y<&4S+T9O M;G1"0F]X6S,@+3(@-CD@-C5=+T9O;G1-871R:7A;,2`P(#`@+3$@,"`P72]4 M;U5N:6-O9&4@,S$S(#`@4B]7:61T:'-;-C@@-3(@,SD@-34@-C(@-C(@-3(@ M-C<@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@ M,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P M(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@ M,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P M(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@ M,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P M(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@ M,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`P M(#`@,"`P(#`@,"`P(#`@,"`P(#`@,"`T,%T^/@UE;F1O8FH-,S$R(#`@;V)J M/#PO044@,S$T(#`@4B]#1"`S,34@,"!2+T)4(#,Q-B`P(%(O0S0@,S$W(#`@ M4B]$,B`S,3@@,"!2+T)8(#,Q.2`P(%(O1$,@,S(P(#`@4B]#42`S,C$@,"!2 M+T%+(#,R,B`P(%(^/@UE;F1O8FH-,S$S(#`@;V)J/#PO3&5N9W1H(#(Q-2]& M:6QT97(O1FQA=&5$96-O9&4^/G-TO M-+*B":'6IV8')/-_]J>XCOI;L=@WBZ(]-2+N'FQ2I6-N"?3*',/EBW.$9&X) M;T_QSB>O5.)7@`$`@>9IA@H-"F5N9'-T76HN:7VK[V\?SU)?"7!8`O".=(ZOK8N!KXXD/D:H M1H-W0H7$`$)R9#F#^,C"V!L[HS6Z9F:P#H,5#]-RC?X0&8L9#IC8__D68`"61P`##0IE;F1S=')E86T-96YD;V)J#3,Q M-2`P(&]B:CP\+TQE;F=T:"`R-#O2F1XV"`::#1L#!MMIOT7N,4X5!CVB*.F33K>Y(XR<(23Q! MC$332>DB2J,]U=$0Q^"[AI_X1X`!`.C)G'\-"F5N9'-T4/-'HP5D^Z$T0^))Y.ASC!55M5J`WNZMZMSQ).4*TQ]S>;5:GN MBE7Q@+%5'T?8-9"_JVH-^3/6(=^]/JD2\N9%_7S_?D&S!^O"M#92+^S9\T(1 MK#WSPHD=@X^DZ6.O[(4=V#^VZS.O#&KOG)%3L";'-M;8V"M[80>VLUK>*JUQ MSL&,G((U.;:L$,)I6+UPP%)Z8X=/G"DKR3F8D=@F$MMY3="2FNV="I6L#2^)A9:)`_"CR]9\KW01Q<#.CS"T,>]!R?@G++;QL#V<.+: M[I0FPMT1DA2<+7(.SI+>P4G6,_3`25=X.5\/D,Y!-(PQ2TA,T4\$F8D60_Q3 MC_148SVEJ:>P]-QC/7FFAPDM_4(?Q;M#-GD->"B>JLF/VP!3-;$DS8`ZEDTJ M1:DH,@E]E9JTV\[IGHF MI?4HJ-CSY@WR2NFIK['2%XBW/2SY.RK$:H?AFY^9TY0EYSSGJ>92NQ-8E<*9UZ;=*5)TYTXZ:!I!P:8RO\,\8 MP^2/8X=$#&)(_*A/O4_%SU_5\<=>'E&',ULQ7*G>8(A,;X'(>@![T$U-]W02 M8``KDK1U#0IE;F1S=')E86T-96YD;V)J#3,Q.2`P(&]B:CP\+TQE;F=T:"`R M-38O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB)A-$Q;L(P%`9@5QF0 MWI(CY%T`DH!DRH0$K=0,E\#^'D88HQA,D]OKZ\/4-R`U0JV\:70.4542&@K>!CY9`Q"J^)O!5YYPC4 MALROD;?@RQP<\/"+;P%5.%CP+3\%>\':H?0L4J4Z_T%1X3O0&9@,@AKO?["Q M&93C&KX@YP[["XUXJ5K[&2 MPG;3;;'D=Z)8>7_Q#HGY\R5)1^TF_X7GP M7[9;K=3ILE:[T;758RWO8AV7E:Z=NE7-NU6UY=GHX4UVK91[M4[*1]Y+N7N^ MTUK*]DD_/[Y>I;T7#`W@30SZ+N:$F"3FG,:,V86A(Z9`N#$YPJ+)$`Q^$!+\ MPJ?HX;.)G(_Z`D/'Q@M],\.>:SKVG*YAC\$_$LXY03IUI1AGLHE\:BCP#3_3 ML(:E/0<#%_"WS`],QCC_$%>0AU9>Y$^``0!9F_5$#0IE;F1S=')E86T-96YD M;V)J#3,R,B`P(&]B:CP\+TQE;F=T:"`R,C@O1FEL=&5R+T9L871E1&5C;V1E M/CYS=')E86T-"DB)7,XQ3L-`$`707;DPF@*.X+D`L4W64E)9"B#A`@FJ'``H M*4!)O4F5DB-P%:?B&D1:,*V[XM&8W8U?Q?4U/ MY*9XK-C5W$QJ9)-JCIKQW2,M.BJ7[*947B&GUP_479!\B-96 M9*]&(V)E-!X.R8/Q,9.=$7BBQM;"X'<2\@&C(3\DWY(O<#PNU*-6-9+,X&"P M444\%'VNC@C>VVA5^7;S8^_A:[+X*R(;VW]N@[>_CKIJG^P_14]DHN=RP?]" M(739T2U]"3``C)^[/0T*96YD7!E+T9O;G0O4F5S;W5R8V5S(#$S(#`@4B].86UE+U0Q-2]%;F-O9&EN9R`Q M-"`P(%(O1FERC]@V]!6=%*J@AT$G?P`=710=&X^+9^23^C8 MH1@O;4$*!1?Q('E)#I([DLTHIH0F@K*4I@F=!=XP$WP8TS2E)!2<"^,YQXQ. M5\P+C(Z4"8RVG,BT\T0^B`NFAE" M'V"'T`\(T?\A]/\13S\`10\[09K+U9,KD`L@P`"C8U:;#0IE;F1S=')E86T- M96YD;V)J#3,R-B`P(&]B:CP\+TQE;F=T:"`S,#$O1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB)C-$[3L0P$`;@B8R(Y`(?(;X`F\<*PE:1%I!(@005 M!P!*"A#4R=%RE!S!I0O+PTS&WA8L)?JR:`XT;^_JACZ.N7VS?Z?J!WNOZ^'1K6UV/C_;[Z^==CW=Z!H") M;N8_P!4JQ!D1'0,V&(P%P4.)41$"*`QE@F=$*-`9`@*@JQ@SX#H(IF7#`L,R M,588.(%1%1L<7`@\G*N$LW)#`!!$_CV4C"IC$,P9"TP9Z4/(62ECSAE%RH@90:6,4*8,;X+`F2`9KHJ205424'$D8YZX:+CU M03**!.[#EA%4.@?WP3.\20=R1AJ#:\5%4[0IK0`9?T+?C_I9_PHP`(A792`- M"F5N9'-T`16RQ,HR-H+A#] M>(6Q*H$20U0$DLH'2%*ZL'%J"7(QY29[A"U5"#V/A$+J+,M7[+!O7KF77)P\ M%%+NEOM1\(5=I8^YE$Y<6N@LS2L]!WD_<]-R=A)7CA&\"0:#\2M6&3:\7=&,$"M3O:2! M_DW_AU6&7WYTY4;0BM_:!6:D;K8SU5.LGT."GJA>V@,CF24B`1];?N.[``,` M4>./30T*96YD68 MED?Z_KI>L-PA=*&,#:=)+S?)2GK%7;J*S!TB+U-!D'9+.M4@O1J8#M2Z#7\J M_A>-[9R,W$7V-PUWUGT=MU!;R^VH0@]8,LB]QG/*KVA\-O+'>D0U3&OEZJXAT:P&XLK]):JU#Y;IFL327+N-N_T,N"_Q M#1\"#`!4<)M8#0IE;F1S=')E86T-96YD;V)J#3,S,"`P(&]B:CP\+TQE;F=T M:"`Q,3X5FJIU>YRF7RB-ESF=QA@DQ6ZVNE99+?JX_WSQ>9WT@AQ*A!$Q$Z_P'A MI19]HAU1,6`/"$9X1&<1$)4#0H8!,-H!%4`648]1C\SG`_6`F(%&"(>F1Y,- M,!X1]@'JS!W2P3BT`I$.(;43H#+ND`Y$]I#-!)$EP(=LEL#AB,AC1/46V#$* MC[V%H&J+R`&<'S!*C]#!E-BLLT`1#LCG$ECLD-\CVR._M:@]8H6WC4%O$)-!Q49G_!%K8R'KR6[P?)VUP^R&\!!@#8 MYRY M7#$`XVE!2X[%$%7`0$0YT%'>^#IRT>E.XZ&9`+6/6C$Y:A?0;?,&+,,'>F#1 M%RRM/])7(\!/>A=DXSN#?J)YT?X'AIR>R/L&KTO>\U.``0#F\!@L#0IE;F1S M=')E86T-96YD;V)J#3,S,R`P(&]B:CP\+TQE;F=T:"`R,#(O1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)?,TQ"L)0#`;@2(="EJYNS06TK\4'"H5" M5;"#H),'4$<'1=>VXL7J33Q"QPZE,>\-6A!,"!\AA%]K4A3)Z(@FB@XAGE$K M,BUK-`YII,9J)C6E_0G3#(,=:87!2NX8I)LYA1AD:[I>;D?,%@C@<`7)/SI( MN/6Y`Y^;#VQ(!(]KH10*P>674`DLR+OPP+\!F(S;1 M4EE:0"(%$E2T2`LE!0CJI.-:/HJ/X#)%E.&/LP@M+EXQ8WN^W>[=UG7NHG%M MX[I+]]+P.^]:%%'>N6[3H+?97F'MW?&-#SW73V[7'AVO7<-W?N\^/ MKU?N;U@6&A8OD2&B9S]#\A.AYE,IB7RL9(86)WRP(J,/7AUAR,:3 M!-.YV#_]6F5-MA`LTH8.SVH_8/BJ1M.2:A"#J)0)%I(3(@S6(#GMZ8!NLKF! M:V.^3U93=M'9*&5'\PV#,?^,9Y9JM5K\:4GW6\S'#U#^#9U``^(MI.^:*$KR_D'Z_T#U;];PKZ\J5XDT/THHC M.%RX0MMF#1-H;]B:+ZCA)E&3+IGM+?QG`*Q?X#S'-;X$&``HE?^_#0IE;F1S M=')E86T-96YD;V)J#3,S-B`P(&]B:CP\+TQE;F=T:"`R,C@O1FEL=&5R+T9L M871E1&5C;V1E/CYS=')E86T-"DB)=(\Q;L,P#$4I>!!`!,D-8EZ@L54[2#(5 M2%.@'@(D4P_0=NR0HIVCH0?3470$CQZ,L*2$-E,X/$B?Y/_@TE%-#=TY6M;4 MWM.;PQ.V*Q'EVU"S<-);U!NI-;U^X+;#ZH7:%5;/TL=J>W@DAU6WIZ_/[W?L M=L@CG)DY@!4"&!8!1!J*"SQP;]F7W!<<9AR%EJ/AF%DH^\ST^.=@E9=2*949 ME%X#AA03$J&0[@BE[H).9K>0?$+R\;,KH;S)^$>9E"QO^0?49P(FFG$*I@>8 M@Y'KSEY&C4\='O%7@`$`4-.B/@T*96YDX&=@;#_`_X.1OX']#Z,\D&``$_9@X@#_'X9Z,/%`'DC\J`<2____ M8?A/'X*!@0$[P>7JR17(!1!@`$)ZB_`-"F5N9'-TPKW$J:P][SA$#%N)0<+$0YNT,[P' M66UYWG.SE!"Y>8'.S?SM43PW_:M\O.\WW#^QENI;E>BH`Y'3DZ'49ZC;G"Q%=9;5ALA(8;A[=%_H. M3ME0NNH/L"SI"ASF9.:6>T#NX)"+#$W/F MO?EW.S@]JW=L9N9SRV>.V2Q;L.;V?=)5H>G>O--TRUS3U=?:>"@^[7JY';78 M*%!-`702TT:$5B)+0*8,!BF;"?H(;8(N0A>CC='':)+0I$.$FLK#:L*'_RWO MRMU0KV3*_,GJV218_]F6S2BG"D9<@:LP0R\E?CC2A.?P48`+7=J?T-"F5N9'-T[XRZ/.6;%HX2SE-,Q[Q(Z4J;8E5/%*DK0B^()(N?M@0I#G=@BU M0K\4HH&TG<"_=K("TY5VNF*7WXG5NO%WL%"(P*N%!EX6"GO5HA]S17?!TMS0 MFAX"#`#%C^4_#0IE;F1S=')E86T-96YD;V)J#3,T,2`P(&]B:CP\+U!R;V-3 M971;+U!$1B]);6%G94)=/CX-96YD;V)J#3,T,B`P(&]B:CP\+U1Y<&4O16YC M;V1I;F"JN"/0AZ M\@'4HP=%SYU]L_HF?81Z\[!L_&=F9ZDK7FWIS-=))B3Y,S^14JP<5U);L:?R M6/$+UZ7XUYZ)G56PS.9%R\6]U"47U[!SL;B]D(J+]D;>7M^?N+UD M5?VB3C^Q*U&N'^&`C"X!`Q%U!N``AX`U_H<]XUUR7<%+1VIP"NC)^YGX:8^% M,A_"P)P#1@\'/KK!S2/`RB!,,X6U0;PNPA!`?X!&Z'=@B?P2F#\`Z?P/C&A5 MYGX#[4),WJ4J7"K0I9*G,":(C9I";*9+[76IX2Y)L!7%!9FZE.=&N*V405Q* M39'.,MFJSV;:K^"+GZ'Q_U9LU^S4)`V$:+0TF\ZO8R)],KKWXG?T!\& M"L.''E,16G:@5"AM3DHD:I2&)B,=)2-CF0IO/!_X+<``3OFOV0T*96YD^4X"S`I#CZ:M04E2:RN7I MPO6/__\/YO\?&/\_8`"A`PSUR`@B")0%J@&JQ`D8_C.@HA\,___0!C$PL",C M+E=/KD`N@``#`)$7A3X-"F5N9'-TL&:"LR:>_IX_WS!Y@8#CPEQ_&:)$58Q0+M`#_4:'&P7L)"NH8-D M`8`I\>P'9H(W$%M]TD6,21<3QW2&C1@8VCIL743/T,2^GC%%.X:VNG:(L`Q- MM&'&&-$QM/4K^(@-0Q/-+X+""Z35`X]@%,"?&!2M),OGM9(LJ*7T!!KM%'(O M#P16D4KI"32Z4R22'#1:6B3)_`=XV^`C?@LP`/I:C^X-"F5N9'-T^4X"S@B&7OJ>O0DE1:2J7IPL7`Q#P M__^/C_I_@.'__^%+'<1#-4*H9CP4,S+U#X7ZPXY,_8!0'R#4`PC5`*$8P-0/ M!G8&,(!2_%@I+E=/KD`N@``#`/6F,_,-"F5N9'-T.3HXJ\TVVGF26.YS:Q"TW.QPN5@=(# M>T?I1G]*R]V2E;#EV_5^IK`B@72Q"""()D:@5@:@4/H"1NFD,3+@*4TL(Q[2 M3L1OC+2S#\47]7\#1#U@?D'K0'MZ"3``M8ZFT`T*96YD3Y<]R!DP%^:'5U,R8].IR0=I;F3IJ<@J M[U>_U/DWZZ#/JK,9Q8PB2Y(YV9^V][OW;3_;__ZI?COX1[L@WZ&S]M2>%:D" MVB?)G#6I6$#_XYO[PES"&IX"#`"&L25W#0IE;F1S=')E86T-96YD;V)J#3,U M,"`P(&]B:CP\+TQE;F=T:"`Q-C4O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E M86T-"DB),C%4,%`P4M`U5#`V43"U4$@QY"KD,C8""AHHF%HJ&.D9`N7T#"R! MP$(A.9?+R9-+/US!V(A+WP,HSZ7O%."L8,BE[^FK4%)4FLKEZ<+U_X?]?PS\ M`0T_0,('@+@!BAGL__\#XA]`_("!@9$!#?^#RE,)/R"$@>XY^)]!_N#_!OGF M_PW\S/\/,+#__\#`__\/@SS0L?5 M0"TM-%JS1^,H'(&2@K@.K%;&QLWD2^;-S\[+YY121C-%NB`]IY/"*^J^4X"S@B&7OJ>O0DE1:2J7IPO7 M?X;_#*CH!\/_/X,5,3`P(R,N5T^N0"Z```,`ZJ9_,0T*96YDU+`>5AU-I/+W)-T7,TZD&"$7!HE$UFD*3O`^JR129 MY%Y_*@;Y4E\NZDR-JC:!2]94E^RBFS-I]/%7GDGS_5G1Y#2(@EGA>"Z)MGC3 MX"3Y%F_1+EE2?9>LT=U*#;AX53NK!1>O@ED=>"OUX%_0`,%*]L_/-$%T00SE M6DV]EDO&^PJ?\4>``0#9W]JC#0IE;F1S=')E86T-96YD;V)J#3,U-"`P(&]B M:CP\+TQE;F=T:"`Q,#,O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) M,K)4,%`P!V(C(P5#4X440ZY"+B!M`(1`RES/4$'7`$@DYW(Y>7+IAP,%N?0] MP*13@+."(9>^IZ]"25%I*I>G"]"SC=,,U0G,AK5SNZC2@]K MBE!E>WH^7E?,-MA#[P8,TPCM_ZG!&Z+S^&0[8IJ`J:9,F3!%SGAN%+NE!)9[7TZ?XEDW/70F?+N$O; M,C:U`4A^@=L,C_@18`!VLS=?#0IE;F1S=')E86T-96YD;V)J#3,U-B`P(&]B M:CP\+TQE;F=T:"`Q-C8O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) MS,TQ"L)`$`70@13"-#E"Y@)F-ZX8K0)1P2T$K3R`6EHH6F>/EJ/D""E3Y3L1 M[-+8.3`//I]AW$RL.-VYE<5<+AG?V>6:/]&EF4QM:EC]>5_8:!+@(0".B)"G1$"=JHCM'$2IV$"4(Q4"GTI2?4L=(D M2ELHP_T/T/!GC)XJ_!^!U#%XZ_G(;P$&`#%GUFH-"F5N9'-T,-"F5N9'-T=TUP"@=\2\O^*I%E1 M26NYZX:V*WJM\!WKC9Q+/:Z7%5V4R_)2KH9>]KAKL7BB>H/%G3S'8O=P3146 M[3U]?GR]87N#`/#-[``6HJL9+4SG2>SAC)F#O.;AU'0R(_B5TY$HK^FHH.FH M3M,3]3H2->A(U*CI*-9TDJ:GTI$D'4G2=)*FDS0]E8XDZ4B6I+,DG27I(\E( MEHQD23I+TEF2/I:#YRP/E!7BYU%UIGY&@VDT\4'.Y&<43)VI-PVF\9_27\(' M.9,WA;_"VQ8?\4>``0"GUK3S#0IE;F1S=')E86T-96YD;V)J#3,U.2`P(&]B M:CP\+TQE;F=T:"`R-S0O1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) MO)([;L)`$(9GY6*E:3B"YP+!.$'K4%F"1(H+)*AR`$B9(E%2VT?C*!S!I0N+ M91X6!(%2LL7ZT[P\_G\7,YI0H(>?&DQF?9]I\ MXKS"[)U"P.R-\YC-5PO*,:N6]//]^X'5"\88=Q#U]``CA3V`4V@`H.;G`7P' M*4,'903/T'+/+I%F)_52ZR7-U3*DY[J#0]?_``Q0WA-N[#/L?(9;WZ4BP%\U!FA-NN0,G:ZOG`OGCHUEUWV:C_Q.DG&;;%UPK7>!1@`/6` M>@P-"F5N9'-T(3> M!=(Q(6TE6$$N@EIU@&K9HJAU'JVC=`27+<1IGO-'L"`;4'_J,.KWF7+D&,LM MB7$ZP4,$9TCH&J?3.(APS`,^DR/%_0GR`L(=)AS"E;P/8;Z98P1AL<;KY7:$ M8@&,AB>$:,'N!KZ!:\#DI(-:<<6 M1U4R4[PZ.J9NSR#3H)\$E@5LX2W``$TC62P-"F5N9'-T7=I*BF:6_/V^OXDS94`&*C%IY(HPR8\ MB;$&>B)J&>B4QPROK_Z(-9UAU`I'"XTQ+&EKQ^$"ED$39<]>RP"GS,,*'FFJ M''G0MA]Z=KH\L(_$'A%I?W/=;=!VF:;%;)-UM&WU`A^B+UUST:8+D6Y@1?`RSU=?T%XJC0D## M7/KA"F:&7/H> M0'DN?:<`9P4@Q]-7H:2H-)7+TX6+`03X__\'TPPPVAY*\T-IYO__#S#^_\?` M"*;_'V"`T`\8ZL'T!Z`&$/V#01Z%_@,T`#O-3B&-W5R$_?8H[H*Y$^INB#]` M_F*'TOQ0NAY"`[T+5OM_Z-"06"-,<[EZ<@5R`008`*D&2`H-"F5N9'-T0BY0R+N`S9]2<4`(5`6S M$.K*`]@N75CJ3F@70I=>*="+I#>8918A\?>L"W&>S4!F/D-67^99RPE/\-DI M7V:\2.F5;(9[(M?)..6+9)Q8K"M^?J%90?$3VXSB>_RG>#:_X93BXH'7J[](7/XQMA_GLH7B#\_;0'SNN1R"[(Z;>*$:,ZQ3C.&A&P+[/ MB-FIW@:AZO(L4EWA[+/#:VBN\7J:FX%OF0JY#*U`/+!(L9ZA@JZ!GH&ED!@H9"NST M!Q3ZASTZ_<,>'_W!_O]_9/H!E#X`I1D@-`.1-$P]7/\#-/-A]`\2:3"@$0V* M''XB:"Y73ZY`+H```P"*&DX4#0IE;F1S=')E86T-96YD;V)J#3,V-2`P(&]B M:CP\+TQE;F=T:"`R,CDO1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB) MG-&]#H(P$`?P(PXDM_`(W`MHBQ#0R<2/1`83G7P`=730Z(R/QJ/X"(X,A-J6 MJXFHBTV:7RCT[E^:)20IUC/-*!W2/L(3IF9-FL=X$%%?#N18CQ'MCCC-46PI M35`L]7L4T_6,(A3YBB[GZP'S.8(9OE)6*-B`[;&>4J69T'K7'QH?,+%6$%KK ME\&;C>[PG\'7>JY?Q?T?G.?.^6YMWD;GUYGM>5H+UF?Y7&Z_J^?.X?HKSJ-T M0>O-Z76$[Y8_UC_LU`/OK:_[+S5;<;X2PO:>8,)V[L_=IU*XR'P$&`([R M%]$-"F5N9'-TONC@ MQFZ8\8MW8?FF8N>?JT]F[()OENP&?TJ`0&X=R:W+7XB+`C?X%&``@@/Z90T* M96YD@'9$*@JU2@GBQ\"9Y!(\>+!^7I$NE#ECGT_?KEK^\)$4Y73C:%/B.92'9C7&U+.C,+=V5O(I>WW#=8/Y"98'Y@]PQ7S_=DH3FD3X_ MOK;8W"&`[D%'4/]!A):A9M]&V3)#FV1W`DZ0L4S//.-WAIU@D^";N9OP(S`1 M#&OFWL2%83-#3]!AH9-@T*'2R0J4KU04>.7W*F8S8`(,>PB"`$."4(^X3K`3 M1+AAV+4'N`F*@00)-'?$!X"`I4VO3J.S4N4(?<;>G,90<[!'\"V/3?&^P6?\ M$V``7M/="@T*96YD/0A?[^0,G2(A/LSQ M_S[OR5$OS_?TX.BCPV_TCO3*9[_M:..V[DG.([U_X6[`]I6\P_8HMC.MLNK$&QA+G!*$$0*O9@/YHL5')5=(E1V(F68E2[`C(PP:>XH,'GB<[\'<@^E5/47 MAHP%GP`#`*J%$18-"F5N9'-T;K)2UAG=/^&NQG1/ M-L/T6BY@NKN]H!S3^H9>7]X>L;Y$@(A[J/YCAHHGPS,8'K]AI1(2=D(K-$+, M@]`++,AS88CXJ*RX$]R*WQ7@Z(<1./8KS+`UGK7"L*X\X&FA^4T/ MO$#F+G#1DC%>,B5_8<*5+W3JAZ_,?:.5N:VTLOS'X'.9/G`,=`$_#L[]G-;7 MXLZ9D-(7&4)T%U).(1!>U7B'GP(,`&"Q,TT-"F5N9'-T5PLB-+X`47X#-1BLEV89("TBD0(**`P`E!0CJ M6.)@Y"A[A)0IHI@_XR1:M!$E+NQG>V8\_C/YSFYL9B_2PN9;FVWM];[2R:/-^X:\ MC)[("!R(5@*.B&JL`ZF.8D!'I2<%:.'31#Y,O/%.\36L.4@/NX$]>)J!3^5> M[-AC!@G!P0X!5D<@N6!N`M`"N!%JQSDAV@+0".5_PD(^L>A.9@4B>,5[JW:&R M4MR!HE!NC_I+S;@E8/LM30+O6-JF*06`\02JCZ<6.H7R-[1+4`MT1R`KOSM! MZ$W.,D!(FH5?M`_`@P`(9SH8@T*96YD4L)-@!7D(ZM0#5,<.19U5?+%] M%!^AHP=QFK%;M;`_#+L?3+H@2XG<-*;4TCG&&R:9S%;')(QI;D.[E)/1Z8I% MB=&1D@RCK;QC5.Q7%&-4[NAQ?UZP7*,SQL!W8#2!RTZ\%WCH8HQCW\=)@-,(]QZR;N1NJ,RWRC!:BD'%,EWM#_Y]>[P[WX?97[NVN_?V#=:;^)1O#:> MQ7N?@V0BV4A&DI4,R8!DV&2INFSK'!89;.`EP`!6!1=+#0IE;F1S=')E86T- M96YD;V)J#3,W-"`P(&]B:CP\+TQE;F=T:"`R,34O1FEL=&5R+T9L871E1&5C M;V1E/CYS=')E86T-"DB)I)`Q#H)`$$4E%B33<`3G`K(L"H&*!#5Q"Q.M/(!: M6FBTAH0#>"6]"4>@I""L,P*!0BLW>SA`K$'MT/1!K$H"(MPN4(-0&;]?["=02'EIKD\B(%_$D M\DCK8J)U250608)Z3`.C)642$C-1LY!/FJ7":BC-`;1<=1@]]>@7R9]\?W?X M=^>'O0V]=OXY"V?B;)R1LW+FM.^!.^%NN*.RC?^1MUUF;;?<,:P4[.`MP`!T M71YH#0IE;F1S=')E86T-96YD;V)J#3,W-2`P(&]B:CP\+TQE;F=T:"`Q-C8O M1FEL=&5R+T9L871E1&5C;V1E/CYS=')E86T-"DB),K50,%`P`6)34P4S8X44 M0ZY"+E-#(-\`Q#71,U30-=`SL`0""X7D7"XG3R[]<`530RY]#Z`\E[Y3@+," MD./IJU!25)K*Y>G"]9_A/P@P$*9^0*@_=*0>\(,I!D80]8>!H1Y(_6!@L`=2 M'^P9Y('4@_\-(.KP_P9^(-7^_P"(XH=0\O\/L",H>Q2J?G!3#`R,'Q@8Y/%1 M7*Z>7(%<``$&`%UP*B4-"F5N9'-T>EZN+4 MP=$C(=]Q7'X@)B)-L4R3D!G1+L(3&B.Y=FD<1C34H1Y+)+0]8&I1;<@85`NI MHTI74XI0V25=SM<]VADR,_2X&,A:@5=#(,@!CN`Q-]`&\P.R$OJ0<>5S"3=! M*3U0P\1UYIY#+L-WD.HW9,N@@:P1%(&D;]Q;5.[(GP,^=^^"YO6T/UKD[A^` MWPTXM[C&IP`#`*4@=,`-"F5N9'-TDUPO'DH,:+W#48'1BE*' MT4SR&(T68Y)%,:?CX;3%8H(,K`'?\2W-'RFMXL$HM?;10`F0"\!@!<-5((1< M&\'RI2/D[)6SEGCC_,(5\D_8G_"A]/OMCY8\/-N5YDT[BDX40"UM%"T=L&+Y2@Y MPI86DG%VQ=;*RH&9QV>:;RK25,B:DB8E[7,\H0E9AUAD.8UUIJV.V%A4 M6S(%JJ7\437K&>6H[(HNY^L![1R9V0WEL`>XAP"0,/<0>4#="3[A5NC22#N* MN)J=`+>8/G"@%[HW_E<`Q$K?Z*6$_RL<#)X`Z3=P87&#+P$&`/)[$A`-"F5N M9'-T^4X"S@B&7 MOJ>O0DE1:2J7IPO7#P80^(!$/I#_P_Z_X<%_(/GP(9@\#":/___!#R/[0>1C M&/E!_O]C>0S2_O\#^__/_X/(_ZCD@7KL9,-_["0C=O(?,W82Z%BL)-"QK)U<@%T"``0`*.]K-#0IE;F1S=')E86T-96YD;V)J M#3,X,B`P(&]B:CP\+U1Y<&4O1F]N="]297-O=7)C97,@,S@S(#`@4B].86UE M+U0Q-R]%;F-O9&EN9R`S.#0@,"!2+T9I7!E+T9O;G0O4F5S;W5R8V5S(#8T(#`@4B].86UE+U0Q M.2]%;F-O9&EN9R`V-2`P(%(O1FER%LQ(#`@,"`M,2`P(#!=+U=I9'1H7!E+U1Y<&4S+T9O;G1"0F]X6S,@ M+3(@-C@@-C)=+T9O;G1-871R:7A;,2`P(#`@+3$@,"`P72]7:61T:'-;-S0@ M-38@,C<@-39=/CX-96YD;V)J#3,X."`P(&]B:CP\+U1Y<&4O1F]N="]297-O M=7)C97,@-S@@,"!2+TYA;64O5#(P+T5N8V]D:6YG(#7!E+TUE=&%D M871A+U-U8G1Y<&4O6$U,/CYS=')E86T-"CP_>'!A8VME="!B96=I;CTG[[N_ M)R!I9#TG5S5-,$UP0V5H:4AZDY48WIK8SED)S\^"CP_861O8F4M>&%P M+69I;'1E#IX;7!M971A('AM;&YS.G@])V%D M;V)E.FYS.FUE=&$O)R!X.GAM<'1K/2=835`@=&]O;&MI="`R+CDN,2TQ,RP@ M9G)A;65W;W)K(#$N-B<^#0H\"UN&%P.D-R96%T941A=&4])S(P,#4M,#0M,3)4,3`Z,3`Z,C(M M,#&%P.D-R96%T;W)4;V]L/F1V:7!S*&LI(#4N.#8@0V]P>7)I9VAT M(#$Y.3D@4F%D:6-A;"!%>64@4V]F='=A&UL;G,Z>&%P34T])VAT='`Z+R]N&%P+S$N,"]M;2\G M('AA<$U-.D1O8W5M96YT240])W5U:60Z960T.&(R-&$M9&-C92TT.#`Y+3@S M,#`M,#'!A8VME="!E;F0])WF4@,SDW/CX-"G-T 587)T>')E9@T*,3$V#0HE)45/1@T* ` end SHAR_EOF (set 20 05 04 12 14 30 03 'sparse/spDoc.pdf'; eval "$shar_touch") && chmod 0600 'sparse/spDoc.pdf' if test $? -ne 0 then ${echo} 'restore of sparse/spDoc.pdf failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spDoc.pdf: MD5 check failed' ) << \SHAR_EOF c027ba9d27cf1ec4aa8a94dc8edb203e sparse/spDoc.pdf SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spDoc.pdf'` -ne 257781 && \ ${echo} 'restoration warning: size of sparse/spDoc.pdf is not 257781' fi fi # ============= sparse/spFactor.c ============== if test -f 'sparse/spFactor.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spFactor.c (file already exists)' else ${echo} 'x - extracting sparse/spFactor.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spFactor.c' && /* X * MATRIX FACTORIZATION MODULE X * X * Author: Advising Professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*! \file X * This file contains the routines to factor the matrix into LU form. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ /* >>> User accessible functions contained in this file: X * spOrderAndFactor X * spFactor X * spPartition X * X * >>> Other functions contained in this file: X * FactorComplexMatrix spcCreateInternalVectors X * CountMarkowitz MarkowitzProducts X * SearchForPivot SearchForSingleton X * QuicklySearchDiagonal SearchDiagonal X * SearchEntireMatrix FindLargestInCol X * FindBiggestInColExclude ExchangeRowsAndCols X * spcRowExchange spcColExchange X * ExchangeColElements ExchangeRowElements X * RealRowColElimination ComplexRowColElimination X * UpdateMarkowitzNumbers MatrixIsSingular X * ZeroPivot WriteStatus X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2004 X * by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spFactor.c,v 1.3 2003/06/29 04:19:52 kundert Exp $"; #endif X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X X X X X /* X * Function declarations X */ X static int FactorComplexMatrix( MatrixPtr ); static void CreateInternalVectors( MatrixPtr ); static void CountMarkowitz( MatrixPtr, RealVector, int ); static void MarkowitzProducts( MatrixPtr, int ); static ElementPtr SearchForPivot( MatrixPtr, int, int ); static ElementPtr SearchForSingleton( MatrixPtr, int ); static ElementPtr QuicklySearchDiagonal( MatrixPtr, int ); static ElementPtr SearchDiagonal( MatrixPtr, int ); static ElementPtr SearchEntireMatrix( MatrixPtr, int ); static RealNumber FindLargestInCol( ElementPtr ); static RealNumber FindBiggestInColExclude( MatrixPtr, ElementPtr, int ); static void ExchangeRowsAndCols( MatrixPtr, ElementPtr, int ); static void ExchangeColElements( MatrixPtr, int, ElementPtr, int, X ElementPtr, int ); static void ExchangeRowElements( MatrixPtr, int, ElementPtr, int, X ElementPtr, int ); static void RealRowColElimination( MatrixPtr, ElementPtr ); static void ComplexRowColElimination( MatrixPtr, ElementPtr ); static void UpdateMarkowitzNumbers( MatrixPtr, ElementPtr ); static int MatrixIsSingular( MatrixPtr, int ); static int ZeroPivot( MatrixPtr, int ); static void WriteStatus( MatrixPtr, int ); X X X X X /*! X * This routine chooses a pivot order for the matrix and factors it X * into \a LU form. It handles both the initial factorization and subsequent X * factorizations when a reordering is desired. This is handled in a manner X * that is transparent to the user. The routine uses a variation of X * Gauss's method where the pivots are associated with \a L and the X * diagonal terms of \a U are one. X * X * \return X * The error code is returned. Possible errors are \a spNO_MEMORY, X * \a spSINGULAR and \a spSMALL_PIVOT. X * Error is cleared upon entering this function. X * X * \param eMatrix X * Pointer to the matrix. X * \param RHS X * Representative right-hand side vector that is used to determine X * pivoting order when the right hand side vector is sparse. If X * RHS is a NULL pointer then the RHS vector is assumed to X * be full and it is not used when determining the pivoting X * order. X * \param RelThreshold X * This number determines what the pivot relative threshold will X * be. It should be between zero and one. If it is one then the X * pivoting method becomes complete pivoting, which is very slow X * and tends to fill up the matrix. If it is set close to zero X * the pivoting method becomes strict Markowitz with no X * threshold. The pivot threshold is used to eliminate pivot X * candidates that would cause excessive element growth if they X * were used. Element growth is the cause of roundoff error. X * Element growth occurs even in well-conditioned matrices. X * Setting the \a RelThreshold large will reduce element growth and X * roundoff error, but setting it too large will cause execution X * time to be excessive and will result in a large number of X * fill-ins. If this occurs, accuracy can actually be degraded X * because of the large number of operations required on the X * matrix due to the large number of fill-ins. A good value seems X * to be 0.001. The default is chosen by giving a value larger X * than one or less than or equal to zero. This value should be X * increased and the matrix resolved if growth is found to be X * excessive. Changing the pivot threshold does not improve X * performance on matrices where growth is low, as is often the X * case with ill-conditioned matrices. Once a valid threshold is X * given, it becomes the new default. The default value of X * \a RelThreshold was choosen for use with nearly diagonally X * dominant matrices such as node- and modified-node admittance X * matrices. For these matrices it is usually best to use X * diagonal pivoting. For matrices without a strong diagonal, it X * is usually best to use a larger threshold, such as 0.01 or X * 0.1. X * \param AbsThreshold X * The absolute magnitude an element must have to be considered X * as a pivot candidate, except as a last resort. This number X * should be set significantly smaller than the smallest diagonal X * element that is is expected to be placed in the matrix. If X * there is no reasonable prediction for the lower bound on these X * elements, then \a AbsThreshold should be set to zero. X * \a AbsThreshold is used to reduce the possibility of choosing as a X * pivot an element that has suffered heavy cancellation and as a X * result mainly consists of roundoff error. Once a valid X * threshold is given, it becomes the new default. X * \param DiagPivoting X * A flag indicating that pivot selection should be confined to the X * diagonal if possible. If \a DiagPivoting is nonzero and if X * \a DIAGONAL_PIVOTING is enabled pivots will be chosen only from X * the diagonal unless there are no diagonal elements that satisfy X * the threshold criteria. Otherwise, the entire reduced X * submatrix is searched when looking for a pivot. The diagonal X * pivoting in Sparse is efficient and well refined, while the X * off-diagonal pivoting is not. For symmetric and near symmetric X * matrices, it is best to use diagonal pivoting because it X * results in the best performance when reordering the matrix and X * when factoring the matrix without ordering. If there is a X * considerable amount of nonsymmetry in the matrix, then X * off-diagonal pivoting may result in a better equation ordering X * simply because there are more pivot candidates to choose from. X * A better ordering results in faster subsequent factorizations. X * However, the initial pivot selection process takes considerably X * longer for off-diagonal pivoting. X * X * \see spFactor() X */ /* >>> Local variables: X * pPivot (ElementPtr) X * Pointer to the element being used as a pivot. X * X */ X spError spOrderAndFactor( X spMatrix eMatrix, X spREAL RHS[], X spREAL RelThreshold, X spREAL AbsThreshold, X int DiagPivoting ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; ElementPtr pPivot; int Step, Size; ElementPtr SearchForPivot(); RealNumber LargestInCol, FindLargestInCol(); X /* Begin `spOrderAndFactor'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X X Matrix->Error = spOKAY; X Size = Matrix->Size; X if (RelThreshold <= 0.0) RelThreshold = Matrix->RelThreshold; X if (RelThreshold > 1.0) RelThreshold = Matrix->RelThreshold; X Matrix->RelThreshold = RelThreshold; X if (AbsThreshold < 0.0) AbsThreshold = Matrix->AbsThreshold; X Matrix->AbsThreshold = AbsThreshold; X X if (NOT Matrix->NeedsOrdering) X { /* Matrix has been factored before and reordering is not required. */ X for (Step = 1; Step <= Size; Step++) X { pPivot = Matrix->Diag[Step]; X LargestInCol = FindLargestInCol(pPivot->NextInCol); X if ((LargestInCol * RelThreshold < ELEMENT_MAG(pPivot))) X { if (Matrix->Complex) X ComplexRowColElimination( Matrix, pPivot ); X else X RealRowColElimination( Matrix, pPivot ); X } X else X { Matrix->NeedsOrdering = YES; X break; /* for loop */ X } X } X if (NOT Matrix->NeedsOrdering) X goto Done; X else X { /* X * A pivot was not large enough to maintain accuracy, X * so a partial reordering is required. X */ X #if (ANNOTATE >= ON_STRANGE_BEHAVIOR) X printf("Reordering, Step = %1d\n", Step); #endif X } X } /* End of if(NOT Matrix->NeedsOrdering) */ X else X { /* X * This is the first time the matrix has been factored. These few statements X * indicate to the rest of the code that a full reodering is required rather X * than a partial reordering, which occurs during a failure of a fast X * factorization. X */ X Step = 1; X if (NOT Matrix->RowsLinked) X spcLinkRows( Matrix ); X if (NOT Matrix->InternalVectorsAllocated) X spcCreateInternalVectors( Matrix ); X if (Matrix->Error >= spFATAL) X return Matrix->Error; X } X /* Form initial Markowitz products. */ X CountMarkowitz( Matrix, RHS, Step ); X MarkowitzProducts( Matrix, Step ); X Matrix->MaxRowCountInLowerTri = -1; X /* Perform reordering and factorization. */ X for (; Step <= Size; Step++) X { pPivot = SearchForPivot( Matrix, Step, DiagPivoting ); X if (pPivot == NULL) return MatrixIsSingular( Matrix, Step ); X ExchangeRowsAndCols( Matrix, pPivot, Step ); X X if (Matrix->Complex) X ComplexRowColElimination( Matrix, pPivot ); X else X RealRowColElimination( Matrix, pPivot ); X X if (Matrix->Error >= spFATAL) return Matrix->Error; X UpdateMarkowitzNumbers( Matrix, pPivot ); X #if (ANNOTATE == FULL) X WriteStatus( Matrix, Step ); #endif X } X Done: X Matrix->NeedsOrdering = NO; X Matrix->Reordered = YES; X Matrix->Factored = YES; X X return Matrix->Error; } X X X X X X X /*! X * This routine is the companion routine to spOrderAndFactor(). X * Unlike spOrderAndFactor(), spFactor() cannot change the ordering. X * It is also faster than spOrderAndFactor(). The standard way of X * using these two routines is to first use spOrderAndFactor() for the X * initial factorization. For subsequent factorizations, spFactor() X * is used if there is some assurance that little growth will occur X * (say for example, that the matrix is diagonally dominant). If X * spFactor() is called for the initial factorization of the matrix, X * then spOrderAndFactor() is automatically called with the default X * threshold. This routine uses "row at a time" \a LU factorization. X * Pivots are associated with the lower triangular matrix and the X * diagonals of the upper triangular matrix are ones. X * X * \return X * The error code is returned. Possible errors are X * \a spNO_MEMORY, \a spSINGULAR, \a spZERO_DIAG and \a spSMALL_PIVOT. X * Error is cleared upon entering this function. X * X * \param eMatrix X * Pointer to matrix. X * \see spOrderAndFactor() X */ X spError spFactor( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register ElementPtr pColumn; register int Step, Size; RealNumber Mult; X /* Begin `spFactor'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X X if (Matrix->NeedsOrdering) X { return spOrderAndFactor( eMatrix, (RealVector)NULL, X 0.0, 0.0, DIAG_PIVOTING_AS_DEFAULT ); X } X if (NOT Matrix->Partitioned) spPartition( eMatrix, spDEFAULT_PARTITION ); #if spCOMPLEX X if (Matrix->Complex) return FactorComplexMatrix( Matrix ); #endif X #if REAL X Size = Matrix->Size; X X if (Matrix->Diag[1]->Real == 0.0) return ZeroPivot( Matrix, 1 ); X Matrix->Diag[1]->Real = 1.0 / Matrix->Diag[1]->Real; X /* Start factorization. */ X for (Step = 2; Step <= Size; Step++) X { if (Matrix->DoRealDirect[Step]) X { /* Update column using direct addressing scatter-gather. */ X register RealNumber *Dest = (RealNumber *)Matrix->Intermediate; X /* Scatter. */ X pElement = Matrix->FirstInCol[Step]; X while (pElement != NULL) X { Dest[pElement->Row] = pElement->Real; X pElement = pElement->NextInCol; X } X /* Update column. */ X pColumn = Matrix->FirstInCol[Step]; X while (pColumn->Row < Step) X { pElement = Matrix->Diag[pColumn->Row]; X pColumn->Real = Dest[pColumn->Row] * pElement->Real; X while ((pElement = pElement->NextInCol) != NULL) X Dest[pElement->Row] -= pColumn->Real * pElement->Real; X pColumn = pColumn->NextInCol; X } X /* Gather. */ X pElement = Matrix->Diag[Step]->NextInCol; X while (pElement != NULL) X { pElement->Real = Dest[pElement->Row]; X pElement = pElement->NextInCol; X } X /* Check for singular matrix. */ X if (Dest[Step] == 0.0) return ZeroPivot( Matrix, Step ); X Matrix->Diag[Step]->Real = 1.0 / Dest[Step]; X } X else X { /* Update column using indirect addressing scatter-gather. */ X register RealNumber **pDest = (RealNumber **)Matrix->Intermediate; X /* Scatter. */ X pElement = Matrix->FirstInCol[Step]; X while (pElement != NULL) X { pDest[pElement->Row] = &pElement->Real; X pElement = pElement->NextInCol; X } X /* Update column. */ X pColumn = Matrix->FirstInCol[Step]; X while (pColumn->Row < Step) X { pElement = Matrix->Diag[pColumn->Row]; X Mult = (*pDest[pColumn->Row] *= pElement->Real); X while ((pElement = pElement->NextInCol) != NULL) X *pDest[pElement->Row] -= Mult * pElement->Real; X pColumn = pColumn->NextInCol; X } X /* Check for singular matrix. */ X if (Matrix->Diag[Step]->Real == 0.0) X return ZeroPivot( Matrix, Step ); X Matrix->Diag[Step]->Real = 1.0 / Matrix->Diag[Step]->Real; X } X } X X Matrix->Factored = YES; X return (Matrix->Error = spOKAY); #endif /* REAL */ } X X X X X X #if spCOMPLEX /* X * FACTOR COMPLEX MATRIX X * X * This routine is the companion routine to spFactor(), it X * handles complex matrices. It is otherwise identical. X * X * >>> Returned: X * The error code is returned. Possible errors are listed below. X * X * >>> Arguments: X * Matrix (char *) X * Pointer to matrix. X * X * >>> Possible errors: X * spSINGULAR X * Error is cleared in this function. X */ X static int FactorComplexMatrix( MatrixPtr Matrix ) { register ElementPtr pElement; register ElementPtr pColumn; register int Step, Size; ComplexNumber Mult, Pivot; X /* Begin `FactorComplexMatrix'. */ X ASSERT(Matrix->Complex); X X Size = Matrix->Size; X pElement = Matrix->Diag[1]; X if (ELEMENT_MAG(pElement) == 0.0) return ZeroPivot( Matrix, 1 ); /* Cmplx expr: *pPivot = 1.0 / *pPivot. */ X CMPLX_RECIPROCAL( *pElement, *pElement ); X /* Start factorization. */ X for (Step = 2; Step <= Size; Step++) X { if (Matrix->DoCmplxDirect[Step]) X { /* Update column using direct addressing scatter-gather. */ X register ComplexNumber *Dest; X Dest = (ComplexNumber *)Matrix->Intermediate; X /* Scatter. */ X pElement = Matrix->FirstInCol[Step]; X while (pElement != NULL) X { Dest[pElement->Row] = *(ComplexNumber *)pElement; X pElement = pElement->NextInCol; X } X /* Update column. */ X pColumn = Matrix->FirstInCol[Step]; X while (pColumn->Row < Step) X { pElement = Matrix->Diag[pColumn->Row]; X /* Cmplx expr: Mult = Dest[pColumn->Row] * (1.0 / *pPivot). */ X CMPLX_MULT(Mult, Dest[pColumn->Row], *pElement); X CMPLX_ASSIGN(*pColumn, Mult); X while ((pElement = pElement->NextInCol) != NULL) X { /* Cmplx expr: Dest[pElement->Row] -= Mult * pElement */ X CMPLX_MULT_SUBT_ASSIGN(Dest[pElement->Row],Mult,*pElement); X } X pColumn = pColumn->NextInCol; X } X /* Gather. */ X pElement = Matrix->Diag[Step]->NextInCol; X while (pElement != NULL) X { *(ComplexNumber *)pElement = Dest[pElement->Row]; X pElement = pElement->NextInCol; X } X /* Check for singular matrix. */ X Pivot = Dest[Step]; X if (CMPLX_1_NORM(Pivot) == 0.0) return ZeroPivot( Matrix, Step ); X CMPLX_RECIPROCAL( *Matrix->Diag[Step], Pivot ); X } X else X { /* Update column using direct addressing scatter-gather. */ X register ComplexNumber **pDest; X pDest = (ComplexNumber **)Matrix->Intermediate; X /* Scatter. */ X pElement = Matrix->FirstInCol[Step]; X while (pElement != NULL) X { pDest[pElement->Row] = (ComplexNumber *)pElement; X pElement = pElement->NextInCol; X } X /* Update column. */ X pColumn = Matrix->FirstInCol[Step]; X while (pColumn->Row < Step) X { pElement = Matrix->Diag[pColumn->Row]; X /* Cmplx expr: Mult = *pDest[pColumn->Row] * (1.0 / *pPivot). */ X CMPLX_MULT(Mult, *pDest[pColumn->Row], *pElement); X CMPLX_ASSIGN(*pDest[pColumn->Row], Mult); X while ((pElement = pElement->NextInCol) != NULL) X { /* Cmplx expr: *pDest[pElement->Row] -= Mult * pElement */ X CMPLX_MULT_SUBT_ASSIGN(*pDest[pElement->Row],Mult,*pElement); X } X pColumn = pColumn->NextInCol; X } X /* Check for singular matrix. */ X pElement = Matrix->Diag[Step]; X if (ELEMENT_MAG(pElement) == 0.0) return ZeroPivot( Matrix, Step ); X CMPLX_RECIPROCAL( *pElement, *pElement ); X } X } X X Matrix->Factored = YES; X return (Matrix->Error = spOKAY); } #endif /* spCOMPLEX */ X X X X X X /*! X * This routine determines the cost to factor each row using both X * direct and indirect addressing and decides, on a row-by-row basis, X * which addressing mode is fastest. This information is used in X * spFactor() to speed the factorization. X * X * When factoring a previously ordered matrix using spFactor(), Sparse X * operates on a row-at-a-time basis. For speed, on each step, the X * row being updated is copied into a full vector and the operations X * are performed on that vector. This can be done one of two ways, X * either using direct addressing or indirect addressing. Direct X * addressing is fastest when the matrix is relatively dense and X * indirect addressing is best when the matrix is quite sparse. The X * user selects the type of partition used with \a Mode. If \a Mode is set X * to \a spDIRECT_PARTITION, then the all rows are placed in the direct X * addressing partition. Similarly, if \a Mode is set to X * \a spINDIRECT_PARTITION, then the all rows are placed in the indirect X * addressing partition. By setting \a Mode to \a spAUTO_PARTITION, the X * user allows Sparse to select the partition for each row X * individually. spFactor() generally runs faster if Sparse is X * allowed to choose its own partitioning, however choosing a X * partition is expensive. The time required to choose a partition is X * of the same order of the cost to factor the matrix. If you plan to X * factor a large number of matrices with the same structure, it is X * best to let Sparse choose the partition. Otherwise, you should X * choose the partition based on the predicted density of the matrix. X * X * \param eMatrix X * Pointer to matrix. X * \param Mode X * Mode must be one of three special codes: \a spDIRECT_PARTITION, X * \a spINDIRECT_PARTITION, or \a spAUTO_PARTITION. X */ X void spPartition( X spMatrix eMatrix, X int Mode ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement, pColumn; register int Step, Size; register int *Nc, *No; register long *Nm; BOOLEAN *DoRealDirect, *DoCmplxDirect; X /* Begin `spPartition'. */ X ASSERT_IS_SPARSE( Matrix ); X X if (Matrix->Partitioned) return; X Size = Matrix->Size; X DoRealDirect = Matrix->DoRealDirect; X DoCmplxDirect = Matrix->DoCmplxDirect; X Matrix->Partitioned = YES; X /* If partition is specified by the user, this is easy. */ X if (Mode == spDEFAULT_PARTITION) Mode = DEFAULT_PARTITION; X if (Mode == spDIRECT_PARTITION) X { for (Step = 1; Step <= Size; Step++) #if REAL X DoRealDirect[Step] = YES; #endif #if spCOMPLEX X DoCmplxDirect[Step] = YES; #endif X return; X } X else if (Mode == spINDIRECT_PARTITION) X { for (Step = 1; Step <= Size; Step++) #if REAL X DoRealDirect[Step] = NO; #endif #if spCOMPLEX X DoCmplxDirect[Step] = NO; #endif X return; X } X else vASSERT( Mode == spAUTO_PARTITION, "Invalid partition code" );; X /* Otherwise, count all operations needed in when factoring matrix. */ X Nc = (int *)Matrix->MarkowitzRow; X No = (int *)Matrix->MarkowitzCol; X Nm = (long *)Matrix->MarkowitzProd; X /* Start mock-factorization. */ X for (Step = 1; Step <= Size; Step++) X { Nc[Step] = No[Step] = Nm[Step] = 0; X X pElement = Matrix->FirstInCol[Step]; X while (pElement != NULL) X { Nc[Step]++; X pElement = pElement->NextInCol; X } X X pColumn = Matrix->FirstInCol[Step]; X while (pColumn->Row < Step) X { pElement = Matrix->Diag[pColumn->Row]; X Nm[Step]++; X while ((pElement = pElement->NextInCol) != NULL) X No[Step]++; X pColumn = pColumn->NextInCol; X } X } X X for (Step = 1; Step <= Size; Step++) X { /* X * The following are just estimates based on a count on the number of X * machine instructions used on each machine to perform the various X * tasks. It was assumed that each machine instruction required the X * same amount of time (I don't believe this is true for the VAX, and X * have no idea if this is true for the 68000 family). For optimum X * performance, these numbers should be tuned to the machine. X * Nc is the number of nonzero elements in the column. X * Nm is the number of multipliers in the column. X * No is the number of operations in the inner loop. X */ X #define generic #ifdef hp9000s300 #if REAL X DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]); #endif #if spCOMPLEX X /* On the hp350, it is never profitable to use direct for complex. */ X DoCmplxDirect[Step] = NO; #endif #undef generic #endif X #ifdef vax #if REAL X DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]); #endif #if spCOMPLEX X DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7*Nc[Step] - 4*Nm[Step]); #endif #undef generic #endif X #ifdef generic #if REAL X DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]); #endif #if spCOMPLEX X DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7*Nc[Step] - 4*Nm[Step]); #endif #undef generic #endif X } X #if (ANNOTATE == FULL) X { int Ops = 0; X for (Step = 1; Step <= Size; Step++) X Ops += No[Step]; X printf("Operation count for inner loop of factorization = %d.\n", Ops); X } #endif X return; } X X X X X X X /* X * CREATE INTERNAL VECTORS X * X * Creates the Markowitz and Intermediate vectors. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * X * >>> Possible errors: X * spNO_MEMORY X */ X void spcCreateInternalVectors( MatrixPtr Matrix ) { int Size; X /* Begin `spcCreateInternalVectors'. */ /* Create Markowitz arrays. */ X Size= Matrix->Size; X X if (Matrix->MarkowitzRow == NULL) X { if (( Matrix->MarkowitzRow = ALLOC(int, Size+1)) == NULL) X Matrix->Error = spNO_MEMORY; X } X if (Matrix->MarkowitzCol == NULL) X { if (( Matrix->MarkowitzCol = ALLOC(int, Size+1)) == NULL) X Matrix->Error = spNO_MEMORY; X } X if (Matrix->MarkowitzProd == NULL) X { if (( Matrix->MarkowitzProd = ALLOC(long, Size+2)) == NULL) X Matrix->Error = spNO_MEMORY; X } X /* Create DoDirect vectors for use in spFactor(). */ #if REAL X if (Matrix->DoRealDirect == NULL) X { if (( Matrix->DoRealDirect = ALLOC(BOOLEAN, Size+1)) == NULL) X Matrix->Error = spNO_MEMORY; X } #endif #if spCOMPLEX X if (Matrix->DoCmplxDirect == NULL) X { if (( Matrix->DoCmplxDirect = ALLOC(BOOLEAN, Size+1)) == NULL) X Matrix->Error = spNO_MEMORY; X } #endif X /* Create Intermediate vectors for use in MatrixSolve. */ #if spCOMPLEX X if (Matrix->Intermediate == NULL) X { if ((Matrix->Intermediate = ALLOC(RealNumber,2*(Size+1))) == NULL) X Matrix->Error = spNO_MEMORY; X } #else X if (Matrix->Intermediate == NULL) X { if ((Matrix->Intermediate = ALLOC(RealNumber, Size+1)) == NULL) X Matrix->Error = spNO_MEMORY; X } #endif X X if (Matrix->Error != spNO_MEMORY) X Matrix->InternalVectorsAllocated = YES; X return; } X X X X X X X /* X * COUNT MARKOWITZ X * X * Scans Matrix to determine the Markowitz counts for each row and column. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * RHS (RealVector) X * Representative right-hand side vector that is used to determine X * pivoting order when the right hand side vector is sparse. If X * RHS is a NULL pointer then the RHS vector is assumed to be full X * and it is not used when determining the pivoting order. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * Count (int) X * Temporary counting variable. X * ExtRow (int) X * The external row number that corresponds to I. X * pElement (ElementPtr) X * Pointer to matrix elements. X * Size (int) X * The size of the matrix. X */ X static void CountMarkowitz( X MatrixPtr Matrix, X register RealVector RHS, X int Step ) { register int Count, I, Size = Matrix->Size; register ElementPtr pElement; int ExtRow; X /* Begin `CountMarkowitz'. */ X /* Correct array pointer for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET #if spSEPARATED_COMPLEX_VECTORS OR NOT spCOMPLEX X if (RHS != NULL) --RHS; #else X if (RHS != NULL) X { if (Matrix->Complex) RHS -= 2; X else --RHS; X } #endif #endif X /* Generate MarkowitzRow Count for each row. */ X for (I = Step; I <= Size; I++) X { /* Set Count to -1 initially to remove count due to pivot element. */ X Count = -1; X pElement = Matrix->FirstInRow[I]; X while (pElement != NULL AND pElement->Col < Step) X pElement = pElement->NextInRow; X while (pElement != NULL) X { Count++; X pElement = pElement->NextInRow; X } X /* Include nonzero elements in the RHS vector. */ X ExtRow = Matrix->IntToExtRowMap[I]; X #if spSEPARATED_COMPLEX_VECTORS OR NOT spCOMPLEX X if (RHS != NULL) X if (RHS[ExtRow] != 0.0) Count++; #else X if (RHS != NULL) X { if (Matrix->Complex) X { if ((RHS[2*ExtRow] != 0.0) OR (RHS[2*ExtRow+1] != 0.0)) X Count++; X } X else if (RHS[I] != 0.0) Count++; X } #endif X Matrix->MarkowitzRow[I] = Count; X } X /* Generate the MarkowitzCol count for each column. */ X for (I = Step; I <= Size; I++) X { /* Set Count to -1 initially to remove count due to pivot element. */ X Count = -1; X pElement = Matrix->FirstInCol[I]; X while (pElement != NULL AND pElement->Row < Step) X pElement = pElement->NextInCol; X while (pElement != NULL) X { Count++; X pElement = pElement->NextInCol; X } X Matrix->MarkowitzCol[I] = Count; X } X return; } X X X X X X X X X X /* X * MARKOWITZ PRODUCTS X * X * Calculates MarkowitzProduct for each diagonal element from the Markowitz X * counts. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local Variables: X * pMarkowitzProduct (long *) X * Pointer that points into MarkowitzProduct array. Is used to X * sequentially access entries quickly. X * pMarkowitzRow (int *) X * Pointer that points into MarkowitzRow array. Is used to sequentially X * access entries quickly. X * pMarkowitzCol (int *) X * Pointer that points into MarkowitzCol array. Is used to sequentially X * access entries quickly. X * Product (long) X * Temporary storage for Markowitz product./ X * Size (int) X * The size of the matrix. X */ X static void MarkowitzProducts( X MatrixPtr Matrix, X int Step ) { register int I, *pMarkowitzRow, *pMarkowitzCol; register long Product, *pMarkowitzProduct; register int Size = Matrix->Size; double fProduct; X /* Begin `MarkowitzProducts'. */ X Matrix->Singletons = 0; X X pMarkowitzProduct = &(Matrix->MarkowitzProd[Step]); X pMarkowitzRow = &(Matrix->MarkowitzRow[Step]); X pMarkowitzCol = &(Matrix->MarkowitzCol[Step]); X X for (I = Step; I <= Size; I++) X { /* If chance of overflow, use real numbers. */ X if ((*pMarkowitzRow > LARGEST_SHORT_INTEGER AND *pMarkowitzCol != 0) OR X (*pMarkowitzCol > LARGEST_SHORT_INTEGER AND *pMarkowitzRow != 0)) X { fProduct = (double)(*pMarkowitzRow++) * (double)(*pMarkowitzCol++); X if (fProduct >= LARGEST_LONG_INTEGER) X *pMarkowitzProduct++ = LARGEST_LONG_INTEGER; X else X *pMarkowitzProduct++ = (long)fProduct; X } X else X { Product = *pMarkowitzRow++ * *pMarkowitzCol++; X if ((*pMarkowitzProduct++ = Product) == 0) X Matrix->Singletons++; X } X } X return; } X X X X X X X X X X X /* X * SEARCH FOR BEST PIVOT X * X * Performs a search to determine the element with the lowest Markowitz X * Product that is also acceptable. An acceptable element is one that is X * larger than the AbsThreshold and at least as large as RelThreshold times X * the largest element in the same column. The first step is to look for X * singletons if any exist. If none are found, then all the diagonals are X * searched. The diagonal is searched once quickly using the assumption that X * elements on the diagonal are large compared to other elements in their X * column, and so the pivot can be chosen only on the basis of the Markowitz X * criterion. After a element has been chosen to be pivot on the basis of X * its Markowitz product, it is checked to see if it is large enough. X * Waiting to the end of the Markowitz search to check the size of a pivot X * candidate saves considerable time, but is not guaranteed to find an X * acceptable pivot. Thus if unsuccessful a second pass of the diagonal is X * made. This second pass checks to see if an element is large enough during X * the search, not after it. If still no acceptable pivot candidate has X * been found, the search expands to cover the entire matrix. X * X * >>> Returned: X * A pointer to the element chosen to be pivot. If every element in the X * matrix is zero, then NULL is returned. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * The row and column number of the beginning of the reduced submatrix. X * X * >>> Local variables: X * ChosenPivot (ElementPtr) X * Pointer to element that has been chosen to be the pivot. X * X * >>> Possible errors: X * spSINGULAR X * spSMALL_PIVOT X */ X static ElementPtr SearchForPivot( X MatrixPtr Matrix, X int Step, X BOOLEAN DiagPivoting ) { register ElementPtr ChosenPivot; ElementPtr SearchForSingleton(); ElementPtr QuicklySearchDiagonal(); ElementPtr SearchDiagonal(); ElementPtr SearchEntireMatrix(); X /* Begin `SearchForPivot'. */ X /* If singletons exist, look for an acceptable one to use as pivot. */ X if (Matrix->Singletons) X { ChosenPivot = SearchForSingleton( Matrix, Step ); X if (ChosenPivot != NULL) X { Matrix->PivotSelectionMethod = 's'; X return ChosenPivot; X } X } X #if DIAGONAL_PIVOTING X if (DiagPivoting) X { /* X * Either no singletons exist or they weren't acceptable. Take quick first X * pass at searching diagonal. First search for element on diagonal of X * remaining submatrix with smallest Markowitz product, then check to see X * if it okay numerically. If not, QuicklySearchDiagonal fails. X */ X ChosenPivot = QuicklySearchDiagonal( Matrix, Step ); X if (ChosenPivot != NULL) X { Matrix->PivotSelectionMethod = 'q'; X return ChosenPivot; X } X /* X * Quick search of diagonal failed, carefully search diagonal and check each X * pivot candidate numerically before even tentatively accepting it. X */ X ChosenPivot = SearchDiagonal( Matrix, Step ); X if (ChosenPivot != NULL) X { Matrix->PivotSelectionMethod = 'd'; X return ChosenPivot; X } X } #endif /* DIAGONAL_PIVOTING */ X /* No acceptable pivot found yet, search entire matrix. */ X ChosenPivot = SearchEntireMatrix( Matrix, Step ); X Matrix->PivotSelectionMethod = 'e'; X X return ChosenPivot; } X X X X X X X X X /* X * SEARCH FOR SINGLETON TO USE AS PIVOT X * X * Performs a search to find a singleton to use as the pivot. The X * first acceptable singleton is used. A singleton is acceptable if X * it is larger in magnitude than the AbsThreshold and larger X * than RelThreshold times the largest of any other elements in the same X * column. It may seem that a singleton need not satisfy the X * relative threshold criterion, however it is necessary to prevent X * excessive growth in the RHS from resulting in overflow during the X * forward and backward substitution. A singleton does not need to X * be on the diagonal to be selected. X * X * >>> Returned: X * A pointer to the singleton chosen to be pivot. In no singleton is X * acceptable, return NULL. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * ChosenPivot (ElementPtr) X * Pointer to element that has been chosen to be the pivot. X * PivotMag (RealNumber) X * Magnitude of ChosenPivot. X * Singletons (int) X * The count of the number of singletons that can be used as pivots. X * A local version of Matrix->Singletons. X * pMarkowitzProduct (long *) X * Pointer that points into MarkowitzProduct array. It is used to quickly X * access successive Markowitz products. X */ X static ElementPtr SearchForSingleton( X MatrixPtr Matrix, X int Step ) { register ElementPtr ChosenPivot; register int I; register long *pMarkowitzProduct; int Singletons; RealNumber PivotMag, FindBiggestInColExclude(); X /* Begin `SearchForSingleton'. */ /* Initialize pointer that is to scan through MarkowitzProduct vector. */ X pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+1]); X Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step]; X /* Decrement the count of available singletons, on the assumption that an X * acceptable one will be found. */ X Singletons = Matrix->Singletons--; X /* X * Assure that following while loop will always terminate, this is just X * preventive medicine, if things are working right this should never X * be needed. X */ X Matrix->MarkowitzProd[Step-1] = 0; X X while (Singletons-- > 0) X { /* Singletons exist, find them. */ X /* X * This is tricky. Am using a pointer to sequentially step through the X * MarkowitzProduct array. Search terminates when singleton (Product = 0) X * is found. Note that the conditional in the while statement X * ( *pMarkowitzProduct ) is true as long as the MarkowitzProduct is not X * equal to zero. The row (and column) index on the diagonal is then X * calculated by subtracting the pointer to the Markowitz product of X * the first diagonal from the pointer to the Markowitz product of the X * desired element, the singleton. X * X * Search proceeds from the end (high row and column numbers) to the X * beginning (low row and column numbers) so that rows and columns with X * large Markowitz products will tend to be move to the bottom of the X * matrix. However, choosing Diag[Step] is desirable because it would X * require no row and column interchanges, so inspect it first by X * putting its Markowitz product at the end of the MarkowitzProd X * vector. X */ X X while ( *pMarkowitzProduct-- ) X { /* X * N bottles of beer on the wall; X * N bottles of beer. X * you take one down and pass it around; X * N-1 bottles of beer on the wall. X */ X } X I = pMarkowitzProduct - Matrix->MarkowitzProd + 1; X /* Assure that I is valid. */ X if (I < Step) break; /* while (Singletons-- > 0) */ X if (I > Matrix->Size) I = Step; X /* Singleton has been found in either/both row or/and column I. */ X if ((ChosenPivot = Matrix->Diag[I]) != NULL) X { /* Singleton lies on the diagonal. */ X PivotMag = ELEMENT_MAG(ChosenPivot); X if X ( PivotMag > Matrix->AbsThreshold AND X PivotMag > Matrix->RelThreshold * X FindBiggestInColExclude( Matrix, ChosenPivot, Step ) X ) return ChosenPivot; X } X else X { /* Singleton does not lie on diagonal, find it. */ X if (Matrix->MarkowitzCol[I] == 0) X { ChosenPivot = Matrix->FirstInCol[I]; X while ((ChosenPivot != NULL) AND (ChosenPivot->Row < Step)) X ChosenPivot = ChosenPivot->NextInCol; X if (ChosenPivot == NULL) X { /* Reduced column has no elements, matrix is singular. */ X break; X } X PivotMag = ELEMENT_MAG( ChosenPivot ); X if X ( PivotMag > Matrix->AbsThreshold AND X PivotMag > Matrix->RelThreshold * X FindBiggestInColExclude( Matrix, ChosenPivot, X Step ) X ) return ChosenPivot; X else X { if (Matrix->MarkowitzRow[I] == 0) X { ChosenPivot = Matrix->FirstInRow[I]; X while((ChosenPivot != NULL) AND (ChosenPivot->ColNextInRow; X if (ChosenPivot == NULL) X {/* Reduced row has no elements, matrix is singular. */ X break; X } X PivotMag = ELEMENT_MAG(ChosenPivot); X if X ( PivotMag > Matrix->AbsThreshold AND X PivotMag > Matrix->RelThreshold * X FindBiggestInColExclude( Matrix, X ChosenPivot, X Step ) X ) return ChosenPivot; X } X } X } X else X { ChosenPivot = Matrix->FirstInRow[I]; X while ((ChosenPivot != NULL) AND (ChosenPivot->Col < Step)) X ChosenPivot = ChosenPivot->NextInRow; X if (ChosenPivot == NULL) X { /* Reduced row has no elements, matrix is singular. */ X break; X } X PivotMag = ELEMENT_MAG(ChosenPivot); X if X ( PivotMag > Matrix->AbsThreshold AND X PivotMag > Matrix->RelThreshold * X FindBiggestInColExclude( Matrix, ChosenPivot, X Step ) X ) return ChosenPivot; X } X } /* Singleton not acceptable (too small), try another. */ X } /* end of while(lSingletons>0) */ X /* X * All singletons were unacceptable. Restore Matrix->Singletons count. X * Initial assumption that an acceptable singleton would be found was wrong. X */ X Matrix->Singletons++; X return NULL; } X X X X X X X X X X X X #if DIAGONAL_PIVOTING #if MODIFIED_MARKOWITZ /* X * QUICK SEARCH OF DIAGONAL FOR PIVOT WITH MODIFIED MARKOWITZ CRITERION X * X * Searches the diagonal looking for the best pivot. For a pivot to be X * acceptable it must be larger than the pivot RelThreshold times the largest X * element in its reduced column. Among the acceptable diagonals, the X * one with the smallest MarkowitzProduct is sought. Search terminates X * early if a diagonal is found with a MarkowitzProduct of one and its X * magnitude is larger than the other elements in its row and column. X * Since its MarkowitzProduct is one, there is only one other element in X * both its row and column, and, as a condition for early termination, X * these elements must be located symmetricly in the matrix. If a tie X * occurs between elements of equal MarkowitzProduct, then the element with X * the largest ratio between its magnitude and the largest element in its X * column is used. The search will be terminated after a given number of X * ties have occurred and the best (largest ratio) of the tied element will X * be used as the pivot. The number of ties that will trigger an early X * termination is MinMarkowitzProduct * TIES_MULTIPLIER. X * X * >>> Returned: X * A pointer to the diagonal element chosen to be pivot. If no diagonal is X * acceptable, a NULL is returned. X * X * >>> Arguments: X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * ChosenPivot (ElementPtr) X * Pointer to the element that has been chosen to be the pivot. X * LargestOffDiagonal (RealNumber) X * Magnitude of the largest of the off-diagonal terms associated with X * a diagonal with MarkowitzProduct equal to one. X * Magnitude (RealNumber) X * Absolute value of diagonal element. X * MaxRatio (RealNumber) X * Among the elements tied with the smallest Markowitz product, MaxRatio X * is the best (smallest) ratio of LargestInCol to the diagonal Magnitude X * found so far. The smaller the ratio, the better numerically the X * element will be as pivot. X * MinMarkowitzProduct (long) X * Smallest Markowitz product found of pivot candidates that lie along X * diagonal. X * NumberOfTies (int) X * A count of the number of Markowitz ties that have occurred at current X * MarkowitzProduct. X * pDiag (ElementPtr) X * Pointer to current diagonal element. X * pMarkowitzProduct (long *) X * Pointer that points into MarkowitzProduct array. It is used to quickly X * access successive Markowitz products. X * Ratio (RealNumber) X * For the current pivot candidate, Ratio is the ratio of the largest X * element in its column (excluding itself) to its magnitude. X * TiedElements (ElementPtr[]) X * Array of pointers to the elements with the minimum Markowitz X * product. X * pOtherInCol (ElementPtr) X * When there is only one other element in a column other than the X * diagonal, pOtherInCol is used to point to it. Used when Markowitz X * product is to determine if off diagonals are placed symmetricly. X * pOtherInRow (ElementPtr) X * When there is only one other element in a row other than the diagonal, X * pOtherInRow is used to point to it. Used when Markowitz product is X * to determine if off diagonals are placed symmetricly. X */ X static ElementPtr QuicklySearchDiagonal( X MatrixPtr Matrix, X int Step ) { register long MinMarkowitzProduct, *pMarkowitzProduct; register ElementPtr pDiag, pOtherInRow, pOtherInCol; int I, NumberOfTies; ElementPtr ChosenPivot, TiedElements[MAX_MARKOWITZ_TIES + 1]; RealNumber Magnitude, LargestInCol, Ratio, MaxRatio; RealNumber LargestOffDiagonal; RealNumber FindBiggestInColExclude(); X /* Begin `QuicklySearchDiagonal'. */ X NumberOfTies = -1; X MinMarkowitzProduct = LARGEST_LONG_INTEGER; X pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+2]); X Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step]; X /* Assure that following while loop will always terminate. */ X Matrix->MarkowitzProd[Step-1] = -1; X /* X * This is tricky. Am using a pointer in the inner while loop to X * sequentially step through the MarkowitzProduct array. Search X * terminates when the Markowitz product of zero placed at location X * Step-1 is found. The row (and column) index on the diagonal is then X * calculated by subtracting the pointer to the Markowitz product of X * the first diagonal from the pointer to the Markowitz product of the X * desired element. The outer for loop is infinite, broken by using X * break. X * X * Search proceeds from the end (high row and column numbers) to the X * beginning (low row and column numbers) so that rows and columns with X * large Markowitz products will tend to be move to the bottom of the X * matrix. However, choosing Diag[Step] is desirable because it would X * require no row and column interchanges, so inspect it first by X * putting its Markowitz product at the end of the MarkowitzProd X * vector. X */ X X for(;;) /* Endless for loop. */ X { while (MinMarkowitzProduct < *(--pMarkowitzProduct)) X { /* X * N bottles of beer on the wall; X * N bottles of beer. X * You take one down and pass it around; X * N-1 bottles of beer on the wall. X */ X } X X I = pMarkowitzProduct - Matrix->MarkowitzProd; X /* Assure that I is valid; if I < Step, terminate search. */ X if (I < Step) break; /* Endless for loop */ X if (I > Matrix->Size) I = Step; X X if ((pDiag = Matrix->Diag[I]) == NULL) X continue; /* Endless for loop */ X if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold) X continue; /* Endless for loop */ X X if (*pMarkowitzProduct == 1) X { /* Case where only one element exists in row and column other than diagonal. */ X /* Find off diagonal elements. */ X pOtherInRow = pDiag->NextInRow; X pOtherInCol = pDiag->NextInCol; X if (pOtherInRow == NULL AND pOtherInCol == NULL) X { pOtherInRow = Matrix->FirstInRow[I]; X while(pOtherInRow != NULL) X { if (pOtherInRow->Col >= Step AND pOtherInRow->Col != I) X break; X pOtherInRow = pOtherInRow->NextInRow; X } X pOtherInCol = Matrix->FirstInCol[I]; X while(pOtherInCol != NULL) X { if (pOtherInCol->Row >= Step AND pOtherInCol->Row != I) X break; X pOtherInCol = pOtherInCol->NextInCol; X } X } X /* Accept diagonal as pivot if diagonal is larger than off diagonals and the X * off diagonals are placed symmetricly. */ X if (pOtherInRow != NULL AND pOtherInCol != NULL) X { if (pOtherInRow->Col == pOtherInCol->Row) X { LargestOffDiagonal = MAX(ELEMENT_MAG(pOtherInRow), X ELEMENT_MAG(pOtherInCol)); X if (Magnitude >= LargestOffDiagonal) X { /* Accept pivot, it is unlikely to contribute excess error. */ X return pDiag; X } X } X } X } X X if (*pMarkowitzProduct < MinMarkowitzProduct) X { /* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */ X TiedElements[0] = pDiag; X MinMarkowitzProduct = *pMarkowitzProduct; X NumberOfTies = 0; X } X else X { /* This case handles Markowitz ties. */ X if (NumberOfTies < MAX_MARKOWITZ_TIES) X { TiedElements[++NumberOfTies] = pDiag; X if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER) X break; /* Endless for loop */ X } X } X } /* End of endless for loop. */ X /* Test to see if any element was chosen as a pivot candidate. */ X if (NumberOfTies < 0) X return NULL; X /* Determine which of tied elements is best numerically. */ X ChosenPivot = NULL; X MaxRatio = 1.0 / Matrix->RelThreshold; X X for (I = 0; I <= NumberOfTies; I++) X { pDiag = TiedElements[I]; X Magnitude = ELEMENT_MAG(pDiag); X LargestInCol = FindBiggestInColExclude( Matrix, pDiag, Step ); X Ratio = LargestInCol / Magnitude; X if (Ratio < MaxRatio) X { ChosenPivot = pDiag; X MaxRatio = Ratio; X } X } X return ChosenPivot; } X X X X X X X X X X #else /* Not MODIFIED_MARKOWITZ */ /* X * QUICK SEARCH OF DIAGONAL FOR PIVOT WITH CONVENTIONAL MARKOWITZ X * CRITERION X * X * Searches the diagonal looking for the best pivot. For a pivot to be X * acceptable it must be larger than the pivot RelThreshold times the largest X * element in its reduced column. Among the acceptable diagonals, the X * one with the smallest MarkowitzProduct is sought. Search terminates X * early if a diagonal is found with a MarkowitzProduct of one and its X * magnitude is larger than the other elements in its row and column. X * Since its MarkowitzProduct is one, there is only one other element in X * both its row and column, and, as a condition for early termination, X * these elements must be located symmetricly in the matrix. X * X * >>> Returned: X * A pointer to the diagonal element chosen to be pivot. If no diagonal is X * acceptable, a NULL is returned. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * ChosenPivot (ElementPtr) X * Pointer to the element that has been chosen to be the pivot. X * LargestOffDiagonal (RealNumber) X * Magnitude of the largest of the off-diagonal terms associated with X * a diagonal with MarkowitzProduct equal to one. X * Magnitude (RealNumber) X * Absolute value of diagonal element. X * MinMarkowitzProduct (long) X * Smallest Markowitz product found of pivot candidates which are X * acceptable. X * pDiag (ElementPtr) X * Pointer to current diagonal element. X * pMarkowitzProduct (long *) X * Pointer that points into MarkowitzProduct array. It is used to quickly X * access successive Markowitz products. X * pOtherInCol (ElementPtr) X * When there is only one other element in a column other than the X * diagonal, pOtherInCol is used to point to it. Used when Markowitz X * product is to determine if off diagonals are placed symmetricly. X * pOtherInRow (ElementPtr) X * When there is only one other element in a row other than the diagonal, X * pOtherInRow is used to point to it. Used when Markowitz product is X * to determine if off diagonals are placed symmetricly. X */ X static ElementPtr QuicklySearchDiagonal( X MatrixPtr Matrix, X int Step ) { register long MinMarkowitzProduct, *pMarkowitzProduct; register ElementPtr pDiag; int I; ElementPtr ChosenPivot, pOtherInRow, pOtherInCol; RealNumber Magnitude, LargestInCol, LargestOffDiagonal; RealNumber FindBiggestInColExclude(); X /* Begin `QuicklySearchDiagonal'. */ X ChosenPivot = NULL; X MinMarkowitzProduct = LARGEST_LONG_INTEGER; X pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+2]); X Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step]; X /* Assure that following while loop will always terminate. */ X Matrix->MarkowitzProd[Step-1] = -1; X /* X * This is tricky. Am using a pointer in the inner while loop to X * sequentially step through the MarkowitzProduct array. Search X * terminates when the Markowitz product of zero placed at location X * Step-1 is found. The row (and column) index on the diagonal is then X * calculated by subtracting the pointer to the Markowitz product of X * the first diagonal from the pointer to the Markowitz product of the X * desired element. The outer for loop is infinite, broken by using X * break. X * X * Search proceeds from the end (high row and column numbers) to the X * beginning (low row and column numbers) so that rows and columns with X * large Markowitz products will tend to be move to the bottom of the X * matrix. However, choosing Diag[Step] is desirable because it would X * require no row and column interchanges, so inspect it first by X * putting its Markowitz product at the end of the MarkowitzProd X * vector. X */ X X for (;;) /* Endless for loop. */ X { while (*(--pMarkowitzProduct) >= MinMarkowitzProduct) X { /* Just passing through. */ X } X X I = pMarkowitzProduct - Matrix->MarkowitzProd; X /* Assure that I is valid; if I < Step, terminate search. */ X if (I < Step) break; /* Endless for loop */ X if (I > Matrix->Size) I = Step; X X if ((pDiag = Matrix->Diag[I]) == NULL) X continue; /* Endless for loop */ X if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold) X continue; /* Endless for loop */ X X if (*pMarkowitzProduct == 1) X { /* Case where only one element exists in row and column other than diagonal. */ X /* Find off-diagonal elements. */ X pOtherInRow = pDiag->NextInRow; X pOtherInCol = pDiag->NextInCol; X if (pOtherInRow == NULL AND pOtherInCol == NULL) X { pOtherInRow = Matrix->FirstInRow[I]; X while(pOtherInRow != NULL) X { if (pOtherInRow->Col >= Step AND pOtherInRow->Col != I) X break; X pOtherInRow = pOtherInRow->NextInRow; X } X pOtherInCol = Matrix->FirstInCol[I]; X while(pOtherInCol != NULL) X { if (pOtherInCol->Row >= Step AND pOtherInCol->Row != I) X break; X pOtherInCol = pOtherInCol->NextInCol; X } X } X /* Accept diagonal as pivot if diagonal is larger than off-diagonals and the X * off-diagonals are placed symmetricly. */ X if (pOtherInRow != NULL AND pOtherInCol != NULL) X { if (pOtherInRow->Col == pOtherInCol->Row) X { LargestOffDiagonal = MAX(ELEMENT_MAG(pOtherInRow), X ELEMENT_MAG(pOtherInCol)); X if (Magnitude >= LargestOffDiagonal) X { /* Accept pivot, it is unlikely to contribute excess error. */ X return pDiag; X } X } X } X } X X MinMarkowitzProduct = *pMarkowitzProduct; X ChosenPivot = pDiag; X } /* End of endless for loop. */ X X if (ChosenPivot != NULL) X { LargestInCol = FindBiggestInColExclude( Matrix, ChosenPivot, Step ); X if( ELEMENT_MAG(ChosenPivot) <= Matrix->RelThreshold * LargestInCol ) X ChosenPivot = NULL; X } X return ChosenPivot; } #endif /* Not MODIFIED_MARKOWITZ */ X X X X X X X X X /* X * SEARCH DIAGONAL FOR PIVOT WITH MODIFIED MARKOWITZ CRITERION X * X * Searches the diagonal looking for the best pivot. For a pivot to be X * acceptable it must be larger than the pivot RelThreshold times the largest X * element in its reduced column. Among the acceptable diagonals, the X * one with the smallest MarkowitzProduct is sought. If a tie occurs X * between elements of equal MarkowitzProduct, then the element with X * the largest ratio between its magnitude and the largest element in its X * column is used. The search will be terminated after a given number of X * ties have occurred and the best (smallest ratio) of the tied element will X * be used as the pivot. The number of ties that will trigger an early X * termination is MinMarkowitzProduct * TIES_MULTIPLIER. X * X * >>> Returned: X * A pointer to the diagonal element chosen to be pivot. If no diagonal is X * acceptable, a NULL is returned. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * ChosenPivot (ElementPtr) X * Pointer to the element that has been chosen to be the pivot. X * Size (int) X * Local version of size which is placed in a register to increase speed. X * Magnitude (RealNumber) X * Absolute value of diagonal element. X * MinMarkowitzProduct (long) X * Smallest Markowitz product found of those pivot candidates which are X * acceptable. X * NumberOfTies (int) X * A count of the number of Markowitz ties that have occurred at current X * MarkowitzProduct. X * pDiag (ElementPtr) X * Pointer to current diagonal element. X * pMarkowitzProduct (long*) X * Pointer that points into MarkowitzProduct array. It is used to quickly X * access successive Markowitz products. X * Ratio (RealNumber) X * For the current pivot candidate, Ratio is the X * Ratio of the largest element in its column to its magnitude. X * RatioOfAccepted (RealNumber) X * For the best pivot candidate found so far, RatioOfAccepted is the X * Ratio of the largest element in its column to its magnitude. X */ X static ElementPtr SearchDiagonal( X MatrixPtr Matrix, X register int Step ) { register int J; register long MinMarkowitzProduct, *pMarkowitzProduct; register int I; register ElementPtr pDiag; int NumberOfTies, Size = Matrix->Size; ElementPtr ChosenPivot; RealNumber Magnitude, Ratio, RatioOfAccepted, LargestInCol; RealNumber FindBiggestInColExclude(); X /* Begin `SearchDiagonal'. */ X ChosenPivot = NULL; X MinMarkowitzProduct = LARGEST_LONG_INTEGER; X pMarkowitzProduct = &(Matrix->MarkowitzProd[Size+2]); X Matrix->MarkowitzProd[Size+1] = Matrix->MarkowitzProd[Step]; X /* Start search of diagonal. */ X for (J = Size+1; J > Step; J--) X { X if (*(--pMarkowitzProduct) > MinMarkowitzProduct) X continue; /* for loop */ X if (J > Matrix->Size) X I = Step; X else X I = J; X if ((pDiag = Matrix->Diag[I]) == NULL) X continue; /* for loop */ X if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold) X continue; /* for loop */ X /* Test to see if diagonal's magnitude is acceptable. */ X LargestInCol = FindBiggestInColExclude( Matrix, pDiag, Step ); X if (Magnitude <= Matrix->RelThreshold * LargestInCol) X continue; /* for loop */ X X if (*pMarkowitzProduct < MinMarkowitzProduct) X { /* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */ X ChosenPivot = pDiag; X MinMarkowitzProduct = *pMarkowitzProduct; X RatioOfAccepted = LargestInCol / Magnitude; X NumberOfTies = 0; X } X else X { /* This case handles Markowitz ties. */ X NumberOfTies++; X Ratio = LargestInCol / Magnitude; X if (Ratio < RatioOfAccepted) X { ChosenPivot = pDiag; X RatioOfAccepted = Ratio; X } X if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER) X return ChosenPivot; X } X } /* End of for(Step) */ X return ChosenPivot; } #endif /* DIAGONAL_PIVOTING */ X X X X X X X X X X /* X * SEARCH ENTIRE MATRIX FOR BEST PIVOT X * X * Performs a search over the entire matrix looking for the acceptable X * element with the lowest MarkowitzProduct. If there are several that X * are tied for the smallest MarkowitzProduct, the tie is broken by using X * the ratio of the magnitude of the element being considered to the largest X * element in the same column. If no element is acceptable then the largest X * element in the reduced submatrix is used as the pivot and the X * matrix is declared to be spSMALL_PIVOT. If the largest element is X * zero, the matrix is declared to be spSINGULAR. X * X * >>> Returned: X * A pointer to the diagonal element chosen to be pivot. If no element is X * found, then NULL is returned and the matrix is spSINGULAR. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * ChosenPivot (ElementPtr) X * Pointer to the element that has been chosen to be the pivot. X * LargestElementMag (RealNumber) X * Magnitude of the largest element yet found in the reduced submatrix. X * Size (int) X * Local version of Size; placed in a register for speed. X * Magnitude (RealNumber) X * Absolute value of diagonal element. X * MinMarkowitzProduct (long) X * Smallest Markowitz product found of pivot candidates which are X * acceptable. X * NumberOfTies (int) X * A count of the number of Markowitz ties that have occurred at current X * MarkowitzProduct. X * pElement (ElementPtr) X * Pointer to current element. X * pLargestElement (ElementPtr) X * Pointer to the largest element yet found in the reduced submatrix. X * Product (long) X * Markowitz product for the current row and column. X * Ratio (RealNumber) X * For the current pivot candidate, Ratio is the X * Ratio of the largest element in its column to its magnitude. X * RatioOfAccepted (RealNumber) X * For the best pivot candidate found so far, RatioOfAccepted is the X * Ratio of the largest element in its column to its magnitude. X * X * >>> Possible errors: X * spSINGULAR X * spSMALL_PIVOT X */ X static ElementPtr SearchEntireMatrix( X MatrixPtr Matrix, X int Step ) { register int I, Size = Matrix->Size; register ElementPtr pElement; int NumberOfTies; long Product, MinMarkowitzProduct; ElementPtr ChosenPivot, pLargestElement; RealNumber Magnitude, LargestElementMag, Ratio, RatioOfAccepted, LargestInCol; RealNumber FindLargestInCol(); X /* Begin `SearchEntireMatrix'. */ X ChosenPivot = NULL; X LargestElementMag = 0.0; X MinMarkowitzProduct = LARGEST_LONG_INTEGER; X /* Start search of matrix on column by column basis. */ X for (I = Step; I <= Size; I++) X { pElement = Matrix->FirstInCol[I]; X X while (pElement != NULL AND pElement->Row < Step) X pElement = pElement->NextInCol; X X if((LargestInCol = FindLargestInCol(pElement)) == 0.0) X continue; /* for loop */ X X while (pElement != NULL) X { /* Check to see if element is the largest encountered so far. If so, record X its magnitude and address. */ X if ((Magnitude = ELEMENT_MAG(pElement)) > LargestElementMag) X { LargestElementMag = Magnitude; X pLargestElement = pElement; X } /* Calculate element's MarkowitzProduct. */ X spcMarkoProd( Product, Matrix->MarkowitzRow[pElement->Row], X Matrix->MarkowitzCol[pElement->Col] ); X /* Test to see if element is acceptable as a pivot candidate. */ X if ((Product <= MinMarkowitzProduct) AND X (Magnitude > Matrix->RelThreshold * LargestInCol) AND X (Magnitude > Matrix->AbsThreshold)) X { /* Test to see if element has lowest MarkowitzProduct yet found, or whether it X is tied with an element found earlier. */ X if (Product < MinMarkowitzProduct) X { /* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */ X ChosenPivot = pElement; X MinMarkowitzProduct = Product; X RatioOfAccepted = LargestInCol / Magnitude; X NumberOfTies = 0; X } X else X { /* This case handles Markowitz ties. */ X NumberOfTies++; X Ratio = LargestInCol / Magnitude; X if (Ratio < RatioOfAccepted) X { ChosenPivot = pElement; X RatioOfAccepted = Ratio; X } X if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER) X return ChosenPivot; X } X } X pElement = pElement->NextInCol; X } /* End of while(pElement != NULL) */ X } /* End of for(Step) */ X X if (ChosenPivot != NULL) return ChosenPivot; X X if (LargestElementMag == 0.0) X { Matrix->Error = spSINGULAR; X return NULL; X } X X Matrix->Error = spSMALL_PIVOT; X return pLargestElement; } X X X X X X X X X X X X /* X * DETERMINE THE MAGNITUDE OF THE LARGEST ELEMENT IN A COLUMN X * X * This routine searches a column and returns the magnitude of the largest X * element. This routine begins the search at the element pointed to by X * pElement, the parameter. X * X * The search is conducted by starting at the element specified by a pointer, X * which should be one below the diagonal, and moving down the column. On X * the way down the column, the magnitudes of the elements are tested to see X * if they are the largest yet found. X * X * >>> Returned: X * The magnitude of the largest element in the column below and including X * the one pointed to by the input parameter. X * X * >>> Arguments: X * pElement (ElementPtr) X * The pointer to the first element to be tested. Also, used by the X * routine to access all lower elements in the column. X * X * >>> Local variables: X * Largest (RealNumber) X * The magnitude of the largest element. X * Magnitude (RealNumber) X * The magnitude of the currently active element. X */ X static RealNumber FindLargestInCol( register ElementPtr pElement ) { RealNumber Magnitude, Largest = 0.0; X /* Begin `FindLargestInCol'. */ /* Search column for largest element beginning at Element. */ X while (pElement != NULL) X { if ((Magnitude = ELEMENT_MAG(pElement)) > Largest) X Largest = Magnitude; X pElement = pElement->NextInCol; X } X X return Largest; } X X X X X X X X X X /* X * DETERMINE THE MAGNITUDE OF THE LARGEST ELEMENT IN A COLUMN X * EXCLUDING AN ELEMENT X * X * This routine searches a column and returns the magnitude of the largest X * element. One given element is specifically excluded from the search. X * X * The search is conducted by starting at the first element in the column X * and moving down the column until the active part of the matrix is entered, X * i.e. the reduced submatrix. The rest of the column is then traversed X * looking for the largest element. X * X * >>> Returned: X * The magnitude of the largest element in the active portion of the column, X * excluding the specified element, is returned. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * pElement (ElementPtr) X * The pointer to the element that is to be excluded from search. Column X * to be searched is one that contains this element. Also used to X * access the elements in the column. X * Step (int) X * Index of the diagonal currently being eliminated. Indicates where X * the active part of the matrix begins. X * X * >>> Local variables: X * Col (int) X * The number of the column to be searched. Also the column number of X * the element to be avoided in the search. X * Largest (RealNumber) X * The magnitude of the largest element. X * Magnitude (RealNumber) X * The magnitude of the currently active element. X * Row (int) X * The row number of element to be excluded from the search. X */ X static RealNumber FindBiggestInColExclude( X MatrixPtr Matrix, X register ElementPtr pElement, X register int Step ) { register int Row; int Col; RealNumber Largest, Magnitude; X /* Begin `FindBiggestInColExclude'. */ X Row = pElement->Row; X Col = pElement->Col; X pElement = Matrix->FirstInCol[Col]; X /* Travel down column until reduced submatrix is entered. */ X while ((pElement != NULL) AND (pElement->Row < Step)) X pElement = pElement->NextInCol; X /* Initialize the variable Largest. */ X if (pElement->Row != Row) X Largest = ELEMENT_MAG(pElement); X else X Largest = 0.0; X /* Search rest of column for largest element, avoiding excluded element. */ X while ((pElement = pElement->NextInCol) != NULL) X { if ((Magnitude = ELEMENT_MAG(pElement)) > Largest) X { if (pElement->Row != Row) X Largest = Magnitude; X } X } X X return Largest; } X X X X X X X X X X /* X * EXCHANGE ROWS AND COLUMNS X * X * Exchanges two rows and two columns so that the selected pivot is moved to X * the upper left corner of the remaining submatrix. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * pPivot (ElementPtr) X * Pointer to the current pivot. X * Step (int) X * Index of the diagonal currently being eliminated. X * X * >>> Local variables: X * Col (int) X * Column where the pivot was found. X * Row (int) X * Row where the pivot was found. X * OldMarkowitzProd_Col (long) X * Markowitz product associated with the diagonal element in the row X * the pivot was found in. X * OldMarkowitzProd_Row (long) X * Markowitz product associated with the diagonal element in the column X * the pivot was found in. X * OldMarkowitzProd_Step (long) X * Markowitz product associated with the diagonal element that is being X * moved so that the pivot can be placed in the upper left-hand corner X * of the reduced submatrix. X */ X static void ExchangeRowsAndCols( X MatrixPtr Matrix, X ElementPtr pPivot, X register int Step ) { register int Row, Col; long OldMarkowitzProd_Step, OldMarkowitzProd_Row, OldMarkowitzProd_Col; X /* Begin `ExchangeRowsAndCols'. */ X Row = pPivot->Row; X Col = pPivot->Col; X Matrix->PivotsOriginalRow = Row; X Matrix->PivotsOriginalCol = Col; X X if ((Row == Step) AND (Col == Step)) return; X /* Exchange rows and columns. */ X if (Row == Col) X { spcRowExchange( Matrix, Step, Row ); X spcColExchange( Matrix, Step, Col ); X SWAP( long, Matrix->MarkowitzProd[Step], Matrix->MarkowitzProd[Row] ); X SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Step] ); X } X else X { X /* Initialize variables that hold old Markowitz products. */ X OldMarkowitzProd_Step = Matrix->MarkowitzProd[Step]; X OldMarkowitzProd_Row = Matrix->MarkowitzProd[Row]; X OldMarkowitzProd_Col = Matrix->MarkowitzProd[Col]; X /* Exchange rows. */ X if (Row != Step) X { spcRowExchange( Matrix, Step, Row ); X Matrix->NumberOfInterchangesIsOdd = X NOT Matrix->NumberOfInterchangesIsOdd; X spcMarkoProd( Matrix->MarkowitzProd[Row], X Matrix->MarkowitzRow[Row], X Matrix->MarkowitzCol[Row] ); X /* Update singleton count. */ X if ((Matrix->MarkowitzProd[Row]==0) != (OldMarkowitzProd_Row==0)) X { if (OldMarkowitzProd_Row == 0) X Matrix->Singletons--; X else X Matrix->Singletons++; X } X } X /* Exchange columns. */ X if (Col != Step) X { spcColExchange( Matrix, Step, Col ); X Matrix->NumberOfInterchangesIsOdd = X NOT Matrix->NumberOfInterchangesIsOdd; X spcMarkoProd( Matrix->MarkowitzProd[Col], X Matrix->MarkowitzCol[Col], X Matrix->MarkowitzRow[Col] ); X /* Update singleton count. */ X if ((Matrix->MarkowitzProd[Col]==0) != (OldMarkowitzProd_Col==0)) X { if (OldMarkowitzProd_Col == 0) X Matrix->Singletons--; X else X Matrix->Singletons++; X } X X Matrix->Diag[Col] = spcFindDiag( Matrix, Col ); X } X if (Row != Step) X Matrix->Diag[Row] = spcFindDiag( Matrix, Row ); X Matrix->Diag[Step] = spcFindDiag( Matrix, Step ); X /* Update singleton count. */ X Matrix->MarkowitzProd[Step] = Matrix->MarkowitzCol[Step] * X Matrix->MarkowitzRow[Step]; X if ((Matrix->MarkowitzProd[Step]==0) != (OldMarkowitzProd_Step==0)) X { if (OldMarkowitzProd_Step == 0) X Matrix->Singletons--; X else X Matrix->Singletons++; X } X } X return; } X X X X X X X X X /* X * EXCHANGE ROWS X * X * Performs all required operations to exchange two rows. Those operations X * include: swap FirstInRow pointers, fixing up the NextInCol pointers, X * swapping row indexes in MatrixElements, and swapping Markowitz row X * counts. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * Row1 (int) X * Row index of one of the rows, becomes the smallest index. X * Row2 (int) X * Row index of the other row, becomes the largest index. X * X * Local variables: X * Column (int) X * Column in which row elements are currently being exchanged. X * Row1Ptr (ElementPtr) X * Pointer to an element in Row1. X * Row2Ptr (ElementPtr) X * Pointer to an element in Row2. X * Element1 (ElementPtr) X * Pointer to the element in Row1 to be exchanged. X * Element2 (ElementPtr) X * Pointer to the element in Row2 to be exchanged. X */ X void spcRowExchange( X MatrixPtr Matrix, X int Row1, X int Row2 ) { register ElementPtr Row1Ptr, Row2Ptr; int Column; ElementPtr Element1, Element2; X /* Begin `spcRowExchange'. */ X if (Row1 > Row2) SWAP(int, Row1, Row2); X X Row1Ptr = Matrix->FirstInRow[Row1]; X Row2Ptr = Matrix->FirstInRow[Row2]; X while (Row1Ptr != NULL OR Row2Ptr != NULL) X { /* Exchange elements in rows while traveling from left to right. */ X if (Row1Ptr == NULL) X { Column = Row2Ptr->Col; X Element1 = NULL; X Element2 = Row2Ptr; X Row2Ptr = Row2Ptr->NextInRow; X } X else if (Row2Ptr == NULL) X { Column = Row1Ptr->Col; X Element1 = Row1Ptr; X Element2 = NULL; X Row1Ptr = Row1Ptr->NextInRow; X } X else if (Row1Ptr->Col < Row2Ptr->Col) X { Column = Row1Ptr->Col; X Element1 = Row1Ptr; X Element2 = NULL; X Row1Ptr = Row1Ptr->NextInRow; X } X else if (Row1Ptr->Col > Row2Ptr->Col) X { Column = Row2Ptr->Col; X Element1 = NULL; X Element2 = Row2Ptr; X Row2Ptr = Row2Ptr->NextInRow; X } X else /* Row1Ptr->Col == Row2Ptr->Col */ X { Column = Row1Ptr->Col; X Element1 = Row1Ptr; X Element2 = Row2Ptr; X Row1Ptr = Row1Ptr->NextInRow; X Row2Ptr = Row2Ptr->NextInRow; X } X X ExchangeColElements( Matrix, Row1, Element1, Row2, Element2, Column); X } /* end of while(Row1Ptr != NULL OR Row2Ptr != NULL) */ X X if (Matrix->InternalVectorsAllocated) X SWAP( int, Matrix->MarkowitzRow[Row1], Matrix->MarkowitzRow[Row2]); X SWAP( ElementPtr, Matrix->FirstInRow[Row1], Matrix->FirstInRow[Row2]); X SWAP( int, Matrix->IntToExtRowMap[Row1], Matrix->IntToExtRowMap[Row2]); #if TRANSLATE X Matrix->ExtToIntRowMap[ Matrix->IntToExtRowMap[Row1] ] = Row1; X Matrix->ExtToIntRowMap[ Matrix->IntToExtRowMap[Row2] ] = Row2; #endif X X return; } X X X X X X X X X /* X * EXCHANGE COLUMNS X * X * Performs all required operations to exchange two columns. Those operations X * include: swap FirstInCol pointers, fixing up the NextInRow pointers, X * swapping column indexes in MatrixElements, and swapping Markowitz X * column counts. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * Col1 (int) X * Column index of one of the columns, becomes the smallest index. X * Col2 (int) X * Column index of the other column, becomes the largest index X * X * Local variables: X * Row (int) X * Row in which column elements are currently being exchanged. X * Col1Ptr (ElementPtr) X * Pointer to an element in Col1. X * Col2Ptr (ElementPtr) X * Pointer to an element in Col2. X * Element1 (ElementPtr) X * Pointer to the element in Col1 to be exchanged. X * Element2 (ElementPtr) X * Pointer to the element in Col2 to be exchanged. X */ X void spcColExchange( X MatrixPtr Matrix, X int Col1, X int Col2 ) { register ElementPtr Col1Ptr, Col2Ptr; int Row; ElementPtr Element1, Element2; X /* Begin `spcColExchange'. */ X if (Col1 > Col2) SWAP(int, Col1, Col2); X X Col1Ptr = Matrix->FirstInCol[Col1]; X Col2Ptr = Matrix->FirstInCol[Col2]; X while (Col1Ptr != NULL OR Col2Ptr != NULL) X { /* Exchange elements in rows while traveling from top to bottom. */ X if (Col1Ptr == NULL) X { Row = Col2Ptr->Row; X Element1 = NULL; X Element2 = Col2Ptr; X Col2Ptr = Col2Ptr->NextInCol; X } X else if (Col2Ptr == NULL) X { Row = Col1Ptr->Row; X Element1 = Col1Ptr; X Element2 = NULL; X Col1Ptr = Col1Ptr->NextInCol; X } X else if (Col1Ptr->Row < Col2Ptr->Row) X { Row = Col1Ptr->Row; X Element1 = Col1Ptr; X Element2 = NULL; X Col1Ptr = Col1Ptr->NextInCol; X } X else if (Col1Ptr->Row > Col2Ptr->Row) X { Row = Col2Ptr->Row; X Element1 = NULL; X Element2 = Col2Ptr; X Col2Ptr = Col2Ptr->NextInCol; X } X else /* Col1Ptr->Row == Col2Ptr->Row */ X { Row = Col1Ptr->Row; X Element1 = Col1Ptr; X Element2 = Col2Ptr; X Col1Ptr = Col1Ptr->NextInCol; X Col2Ptr = Col2Ptr->NextInCol; X } X X ExchangeRowElements( Matrix, Col1, Element1, Col2, Element2, Row); X } /* end of while(Col1Ptr != NULL OR Col2Ptr != NULL) */ X X if (Matrix->InternalVectorsAllocated) X SWAP( int, Matrix->MarkowitzCol[Col1], Matrix->MarkowitzCol[Col2]); X SWAP( ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]); X SWAP( int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]); #if TRANSLATE X Matrix->ExtToIntColMap[ Matrix->IntToExtColMap[Col1] ] = Col1; X Matrix->ExtToIntColMap[ Matrix->IntToExtColMap[Col2] ] = Col2; #endif X X return; } X X X X X X X /* X * EXCHANGE TWO ELEMENTS IN A COLUMN X * X * Performs all required operations to exchange two elements in a column. X * Those operations are: restring NextInCol pointers and swapping row indexes X * in the MatrixElements. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * Row1 (int) X * Row of top element to be exchanged. X * Element1 (ElementPtr) X * Pointer to top element to be exchanged. X * Row2 (int) X * Row of bottom element to be exchanged. X * Element2 (ElementPtr) X * Pointer to bottom element to be exchanged. X * Column (int) X * Column that exchange is to take place in. X * X * >>> Local variables: X * ElementAboveRow1 (ElementPtr *) X * Location of pointer which points to the element above Element1. This X * pointer is modified so that it points to correct element on exit. X * ElementAboveRow2 (ElementPtr *) X * Location of pointer which points to the element above Element2. This X * pointer is modified so that it points to correct element on exit. X * ElementBelowRow1 (ElementPtr) X * Pointer to element below Element1. X * ElementBelowRow2 (ElementPtr) X * Pointer to element below Element2. X * pElement (ElementPtr) X * Pointer used to traverse the column. X */ X static void ExchangeColElements( X MatrixPtr Matrix, X int Row1, X register ElementPtr Element1, X int Row2, X register ElementPtr Element2, X int Column ) { ElementPtr *ElementAboveRow1, *ElementAboveRow2; ElementPtr ElementBelowRow1, ElementBelowRow2; register ElementPtr pElement; X /* Begin `ExchangeColElements'. */ /* Search to find the ElementAboveRow1. */ X ElementAboveRow1 = &(Matrix->FirstInCol[Column]); X pElement = *ElementAboveRow1; X while (pElement->Row < Row1) X { ElementAboveRow1 = &(pElement->NextInCol); X pElement = *ElementAboveRow1; X } X if (Element1 != NULL) X { ElementBelowRow1 = Element1->NextInCol; X if (Element2 == NULL) X { /* Element2 does not exist, move Element1 down to Row2. */ X if ( ElementBelowRow1 != NULL AND ElementBelowRow1->Row < Row2 ) X { /* Element1 must be removed from linked list and moved. */ X *ElementAboveRow1 = ElementBelowRow1; X /* Search column for Row2. */ X pElement = ElementBelowRow1; X do X { ElementAboveRow2 = &(pElement->NextInCol); X pElement = *ElementAboveRow2; X } while (pElement != NULL AND pElement->Row < Row2); X /* Place Element1 in Row2. */ X *ElementAboveRow2 = Element1; X Element1->NextInCol = pElement; X *ElementAboveRow1 =ElementBelowRow1; X } X Element1->Row = Row2; X } X else X { /* Element2 does exist, and the two elements must be exchanged. */ X if ( ElementBelowRow1->Row == Row2) X { /* Element2 is just below Element1, exchange them. */ X Element1->NextInCol = Element2->NextInCol; X Element2->NextInCol = Element1; X *ElementAboveRow1 = Element2; X } X else X { /* Element2 is not just below Element1 and must be searched for. */ X pElement = ElementBelowRow1; X do X { ElementAboveRow2 = &(pElement->NextInCol); X pElement = *ElementAboveRow2; X } while (pElement->Row < Row2); X X ElementBelowRow2 = Element2->NextInCol; X /* Switch Element1 and Element2. */ X *ElementAboveRow1 = Element2; X Element2->NextInCol = ElementBelowRow1; X *ElementAboveRow2 = Element1; X Element1->NextInCol = ElementBelowRow2; X } X Element1->Row = Row2; X Element2->Row = Row1; X } X } X else X { /* Element1 does not exist. */ X ElementBelowRow1 = pElement; X /* Find Element2. */ X if (ElementBelowRow1->Row != Row2) X { do X { ElementAboveRow2 = &(pElement->NextInCol); X pElement = *ElementAboveRow2; X } while (pElement->Row < Row2); X X ElementBelowRow2 = Element2->NextInCol; X /* Move Element2 to Row1. */ X *ElementAboveRow2 = Element2->NextInCol; X *ElementAboveRow1 = Element2; X Element2->NextInCol = ElementBelowRow1; X } X Element2->Row = Row1; X } X return; } X X X X X X X /* X * EXCHANGE TWO ELEMENTS IN A ROW X * X * Performs all required operations to exchange two elements in a row. X * Those operations are: restring NextInRow pointers and swapping column X * indexes in the MatrixElements. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * Col1 (int) X * Col of left-most element to be exchanged. X * Element1 (ElementPtr) X * Pointer to left-most element to be exchanged. X * Col2 (int) X * Col of right-most element to be exchanged. X * Element2 (ElementPtr) X * Pointer to right-most element to be exchanged. X * Row (int) X * Row that exchange is to take place in. X * X * >>> Local variables: X * ElementLeftOfCol1 (ElementPtr *) X * Location of pointer which points to the element to the left of X * Element1. This pointer is modified so that it points to correct X * element on exit. X * ElementLeftOfCol2 (ElementPtr *) X * Location of pointer which points to the element to the left of X * Element2. This pointer is modified so that it points to correct X * element on exit. X * ElementRightOfCol1 (ElementPtr) X * Pointer to element right of Element1. X * ElementRightOfCol2 (ElementPtr) X * Pointer to element right of Element2. X * pElement (ElementPtr) X * Pointer used to traverse the row. X */ X static void ExchangeRowElements( X MatrixPtr Matrix, X int Col1, X register ElementPtr Element1, X int Col2, X register ElementPtr Element2, X int Row ) { ElementPtr *ElementLeftOfCol1, *ElementLeftOfCol2; ElementPtr ElementRightOfCol1, ElementRightOfCol2; register ElementPtr pElement; X /* Begin `ExchangeRowElements'. */ /* Search to find the ElementLeftOfCol1. */ X ElementLeftOfCol1 = &(Matrix->FirstInRow[Row]); X pElement = *ElementLeftOfCol1; X while (pElement->Col < Col1) X { ElementLeftOfCol1 = &(pElement->NextInRow); X pElement = *ElementLeftOfCol1; X } X if (Element1 != NULL) X { ElementRightOfCol1 = Element1->NextInRow; X if (Element2 == NULL) X { /* Element2 does not exist, move Element1 to right to Col2. */ X if ( ElementRightOfCol1 != NULL AND ElementRightOfCol1->Col < Col2 ) X { /* Element1 must be removed from linked list and moved. */ X *ElementLeftOfCol1 = ElementRightOfCol1; X /* Search Row for Col2. */ X pElement = ElementRightOfCol1; X do X { ElementLeftOfCol2 = &(pElement->NextInRow); X pElement = *ElementLeftOfCol2; X } while (pElement != NULL AND pElement->Col < Col2); X /* Place Element1 in Col2. */ X *ElementLeftOfCol2 = Element1; X Element1->NextInRow = pElement; X *ElementLeftOfCol1 =ElementRightOfCol1; X } X Element1->Col = Col2; X } X else X { /* Element2 does exist, and the two elements must be exchanged. */ X if ( ElementRightOfCol1->Col == Col2) X { /* Element2 is just right of Element1, exchange them. */ X Element1->NextInRow = Element2->NextInRow; X Element2->NextInRow = Element1; X *ElementLeftOfCol1 = Element2; X } X else X { /* Element2 is not just right of Element1 and must be searched for. */ X pElement = ElementRightOfCol1; X do X { ElementLeftOfCol2 = &(pElement->NextInRow); X pElement = *ElementLeftOfCol2; X } while (pElement->Col < Col2); X X ElementRightOfCol2 = Element2->NextInRow; X /* Switch Element1 and Element2. */ X *ElementLeftOfCol1 = Element2; X Element2->NextInRow = ElementRightOfCol1; X *ElementLeftOfCol2 = Element1; X Element1->NextInRow = ElementRightOfCol2; X } X Element1->Col = Col2; X Element2->Col = Col1; X } X } X else X { /* Element1 does not exist. */ X ElementRightOfCol1 = pElement; X /* Find Element2. */ X if (ElementRightOfCol1->Col != Col2) X { do X { ElementLeftOfCol2 = &(pElement->NextInRow); X pElement = *ElementLeftOfCol2; X } while (pElement->Col < Col2); X X ElementRightOfCol2 = Element2->NextInRow; X /* Move Element2 to Col1. */ X *ElementLeftOfCol2 = Element2->NextInRow; X *ElementLeftOfCol1 = Element2; X Element2->NextInRow = ElementRightOfCol1; X } X Element2->Col = Col1; X } X return; } X X X X X X X X X X X /* X * PERFORM ROW AND COLUMN ELIMINATION ON REAL MATRIX X * X * Eliminates a single row and column of the matrix and leaves single row of X * the upper triangular matrix and a single column of the lower triangular X * matrix in its wake. Uses Gauss's method. X * X * >>> Argument: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * pPivot (ElementPtr) X * Pointer to the current pivot. X * X * >>> Local variables: X * pLower (ElementPtr) X * Points to matrix element in lower triangular column. X * pSub (ElementPtr) X * Points to elements in the reduced submatrix. X * Row (int) X * Row index. X * pUpper (ElementPtr) X * Points to matrix element in upper triangular row. X * X * >>> Possible errors: X * spNO_MEMORY X */ X static void RealRowColElimination( X MatrixPtr Matrix, X register ElementPtr pPivot ) { #if REAL register ElementPtr pSub, *ppAbove; register int Row; register ElementPtr pLower, pUpper; X /* Begin `RealRowColElimination'. */ X /* Test for zero pivot. */ X if (ABS(pPivot->Real) == 0.0) X { (void)MatrixIsSingular( Matrix, pPivot->Row ); X return; X } X pPivot->Real = 1.0 / pPivot->Real; X X pUpper = pPivot->NextInRow; X while (pUpper != NULL) X { /* Calculate upper triangular element. */ X pUpper->Real *= pPivot->Real; X X pSub = pUpper->NextInCol; X pLower = pPivot->NextInCol; X ppAbove = &pUpper->NextInCol; X while (pLower != NULL) X { Row = pLower->Row; X /* Find element in row that lines up with current lower triangular element. */ X while (pSub != NULL AND pSub->Row < Row) X { ppAbove = &pSub->NextInCol; X pSub = pSub->NextInCol; X } X /* Test to see if desired element was not found, if not, create fill-in. */ X if (pSub == NULL OR pSub->Row > Row) X { pSub = spcCreateElement( Matrix, Row, pUpper->Col, X &pLower->NextInRow, ppAbove, YES ); X if (pSub == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X } X pSub->Real -= pUpper->Real * pLower->Real; X pSub = pSub->NextInCol; X pLower = pLower->NextInCol; X } X pUpper = pUpper->NextInRow; X } X return; #endif /* REAL */ } X X X X X X X X X /* X * PERFORM ROW AND COLUMN ELIMINATION ON COMPLEX MATRIX X * X * Eliminates a single row and column of the matrix and leaves single row of X * the upper triangular matrix and a single column of the lower triangular X * matrix in its wake. Uses Gauss's method. X * X * >>> Argument: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * pPivot (ElementPtr) X * Pointer to the current pivot. X * X * >>> Local variables: X * pLower (ElementPtr) X * Points to matrix element in lower triangular column. X * pSub (ElementPtr) X * Points to elements in the reduced submatrix. X * Row (int) X * Row index. X * pUpper (ElementPtr) X * Points to matrix element in upper triangular row. X * X * Possible errors: X * spNO_MEMORY X */ X static void ComplexRowColElimination( X MatrixPtr Matrix, X register ElementPtr pPivot ) { #if spCOMPLEX register ElementPtr pSub, *ppAbove; register int Row; register ElementPtr pLower, pUpper; X /* Begin `ComplexRowColElimination'. */ X /* Test for zero pivot. */ X if (ELEMENT_MAG(pPivot) == 0.0) X { (void)MatrixIsSingular( Matrix, pPivot->Row ); X return; X } X CMPLX_RECIPROCAL(*pPivot, *pPivot); X X pUpper = pPivot->NextInRow; X while (pUpper != NULL) X { /* Calculate upper triangular element. */ /* Cmplx expr: *pUpper = *pUpper * (1.0 / *pPivot). */ X CMPLX_MULT_ASSIGN(*pUpper, *pPivot); X X pSub = pUpper->NextInCol; X pLower = pPivot->NextInCol; X ppAbove = &pUpper->NextInCol; X while (pLower != NULL) X { Row = pLower->Row; X /* Find element in row that lines up with current lower triangular element. */ X while (pSub != NULL AND pSub->Row < Row) X { ppAbove = &pSub->NextInCol; X pSub = pSub->NextInCol; X } X /* Test to see if desired element was not found, if not, create fill-in. */ X if (pSub == NULL OR pSub->Row > Row) X { pSub = spcCreateElement( Matrix, Row, pUpper->Col, X &pLower->NextInRow, ppAbove, YES ); X if (pSub == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X } X /* Cmplx expr: pElement -= *pUpper * pLower. */ X CMPLX_MULT_SUBT_ASSIGN(*pSub, *pUpper, *pLower); X pSub = pSub->NextInCol; X pLower = pLower->NextInCol; X } X pUpper = pUpper->NextInRow; X } X return; #endif /* spCOMPLEX */ } X X X X X /* X * UPDATE MARKOWITZ NUMBERS X * X * Updates the Markowitz numbers after a row and column have been eliminated. X * Also updates singleton count. X * X * >>> Argument: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * pPivot (ElementPtr) X * Pointer to the current pivot. X * X * >>> Local variables: X * Row (int) X * Row index. X * Col (int) X * Column index. X * ColPtr (ElementPtr) X * Points to matrix element in upper triangular column. X * RowPtr (ElementPtr) X * Points to matrix element in lower triangular row. X */ X static void UpdateMarkowitzNumbers( X MatrixPtr Matrix, X ElementPtr pPivot ) { register int Row, Col; register ElementPtr ColPtr, RowPtr; register int *MarkoRow = Matrix->MarkowitzRow, *MarkoCol = Matrix->MarkowitzCol; double Product; X /* Begin `UpdateMarkowitzNumbers'. */ X /* Update Markowitz numbers. */ X for (ColPtr = pPivot->NextInCol; ColPtr != NULL; ColPtr = ColPtr->NextInCol) X { Row = ColPtr->Row; X --MarkoRow[Row]; X /* Form Markowitz product while being cautious of overflows. */ X if ((MarkoRow[Row] > LARGEST_SHORT_INTEGER AND MarkoCol[Row] != 0) OR X (MarkoCol[Row] > LARGEST_SHORT_INTEGER AND MarkoRow[Row] != 0)) X { Product = (double)MarkoCol[Row] * (double)MarkoRow[Row]; X if (Product >= LARGEST_LONG_INTEGER) X Matrix->MarkowitzProd[Row] = LARGEST_LONG_INTEGER; X else X Matrix->MarkowitzProd[Row] = (long)Product; X } X else Matrix->MarkowitzProd[Row] = MarkoRow[Row] * MarkoCol[Row]; X if (MarkoRow[Row] == 0) X Matrix->Singletons++; X } X X for (RowPtr = pPivot->NextInRow; RowPtr != NULL; RowPtr = RowPtr->NextInRow) X { Col = RowPtr->Col; X --MarkoCol[Col]; X /* Form Markowitz product while being cautious of overflows. */ X if ((MarkoRow[Col] > LARGEST_SHORT_INTEGER AND MarkoCol[Col] != 0) OR X (MarkoCol[Col] > LARGEST_SHORT_INTEGER AND MarkoRow[Col] != 0)) X { Product = (double)MarkoCol[Col] * (double)MarkoRow[Col]; X if (Product >= LARGEST_LONG_INTEGER) X Matrix->MarkowitzProd[Col] = LARGEST_LONG_INTEGER; X else X Matrix->MarkowitzProd[Col] = (long)Product; X } X else Matrix->MarkowitzProd[Col] = MarkoRow[Col] * MarkoCol[Col]; X if ((MarkoCol[Col] == 0) AND (MarkoRow[Col] != 0)) X Matrix->Singletons++; X } X return; } X X X X X X /* X * ZERO PIVOT ENCOUNTERED X * X * This routine is called when a singular matrix is found. It then X * records the current row and column and exits. X * X * >>> Returned: X * The error code spSINGULAR or spZERO_DIAG is returned. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to matrix. X * Step (int) X * Index of diagonal that is zero. X */ X static int MatrixIsSingular( X MatrixPtr Matrix, X int Step ) { /* Begin `MatrixIsSingular'. */ X X Matrix->SingularRow = Matrix->IntToExtRowMap[ Step ]; X Matrix->SingularCol = Matrix->IntToExtColMap[ Step ]; X return (Matrix->Error = spSINGULAR); } X X static int ZeroPivot( X MatrixPtr Matrix, X int Step ) { /* Begin `ZeroPivot'. */ X X Matrix->SingularRow = Matrix->IntToExtRowMap[ Step ]; X Matrix->SingularCol = Matrix->IntToExtColMap[ Step ]; X return (Matrix->Error = spZERO_DIAG); } X X X X X X #if (ANNOTATE == FULL) X /* X * X * WRITE STATUS X * X * Write a summary of important variables to standard output. X */ X static void WriteStatus( X MatrixPtr Matrix, X int Step ) { int I; X /* Begin `WriteStatus'. */ X X printf("Step = %1d ", Step); X printf("Pivot found at %1d,%1d using ", Matrix->PivotsOriginalRow, X Matrix->PivotsOriginalCol); X switch(Matrix->PivotSelectionMethod) X { case 's': printf("SearchForSingleton\n"); break; X case 'q': printf("QuicklySearchDiagonal\n"); break; X case 'd': printf("SearchDiagonal\n"); break; X case 'e': printf("SearchEntireMatrix\n"); break; X } X X printf("MarkowitzRow = "); X for (I = 1; I <= Matrix->Size; I++) X printf("%2d ", Matrix->MarkowitzRow[I]); X printf("\n"); X X printf("MarkowitzCol = "); X for (I = 1; I <= Matrix->Size; I++) X printf("%2d ", Matrix->MarkowitzCol[I]); X printf("\n"); X X printf("MarkowitzProduct = "); X for (I = 1; I <= Matrix->Size; I++) X printf("%2d ", Matrix->MarkowitzProd[I]); X printf("\n"); X X printf("Singletons = %2d\n", Matrix->Singletons); X X printf("IntToExtRowMap = "); X for (I = 1; I <= Matrix->Size; I++) X printf("%2d ", Matrix->IntToExtRowMap[I]); X printf("\n"); X X printf("IntToExtColMap = "); X for (I = 1; I <= Matrix->Size; I++) X printf("%2d ", Matrix->IntToExtColMap[I]); X printf("\n"); X X printf("ExtToIntRowMap = "); X for (I = 1; I <= Matrix->ExtSize; I++) X printf("%2d ", Matrix->ExtToIntRowMap[I]); X printf("\n"); X X printf("ExtToIntColMap = "); X for (I = 1; I <= Matrix->ExtSize; I++) X printf("%2d ", Matrix->ExtToIntColMap[I]); X printf("\n\n"); X /* spPrint((char *)Matrix, NO, YES); */ X X return; X } #endif /* ANNOTATE == FULL */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spFactor.c'; eval "$shar_touch") && chmod 0600 'sparse/spFactor.c' if test $? -ne 0 then ${echo} 'restore of sparse/spFactor.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spFactor.c: MD5 check failed' ) << \SHAR_EOF b19227b1d98c9cb5516f01b2d092177c sparse/spFactor.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spFactor.c'` -ne 98805 && \ ${echo} 'restoration warning: size of sparse/spFactor.c is not 98805' fi fi # ============= sparse/spFortran.c ============== if test -f 'sparse/spFortran.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spFortran.c (file already exists)' else ${echo} 'x - extracting sparse/spFortran.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spFortran.c' && /* X * SPARSE FORTRAN MODULE X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*! \file * X * This module contains routines that interface Sparse1.4 to a calling X * program written in fortran. Almost every externally available Sparse1.4 X * routine has a counterpart defined in this file, with the name the X * same except the \a sp prefix is changed to \a sf. The \a spADD_ELEMENT X * and \a spADD_QUAD macros are also replaced with the \a sfAdd1 and \a sfAdd4 X * functions defined in this file. X * X * To ease porting this file to different operating systems, the names of X * the functions can be easily redefined (search for `Routine Renaming'). X * A simple example of a FORTRAN program that calls Sparse is included in X * this file (search for Example). When interfacing to a FORTRAN program, X * the ARRAY_OFFSET option should be set to NO (see spConfig.h). X * X * DISCLAIMER: X * These interface routines were written by a C programmer who has little X * experience with FORTRAN. The routines have had minimal testing. X * Any interface between two languages is going to have portability X * problems, this one is no exception. X */ /* >>> User accessible functions contained in this file: X * sfCreate() X * sfDestroy() X * sfStripFills() X * sfClear() X * sfGetElement() X * sfGetAdmittance() X * sfGetQuad() X * sfGetOnes() X * sfAdd1Real() X * sfAdd1Imag() X * sfAdd1Complex() X * sfAdd4Real() X * sfAdd4Imag() X * sfAdd4Complex() X * sfOrderAndFactor() X * sfFactor() X * sfPartition() X * sfSolve() X * sfSolveTransposed() X * sfPrint() X * sfFileMatrix() X * sfFileVector() X * sfFileStats() X * sfMNA_Preorder() X * sfScale() X * sfMultiply() X * sfMultTransposed() X * sfDeterminant() X * sfError() X * sfErrorMessage() X * sfWhereSingular() X * sfGetSize() X * sfSetReal() X * sfSetComplex() X * sfFillinCount() X * sfElementCount() X * sfDeleteRowAndCol() X * sfPseudoCondition() X * sfCondition() X * sfNorm() X * sfLargestElement() X * sfRoundoff() X */ X /* X * FORTRAN -- C COMPATIBILITY X * X * Fortran and C data types correspond in the following way: X * -- C -- -- FORTRAN -- X * int INTEGER*4 or INTEGER*2 (machine dependent, usually int*4) X * long INTEGER*4 X * float REAL X * double DOUBLE PRECISION (used by default in preference to float) X * X * The complex number format used by Sparse is compatible with that X * used by FORTRAN. C pointers are passed to FORTRAN as longs, they should X * not be used in any way in FORTRAN. X */ X X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spFortran.c,v 1.3 2003/06/30 19:40:51 kundert Exp $"; #endif X X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X #if FORTRAN X X X X /* X * Routine Renaming X */ X #define sfCreate sfcreate #define sfStripFills sfstripfills #define sfDestroy sfdestroy #define sfClear sfclear #define sfGetElement sfgetelement #define sfGetAdmittance sfgetadmittance #define sfGetQuad sfgetquad #define sfGetOnes sfgetones #define sfAdd1Real sfadd1real #define sfAdd1Imag sfadd1imag #define sfAdd1Complex sfadd1complex #define sfAdd4Real sfadd4real #define sfAdd4Imag sfadd4imag #define sfAdd4Complex sfadd4complex #define sfOrderAndFactor sforderandfactor #define sfFactor sffactor #define sfPartition sfpartition #define sfSolve sfsolve #define sfSolveTransposed sfsolvetransposed #define sfPrint sfprint #define sfFileMatrix sffilematrix #define sfFileVector sffilevector #define sfFileStats sffilestats #define sfMNA_Preorder sfmna_preorder #define sfScale sfscale #define sfMultiply sfmultiply #define sfMultTransposed sfmulttransposed #define sfDeterminant sfdeterminant #define sfError sferror #define sfErrorMessage sferrormessage #define sfWhereSingular sfwheresingular #define sfGetSize sfgetsize #define sfSetReal sfsetreal #define sfSetComplex sfsetcomplex #define sfFillinCount sffillincount #define sfElementCount sfelementcount #define sfDeleteRowAndCol sfdeleterowandcol #define sfPseudoCondition sfpseudocondition #define sfCondition sfcondition #define sfNorm sfnorm #define sfLargestElement sflargestelement #define sfRoundoff sfroundoff X X #define MATRIX_FILE_NAME "spMatrix" #define STATS_FILE_NAME "spStats" X /* X * X * Example of a FORTRAN Program Calling Sparse X * X X integer matrix, error, sfCreate, sfGetElement, spFactor X integer element(10) X double precision rhs(4), solution(4) c X matrix = sfCreate(4,0,error) X element(1) = sfGetElement(matrix,1,1) X element(2) = sfGetElement(matrix,1,2) X element(3) = sfGetElement(matrix,2,1) X element(4) = sfGetElement(matrix,2,2) X element(5) = sfGetElement(matrix,2,3) X element(6) = sfGetElement(matrix,3,2) X element(7) = sfGetElement(matrix,3,3) X element(8) = sfGetElement(matrix,3,4) X element(9) = sfGetElement(matrix,4,3) X element(10) = sfGetElement(matrix,4,4) X call sfClear(matrix) X call sfAdd1Real(element(1), 2d0) X call sfAdd1Real(element(2), -1d0) X call sfAdd1Real(element(3), -1d0) X call sfAdd1Real(element(4), 3d0) X call sfAdd1Real(element(5), -1d0) X call sfAdd1Real(element(6), -1d0) X call sfAdd1Real(element(7), 3d0) X call sfAdd1Real(element(8), -1d0) X call sfAdd1Real(element(9), -1d0) X call sfAdd1Real(element(10), 3d0) X call sfprint(matrix, .false., .false.) X rhs(1) = 34d0 X rhs(2) = 0d0 X rhs(3) = 0d0 X rhs(4) = 0d0 X error = sfFactor(matrix) X call sfSolve(matrix, rhs, solution) X write (6, 10) rhs(1), rhs(2), rhs(3), rhs(4) X 10 format (f 10.2) X end X X * X */ X X X X X X /* MATRIX ALLOCATION */ /*! X * Allocates and initializes the data structures associated with a matrix. X * X * \return [INTEGER] X * A pointer to the matrix is returned cast into an integer. This pointer X * is then passed and used by the other matrix routines to refer to a X * particular matrix. If an error occurs, the NULL pointer is returned. X * X * \param Size [INTEGER] X * Size of matrix or estimate of size of matrix if matrix is \a EXPANDABLE. X * \param Complex [INTEGER or INTEGER*2] X * Type of matrix. If \a Complex is 0 then the matrix is real, otherwise X * the matrix will be complex. X * Note that if a matrix will be both real and complex, it must X * be specified here as being complex. X * \param Error [INTEGER or INTEGER*2] X * Returns error flag, needed because function spError() will not work X * correctly if spCreate() returns NULL. Possible errors include \a spNO_MEMORY. X */ X long sfCreate( int *Size, int *Complex, int *Error ) { /* Begin `sfCreate'. */ X return (long)spCreate(*Size, *Complex, Error ); } X X X X X X /* MATRIX DEALLOCATION */ /*! X * Deallocates pointers and elements of matrix. X * X * \param Matrix [INTEGER] X * Pointer to the matrix frame which is to be removed from memory. X */ X void sfDestroy( long *Matrix ) { /* Begin `sfDestroy'. */ X spDestroy((spMatrix)*Matrix); X return; } X X X X X X #if STRIP X /* STRIP FILL-INS FROM MATRIX */ /*! X * Strips the matrix of all fill-ins. X * X * \param Matrix [INTEGER] X * Pointer to the matrix to be stripped. X */ X void sfStripFills( long *Matrix ) { /* Begin `sfStripFills'. */ X spStripFills((spMatrix)*Matrix); X return; } #endif X X X X X X X /* CLEAR MATRIX */ /*! X * Sets every element of the matrix to zero and clears the error flag. X * X * \param Matrix [INTEGER] X * Pointer to matrix that is to be cleared. X */ X void sfClear( long *Matrix ) { /* Begin `sfClear'. */ X spClear((spMatrix)*Matrix); X return; } X X X X X X /* SINGLE ELEMENT ADDITION TO MATRIX BY INDEX */ /*! X * Finds element [Row,Col] and returns a pointer to it. If element is X * not found then it is created and spliced into matrix. This routine X * is only to be used after spCreate() and before spMNA_Preorder(), X * spFactor() or spOrderAndFactor(). Returns a pointer to the X * Real portion of a matrix element. This pointer is later used by X * sfAddxxxxx() to directly access element. X * X * \return [INTEGER] X * Returns a pointer to the element. This pointer is then used to directly X * access the element during successive builds. Returns NULL if \a spNO_MEMORY X * error occurs. Error is not cleared in this routine. X * X * \param Matrix [INTEGER] X * Pointer to the matrix that the element is to be added to. X * \param Row [INTEGER or INTEGER*2] X * Row index for element. Must be in the range of [0..Size] unless X * the options \a EXPANDABLE or \a TRANSLATE are used. Elements placed in X * row zero are discarded. In no case may \a Row be less than zero. X * \param Col [INTEGER or INTEGER*2] X * Column index for element. Must be in the range of [0..Size] unless X * the options \a EXPANDABLE or \a TRANSLATE are used. Elements placed in X * column zero are discarded. In no case may \a Col be less than zero. X */ X long sfGetElement( long *Matrix, int *Row, int *Col ) { /* Begin `sfGetElement'. */ X return (long)spGetElement((spMatrix)*Matrix, *Row, *Col); } X X X X X X X #if QUAD_ELEMENT /* ADDITION OF ADMITTANCE TO MATRIX BY INDEX */ /*! X * Performs same function as sfGetElement() except rather than one X * element, all four Matrix elements for a floating component are X * added. This routine also works if component is grounded. Positive X * elements are placed at [Node1,Node2] and [Node2,Node1]. This X * routine is only to be used after sfCreate() and before X * sfMNA_Preorder(), sfFactor() or sfOrderAndFactor(). X * X * \return [INTEGER or INTEGER*2] X * The error code. Possible errors include \a spNO_MEMORY. X * Error is not cleared in this routine. X * X * \param Matrix [INTEGER] X * Pointer to the matrix that component is to be entered in. X * \param Node1 [INTEGER or INTEGER*2] X * Row and column indices for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Node zero is the X * ground node. In no case may \a Node1 be less than zero. X * \param Node2 [INTEGER or INTEGER*2] X * Row and column indices for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Node zero is the X * ground node. In no case may \a Node2 be less than zero. X * \param Template [INTEGER (4)] X * Collection of pointers to four elements that are later used to directly X * address elements. User must supply the template, this routine will X * fill it. X */ X int sfGetAdmittance( long *Matrix, int *Node1, int *Node2, long Template[4] ) { /* Begin `spGetAdmittance'. */ X return X ( spGetAdmittance((spMatrix)*Matrix, *Node1, *Node2, X (struct spTemplate *)Template ) X ); } #endif /* QUAD_ELEMENT */ X X X X X X X X X #if QUAD_ELEMENT /* ADDITION OF FOUR ELEMENTS TO MATRIX BY INDEX */ /*! X * Similar to sfGetAdmittance(), except that sfGetAdmittance() only X * handles 2-terminal components, whereas sfGetQuad() handles simple X * 4-terminals as well. These 4-terminals are simply generalized X * 2-terminals with the option of having the sense terminals different X * from the source and sink terminals. sfGetQuad() adds four X * elements to the matrix. Positive elements occur at Row1,Col1 X * Row2,Col2 while negative elements occur at Row1,Col2 and Row2,Col1. X * The routine works fine if any of the rows and columns are zero. X * This routine is only to be used after sfCreate() and before X * sfMNA_Preorder(), sfFactor() or sfOrderAndFactor() X * unless TRANSLATE is set true. X * X * \return [INTEGER or INTEGER*2] X * Error code. Possible errors include \a spNO_MEMORY. X * Error is not cleared in this routine. X * X * \param Matrix [INTEGER] X * Pointer to the matrix that component is to be entered in. X * \param Row1 [INTEGER or INTEGER*2] X * First row index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Row1 be less than zero. X * \param Row2 [INTEGER or INTEGER*2] X * Second row index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Row2 be less than zero. X * \param Col1 [INTEGER or INTEGER*2] X * First column index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground column. In no case may \a Col1 be less than zero. X * \param Col2 [INTEGER or INTEGER*2] X * Second column index for elements. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground column. In no case may \a Col2 be less than zero. X * \param Template [INTEGER (4)] X * Collection of pointers to four elements that are later used to directly X * address elements. User must supply the template, this routine will X * fill it. X */ X int sfGetQuad( long *Matrix, int *Row1, int *Row2, int *Col1, int *Col2, long Template[4] ) { /* Begin `spGetQuad'. */ X return X ( spGetQuad( (spMatrix)*Matrix, *Row1, *Row2, *Col1, *Col2, X (struct spTemplate *)Template ) X ); } #endif /* QUAD_ELEMENT */ X X X X X X X X X #if QUAD_ELEMENT /* ADDITION OF FOUR STRUCTURAL ONES TO MATRIX BY INDEX */ /*! X * Performs similar function to sfGetQuad() except this routine is X * meant for components that do not have an admittance representation. X * X * The following stamp is used: \code X * Pos Neg Eqn X * Pos [ . . 1 ] X * Neg [ . . -1 ] X * Eqn [ 1 -1 . ] X * \endcode X * X * \return [INTEGER or INTEGER*2] X * Error code. Possible errors include \a spNO_MEMORY. X * Error is not cleared in this routine. X * X * \param Matrix [INTEGER] X * Pointer to the matrix that component is to be entered in. X * \param Pos [INTEGER or INTEGER*2] X * See stamp above. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Pos be less than zero. X * \param Neg [INTEGER or INTEGER*2] X * See stamp above. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Neg be less than zero. X * \param Eqn [INTEGER or INTEGER*2] X * See stamp above. Must be in the range of [0..Size] X * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the X * ground row. In no case may \a Eqn be less than zero. X * \param Template [4]) [INTEGER (4)] X * Collection of pointers to four elements that are later used to directly X * address elements. User must supply the template, this routine will X * fill it. X */ X int sfGetOnes( long *Matrix, int *Pos, int *Neg, int *Eqn, long Template[4] ) { /* Begin `sfGetOnes'. */ X return X ( spGetOnes( (spMatrix)*Matrix, *Pos, *Neg, *Eqn, X (struct spTemplate *)Template ) X ); } #endif /* QUAD_ELEMENT */ X X X X X X X /* ADD ELEMENT(S) DIRECTLY TO MATRIX */ /*! X * Adds a real value to a matrix element. X * These elements are referenced by pointer, and so must already have X * been created by spGetElement(). X * X * \param Element [INTEGER] X * Pointer to the element that is to be added to. X * \param Real [REAL or DOUBLE PRECISION] X * Real portion of the number to be added to the element. X */ X void sfAdd1Real( long *Element, spREAL *Real ) { /* Begin `sfAdd1Real'. */ X *((RealNumber *)*Element) += *Real; } X X #if spCOMPLEX /*! X * Adds an imaginary value to a matrix element. X * These elements are referenced by pointer, and so must already have X * been created by spGetElement(). X * X * \param Element [INTEGER] X * Pointer to the element that is to be added to. X * \param Imag [REAL or DOUBLE PRECISION] X * Imaginary portion of the number to be added to the element. X */ X void sfAdd1Imag( long *Element, spREAL *Imag ) { /* Begin `sfAdd1Imag'. */ X *(((RealNumber *)*Element)+1) += *Imag; } X X /*! X * Adds a complex value to a matrix element. X * These elements are referenced by pointer, and so must already have X * been created by spGetElement(). X * X * \param Element [INTEGER] X * Pointer to the element that is to be added to. X * \param Real [REAL or DOUBLE PRECISION] X * Real portion of the number to be added to the element. X * \param Imag [REAL or DOUBLE PRECISION] X * Imaginary portion of the number to be added to the element. X */ void sfAdd1Complex( long *Element, spREAL *Real, spREAL *Imag ) { /* Begin `sfAdd1Complex'. */ X *((RealNumber *)*Element) += *Real; X *(((RealNumber *)*Element)+1) += *Imag; } #endif /* spCOMPLEX */ X X #if QUAD_ELEMENT /*! X * Adds a real value to a set of four elements in a matrix. X * These elements are referenced by pointer, and so must already have X * been created by spGetAdmittance(), spGetQuad(), or spGetOnes(). X * X * \param Template [4]) [INTEGER (4)] X * Pointer to the element that is to be added to. X * \param Real [REAL or DOUBLE PRECISION] X * Real portion of the number to be added to the element. X */ X void sfAdd4Real( long Template[4], spREAL *Real ) { /* Begin `sfAdd4Real'. */ X *((RealNumber *)Template[0]) += *Real; X *((RealNumber *)Template[1]) += *Real; X *((RealNumber *)Template[2]) -= *Real; X *((RealNumber *)Template[3]) -= *Real; } X X #if spCOMPLEX /*! X * Adds an imaginary value to a set of four elements in a matrix. X * These elements are referenced by pointer, and so must already have X * been created by spGetAdmittance(), spGetQuad(), or spGetOnes(). X * X * \param Template [4]) [INTEGER (4)] X * Pointer to the element that is to be added to. X * \param Imag [REAL or DOUBLE PRECISION] X * Imaginary portion of the number to be added to the element. X */ X void sfAdd4Imag( long Template[4], spREAL *Imag ) { /* Begin `sfAdd4Imag'. */ X *(((RealNumber *)Template[0])+1) += *Imag; X *(((RealNumber *)Template[1])+1) += *Imag; X *(((RealNumber *)Template[2])+1) -= *Imag; X *(((RealNumber *)Template[3])+1) -= *Imag; } X X /*! X * Adds a complex value to a set of four elements in a matrix. X * These elements are referenced by pointer, and so must already have X * been created by spGetAdmittance(), spGetQuad(), or spGetOnes(). X * X * \param Template [4]) [INTEGER (4)] X * Pointer to the element that is to be added to. X * \param Real [REAL or DOUBLE PRECISION] X * Real portion of the number to be added to the element. X * \param Imag [REAL or DOUBLE PRECISION] X * Imaginary portion of the number to be added to the element. X */ X void sfAdd4Complex( long Template[4], spREAL *Real, spREAL *Imag ) { /* Begin `sfAdd4Complex'. */ X *((RealNumber *)Template[0]) += *Real; X *((RealNumber *)Template[1]) += *Real; X *((RealNumber *)Template[2]) -= *Real; X *((RealNumber *)Template[3]) -= *Real; X *(((RealNumber *)Template[0])+1) += *Imag; X *(((RealNumber *)Template[1])+1) += *Imag; X *(((RealNumber *)Template[2])+1) -= *Imag; X *(((RealNumber *)Template[3])+1) -= *Imag; } #endif /* spCOMPLEX */ #endif /* QUAD_ELEMENT */ X X X X X X /* ORDER AND FACTOR MATRIX */ /*! X * This routine chooses a pivot order for the matrix and factors it X * into LU form. It handles both the initial factorization and subsequent X * factorizations when a reordering is desired. This is handled in a manner X * that is transparent to the user. The routine uses a variation of X * Gauss's method where the pivots are associated with L and the X * diagonal terms of U are one. X * X * \return [INTEGER of INTEGER*2] X * The error code is returned. Possible errors include \a spNO_MEMORY, X * \a spSINGULAR, and \a spSMALL_PIVOT. X * Error is cleared in this function. X * X * \return Matrix [INTEGER] X * Pointer to matrix. X * \return RHS [REAL (1) or DOUBLE PRECISION (1)] X * Representative right-hand side vector that is used to determine X * pivoting order when the right hand side vector is sparse. If X * \a RHS is a NULL pointer then the RHS vector is assumed to X * be full and it is not used when determining the pivoting X * order. X * \return RelThreshold [REAL or DOUBLE PRECISION] X * This number determines what the pivot relative threshold will X * be. It should be between zero and one. If it is one then the X * pivoting method becomes complete pivoting, which is very slow X * and tends to fill up the matrix. If it is set close to zero X * the pivoting method becomes strict Markowitz with no X * threshold. The pivot threshold is used to eliminate pivot X * candidates that would cause excessive element growth if they X * were used. Element growth is the cause of roundoff error. X * Element growth occurs even in well-conditioned matrices. X * Setting the \a RelThreshold large will reduce element growth and X * roundoff error, but setting it too large will cause execution X * time to be excessive and will result in a large number of X * fill-ins. If this occurs, accuracy can actually be degraded X * because of the large number of operations required on the X * matrix due to the large number of fill-ins. A good value seems X * to be 0.001. The default is chosen by giving a value larger X * than one or less than or equal to zero. This value should be X * increased and the matrix resolved if growth is found to be X * excessive. Changing the pivot threshold does not improve X * performance on matrices where growth is low, as is often the X * case with ill-conditioned matrices. Once a valid threshold is X * given, it becomes the new default. The default value of X * RelThreshold was chosen for use with nearly diagonally X * dominant matrices such as node- and modified-node admittance X * matrices. For these matrices it is usually best to use X * diagonal pivoting. For matrices without a strong diagonal, it X * is usually best to use a larger threshold, such as 0.01 or X * 0.1. X * \return AbsThreshold [REAL or DOUBLE PRECISION] X * The absolute magnitude an element must have to be considered X * as a pivot candidate, except as a last resort. This number X * should be set significantly smaller than the smallest diagonal X * element that is is expected to be placed in the matrix. If X * there is no reasonable prediction for the lower bound on these X * elements, then \a AbsThreshold should be set to zero. X * \a AbsThreshold is used to reduce the possibility of choosing as a X * pivot an element that has suffered heavy cancellation and as a X * result mainly consists of roundoff error. Once a valid X * threshold is given, it becomes the new default. X * \return DiagPivoting [LOGICAL] X * A flag indicating that pivot selection should be confined to the X * diagonal if possible. If DiagPivoting is nonzero and if X * \a DIAGONAL_PIVOTING is enabled pivots will be chosen only from X * the diagonal unless there are no diagonal elements that satisfy X * the threshold criteria. Otherwise, the entire reduced X * submatrix is searched when looking for a pivot. The diagonal X * pivoting in Sparse is efficient and well refined, while the X * off-diagonal pivoting is not. For symmetric and near symmetric X * matrices, it is best to use diagonal pivoting because it X * results in the best performance when reordering the matrix and X * when factoring the matrix without ordering. If there is a X * considerable amount of nonsymmetry in the matrix, then X * off-diagonal pivoting may result in a better equation ordering X * simply because there are more pivot candidates to choose from. X * A better ordering results in faster subsequent factorizations. X * However, the initial pivot selection process takes considerably X * longer for off-diagonal pivoting. X */ X int sfOrderAndFactor( X long *Matrix, X spREAL RHS[], X spREAL *RelThreshold, X spREAL *AbsThreshold, X long *DiagPivoting ) { /* Begin `sfOrderAndFactor'. */ X return spOrderAndFactor( (spMatrix)*Matrix, RHS, *RelThreshold, X *AbsThreshold, (BOOLEAN)*DiagPivoting ); } X X X X X X X /* FACTOR MATRIX */ /*! X * This routine is the companion routine to spOrderAndFactor(). X * Unlike sfOrderAndFactor(), sfFactor() cannot change the ordering. X * It is also faster than sfOrderAndFactor(). The standard way of X * using these two routines is to first use sfOrderAndFactor() for the X * initial factorization. For subsequent factorizations, sfFactor() X * is used if there is some assurance that little growth will occur X * (say for example, that the matrix is diagonally dominant). If X * sfFactor() is called for the initial factorization of the matrix, X * then sfOrderAndFactor() is automatically called with the default X * threshold. This routine uses "row at a time" LU factorization. X * Pivots are associated with the lower triangular matrix and the X * diagonals of the upper triangular matrix are ones. X * X * \return [INTEGER or INTEGER*2] X * The error code is returned. Possible errors include X * \a spNO_MEMORY X * \a spSINGULAR X * \a spZERO_DIAG X * \a spSMALL_PIVOT X * Error is cleared in this function. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X */ X int sfFactor( long *Matrix ) { /* Begin `sfFactor'. */ X return spFactor((spMatrix)*Matrix); } X X X X X X /* PARTITION MATRIX */ /*! X * This routine determines the cost to factor each row using both X * direct and indirect addressing and decides, on a row-by-row basis, X * which addressing mode is fastest. This information is used in X * sfFactor() to speed the factorization. X * X * When factoring a previously ordered matrix using sfFactor(), Sparse X * operates on a row-at-a-time basis. For speed, on each step, the X * row being updated is copied into a full vector and the operations X * are performed on that vector. This can be done one of two ways, X * either using direct addressing or indirect addressing. Direct X * addressing is fastest when the matrix is relatively dense and X * indirect addressing is best when the matrix is quite sparse. The X * user selects the type of partition used with \a Mode. If \a Mode is set X * to \a spDIRECT_PARTITION, then the all rows are placed in the direct X * addressing partition. Similarly, if \a Mode is set to X * \a spINDIRECT_PARTITION, then the all rows are placed in the indirect X * addressing partition. By setting \a Mode to \a spAUTO_PARTITION, the X * user allows \a Sparse to select the partition for each row X * individually. sfFactor() generally runs faster if Sparse is X * allowed to choose its own partitioning, however choosing a X * partition is expensive. The time required to choose a partition is X * of the same order of the cost to factor the matrix. If you plan to X * factor a large number of matrices with the same structure, it is X * best to let \a Sparse choose the partition. Otherwise, you should X * choose the partition based on the predicted density of the matrix. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param Mode [INTEGER or INTEGER*2] X * Mode must be one of three special codes: \a spDIRECT_PARTITION, X * \a spINDIRECT_PARTITION, or \a spAUTO_PARTITION. X */ X void sfPartition( long *Matrix, int *Mode ) { /* Begin `sfPartition'. */ X spPartition((spMatrix)*Matrix, *Mode); } X X X X X X X /* SOLVE MATRIX EQUATION */ /*! X * Performs forward elimination and back substitution to find the X * unknown vector from the RHS vector and factored matrix. This X * routine assumes that the pivots are associated with the lower X * triangular (L) matrix and that the diagonal of the upper triangular X * (U) matrix consists of ones. This routine arranges the computation X * in different way than is traditionally used in order to exploit the X * sparsity of the right-hand side. See the reference in spRevision. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param RHS [REAL (1) or DOUBLE PRECISION (1)] X * \a RHS is the input data array, the right hand side. This data is X * undisturbed and may be reused for other solves. X * \param Solution [REAL (1) or DOUBLE PRECISION (1)] X * \a Solution is the output data array. This routine is constructed such that X * \a RHS and \a Solution can be the same array. X * \param iRHS [REAL (1) or DOUBLE PRECISION (1)] X * \a iRHS is the imaginary portion of the input data array, the right X * hand side. This data is undisturbed and may be reused for other solves. X * This argument is only necessary if matrix is complex and if X * \a spSEPARATED_COMPLEX_VECTOR is set true. X * \param iSolution [REAL (1) or DOUBLE PRECISION (1)] X * \a iSolution is the imaginary portion of the output data array. This X * routine is constructed such that \a iRHS and \a iSolution can be X * the same array. This argument is only necessary if matrix is complex X * and if \a spSEPARATED_COMPLEX_VECTOR is set true. X */ X /*VARARGS3*/ X void sfSolve( X long *Matrix, X spREAL RHS[], X spREAL Solution[] # if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] # endif ) { /* Begin `sfSolve'. */ X spSolve( (spMatrix)*Matrix, RHS, Solution IMAG_VECTORS ); } X X X X X X #if TRANSPOSE /* SOLVE TRANSPOSED MATRIX EQUATION */ /*! X * Performs forward elimination and back substitution to find the X * unknown vector from the RHS vector and transposed factored X * matrix. This routine is useful when performing sensitivity analysis X * on a circuit using the adjoint method. This routine assumes that X * the pivots are associated with the untransposed lower triangular X * (L) matrix and that the diagonal of the untransposed upper X * triangular (U) matrix consists of ones. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param RHS [REAL (1) or DOUBLE PRECISION (1)] X * \a RHS is the input data array, the right hand side. This data is X * undisturbed and may be reused for other solves. X * \param Solution [REAL (1) or DOUBLE PRECISION (1)] X * \a Solution is the output data array. This routine is constructed such that X * \a RHS and \a Solution can be the same array. X * \param iRHS [REAL (1) or DOUBLE PRECISION (1)] X * \a iRHS is the imaginary portion of the input data array, the right X * hand side. This data is undisturbed and may be reused for other solves. X * If \a spSEPARATED_COMPLEX_VECTOR is set false, or if matrix is real, there X * is no need to supply this array. X * \param iSolution [REAL (1) or DOUBLE PRECISION (1)] X * \a iSolution is the imaginary portion of the output data array. This X * routine is constructed such that \a iRHS and \a iSolution can be X * the same array. If \a spSEPARATED_COMPLEX_VECTOR is set false, or if X * matrix is real, there is no need to supply this array. X */ X /*VARARGS3*/ X void sfSolveTransposed( X long *Matrix, X spREAL RHS[], X spREAL Solution[] # if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] # endif ) { /* Begin `sfSolveTransposed'. */ X spSolveTransposed( (spMatrix)*Matrix, RHS, Solution IMAG_VECTORS ); } #endif /* TRANSPOSE */ X X X X X #if DOCUMENTATION /* PRINT MATRIX */ /*! X * Formats and send the matrix to standard output. Some elementary X * statistics are also output. The matrix is output in a format that is X * readable by people. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param PrintReordered [LOGICAL] X * Indicates whether the matrix should be printed out in its original X * form, as input by the user, or whether it should be printed in its X * reordered form, as used by the matrix routines. A zero indicates that X * the matrix should be printed as inputed, a one indicates that it X * should be printed reordered. X * \param Data [LOGICAL] X * Boolean flag that when false indicates that output should be X * compressed such that only the existence of an element should be X * indicated rather than giving the actual value. Thus 10 times as many X * can be printed on a row. A zero signifies that the matrix should X * be printed compressed. A one indicates that the matrix should be X * printed in all its glory. X * \param Header [LOGICAL] X * Flag indicating that extra information such as the row and column X * numbers should be printed. X */ X void sfPrint( long *Matrix, long *PrintReordered, long *Data, long *Header ) { /* Begin `sfPrint'. */ X spPrint( (spMatrix)*Matrix, (int)*PrintReordered, X (int)*Data, (int)*Header ); } #endif /* DOCUMENTATION */ X X X X X X #if DOCUMENTATION /* OUTPUT MATRIX TO FILE */ /*! X * Writes matrix to file in format suitable to be read back in by the X * matrix test program. Data is sent to a file with a fixed name X * (MATRIX_FILE_NAME) because it is impossible to pass strings from X * FORTRAN to C in a manner that is portable. X * X * \return X * One is returned if routine was successful, otherwise zero is returned. X * The calling function can query errno (the system global error variable) X * as to the reason why this routine failed. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param Reordered [LOGICAL] X * Specifies whether matrix should be output in reordered form, X * or in original order. X * \param Data [LOGICAL] X * Indicates that the element values should be output along with X * the indices for each element. This parameter must be true if X * matrix is to be read by the sparse test program. X * \param Header [LOGICAL] X * Indicates that header is desired. This parameter must be true if X * matrix is to be read by the sparse test program. X */ X long sfFileMatrix( long *Matrix, long *Reordered, long *Data, long *Header ) { /* Begin `sfFileMatrix'. */ X return spFileMatrix( (spMatrix)*Matrix, MATRIX_FILE_NAME, "", X (int)*Reordered, (int)*Data, (int)*Header ); } #endif /* DOCUMENTATION */ X X X X X X #if DOCUMENTATION /* OUTPUT SOURCE VECTOR TO FILE */ /*! X * Writes vector to file in format suitable to be read back in by the X * matrix test program. This routine should be executed after the function X * sfFileMatrix. X * X * \return X * One is returned if routine was successful, otherwise zero is returned. X * The calling function can query errno (the system global error variable) X * as to the reason why this routine failed. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param RHS [REAL (1) or DOUBLE PRECISION (1)] X * Right-hand side vector. This is only the real portion if X * \a spSEPARATED_COMPLEX_VECTORS is true. X * \param iRHS [REAL (1) or DOUBLE PRECISION (1)] X * Right-hand side vector, imaginary portion. Not necessary if matrix X * is real or if \a spSEPARATED_COMPLEX_VECTORS is set false. X */ X int sfFileVector( X long *Matrix, X spREAL RHS[] # if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] # endif ) { /* Begin `sfFileVector'. */ X return spFileVector( (spMatrix)*Matrix, MATRIX_FILE_NAME, RHS IMAG_RHS ); } #endif /* DOCUMENTATION */ X X X X X X X #if DOCUMENTATION /* OUTPUT STATISTICS TO FILE */ /*! X * Writes useful information concerning the matrix to a file. Should be X * executed after the matrix is factored. X * Data is sent to a file with a fixed name (STATS_FILE_NAME) because X * it is impossible to pass strings from FORTRAN to C in a manner that is X * portable. X * X * \return [LOGICAL] X * One is returned if routine was successful, otherwise zero is returned. X * The calling function can query errno (the system global error variable) X * as to the reason why this routine failed. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X */ X int sfFileStats( long *Matrix ) { /* Begin `sfFileStats'. */ X return spFileStats( (spMatrix)*Matrix, STATS_FILE_NAME, "" ); } #endif /* DOCUMENTATION */ X X X X #if MODIFIED_NODAL /* PREORDER MODIFIED NODE ADMITTANCE MATRIX TO REMOVE ZEROS FROM DIAGONAL */ /*! X * This routine massages modified node admittance matrices to remove X * zeros from the diagonal. It takes advantage of the fact that the X * row and column associated with a zero diagonal usually have X * structural ones placed symmetricly. This routine should be used X * only on modified node admittance matrices and should be executed X * after the matrix has been built but before the factorization X * begins. It should be executed for the initial factorization only X * and should be executed before the rows have been linked. Thus it X * should be run before using spScale(), spMultiply(), X * spDeleteRowAndCol(), or spNorm(). X * X * This routine exploits the fact that the structural one are placed X * in the matrix in symmetric twins. For example, the stamps for X * grounded and a floating voltage sources are \code X * grounded: floating: X * [ x x 1 ] [ x x 1 ] X * [ x x ] [ x x -1 ] X * [ 1 ] [ 1 -1 ] X * \endcode X * Notice for the grounded source, there is one set of twins, and for X * the grounded, there are two sets. We remove the zero from the diagonal X * by swapping the rows associated with a set of twins. For example: X * grounded: floating 1: floating 2: \code X * [ 1 ] [ 1 -1 ] [ x x 1 ] X * [ x x ] [ x x -1 ] [ 1 -1 ] X * [ x x 1 ] [ x x 1 ] [ x x -1 ] X * \endcode X * X * It is important to deal with any zero diagonals that only have one X * set of twins before dealing with those that have more than one because X * swapping row destroys the symmetry of any twins in the rows being X * swapped, which may limit future moves. Consider \code X * [ x x 1 ] X * [ x x -1 1 ] X * [ 1 -1 ] X * [ 1 ] X * \endcode X * There is one set of twins for diagonal 4 and two for diagonal3. X * Dealing with diagonal for first requires swapping rows 2 and 4. \code X * [ x x 1 ] X * [ 1 ] X * [ 1 -1 ] X * [ x x -1 1 ] X * \endcode X * We can now deal with diagonal 3 by swapping rows 1 and 3. \code X * [ 1 -1 ] X * [ 1 ] X * [ x x 1 ] X * [ x x -1 1 ] X * \endcode X * And we are done, there are no zeros left on the diagonal. However, if X * we originally dealt with diagonal 3 first, we could swap rows 2 and 3 \code X * [ x x 1 ] X * [ 1 -1 ] X * [ x x -1 1 ] X * [ 1 ] X * \endcode X * Diagonal 4 no longer has a symmetric twin and we cannot continue. X * X * So we always take care of lone twins first. When none remain, we X * choose arbitrarily a set of twins for a diagonal with more than one set X * and swap the rows corresponding to that twin. We then deal with any X * lone twins that were created and repeat the procedure until no X * zero diagonals with symmetric twins remain. X * X * In this particular implementation, columns are swapped rather than rows. X * The algorithm used in this function was developed by Ken Kundert and X * Tom Quarles. X * X * \param Matrix [INTEGER] X * Pointer to the matrix to be preordered. X */ X void sfMNA_Preorder( long *Matrix ) { /* Begin `sfMNA_Preorder'. */ X spMNA_Preorder( (spMatrix)*Matrix ); } #endif /* MODIFIED_NODAL */ X X X X X X #if SCALING /* SCALE MATRIX */ /*! X * This function scales the matrix to enhance the possibility of X * finding a good pivoting order. Note that scaling enhances accuracy X * of the solution only if it affects the pivoting order, so it makes X * no sense to scale the matrix before spFactor(). If scaling is X * desired it should be done before spOrderAndFactor(). There X * are several things to take into account when choosing the scale X * factors. First, the scale factors are directly multiplied against X * the elements in the matrix. To prevent roundoff, each scale factor X * should be equal to an integer power of the number base of the X * machine. Since most machines operate in base two, scale factors X * should be a power of two. Second, the matrix should be scaled such X * that the matrix of element uncertainties is equilibrated. Third, X * this function multiplies the scale factors by the elements, so if X * one row tends to have uncertainties 1000 times smaller than the X * other rows, then its scale factor should be 1024, not 1/1024. X * Fourth, to save time, this function does not scale rows or columns X * if their scale factors are equal to one. Thus, the scale factors X * should be normalized to the most common scale factor. Rows and X * columns should be normalized separately. For example, if the size X * of the matrix is 100 and 10 rows tend to have uncertainties near X * 1e-6 and the remaining 90 have uncertainties near 1e-12, then the X * scale factor for the 10 should be 1/1,048,576 and the scale factors X * for the remaining 90 should be 1. Fifth, since this routine X * directly operates on the matrix, it is necessary to apply the scale X * factors to the RHS and Solution vectors. It may be easier to X * simply use spOrderAndFactor() on a scaled matrix to choose the X * pivoting order, and then throw away the matrix. Subsequent X * factorizations, performed with spFactor(), will not need to have X * the RHS and Solution vectors descaled. Lastly, this function X * should not be executed before the function spMNA_Preorder. X * X * \param Matrix [INTEGER] X * Pointer to the matrix to be scaled. X * \param SolutionScaleFactors [REAL(1) or DOUBLE PRECISION(1)] X * The array of Solution scale factors. These factors scale the columns. X * All scale factors are real valued. X * \param RHS_ScaleFactors [REAL(1) or DOUBLE PRECISION(1)] X * The array of RHS scale factors. These factors scale the rows. X * All scale factors are real valued. X */ X void sfScale( long *Matrix, spREAL RHS_ScaleFactors[], spREAL SolutionScaleFactors[] ) { /* Begin `sfScale'. */ X spScale( (spMatrix)*Matrix, RHS_ScaleFactors, SolutionScaleFactors ); } #endif /* SCALING */ X X X X X X #if MULTIPLICATION /* MATRIX MULTIPLICATION */ /*! X * Multiplies matrix by solution vector to find source vector. X * Assumes matrix has not been factored. This routine can be used X * as a test to see if solutions are correct. It should not be used X * before PreorderFoModifiedNodal(). X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X * \param RHS [REAL(1) or DOUBLE PRECISION(1)] X * RHS is the right hand side. This is what is being solved for. X * \param Solution [REAL(1) or DOUBLE PRECISION(1)] X * Solution is the vector being multiplied by the matrix. X * \param iRHS [REAL(1) or DOUBLE PRECISION(1)] X * iRHS is the imaginary portion of the right hand side. This is X * what is being solved for. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X * \param iSolution [REAL(1) or DOUBLE PRECISION(1)] X * iSolution is the imaginary portion of the vector being multiplied X * by the matrix. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X */ X void sfMultiply( X long *Matrix, X spREAL RHS[], X spREAL Solution[] #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] #endif ) { /* Begin `sfMultiply'. */ X spMultiply( (spMatrix)*Matrix, RHS, Solution IMAG_VECTORS ); } #endif /* MULTIPLICATION */ X X X X X X #if MULTIPLICATION AND TRANSPOSE /* TRANSPOSED MATRIX MULTIPLICATION */ /*! X * Multiplies transposed matrix by solution vector to find source vector. X * Assumes matrix has not been factored. This routine can be used X * as a test to see if solutions are correct. It should not be used X * before PreorderFoModifiedNodal(). X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X * \param RHS [REAL(1) or DOUBLE PRECISION(1)] X * RHS is the right hand side. This is what is being solved for. X * \param Solution [REAL(1) or DOUBLE PRECISION(1)] X * Solution is the vector being multiplied by the matrix. X * \param iRHS [REAL(1) or DOUBLE PRECISION(1)] X * iRHS is the imaginary portion of the right hand side. This is X * what is being solved for. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X * \param iSolution [REAL(1) or DOUBLE PRECISION(1)] X * iSolution is the imaginary portion of the vector being multiplied X * by the matrix. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X */ X void sfMultTransposed( X long *Matrix, X spREAL RHS[], X spREAL Solution[] #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] #endif ) { /* Begin `sfMultTransposed'. */ X spMultTransposed( (spMatrix)*Matrix, RHS, Solution IMAG_VECTORS ); } #endif /* MULTIPLICATION AND TRANSPOSE */ X X X X X X #if DETERMINANT X /* CALCULATE DETERMINANT */ /*! X * This routine in capable of calculating the determinant of the X * matrix once the LU factorization has been performed. Hence, only X * use this routine after spFactor() and before spClear(). X * The determinant equals the product of all the diagonal elements of X * the lower triangular matrix L, except that this product may need X * negating. Whether the product or the negative product equals the X * determinant is determined by the number of row and column X * interchanges performed. Note that the determinants of matrices can X * be very large or very small. On large matrices, the determinant X * can be far larger or smaller than can be represented by a floating X * point number. For this reason the determinant is scaled to a X * reasonable value and the logarithm of the scale factor is returned. X * X * \param Matrix [INTEGER] X * A pointer to the matrix for which the determinant is desired. X * \param pExponent [INTEGER or INTEGER*2] X * The logarithm base 10 of the scale factor for the determinant. To X * find X * the actual determinant, Exponent should be added to the exponent of X * DeterminantReal. X * \param pDeterminant [REAL or DOUBLE PRECISION] X * The real portion of the determinant. This number is scaled to be X * greater than or equal to 1.0 and less than 10.0. X * \param piDeterminant [REAL or DOUBLE PRECISION] X * The imaginary portion of the determinant. When the matrix is real X * this pointer need not be supplied, nothing will be returned. This X * number is scaled to be greater than or equal to 1.0 and less than 10.0. X */ X #if spCOMPLEX X void sfDeterminant( X long *Matrix, X spREAL *pDeterminant, X spREAL *piDeterminant, X int *pExponent ) { /* Begin `sfDeterminant'. */ X spDeterminant( (spMatrix)*Matrix, pExponent, pDeterminant, piDeterminant ); } X #else /* spCOMPLEX */ X void sfDeterminant( Matrix, pExponent, pDeterminant ) X long *Matrix; RealNumber *pDeterminant; int *pExponent; { /* Begin `sfDeterminant'. */ X spDeterminant( (spMatrix)*Matrix, pExponent, pDeterminant ); } #endif /* spCOMPLEX */ #endif /* DETERMINANT */ X X X X X X /* RETURN MATRIX ERROR STATUS */ /*! X * This function is used to determine the error status of the given matrix. X * X * \return [INTEGER or INTEGER*2] X * The error status of the given matrix. X * X * \param Matrix [INTEGER] X * The matrix for which the error status is desired. X */ X int sfErrorState( long *Matrix ) { /* Begin `sfError'. */ X return spErrorState( (spMatrix)*Matrix ); } X X X X X X /* PRINT MATRIX ERROR MESSAGE */ /*! X * This function prints a Sparse error message to stderr. X * X * \param Matrix [INTEGER] X * The matrix for which the error message is desired. X */ X void sfErrorMessage( long *Matrix ) { /* Begin `sfErrorMessage'. */ X spErrorMessage( (spMatrix)*Matrix, stderr, NULL ); } X X X X X X /* WHERE IS MATRIX SINGULAR */ /*! X * This function returns the row and column number where the matrix was X * detected as singular or where a zero was detected on the diagonal. X * X * \param Matrix [INTEGER] X * The matrix for which the error status is desired. X * \param pRow [INTEGER or INTEGER*2] X * The row number. X * \param pCol [INTEGER or INTEGER*2] X * The column number. X */ X void sfWhereSingular( long *Matrix, int *Row, int *Col ) { /* Begin `sfWhereSingular'. */ X spWhereSingular( (spMatrix)*Matrix, Row, Col ); } X X X X X /* MATRIX SIZE */ /*! X * Returns the size of the matrix. Either the internal or external size of X * the matrix is returned. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X * \param External [LOGICAL] X * If External is set true, the external size , i.e., the value of the X * largest external row or column number encountered is returned. X * Otherwise the true size of the matrix is returned. These two sizes X * may differ if the TRANSLATE option is set true. X */ X int sfGetSize( long *Matrix, long *External ) { /* Begin `sfGetSize'. */ X return spGetSize( (spMatrix)*Matrix, (BOOLEAN)*External ); } X X X X X X X X /* SET MATRIX REAL */ /*! X * Forces matrix to be real. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X */ X void sfSetReal( long *Matrix ) { /* Begin `sfSetReal'. */ X spSetReal( (spMatrix)*Matrix ); } X X /* SET MATRIX COMPLEX */ /*! X * Forces matrix to be complex. X * X * \param Matrix [INTEGER] X * Pointer to matrix. X */ X void sfSetComplex( long *Matrix ) { /* Begin `sfSetComplex'. */ X spSetComplex( (spMatrix)*Matrix ); } X X X X X X X X X /* FILL-IN COUNT */ /*! X * Returns the number of fill-ins in the matrix. X * X * >>> Arguments: X * Matrix [INTEGER] X * Pointer to matrix. X */ X int sfFillinCount( long *Matrix ) { /* Begin `sfFillinCount'. */ X return spFillinCount( (spMatrix)*Matrix ); } X X /* ELEMENT COUNT */ /*! X * Returns the total number of total elements in the matrix. X * X * >>> Arguments: X * Matrix [INTEGER] X * Pointer to matrix. X */ X int sfElementCount( long *Matrix ) { /* Begin `sfElementCount'. */ X return spElementCount( (spMatrix)*Matrix ); } X X X X X X #if TRANSLATE AND DELETE X /* DELETE A ROW AND COLUMN FROM THE MATRIX */ /*! X * Deletes a row and a column from a matrix. X * X * Sparse will abort if an attempt is made to delete a row or column that X * doesn't exist. X * X * \param Matrix [INTEGER] X * Pointer to the matrix in which the row and column are to be deleted. X * \param Row [INTEGER or INTEGER*2] X * Row to be deleted. X * \param Col [INTEGER or INTEGER*2] X * Column to be deleted. X */ X void sfDeleteRowAndCol( long *Matrix, int *Row, int *Col ) { /* Begin `sfDeleteRowAndCol'. */ X spDeleteRowAndCol( (spMatrix)*Matrix, *Row, *Col ); } #endif X X X X X #if PSEUDOCONDITION X /* CALCULATE PSEUDOCONDITION */ /*! X * Computes the magnitude of the ratio of the largest to the smallest X * pivots. This quantity is an indicator of ill-conditioning in the X * matrix. If this ratio is large, and if the matrix is scaled such X * that uncertainties in the RHS and the matrix entries are X * equilibrated, then the matrix is ill-conditioned. However, a small X * ratio does not necessarily imply that the matrix is X * well-conditioned. This routine must only be used after a matrix X * has been factored by sfOrderAndFactor() or sfFactor() and before it X * is cleared by sfClear() or spInitialize(). The pseudocondition is faster X * to compute than the condition number calculated by sfCondition(), but X * is not as informative. X * X * \return [REAL or DOUBLE PRECISION] X * The magnitude of the ratio of the largest to smallest pivot used during X * previous factorization. If the matrix was singular, zero is returned. X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X */ X spREAL sfPseudoCondition( long *Matrix ) { /* Begin `sfPseudoCondition'. */ X return spPseudoCondition( (spMatrix)Matrix ); } #endif X X X X X X X #if CONDITION X /* ESTIMATE CONDITION NUMBER */ /*! X * Computes an estimate of the condition number using a variation on X * the LINPACK condition number estimation algorithm. This quantity is X * an indicator of ill-conditioning in the matrix. To avoid problems X * with overflow, the reciprocal of the condition number is returned. X * If this number is small, and if the matrix is scaled such that X * uncertainties in the RHS and the matrix entries are equilibrated, X * then the matrix is ill-conditioned. If the this number is near X * one, the matrix is well conditioned. This routine must only be X * used after a matrix has been factored by sfOrderAndFactor() or X * sfFactor() and before it is cleared by sfClear() or spInitialize(). X * X * Unlike the LINPACK condition number estimator, this routines X * returns the L infinity condition number. This is an artifact of X * Sparse placing ones on the diagonal of the upper triangular matrix X * rather than the lower. This difference should be of no importance. X * X * \b References: X * A.K. Cline, C.B. Moler, G.W. Stewart, J.H. Wilkinson. An estimate X * for the condition number of a matrix. SIAM Journal on Numerical X * Analysis. Vol. 16, No. 2, pages 368-375, April 1979. X * X * J.J. Dongarra, C.B. Moler, J.R. Bunch, G.W. Stewart. LINPACK X * User's Guide. SIAM, 1979. X * X * Roger G. Grimes, John G. Lewis. Condition number estimation for X * sparse matrices. SIAM Journal on Scientific and Statistical X * Computing. Vol. 2, No. 4, pages 384-388, December 1981. X * X * Dianne Prost O'Leary. Estimating matrix condition numbers. SIAM X * Journal on Scientific and Statistical Computing. Vol. 1, No. 2, X * pages 205-209, June 1980. X * X * \return [REAL or DOUBLE PRECISION] X * The reciprocal of the condition number. If the matrix was singular, X * zero is returned. X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X * \param NormOfMatrix [REAL or DOUBLE PRECISION] X * The L-infinity norm of the unfactored matrix as computed by X * spNorm(). X * \param pError [INTEGER or INTEGER*2] X * Used to return error code. Possible errors include \a spSINGULAR X * and \a spNO_MEMORY. X */ X spREAL sfCondition( long *Matrix, spREAL *NormOfMatrix, int *pError ) { /* Begin `sfCondition'. */ X return spCondition( (spMatrix)*Matrix, *NormOfMatrix, pError ); } X X X X X /* L-INFINITY MATRIX NORM */ /*! X * Computes the L-infinity norm of an unfactored matrix. It is a fatal X * error to pass this routine a factored matrix. X * X * \return [REAL or DOUBLE PRECISION] X * The largest absolute row sum of matrix. X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X */ X spREAL sfNorm( long *Matrix ) { /* Begin `sfNorm'. */ X return spNorm( (spMatrix)*Matrix ); } #endif /* CONDITION */ X X X X X #if STABILITY X /* LARGEST ELEMENT IN MATRIX */ /*! X * spLargestElement() finds the magnitude on the largest element in the X * matrix. If the matrix has not yet been factored, the largest X * element is found by direct search. If the matrix is factored, a X * bound on the largest element in any of the reduced submatrices is X * computed. X * X * \return [REAL or DOUBLE PRECISION] X * If matrix is not factored, returns the magnitude of the largest element in X * the matrix. If the matrix is factored, a bound on the magnitude of the X * largest element in any of the reduced submatrices is returned. X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X * X * \see spLargestElement() X */ X spREAL sfLargestElement( long *Matrix ) { /* Begin `sfLargestElement'. */ X return spLargestElement( (spMatrix)Matrix ); } X X X X /* MATRIX ROUNDOFF ERROR */ /*! X * This routine, along with spLargestElement(), are used to gauge the X * stability of a factorization. See description of spLargestElement() X * for more information. X * X * \return [REAL or DOUBLE PRECISION] X * Returns a bound on the magnitude of the largest element in E = A - LU. X * X * \param Matrix [INTEGER] X * Pointer to the matrix. X * \param Rho [REAL or DOUBLE PRECISION] X * The bound on the magnitude of the largest element in any of the X * reduced submatrices. This is the number computed by the function X * spLargestElement() when given a factored matrix. If this number is X * negative, the bound will be computed automatically. X * \see spRoundoff() X */ X spREAL sfRoundoff( long *Matrix, spREAL *Rho ) { /* Begin `sfRoundoff'. */ X return spRoundoff( (spMatrix)*Matrix, *Rho ); } #endif X #endif /* FORTRAN */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spFortran.c'; eval "$shar_touch") && chmod 0600 'sparse/spFortran.c' if test $? -ne 0 then ${echo} 'restore of sparse/spFortran.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spFortran.c: MD5 check failed' ) << \SHAR_EOF 72ce88e9e8badca54a9e44180196194e sparse/spFortran.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spFortran.c'` -ne 56802 && \ ${echo} 'restoration warning: size of sparse/spFortran.c is not 56802' fi fi # ============= sparse/spLicense ============== if test -f 'sparse/spLicense' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spLicense (file already exists)' else ${echo} 'x - extracting sparse/spLicense (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spLicense' && Copyright (c) 2003, Kenneth S. Kundert All rights reserved. X Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: X Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the copyright holder nor the names of the authors may be used to endorse or promote products derived from this software without specific prior written permission. X This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spLicense'; eval "$shar_touch") && chmod 0600 'sparse/spLicense' if test $? -ne 0 then ${echo} 'restore of sparse/spLicense failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spLicense: MD5 check failed' ) << \SHAR_EOF ba0c8e767cb7df78b487d88867dec55c sparse/spLicense SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spLicense'` -ne 1474 && \ ${echo} 'restoration warning: size of sparse/spLicense is not 1474' fi fi # ============= sparse/spMatrix.h ============== if test -f 'sparse/spMatrix.h' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spMatrix.h (file already exists)' else ${echo} 'x - extracting sparse/spMatrix.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spMatrix.h' && /* EXPORTS for sparse matrix routines. */ /*! X * \file X * X * This file contains definitions that are useful to the calling X * program. In particular, this file contains error keyword X * definitions, some macro functions that are used to quickly enter X * data into the matrix and the type definition of a data structure X * that acts as a template for entering admittances into the matrix. X * Also included is the type definitions for the various functions X * available to the user. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X * X * $Date: 2003/06/29 04:19:52 $ X * $Revision: 1.2 $ X */ X X X X #ifndef spOKAY X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X */ X #include "spConfig.h" X X X X X X /* X * ERROR KEYWORDS X * X * The actual numbers used in the error codes are not sacred, they can be X * changed under the condition that the codes for the nonfatal errors are X * less than the code for spFATAL and similarly the codes for the fatal X * errors are greater than that for spFATAL. X */ X /* Begin error macros. */ #define spOKAY 0 /*!< X * Error code that indicates that no error has X * occurred. X */ #define spSMALL_PIVOT 1 /*!< X * Non-fatal error code that indicates that, when X * reordering the matrix, no element was found that X * satisfies the absolute threshold criteria. The X * largest element in the matrix was chosen as pivot. X */ #define spZERO_DIAG 2 /*!< X * Fatal error code that indicates that, a zero was X * encountered on the diagonal the matrix. This does X * not necessarily imply that the matrix is singular. X * When this error occurs, the matrix should be X * reconstructed and factored using X * spOrderAndFactor(). X */ #define spSINGULAR 3 /*!< X * Fatal error code that indicates that, matrix is X * singular, so no unique solution exists. X */ #define spMANGLED 4 /*!< X * Fatal error code that indicates that, matrix has X * been mangled, results of requested operation are X * garbage. X */ #define spNO_MEMORY 5 /*!< X * Fatal error code that indicates that not enough X * memory is available. X */ #define spPANIC 6 /*!< X * Fatal error code that indicates that the routines X * are not prepared to handle the matrix that has X * been requested. This may occur when the matrix X * is specified to be real and the routines are not X * compiled for real matrices, or when the matrix is X * specified to be complex and the routines are not X * compiled to handle complex matrices. X */ #define spFATAL 2 /*!< X * Error code that is not an error flag, but rather X * the dividing line between fatal errors and X * warnings. X */ X X X X X X /* X * KEYWORD DEFINITIONS X */ X #define spREAL double /*!< X * Defines the precision of the arithmetic used by X * \a Sparse will use. Double precision is suggested X * as being most appropriate for circuit simulation X * and for C. However, it is possible to change spREAL X * to a float for single precision arithmetic. Note X * that in C, single precision arithmetic is often X * slower than double precision. Sparse X * internally refers to spREALs as RealNumbers. X */ X X X /* X * PARTITION TYPES X * X * When factoring a previously ordered matrix using spFactor(), Sparse X * operates on a row-at-a-time basis. For speed, on each step, the row X * being updated is copied into a full vector and the operations are X * performed on that vector. This can be done one of two ways, either X * using direct addressing or indirect addressing. Direct addressing X * is fastest when the matrix is relatively dense and indirect addressing X * is quite sparse. The user can select which partitioning mode is used. X * The following keywords are passed to spPartition() and indicate that X * Sparse should use only direct addressing, only indirect addressing, or X * that it should choose the best mode on a row-by-row basis. The time X * required to choose a partition is of the same order of the cost to factor X * the matrix. X * X * If you plan to factor a large number of matrices with the same structure, X * it is best to let Sparse choose the partition. Otherwise, you should X * choose the partition based on the predicted density of the matrix. X */ X /* Begin partition keywords. */ X #define spDEFAULT_PARTITION 0 /*!< X * Partition code for spPartition(). X * Indicates that the default partitioning X * mode should be used. X * \see spPartition() X */ #define spDIRECT_PARTITION 1 /*!< X * Partition code for spPartition(). X * Indicates that all rows should be placed X * in the direct addressing partition. X * \see spPartition() X */ #define spINDIRECT_PARTITION 2 /*!< X * Partition code for spPartition(). X * Indicates that all rows should be placed X * in the indirect addressing partition. X * \see spPartition() X */ #define spAUTO_PARTITION 3 /*!< X * Partition code for spPartition(). X * Indicates that \a Sparse should chose X * the best partition for each row based X * on some simple rules. This is generally X * preferred. X * \see spPartition() X */ X X X X X /* X * MACRO FUNCTION DEFINITIONS X */ X /* Begin Macros. */ /*! X * Macro function that adds data to a real element in the matrix by a pointer. X */ #define spADD_REAL_ELEMENT(element,real) *(element) += real X /*! X * Macro function that adds data to a imaginary element in the matrix by X * a pointer. X */ #define spADD_IMAG_ELEMENT(element,imag) *(element+1) += imag X /*! X * Macro function that adds data to a complex element in the matrix by X * a pointer. X */ #define spADD_COMPLEX_ELEMENT(element,real,imag) \ { *(element) += real; \ X *(element+1) += imag; \ } X /*! X * Macro function that adds data to each of the four real matrix elements X * specified by the given template. X */ #define spADD_REAL_QUAD(template,real) \ { *((template).Element1) += real; \ X *((template).Element2) += real; \ X *((template).Element3Negated) -= real; \ X *((template).Element4Negated) -= real; \ } X /*! X * Macro function that adds data to each of the four imaginary matrix X * elements specified by the given template. X */ #define spADD_IMAG_QUAD(template,imag) \ { *((template).Element1+1) += imag; \ X *((template).Element2+1) += imag; \ X *((template).Element3Negated+1) -= imag; \ X *((template).Element4Negated+1) -= imag; \ } X /*! X * Macro function that adds data to each of the four complex matrix X * elements specified by the given template. X */ #define spADD_COMPLEX_QUAD(template,real,imag) \ { *((template).Element1) += real; \ X *((template).Element2) += real; \ X *((template).Element3Negated) -= real; \ X *((template).Element4Negated) -= real; \ X *((template).Element1+1) += imag; \ X *((template).Element2+1) += imag; \ X *((template).Element3Negated+1) -= imag; \ X *((template).Element4Negated+1) -= imag; \ } X X X X X X X /* X * TYPE DEFINITION FOR EXTERNAL MATRIX ELEMENT REFERENCES X * X * External type definitions for Sparse data objects. X */ X /*! Declares the type of the a pointer to a matrix. */ typedef spGenericPtr spMatrix; X /*! Declares the type of the a pointer to a matrix element. */ typedef spREAL spElement; X /*! Declares the type of the Sparse error codes. */ typedef int spError; X X X X X /* TYPE DEFINITION FOR COMPONENT TEMPLATE */ /*! X * This data structure is used to hold pointers to four related elements in X * matrix. It is used in conjunction with the routines spGetAdmittance(), X * spGetQuad(), and spGetOnes(). These routines stuff the structure which X * is later used by the \a spADD_QUAD macro functions above. It is also X * possible for the user to collect four pointers returned by spGetElement() X * and stuff them into the template. The \a spADD_QUAD routines stuff data X * into the matrix in locations specified by \a Element1 and \a Element2 X * without changing the data. The data is negated before being placed in X * \a Element3 and \a Element4. X */ X /* Begin `spTemplate'. */ struct spTemplate { spElement *Element1; X spElement *Element2; X spElement *Element3Negated; X spElement *Element4Negated; }; X X X X X /* X * FUNCTION TYPE DEFINITIONS X * X * The type of every user accessible function is declared here. X */ X /* Begin function declarations. */ X spcEXTERN void spClear( spMatrix ); spcEXTERN spREAL spCondition( spMatrix, spREAL, int* ); spcEXTERN spMatrix spCreate( int, int, spError* ); spcEXTERN void spDeleteRowAndCol( spMatrix, int, int ); spcEXTERN void spDestroy( spMatrix ); spcEXTERN int spElementCount( spMatrix ); spcEXTERN spError spErrorState( spMatrix ); #ifdef EOF X spcEXTERN void spErrorMessage( spMatrix, FILE*, char* ); #else # define spErrorMessage(a,b,c) spcFUNC_NEEDS_FILE(_spErrorMessage,stdio) #endif X X spcEXTERN spError spFactor( spMatrix ); spcEXTERN int spFileMatrix( spMatrix, char*, char*, int, int, int ); spcEXTERN int spFileStats( spMatrix, char*, char* ); spcEXTERN int spFillinCount( spMatrix ); spcEXTERN spElement *spFindElement( spMatrix, int, int ); spcEXTERN spError spGetAdmittance( spMatrix, int, int, X struct spTemplate* ); spcEXTERN spElement *spGetElement( spMatrix, int, int ); spcEXTERN spGenericPtr spGetInitInfo( spElement* ); spcEXTERN spError spGetOnes( spMatrix, int, int, int, X struct spTemplate* ); spcEXTERN spError spGetQuad( spMatrix, int, int, int, int, X struct spTemplate* ); spcEXTERN int spGetSize( spMatrix, int ); spcEXTERN int spInitialize( spMatrix, int (*pInit)(spElement *, spGenericPtr, int, int) ); spcEXTERN void spInstallInitInfo( spElement*, spGenericPtr ); spcEXTERN spREAL spLargestElement( spMatrix ); spcEXTERN void spMNA_Preorder( spMatrix ); spcEXTERN spREAL spNorm( spMatrix ); spcEXTERN spError spOrderAndFactor( spMatrix, spREAL[], spREAL, X spREAL, int ); spcEXTERN void spPartition( spMatrix, int ); spcEXTERN void spPrint( spMatrix, int, int, int ); spcEXTERN spREAL spPseudoCondition( spMatrix ); spcEXTERN spREAL spRoundoff( spMatrix, spREAL ); spcEXTERN void spScale( spMatrix, spREAL[], spREAL[] ); spcEXTERN void spSetComplex( spMatrix ); spcEXTERN void spSetReal( spMatrix ); spcEXTERN void spStripFills( spMatrix ); spcEXTERN void spWhereSingular( spMatrix, int*, int* ); X /* Functions with argument lists that are dependent on options. */ X #if spCOMPLEX spcEXTERN void spDeterminant( spMatrix, int*, spREAL*, spREAL* ); #else /* NOT spCOMPLEX */ spcEXTERN void spDeterminant( spMatrix, int*, spREAL* ); #endif /* NOT spCOMPLEX */ #if spCOMPLEX && spSEPARATED_COMPLEX_VECTORS spcEXTERN int spFileVector( spMatrix, char* , X spREAL[], spREAL[]); spcEXTERN void spMultiply( spMatrix, spREAL[], spREAL[], X spREAL[], spREAL[] ); spcEXTERN void spMultTransposed( spMatrix, spREAL[], spREAL[], X spREAL[], spREAL[] ); spcEXTERN void spSolve( spMatrix, spREAL[], spREAL[], spREAL[], X spREAL[] ); spcEXTERN void spSolveTransposed( spMatrix, spREAL[], spREAL[], X spREAL[], spREAL[] ); #else /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */ spcEXTERN int spFileVector( spMatrix, char* , spREAL[] ); spcEXTERN void spMultiply( spMatrix, spREAL[], spREAL[] ); spcEXTERN void spMultTransposed( spMatrix, X spREAL[], spREAL[] ); spcEXTERN void spSolve( spMatrix, spREAL[], spREAL[] ); spcEXTERN void spSolveTransposed( spMatrix, X spREAL[], spREAL[] ); #endif /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */ #endif /* spOKAY */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spMatrix.h'; eval "$shar_touch") && chmod 0600 'sparse/spMatrix.h' if test $? -ne 0 then ${echo} 'restore of sparse/spMatrix.h failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spMatrix.h: MD5 check failed' ) << \SHAR_EOF b390c6629b2821f252b0b93900e613d6 sparse/spMatrix.h SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spMatrix.h'` -ne 12377 && \ ${echo} 'restoration warning: size of sparse/spMatrix.h is not 12377' fi fi # ============= sparse/spOutput.c ============== if test -f 'sparse/spOutput.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spOutput.c (file already exists)' else ${echo} 'x - extracting sparse/spOutput.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spOutput.c' && /* X * MATRIX OUTPUT MODULE X * X * Author: Advisor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*! \file X * X * This file contains the output-to-file and output-to-screen routines for X * the matrix package. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ /* >>> User accessible functions contained in this file: X * spPrint X * spFileMatrix X * spFileVector X * spFileStats X * X * >>> Other functions contained in this file: X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 X * by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "$Header: /cvsroot/sparse/src/spOutput.c,v 1.3 2003/06/29 04:19:52 kundert Exp $"; #endif X X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X X X X X #if DOCUMENTATION X /*! X * Formats and send the matrix to standard output. Some elementary X * statistics are also output. The matrix is output in a format that is X * readable by people. X * X * \param eMatrix X * Pointer to matrix. X * \param PrintReordered X * Indicates whether the matrix should be printed out in its original X * form, as input by the user, or whether it should be printed in its X * reordered form, as used by the matrix routines. A zero indicates that X * the matrix should be printed as inputed, a one indicates that it X * should be printed reordered. X * \param Data X * Boolean flag that when false indicates that output should be X * compressed such that only the existence of an element should be X * indicated rather than giving the actual value. Thus 11 times as X * many can be printed on a row. A zero signifies that the matrix X * should be printed compressed. A one indicates that the matrix X * should be printed in all its glory. X * \param Header X * Flag indicating that extra information should be given, such as row X * and column numbers. X */ /* >>> Local variables: X * Col (int) X * Column being printed. X * ElementCount (int) X * Variable used to count the number of nonzero elements in the matrix. X * LargestElement (RealNumber) X * The magnitude of the largest element in the matrix. X * LargestDiag (RealNumber) X * The magnitude of the largest diagonal in the matrix. X * Magnitude (RealNumber) X * The absolute value of the matrix element being printed. X * PrintOrdToIntColMap (int []) X * A translation array that maps the order that columns will be X * printed in (if not PrintReordered) to the internal column numbers. X * PrintOrdToIntRowMap (int []) X * A translation array that maps the order that rows will be X * printed in (if not PrintReordered) to the internal row numbers. X * pElement (ElementPtr) X * Pointer to the element in the matrix that is to be printed. X * pImagElements (ElementPtr [ ]) X * Array of pointers to elements in the matrix. These pointers point X * to the elements whose real values have just been printed. They are X * used to quickly access those same elements so their imaginary values X * can be printed. X * Row (int) X * Row being printed. X * Size (int) X * The size of the matrix. X * SmallestDiag (RealNumber) X * The magnitude of the smallest diagonal in the matrix. X * SmallestElement (RealNumber) X * The magnitude of the smallest element in the matrix excluding zero X * elements. X * StartCol (int) X * The column number of the first column to be printed in the group of X * columns currently being printed. X * StopCol (int) X * The column number of the last column to be printed in the group of X * columns currently being printed. X * Top (int) X * The largest expected external row or column number. X */ X void spPrint( X spMatrix eMatrix, X int PrintReordered, X int Data, X int Header ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int J = 0; int I, Row, Col, Size, Top, StartCol = 1, StopCol, Columns, ElementCount = 0; double Magnitude, SmallestDiag, SmallestElement; double LargestElement = 0.0, LargestDiag = 0.0; ElementPtr pElement, pImagElements[PRINTER_WIDTH/10+1]; int *PrintOrdToIntRowMap, *PrintOrdToIntColMap; X /* Begin `spPrint'. */ X ASSERT_IS_SPARSE( Matrix ); X Size = Matrix->Size; X /* Create a packed external to internal row and column translation array. */ # if TRANSLATE X Top = Matrix->AllocatedExtSize; #else X Top = Matrix->AllocatedSize; #endif X CALLOC( PrintOrdToIntRowMap, int, Top + 1 ); X CALLOC( PrintOrdToIntColMap, int, Top + 1 ); X if ( PrintOrdToIntRowMap == NULL OR PrintOrdToIntColMap == NULL) X { Matrix->Error = spNO_MEMORY; X return; X } X for (I = 1; I <= Size; I++) X { PrintOrdToIntRowMap[ Matrix->IntToExtRowMap[I] ] = I; X PrintOrdToIntColMap[ Matrix->IntToExtColMap[I] ] = I; X } X /* Pack the arrays. */ X for (J = 1, I = 1; I <= Top; I++) X { if (PrintOrdToIntRowMap[I] != 0) X PrintOrdToIntRowMap[ J++ ] = PrintOrdToIntRowMap[ I ]; X } X for (J = 1, I = 1; I <= Top; I++) X { if (PrintOrdToIntColMap[I] != 0) X PrintOrdToIntColMap[ J++ ] = PrintOrdToIntColMap[ I ]; X } X /* Print header. */ X if (Header) X { printf("MATRIX SUMMARY\n\n"); X printf("Size of matrix = %1d x %1d.\n", Size, Size); X if ( Matrix->Reordered AND PrintReordered ) X printf("Matrix has been reordered.\n"); X putchar('\n'); X X if ( Matrix->Factored ) X printf("Matrix after factorization:\n"); X else X printf("Matrix before factorization:\n"); X X SmallestElement = LARGEST_REAL; X SmallestDiag = SmallestElement; X } X if (Size == 0) return; X /* Determine how many columns to use. */ X Columns = PRINTER_WIDTH; X if (Header) Columns -= 5; X if (Data) Columns = (Columns+1) / 10; X /* X * Print matrix by printing groups of complete columns until all the columns X * are printed. X */ X J = 0; X while ( J <= Size ) X /* Calculate index of last column to printed in this group. */ X { StopCol = StartCol + Columns - 1; X if (StopCol > Size) X StopCol = Size; X /* Label the columns. */ X if (Header) X { if (Data) X { printf(" "); X for (I = StartCol; I <= StopCol; I++) X { if (PrintReordered) X Col = I; X else X Col = PrintOrdToIntColMap[I]; X printf(" %9d", Matrix->IntToExtColMap[ Col ]); X } X printf("\n\n"); X } X else X { if (PrintReordered) X printf("Columns %1d to %1d.\n",StartCol,StopCol); X else X { printf("Columns %1d to %1d.\n", X Matrix->IntToExtColMap[ PrintOrdToIntColMap[StartCol] ], X Matrix->IntToExtColMap[ PrintOrdToIntColMap[StopCol] ]); X } X } X } X /* Print every row ... */ X for (I = 1; I <= Size; I++) X { if (PrintReordered) X Row = I; X else X Row = PrintOrdToIntRowMap[I]; X X if (Header) X { if (PrintReordered AND NOT Data) X printf("%4d", I); X else X printf("%4d", Matrix->IntToExtRowMap[ Row ]); X if (NOT Data) putchar(' '); X } X /* ... in each column of the group. */ X for (J = StartCol; J <= StopCol; J++) X { if (PrintReordered) X Col = J; X else X Col = PrintOrdToIntColMap[J]; X X pElement = Matrix->FirstInCol[Col]; X while(pElement != NULL AND pElement->Row != Row) X pElement = pElement->NextInCol; X X if (Data) X pImagElements[J - StartCol] = pElement; X X if (pElement != NULL) X /* Case where element exists */ X { if (Data) X printf(" %9.3g", (double)pElement->Real); X else X putchar('x'); X /* Update status variables */ X if ( (Magnitude = ELEMENT_MAG(pElement)) > LargestElement ) X LargestElement = Magnitude; X if ((Magnitude < SmallestElement) AND (Magnitude != 0.0)) X SmallestElement = Magnitude; X ElementCount++; X } X /* Case where element is structurally zero */ X else X { if (Data) X printf(" ..."); X else X putchar('.'); X } X } X putchar('\n'); X #if spCOMPLEX X if (Matrix->Complex AND Data) X { if (Header) X printf(" "); X for (J = StartCol; J <= StopCol; J++) X { if (pImagElements[J - StartCol] != NULL) X { printf(" %8.2gj", X (double)pImagElements[J-StartCol]->Imag); X } X else printf(" "); X } X putchar('\n'); X } #endif /* spCOMPLEX */ X } X /* Calculate index of first column in next group. */ X StartCol = StopCol; X StartCol++; X putchar('\n'); X } X if (Header) X { printf("\nLargest element in matrix = %-1.4g.\n", LargestElement); X printf("Smallest element in matrix = %-1.4g.\n", SmallestElement); X /* Search for largest and smallest diagonal values */ X for (I = 1; I <= Size; I++) X { if (Matrix->Diag[I] != NULL) X { Magnitude = ELEMENT_MAG( Matrix->Diag[I] ); X if ( Magnitude > LargestDiag ) LargestDiag = Magnitude; X if ( Magnitude < SmallestDiag ) SmallestDiag = Magnitude; X } X } X X /* Print the largest and smallest diagonal values */ X if ( Matrix->Factored ) X { printf("\nLargest diagonal element = %-1.4g.\n", LargestDiag); X printf("Smallest diagonal element = %-1.4g.\n", SmallestDiag); X } X else X { printf("\nLargest pivot element = %-1.4g.\n", LargestDiag); X printf("Smallest pivot element = %-1.4g.\n", SmallestDiag); X } X X /* Calculate and print sparsity and number of fill-ins created. */ X printf("\nDensity = %2.2f%%.\n", ((double)ElementCount * 100.0) X / (((double)Size * (double)Size))); X if (NOT Matrix->NeedsOrdering) X printf("Number of fill-ins = %1d.\n", Matrix->Fillins); X } X putchar('\n'); X (void)fflush(stdout); X X FREE(PrintOrdToIntColMap); X FREE(PrintOrdToIntRowMap); X return; } X X X X X X X X X X X /*! X * Writes matrix to file in format suitable to be read back in by the X * matrix test program. X * X * \return X * One is returned if routine was successful, otherwise zero is returned. X * The calling function can query \a errno (the system global error variable) X * as to the reason why this routine failed. X * X * \param eMatrix X * Pointer to matrix. X * \param File X * Name of file into which matrix is to be written. X * \param Label X * String that is transferred to file and is used as a label. X * \param Reordered X * Specifies whether matrix should be output in reordered form, X * or in original order. X * \param Data X * Indicates that the element values should be output along with X * the indices for each element. This parameter must be true if X * matrix is to be read by the sparse test program. X * \param Header X * Indicates that header is desired. This parameter must be true if X * matrix is to be read by the sparse test program. X */ /* >>> Local variables: X * Col (int) X * The original column number of the element being output. X * pElement (ElementPtr) X * Pointer to an element in the matrix. X * pMatrixFile (FILE *) X * File pointer to the matrix file. X * Row (int) X * The original row number of the element being output. X * Size (int) X * The size of the matrix. X */ X int spFileMatrix( X spMatrix eMatrix, X char *File, X char *Label, X int Reordered, X int Data, X int Header ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int I, Size; register ElementPtr pElement; int Row, Col, Err; FILE *pMatrixFile; X /* Begin `spFileMatrix'. */ X ASSERT_IS_SPARSE( Matrix ); X /* Open file matrix file in write mode. */ X if ((pMatrixFile = fopen(File, "w")) == NULL) X return 0; X /* Output header. */ X Size = Matrix->Size; X if (Header) X { if (Matrix->Factored AND Data) X { Err = fprintf X ( pMatrixFile, X "Warning : The following matrix is factored in to LU form.\n" X ); X if (Err < 0) return 0; X } X if (fprintf(pMatrixFile, "%s\n", Label) < 0) return 0; X Err = fprintf( pMatrixFile, "%d\t%s\n", Size, X (Matrix->Complex ? "complex" : "real")); X if (Err < 0) return 0; X } X if (Size == 0) return 1; X /* Output matrix. */ X if (NOT Data) X { for (I = 1; I <= Size; I++) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { if (Reordered) X { Row = pElement->Row; X Col = I; X } X else X { Row = Matrix->IntToExtRowMap[pElement->Row]; X Col = Matrix->IntToExtColMap[I]; X } X pElement = pElement->NextInCol; X if (fprintf(pMatrixFile, "%d\t%d\n", Row, Col) < 0) return 0; X } X } /* Output terminator, a line of zeros. */ X if (Header) X if (fprintf(pMatrixFile, "0\t0\n") < 0) return 0; X } X #if spCOMPLEX X if (Data AND Matrix->Complex) X { for (I = 1; I <= Size; I++) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { if (Reordered) X { Row = pElement->Row; X Col = I; X } X else X { Row = Matrix->IntToExtRowMap[pElement->Row]; X Col = Matrix->IntToExtColMap[I]; X } X Err = fprintf X ( pMatrixFile,"%d\t%d\t%-.15g\t%-.15g\n", X Row, Col, (double)pElement->Real, (double)pElement->Imag X ); X if (Err < 0) return 0; X pElement = pElement->NextInCol; X } X } /* Output terminator, a line of zeros. */ X if (Header) X if (fprintf(pMatrixFile,"0\t0\t0.0\t0.0\n") < 0) return 0; X X } #endif /* spCOMPLEX */ X #if REAL X if (Data AND NOT Matrix->Complex) X { for (I = 1; I <= Size; I++) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { Row = Matrix->IntToExtRowMap[pElement->Row]; X Col = Matrix->IntToExtColMap[I]; X Err = fprintf X ( pMatrixFile,"%d\t%d\t%-.15g\n", X Row, Col, (double)pElement->Real X ); X if (Err < 0) return 0; X pElement = pElement->NextInCol; X } X } /* Output terminator, a line of zeros. */ X if (Header) X if (fprintf(pMatrixFile,"0\t0\t0.0\n") < 0) return 0; X X } #endif /* REAL */ X /* Close file. */ X if (fclose(pMatrixFile) < 0) return 0; X return 1; } X X X X X X X /*! X * Writes vector to file in format suitable to be read back in by the X * matrix test program. This routine should be executed after the function X * spFileMatrix. X * X * \return X * One is returned if routine was successful, otherwise zero is returned. X * The calling function can query \a errno (the system global error variable) X * as to the reason why this routine failed. X * X * \param eMatrix X * Pointer to matrix. X * \param File X * Name of file into which matrix is to be written. X * \param RHS X * Right-hand side vector. This is only the real portion if X * \a spSEPARATED_COMPLEX_VECTORS is true. X * \param iRHS X * Right-hand side vector, imaginary portion. Not necessary if matrix X * is real or if \a spSEPARATED_COMPLEX_VECTORS is set false. X * \a iRHS is a macro that replaces itself with `, iRHS' if the options X * \a spCOMPLEX and \a spSEPARATED_COMPLEX_VECTORS are set, otherwise X * it disappears without a trace. X */ /* >>> Local variables: X * pMatrixFile (FILE *) X * File pointer to the matrix file. X * Size (int) X * The size of the matrix. X */ X int spFileVector( X spMatrix eMatrix, X char *File, X spREAL RHS[] #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] #endif ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int I, Size, Err; FILE *pMatrixFile; X /* Begin `spFileVector'. */ X ASSERT_IS_SPARSE( Matrix ); X vASSERT( RHS != NULL, "Vector missing" ); X /* Open File in append mode. */ X if ((pMatrixFile = fopen(File,"a")) == NULL) X return 0; X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET #if spCOMPLEX X if (Matrix->Complex) X { #if spSEPARATED_COMPLEX_VECTORS X vASSERT( iRHS != NULL, "Imaginary vector missing" ); X --RHS; X --iRHS; #else X RHS -= 2; #endif X } X else #endif /* spCOMPLEX */ X --RHS; #endif /* NOT ARRAY_OFFSET */ X X /* Output vector. */ X Size = Matrix->Size; X if (Size == 0) return 1; X #if spCOMPLEX X if (Matrix->Complex) X { #if spSEPARATED_COMPLEX_VECTORS X for (I = 1; I <= Size; I++) X { Err = fprintf X ( pMatrixFile, "%-.15g\t%-.15g\n", X (double)RHS[I], (double)iRHS[I] X ); X if (Err < 0) return 0; X } #else X for (I = 1; I <= Size; I++) X { Err = fprintf X ( pMatrixFile, "%-.15g\t%-.15g\n", X (double)RHS[2*I], (double)RHS[2*I+1] X ); X if (Err < 0) return 0; X } #endif X } #endif /* spCOMPLEX */ #if REAL AND spCOMPLEX X else #endif #if REAL X { for (I = 1; I <= Size; I++) X { if (fprintf(pMatrixFile, "%-.15g\n", (double)RHS[I]) < 0) X return 0; X } X } #endif /* REAL */ X /* Close file. */ X if (fclose(pMatrixFile) < 0) return 0; X return 1; } X X X X X X X X X /*! X * Writes useful information concerning the matrix to a file. Should be X * executed after the matrix is factored. X * X * \return X * One is returned if routine was successful, otherwise zero is returned. X * The calling function can query \a errno (the system global error variable) X * as to the reason why this routine failed. X * X * \param eMatrix X * Pointer to matrix. X * \param File X * Name of file into which matrix is to be written. X * \param Label X * String that is transferred to file and is used as a label. X */ /* >>> Local variables: X * Data (RealNumber) X * The value of the matrix element being output. X * LargestElement (RealNumber) X * The largest element in the matrix. X * NumberOfElements (int) X * Number of nonzero elements in the matrix. X * pElement (ElementPtr) X * Pointer to an element in the matrix. X * pStatsFile (FILE *) X * File pointer to the statistics file. X * Size (int) X * The size of the matrix. X * SmallestElement (RealNumber) X * The smallest element in the matrix excluding zero elements. X */ X int spFileStats( X spMatrix eMatrix, X char *File, X char *Label ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int Size, I; register ElementPtr pElement; int NumberOfElements; RealNumber Data, LargestElement, SmallestElement; FILE *pStatsFile; X /* Begin `spFileStats'. */ X ASSERT_IS_SPARSE( Matrix ); X /* Open File in append mode. */ X if ((pStatsFile = fopen(File, "a")) == NULL) X return 0; X /* Output statistics. */ X Size = Matrix->Size; X if (NOT Matrix->Factored) X fprintf(pStatsFile, "Matrix has not been factored.\n"); X fprintf(pStatsFile, "||| Starting new matrix |||\n"); X fprintf(pStatsFile, "%s\n", Label); X if (Matrix->Complex) X fprintf(pStatsFile, "Matrix is complex.\n"); X else X fprintf(pStatsFile, "Matrix is real.\n"); X fprintf(pStatsFile," Size = %d\n",Size); X if (Size == 0) return 1; X /* Search matrix. */ X NumberOfElements = 0; X LargestElement = 0.0; X SmallestElement = LARGEST_REAL; X X for (I = 1; I <= Size; I++) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { NumberOfElements++; X Data = ELEMENT_MAG(pElement); X if (Data > LargestElement) X LargestElement = Data; X if (Data < SmallestElement AND Data != 0.0) X SmallestElement = Data; X pElement = pElement->NextInCol; X } X } X X SmallestElement = MIN( SmallestElement, LargestElement ); X /* Output remaining statistics. */ X fprintf(pStatsFile, " Initial number of elements = %d\n", X NumberOfElements - Matrix->Fillins); X fprintf(pStatsFile, X " Initial average number of elements per row = %f\n", X (double)(NumberOfElements - Matrix->Fillins) / (double)Size); X fprintf(pStatsFile, " Fill-ins = %d\n",Matrix->Fillins); X fprintf(pStatsFile, " Average number of fill-ins per row = %f%%\n", X (double)Matrix->Fillins / (double)Size); X fprintf(pStatsFile, " Total number of elements = %d\n", X NumberOfElements); X fprintf(pStatsFile, " Average number of elements per row = %f\n", X (double)NumberOfElements / (double)Size); X fprintf(pStatsFile," Density = %f%%\n", X (100.0*(double)NumberOfElements)/((double)Size*(double)Size)); X fprintf(pStatsFile," Relative Threshold = %e\n", Matrix->RelThreshold); X fprintf(pStatsFile," Absolute Threshold = %e\n", Matrix->AbsThreshold); X fprintf(pStatsFile," Largest Element = %e\n", LargestElement); X fprintf(pStatsFile," Smallest Element = %e\n\n\n", SmallestElement); X /* Close file. */ X (void)fclose(pStatsFile); X return 1; } #endif /* DOCUMENTATION */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spOutput.c'; eval "$shar_touch") && chmod 0600 'sparse/spOutput.c' if test $? -ne 0 then ${echo} 'restore of sparse/spOutput.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spOutput.c: MD5 check failed' ) << \SHAR_EOF 55952f2ead49c70ba2a68b4a9c0641b7 sparse/spOutput.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spOutput.c'` -ne 22792 && \ ${echo} 'restoration warning: size of sparse/spOutput.c is not 22792' fi fi # ============= sparse/spRevision ============== if test -f 'sparse/spRevision' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spRevision (file already exists)' else ${echo} 'x - extracting sparse/spRevision (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spRevision' && /* X * REVISION HISTORY X * X * Author: X * Kenneth S. Kundert X * kundert@users.sourceforge.net X * X * References X * Kenneth S. Kundert. Sparse matrix techniques. In "Circuit Analysis, X * Simulation and Design," vol. 3, pt. 1, Albert E. Ruehli (editor). X * North-Holland, 1986. X */ X /* X * Copyright information. X * X * Copyright (c) 1985-2003 X * by Kenneth S. Kundert X * All rights reserved X */ X /* X * >>> Current revision information: X * $Author: kundert $ X * $Date: 2003/06/07 05:48:42 $ X * $Revision: 1.2 $ X */ X X X X X X /* X * >>> History: X * Revision 1.1 January 1985 X * Initial release. X * X * Revision 1.1a 20 March 1985 X * Modified DecomposeMatrix() and OrderAndDecomposeMatrix() so that X * the parameters Growth, PseudoCondition and LargestElement may be X * given as NULL. X * X * Revision 1.1b 28 March 1985 X * Corrected a bug that caused OrderAndDecomposeMatrix() to reorder X * the matrix every time it was called. Also made many of the global X * variables defined in MtrxDecom.c static. X * X * Revision 1.2 October 1985 X * This new version of Sparse is meant to make it more compatible X * with interactive circuit simulators. In it the TRANSLATE X * option was added, along with the ability to access the matrix X * with AddElementToMatrix() and AddAdmittanceToMatrix() after it X * has been reordered. Also added were the DeleteRowAndColFromMatrix(), X * CleanMatrix(), GetMatrixSize(), SetMatrixReal() and SetMatrixComplex() X * routines. X * X * Revision 1.2a April 1986 X * Fixed a bug that caused the matrix frame to get freed twice and one X * in the test program that caused sparse to crash when a complex matrix X * with no source vector was attempted. X * X * Revision 1.2b July 1986 X * Modified the test routine so that it allocates vectors from the heap X * rather than the stack. X * X * Revision 1.2c February 1987 X * Fixed off-by-one error in PreorderForModifiedNodal(). X * X * Revision 1.2d X * Modified the pivot selection algorithm so that singletons also meet X * numerical threshold criterion. Deleted some global variables. Modified X * test program to add command line options among other things. Made X * thresholds sticky, so that once a valid threshold has been specified X * for a particular matrix, it becomes the new default. X * X * Revision 1.3a July 1988 X * Made numerous changes. Highlights are: X * Routine names shortened and made unique to 8 characters. X * Unordering factorization is faster. X * Added self initialization feature. X * Sparse now aborts on errors that should not occur. X * Cleaned up test program. X * Separated growth and pseudocondition calculations from factorization. X * Added LINPACK condition number estimator. X * Rewrote spMNA_Preorder, algorithm extended to fix inadequacies. X * Eliminated all global variables. X * Added DIAGONAL_PIVOTING option (only diagonal pivoting before). X * X * Revision 1.3b August 1988 X * Added function declarations at the top of each file. X * X * Revision 1.3c January 1989 X * Fixed bug in spFactor.c:SearchForSingleton that caused the program X * to crash when looking for a singleton in a row or column in the X * reduced submatrix that has no elements. X * X * Revision 1.3d June 1989 X * Compatibility to Spice3c1 added. X * Added spErrorMessage() error message printing function. X * X * Revision 1.3e March 1990 X * Fixed bug that caused sparse to crash when using spMultiply() or X * spMultTransposed() before the matrix had been factored once. X * Compatibility to Spice3d1 added. X * X * Revision 1.3f July 1990 X * Fixed bug in spFactor.c:spOrderAndFactor(). X * Added verbose ASSERT() and ABORT(). X * X * Revision 1.4a June 2003 X * Modernized the API a bit. X * Fixed the Markowitz overflow problem. X * Released on SourceForge as open source software. X */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spRevision'; eval "$shar_touch") && chmod 0600 'sparse/spRevision' if test $? -ne 0 then ${echo} 'restore of sparse/spRevision failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spRevision: MD5 check failed' ) << \SHAR_EOF 832dc3fa5d0c8012c2acbd97ec670ffa sparse/spRevision SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spRevision'` -ne 3994 && \ ${echo} 'restoration warning: size of sparse/spRevision is not 3994' fi fi # ============= sparse/spSMP.c ============== if test -f 'sparse/spSMP.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spSMP.c (file already exists)' else ${echo} 'x - extracting sparse/spSMP.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spSMP.c' && /* X * Spice3 COMPATIBILITY MODULE X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X * X * This module contains routines that make Sparse1.4 a direct X * replacement for the SMP sparse matrix package in Spice3c1 and Spice3d1. X * Sparse1.4 is in general a faster and more robust package than SMP. X * These advantages become significant on large circuits. X * X * This module is provided for convience only. It has not been tested X * with the recent version of Spice3 and is not supported. X * X * >>> User accessible functions contained in this file: X * SMPaddElt X * SMPmakeElt X * SMPcClear X * SMPclear X * SMPcLUfac X * SMPluFac X * SMPcReorder X * SMPreorder X * SMPcaSolve X * SMPcSolve X * SMPsolve X * SMPmatSize X * SMPnewMatrix X * SMPdestroy X * SMPpreOrder X * SMPprint X * SMPgetError X * SMPcProdDiag X */ X /* X * To replace SMP with Sparse, rename the file spSpice3.h to X * spMatrix.h and place Sparse in a subdirectory of SPICE called X * `sparse'. Then on UNIX compile Sparse by executing `make spice'. X * If not on UNIX, after compiling Sparse and creating the sparse.a X * archive, compile this file (spSMP.c) and spSMP.o to the archive, X * then copy sparse.a into the SPICE main directory and rename it X * SMP.a. Finally link SPICE. X * X * To be compatible with SPICE, the following Sparse compiler options X * (in spConfig.h) should be set as shown below: X * X * REAL YES X * EXPANDABLE YES X * TRANSLATE NO X * INITIALIZE NO or YES, YES for use with test prog. X * DIAGONAL_PIVOTING YES X * ARRAY_OFFSET YES X * MODIFIED_MARKOWITZ NO X * DELETE NO X * STRIP NO X * MODIFIED_NODAL YES X * QUAD_ELEMENT NO X * TRANSPOSE YES X * SCALING NO X * DOCUMENTATION YES X * MULTIPLICATION NO X * DETERMINANT YES X * STABILITY NO X * CONDITION NO X * PSEUDOCONDITION NO X * FORTRAN NO X * DEBUG YES X * spCOMPLEX 1 X * spSEPARATED_COMPLEX_VECTORS 1 X * X * spREAL double X */ X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spSMP.c,v 1.3 2003/06/30 19:40:51 kundert Exp $"; #endif X X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spMatrix.h X * Sparse macros and declarations. X * SMPdefs.h X * Spice3's matrix macro definitions. X */ X #include "spMatrix.h" #include "../include/SMPdefs.h" X #define NO 0 #define YES 1 X X /* X * SMPaddElt() X */ int SMPaddElt( Matrix, Row, Col, Value ) SMPmatrix *Matrix; int Row, Col; double Value; { X *spGetElement( (char *)Matrix, Row, Col ) = Value; X return spError( (char *)Matrix ); } X /* X * SMPmakeElt() X */ double * SMPmakeElt( Matrix, Row, Col ) SMPmatrix *Matrix; int Row, Col; { X return spGetElement( (char *)Matrix, Row, Col ); } X /* X * SMPcClear() X */ void SMPcClear( Matrix ) SMPmatrix *Matrix; { X spClear( (char *)Matrix ); } X /* X * SMPclear() X */ void SMPclear( Matrix ) SMPmatrix *Matrix; { X spClear( (char *)Matrix ); } X /* X * SMPcLUfac() X */ /*ARGSUSED*/ int SMPcLUfac( Matrix, PivTol ) SMPmatrix *Matrix; double PivTol; { X spSetComplex( (char *)Matrix ); X return spFactor( (char *)Matrix ); } X /* X * SMPluFac() X */ /*ARGSUSED*/ int SMPluFac( Matrix, PivTol, Gmin ) SMPmatrix *Matrix; double PivTol, Gmin; { X spSetReal( (char *)Matrix ); X LoadGmin( (char *)Matrix, Gmin ); X return spFactor( (char *)Matrix ); } X /* X * SMPcReorder() X */ int SMPcReorder( Matrix, PivTol, PivRel, NumSwaps ) SMPmatrix *Matrix; double PivTol, PivRel; int *NumSwaps; { X *NumSwaps = 0; X spSetComplex( (char *)Matrix ); X return spOrderAndFactor( (char *)Matrix, (spREAL*)NULL, X (spREAL)PivRel, (spREAL)PivTol, YES ); } X /* X * SMPreorder() X */ int SMPreorder( Matrix, PivTol, PivRel, Gmin ) SMPmatrix *Matrix; double PivTol, PivRel, Gmin; { X spSetComplex( (char *)Matrix ); X LoadGmin( (char *)Matrix, Gmin ); X return spOrderAndFactor( (char *)Matrix, (spREAL*)NULL, X (spREAL)PivRel, (spREAL)PivTol, YES ); } X /* X * SMPcaSolve() X */ void SMPcaSolve( Matrix, RHS, iRHS, Spare, iSpare) SMPmatrix *Matrix; double RHS[], iRHS[], Spare[], iSpare[]; { X spSolveTransposed( (char *)Matrix, RHS, RHS, iRHS, iRHS ); } X /* X * SMPcSolve() X */ void SMPcSolve( Matrix, RHS, iRHS, Spare, iSpare) SMPmatrix *Matrix; double RHS[], iRHS[], Spare[], iSpare[]; { X spSolve( (char *)Matrix, RHS, RHS, iRHS, iRHS ); } X /* X * SMPsolve() X */ void SMPsolve( Matrix, RHS, Spare ) SMPmatrix *Matrix; double RHS[], Spare[]; { X spSolve( (char *)Matrix, RHS, RHS, (spREAL*)NULL, (spREAL*)NULL ); } X /* X * SMPmatSize() X */ int SMPmatSize( Matrix ) SMPmatrix *Matrix; { X return spGetSize( (char *)Matrix, 1 ); } X /* X * SMPnewMatrix() X */ int SMPnewMatrix( pMatrix ) SMPmatrix **pMatrix; { int Error; X *pMatrix = (SMPmatrix *)spCreate( 0, 1, &Error ); X return Error; } X /* X * SMPdestroy() X */ void SMPdestroy( Matrix ) SMPmatrix *Matrix; { X spDestroy( (char *)Matrix ); } X /* X * SMPpreOrder() X */ int SMPpreOrder( Matrix ) SMPmatrix *Matrix; { X spMNA_Preorder( (char *)Matrix ); X return spError( (char *)Matrix ); } X /* X * SMPprint() X */ /*ARGSUSED*/ void SMPprint( Matrix, File ) SMPmatrix *Matrix; FILE *File; { X spPrint( (char *)Matrix, 0, 1, 1 ); } X /* X * SMPgetError() X */ void SMPgetError( Matrix, Col, Row) SMPmatrix *Matrix; int *Row, *Col; { X spWhereSingular( (char *)Matrix, Row, Col ); } X /* X * SMPcProdDiag() X */ int SMPcProdDiag( Matrix, pMantissa, pExponent) SMPmatrix *Matrix; SPcomplex *pMantissa; int *pExponent; { X spDeterminant( (char *)Matrix, pExponent, &(pMantissa->real), X &(pMantissa->imag) ); X return spError( (char *)Matrix ); } X /* X * LOAD GMIN X * X * This routine adds Gmin to each diagonal element. Because Gmin is X * added to the current diagonal, which may bear little relation to X * what the outside world thinks is a diagonal, and because the X * elements that are diagonals may change after calling spOrderAndFactor, X * use of this routine is not recommended. It is included here simply X * for compatibility with Spice3. X */ #include "spDefs.h" X LoadGmin( eMatrix, Gmin ) X char *eMatrix; register double Gmin; { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int I; register ArrayOfElementPtrs Diag; X /* Begin `spLoadGmin'. */ X ASSERT_IS_SPARSE( Matrix ); X X Diag = Matrix->Diag; X for (I = Matrix->Size; I > 0; I--) X Diag[I]->Real += Gmin; X return; } X X X X X /* X * FIND ELEMENT X * X * This routine finds an element in the matrix by row and column number. X * If the element exists, a pointer to it is returned. If not, then NULL X * is returned unless the CreateIfMissing flag is true, in which case a X * pointer to the new element is returned. X */ X SMPelement * SMPfindElt( eMatrix, Row, Col, CreateIfMissing ) X char *eMatrix; int Row, Col; int CreateIfMissing; { MatrixPtr Matrix = (MatrixPtr)eMatrix; spREAL *Element = (spREAL *)Matrix->FirstInCol[Col]; X /* Begin `SMPfindElt'. */ X ASSERT_IS_SPARSE( Matrix ); X if (CreateIfMissing) X { Element = spcCreateElement( Matrix, Row, Col, X &Matrix->FirstInRow[Row], X &Matrix->FirstInCol[Col], NO ); X } X else Element = spcFindElement( Matrix, Row, Col ); X return (SMPelement *)Element; } SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spSMP.c'; eval "$shar_touch") && chmod 0600 'sparse/spSMP.c' if test $? -ne 0 then ${echo} 'restore of sparse/spSMP.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spSMP.c: MD5 check failed' ) << \SHAR_EOF 9d1df87663d33281e556a06efe26afd5 sparse/spSMP.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spSMP.c'` -ne 7999 && \ ${echo} 'restoration warning: size of sparse/spSMP.c is not 7999' fi fi # ============= sparse/spSolve.c ============== if test -f 'sparse/spSolve.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spSolve.c (file already exists)' else ${echo} 'x - extracting sparse/spSolve.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spSolve.c' && /* X * MATRIX SOLVE MODULE X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*! \file X * This file contains the forward and backward substitution routines for X * the sparse matrix routines. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ /* >>> User accessible functions contained in this file: X * spSolve X * spSolveTransposed X * X * >>> Other functions contained in this file: X * SolveComplexMatrix X * SolveComplexTransposedMatrix X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 X * by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spSolve.c,v 1.3 2003/06/29 04:19:52 kundert Exp $"; #endif X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X X X X /* X * Function declarations X */ X #if spSEPARATED_COMPLEX_VECTORS static void SolveComplexMatrix( MatrixPtr, X RealVector, RealVector, RealVector, RealVector ); static void SolveComplexTransposedMatrix( MatrixPtr, X RealVector, RealVector, RealVector, RealVector ); #else static void SolveComplexMatrix( MatrixPtr, RealVector, RealVector ); static void SolveComplexTransposedMatrix( MatrixPtr, X RealVector, RealVector ); #endif X X X X X X X /*! X * Performs forward elimination and back substitution to find the X * unknown vector from the \a RHS vector and factored matrix. This X * routine assumes that the pivots are associated with the lower X * triangular matrix and that the diagonal of the upper triangular X * matrix consists of ones. This routine arranges the computation X * in different way than is traditionally used in order to exploit the X * sparsity of the right-hand side. See the reference in spRevision. X * X * \param eMatrix X * Pointer to matrix. X * \param RHS X * \a RHS is the input data array, the right hand side. This data is X * undisturbed and may be reused for other solves. X * \param Solution X * \a Solution is the output data array. This routine is constructed X * such that \a RHS and \a Solution can be the same array. X * \param iRHS X * \a iRHS is the imaginary portion of the input data array, the right X * hand side. This data is undisturbed and may be reused for other solves. X * This argument is only necessary if matrix is complex and if X * \a spSEPARATED_COMPLEX_VECTOR is set true. X * \param iSolution X * \a iSolution is the imaginary portion of the output data array. This X * routine is constructed such that \a iRHS and \a iSolution can be X * the same array. This argument is only necessary if matrix is complex X * and if \a spSEPARATED_COMPLEX_VECTOR is set true. X */ /* >>> Local variables: X * Intermediate (RealVector) X * Temporary storage for use in forward elimination and backward X * substitution. Commonly referred to as c, when the LU factorization X * equations are given as Ax = b, Lc = b, Ux = c Local version of X * Matrix->Intermediate, which was created during the initial X * factorization in function spcCreateInternalVectors() in the matrix X * factorization module. X * pElement (ElementPtr) X * Pointer used to address elements in both the lower and upper triangle X * matrices. X * pExtOrder (int *) X * Pointer used to sequentially access each entry in IntToExtRowMap X * and IntToExtColMap arrays. Used to quickly scramble and unscramble X * RHS and Solution to account for row and column interchanges. X * pPivot (ElementPtr) X * Pointer that points to current pivot or diagonal element. X * Size (int) X * Size of matrix. Made local to reduce indirection. X * Temp (RealNumber) X * Temporary storage for entries in arrays. X * X * >>> Obscure Macros X * IMAG_VECTORS X * Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and X * spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears X * without a trace. X */ X /*VARARGS3*/ X void spSolve( X spMatrix eMatrix, X spREAL RHS[], X spREAL Solution[] # if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] # endif ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register RealVector Intermediate; register RealNumber Temp; register int I, *pExtOrder, Size; ElementPtr pPivot; void SolveComplexMatrix(); X /* Begin `spSolve'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_FACTORED( Matrix ); X #if spCOMPLEX X if (Matrix->Complex) X { SolveComplexMatrix( Matrix, RHS, Solution IMAG_VECTORS ); X return; X } #endif X #if REAL X Intermediate = Matrix->Intermediate; X Size = Matrix->Size; X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET X --RHS; X --Solution; #endif X /* Initialize Intermediate vector. */ X pExtOrder = &Matrix->IntToExtRowMap[Size]; X for (I = Size; I > 0; I--) X Intermediate[I] = RHS[*(pExtOrder--)]; X /* Forward elimination. Solves Lc = b.*/ X for (I = 1; I <= Size; I++) X { /* This step of the elimination is skipped if Temp equals zero. */ X if ((Temp = Intermediate[I]) != 0.0) X { pPivot = Matrix->Diag[I]; X Intermediate[I] = (Temp *= pPivot->Real); X X pElement = pPivot->NextInCol; X while (pElement != NULL) X { Intermediate[pElement->Row] -= Temp * pElement->Real; X pElement = pElement->NextInCol; X } X } X } X /* Backward Substitution. Solves Ux = c.*/ X for (I = Size; I > 0; I--) X { Temp = Intermediate[I]; X pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { Temp -= pElement->Real * Intermediate[pElement->Col]; X pElement = pElement->NextInRow; X } X Intermediate[I] = Temp; X } X /* Unscramble Intermediate vector while placing data in to Solution vector. */ X pExtOrder = &Matrix->IntToExtColMap[Size]; X for (I = Size; I > 0; I--) X Solution[*(pExtOrder--)] = Intermediate[I]; X X return; #endif /* REAL */ } X X X X X X X X X X X #if spCOMPLEX /*! X * Performs forward elimination and back substitution to find the X * unknown vector from the RHS vector and factored matrix. This X * routine assumes that the pivots are associated with the lower X * triangular matrix and that the diagonal of the upper triangular X * matrix consists of ones. This routine arranges the computation X * in different way than is traditionally used in order to exploit the X * sparsity of the right-hand side. See the reference in spRevision. X * X * \param Matrix X * Pointer to matrix. X * \param RHS X * RHS is the real portion of the input data array, the right hand X * side. This data is undisturbed and may be reused for other solves. X * \param Solution X * Solution is the real portion of the output data array. This routine X * is constructed such that RHS and Solution can be the same X * array. X * \param iRHS X * iRHS is the imaginary portion of the input data array, the right X * hand side. This data is undisturbed and may be reused for other solves. X * If spSEPARATED_COMPLEX_VECTOR is set false, there is no need to X * supply this array. X * \param iSolution X * iSolution is the imaginary portion of the output data array. This X * routine is constructed such that iRHS and iSolution can be X * the same array. If spSEPARATED_COMPLEX_VECTOR is set false, there is no X * need to supply this array. X */ /* >>> Local variables: X * Intermediate (ComplexVector) X * Temporary storage for use in forward elimination and backward X * substitution. Commonly referred to as c, when the LU factorization X * equations are given as Ax = b, Lc = b, Ux = c. X * Local version of Matrix->Intermediate, which was created during X * the initial factorization in function spcCreateInternalVectors() in the X * matrix factorization module. X * pElement (ElementPtr) X * Pointer used to address elements in both the lower and upper triangle X * matrices. X * pExtOrder (int *) X * Pointer used to sequentially access each entry in IntToExtRowMap X * and IntToExtColMap arrays. Used to quickly scramble and unscramble X * RHS and Solution to account for row and column interchanges. X * pPivot (ElementPtr) X * Pointer that points to current pivot or diagonal element. X * Size (int) X * Size of matrix. Made local to reduce indirection. X * Temp (ComplexNumber) X * Temporary storage for entries in arrays. X */ X static void SolveComplexMatrix( X MatrixPtr Matrix, X RealVector RHS, X RealVector Solution # if spSEPARATED_COMPLEX_VECTORS X , RealVector iRHS X , RealVector iSolution # endif ) { register ElementPtr pElement; register ComplexVector Intermediate; register int I, *pExtOrder, Size; ElementPtr pPivot; register ComplexVector ExtVector; ComplexNumber Temp; X /* Begin `SolveComplexMatrix'. */ X X Size = Matrix->Size; X Intermediate = (ComplexVector)Matrix->Intermediate; X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET #if spSEPARATED_COMPLEX_VECTORS X --RHS; --iRHS; X --Solution; --iSolution; #else X RHS -= 2; Solution -= 2; #endif #endif X /* Initialize Intermediate vector. */ X pExtOrder = &Matrix->IntToExtRowMap[Size]; X #if spSEPARATED_COMPLEX_VECTORS X for (I = Size; I > 0; I--) X { Intermediate[I].Real = RHS[*(pExtOrder)]; X Intermediate[I].Imag = iRHS[*(pExtOrder--)]; X } #else X ExtVector = (ComplexVector)RHS; X for (I = Size; I > 0; I--) X Intermediate[I] = ExtVector[*(pExtOrder--)]; #endif X /* Forward substitution. Solves Lc = b.*/ X for (I = 1; I <= Size; I++) X { Temp = Intermediate[I]; X /* This step of the substitution is skipped if Temp equals zero. */ X if ((Temp.Real != 0.0) OR (Temp.Imag != 0.0)) X { pPivot = Matrix->Diag[I]; /* Cmplx expr: Temp *= (1.0 / Pivot). */ X CMPLX_MULT_ASSIGN(Temp, *pPivot); X Intermediate[I] = Temp; X pElement = pPivot->NextInCol; X while (pElement != NULL) X { /* Cmplx expr: Intermediate[Element->Row] -= Temp * *Element. */ X CMPLX_MULT_SUBT_ASSIGN(Intermediate[pElement->Row], X Temp, *pElement); X pElement = pElement->NextInCol; X } X } X } X /* Backward Substitution. Solves Ux = c.*/ X for (I = Size; I > 0; I--) X { Temp = Intermediate[I]; X pElement = Matrix->Diag[I]->NextInRow; X X while (pElement != NULL) X { /* Cmplx expr: Temp -= *Element * Intermediate[Element->Col]. */ X CMPLX_MULT_SUBT_ASSIGN(Temp, *pElement,Intermediate[pElement->Col]); X pElement = pElement->NextInRow; X } X Intermediate[I] = Temp; X } X /* Unscramble Intermediate vector while placing data in to Solution vector. */ X pExtOrder = &Matrix->IntToExtColMap[Size]; X #if spSEPARATED_COMPLEX_VECTORS X for (I = Size; I > 0; I--) X { Solution[*(pExtOrder)] = Intermediate[I].Real; X iSolution[*(pExtOrder--)] = Intermediate[I].Imag; X } #else X ExtVector = (ComplexVector)Solution; X for (I = Size; I > 0; I--) X ExtVector[*(pExtOrder--)] = Intermediate[I]; #endif X X return; } #endif /* spCOMPLEX */ X X X X X X X X X X X X X X #if TRANSPOSE /*! X * Performs forward elimination and back substitution to find the X * unknown vector from the RHS vector and transposed factored X * matrix. This routine is useful when performing sensitivity analysis X * on a circuit using the adjoint method. This routine assumes that X * the pivots are associated with the untransposed lower triangular X * matrix and that the diagonal of the untransposed upper X * triangular matrix consists of ones. X * X * \param eMatrix X * Pointer to matrix. X * \param RHS X * \a RHS is the input data array, the right hand side. This data is X * undisturbed and may be reused for other solves. X * \param Solution X * \a Solution is the output data array. This routine is constructed X * such that \a RHS and \a Solution can be the same array. X * \param iRHS X * \a iRHS is the imaginary portion of the input data array, the right X * hand side. This data is undisturbed and may be reused for other solves. X * If \a spSEPARATED_COMPLEX_VECTOR is set false, or if matrix is real, X * there is no need to supply this array. X * \param iSolution X * \a iSolution is the imaginary portion of the output data array. This X * routine is constructed such that \a iRHS and \a iSolution can be X * the same array. If \a spSEPARATED_COMPLEX_VECTOR is set false, or if X * matrix is real, there is no need to supply this array. X */ /* >>> Local variables: X * Intermediate (RealVector) X * Temporary storage for use in forward elimination and backward X * substitution. Commonly referred to as c, when the LU factorization X * equations are given as Ax = b, Lc = b, Ux = c. Local version of X * Matrix->Intermediate, which was created during the initial X * factorization in function spcCreateInternalVectors() in the matrix X * factorization module. X * pElement (ElementPtr) X * Pointer used to address elements in both the lower and upper triangle X * matrices. X * pExtOrder (int *) X * Pointer used to sequentially access each entry in IntToExtRowMap X * and IntToExtRowMap arrays. Used to quickly scramble and unscramble X * RHS and Solution to account for row and column interchanges. X * pPivot (ElementPtr) X * Pointer that points to current pivot or diagonal element. X * Size (int) X * Size of matrix. Made local to reduce indirection. X * Temp (RealNumber) X * Temporary storage for entries in arrays. X */ X /*VARARGS3*/ X void spSolveTransposed( X spMatrix eMatrix, X spREAL RHS[], X spREAL Solution[] # if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] # endif ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register RealVector Intermediate; register int I, *pExtOrder, Size; ElementPtr pPivot; RealNumber Temp; void SolveComplexTransposedMatrix(); X /* Begin `spSolveTransposed'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_FACTORED( Matrix ); X #if spCOMPLEX X if (Matrix->Complex) X { SolveComplexTransposedMatrix( Matrix, RHS, Solution IMAG_VECTORS ); X return; X } #endif X #if REAL X Size = Matrix->Size; X Intermediate = Matrix->Intermediate; X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET X --RHS; X --Solution; #endif X /* Initialize Intermediate vector. */ X pExtOrder = &Matrix->IntToExtColMap[Size]; X for (I = Size; I > 0; I--) X Intermediate[I] = RHS[*(pExtOrder--)]; X /* Forward elimination. */ X for (I = 1; I <= Size; I++) X { /* This step of the elimination is skipped if Temp equals zero. */ X if ((Temp = Intermediate[I]) != 0.0) X { pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { Intermediate[pElement->Col] -= Temp * pElement->Real; X pElement = pElement->NextInRow; X } X X } X } X /* Backward Substitution. */ X for (I = Size; I > 0; I--) X { pPivot = Matrix->Diag[I]; X Temp = Intermediate[I]; X pElement = pPivot->NextInCol; X while (pElement != NULL) X { Temp -= pElement->Real * Intermediate[pElement->Row]; X pElement = pElement->NextInCol; X } X Intermediate[I] = Temp * pPivot->Real; X } X /* Unscramble Intermediate vector while placing data in to Solution vector. */ X pExtOrder = &Matrix->IntToExtRowMap[Size]; X for (I = Size; I > 0; I--) X Solution[*(pExtOrder--)] = Intermediate[I]; X X return; #endif /* REAL */ } #endif /* TRANSPOSE */ X X X X X X X X X X #if TRANSPOSE AND spCOMPLEX /*! X * Performs forward elimination and back substitution to find the X * unknown vector from the RHS vector and transposed factored X * matrix. This routine is useful when performing sensitivity analysis X * on a circuit using the adjoint method. This routine assumes that X * the pivots are associated with the untransposed lower triangular X * matrix and that the diagonal of the untransposed upper X * triangular matrix consists of ones. X * X * \param Matrix X * Pointer to matrix. X * \param RHS X * \a RHS is the input data array, the right hand X * side. This data is undisturbed and may be reused for other solves. X * This vector is only the real portion if the matrix is complex and X * \a spSEPARATED_COMPLEX_VECTORS is set true. X * \param Solution X * \a Solution is the real portion of the output data array. This routine X * is constructed such that \a RHS and \a Solution can be the same array. X * This vector is only the real portion if the matrix is complex and X * \a spSEPARATED_COMPLEX_VECTORS is set true. X * \param iRHS X * \a iRHS is the imaginary portion of the input data array, the right X * hand side. This data is undisturbed and may be reused for other solves. X * If either \a spCOMPLEX or \a spSEPARATED_COMPLEX_VECTOR is set false, X * there is no need to supply this array. X * \param iSolution X * \a iSolution is the imaginary portion of the output data array. This X * routine is constructed such that \a iRHS and \a iSolution can be X * the same array. If \a spCOMPLEX or \a spSEPARATED_COMPLEX_VECTOR is set X * false, there is no need to supply this array. X */ /* >>> Local variables: X * Intermediate (ComplexVector) X * Temporary storage for use in forward elimination and backward X * substitution. Commonly referred to as c, when the LU factorization X * equations are given as Ax = b, Lc = b, Ux = c. Local version of X * Matrix->Intermediate, which was created during X * the initial factorization in function spcCreateInternalVectors() in the X * matrix factorization module. X * pElement (ElementPtr) X * Pointer used to address elements in both the lower and upper triangle X * matrices. X * pExtOrder (int *) X * Pointer used to sequentially access each entry in IntToExtRowMap X * and IntToExtColMap arrays. Used to quickly scramble and unscramble X * RHS and Solution to account for row and column interchanges. X * pPivot (ElementPtr) X * Pointer that points to current pivot or diagonal element. X * Size (int) X * Size of matrix. Made local to reduce indirection. X * Temp (ComplexNumber) X * Temporary storage for entries in arrays. X */ X static void SolveComplexTransposedMatrix( X MatrixPtr Matrix, X RealVector RHS, X RealVector Solution # if spSEPARATED_COMPLEX_VECTORS X , RealVector iRHS X , RealVector iSolution # endif ) { register ElementPtr pElement; register ComplexVector Intermediate; register int I, *pExtOrder, Size; register ComplexVector ExtVector; ElementPtr pPivot; ComplexNumber Temp; X /* Begin `SolveComplexTransposedMatrix'. */ X X Size = Matrix->Size; X Intermediate = (ComplexVector)Matrix->Intermediate; X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET #if spSEPARATED_COMPLEX_VECTORS X --RHS; --iRHS; X --Solution; --iSolution; #else X RHS -= 2; Solution -= 2; #endif #endif X /* Initialize Intermediate vector. */ X pExtOrder = &Matrix->IntToExtColMap[Size]; X #if spSEPARATED_COMPLEX_VECTORS X for (I = Size; I > 0; I--) X { Intermediate[I].Real = RHS[*(pExtOrder)]; X Intermediate[I].Imag = iRHS[*(pExtOrder--)]; X } #else X ExtVector = (ComplexVector)RHS; X for (I = Size; I > 0; I--) X Intermediate[I] = ExtVector[*(pExtOrder--)]; #endif X /* Forward elimination. */ X for (I = 1; I <= Size; I++) X { Temp = Intermediate[I]; X /* This step of the elimination is skipped if Temp equals zero. */ X if ((Temp.Real != 0.0) OR (Temp.Imag != 0.0)) X { pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { /* Cmplx expr: Intermediate[Element->Col] -= Temp * *Element. */ X CMPLX_MULT_SUBT_ASSIGN( Intermediate[pElement->Col], X Temp, *pElement); X pElement = pElement->NextInRow; X } X } X } X /* Backward Substitution. */ X for (I = Size; I > 0; I--) X { pPivot = Matrix->Diag[I]; X Temp = Intermediate[I]; X pElement = pPivot->NextInCol; X X while (pElement != NULL) X { /* Cmplx expr: Temp -= Intermediate[Element->Row] * *Element. */ X CMPLX_MULT_SUBT_ASSIGN(Temp,Intermediate[pElement->Row],*pElement); X X pElement = pElement->NextInCol; X } /* Cmplx expr: Intermediate = Temp * (1.0 / *pPivot). */ X CMPLX_MULT(Intermediate[I], Temp, *pPivot); X } X /* Unscramble Intermediate vector while placing data in to Solution vector. */ X pExtOrder = &Matrix->IntToExtRowMap[Size]; X #if spSEPARATED_COMPLEX_VECTORS X for (I = Size; I > 0; I--) X { Solution[*(pExtOrder)] = Intermediate[I].Real; X iSolution[*(pExtOrder--)] = Intermediate[I].Imag; X } #else X ExtVector = (ComplexVector)Solution; X for (I = Size; I > 0; I--) X ExtVector[*(pExtOrder--)] = Intermediate[I]; #endif X X return; } #endif /* TRANSPOSE AND spCOMPLEX */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spSolve.c'; eval "$shar_touch") && chmod 0600 'sparse/spSolve.c' if test $? -ne 0 then ${echo} 'restore of sparse/spSolve.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spSolve.c: MD5 check failed' ) << \SHAR_EOF 0fe08947f4394fb87be58ee22870b8fc sparse/spSolve.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spSolve.c'` -ne 22041 && \ ${echo} 'restoration warning: size of sparse/spSolve.c is not 22041' fi fi # ============= sparse/spSpice3.h ============== if test -f 'sparse/spSpice3.h' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spSpice3.h (file already exists)' else ${echo} 'x - extracting sparse/spSpice3.h (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spSpice3.h' && /* X * EXPORTS for sparse matrix routines with SPICE3. X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X * X * This file contains definitions that are useful to the calling X * program. In particular, this file contains error keyword X * definitions, some macro functions that are used to quickly enter X * data into the matrix and the type definition of a data structure X * that acts as a template for entering admittances into the matrix. X * Also included is the type definitions for the various functions X * available to the user. X * X * This file is a modified version of spMatrix.h that is used when X * interfacing to Spice3. X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X * X * $Date: 2003/06/29 04:19:52 $ X * $Revision: 1.2 $ X */ X X X X #ifndef spOKAY X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X */ X #include "spConfig.h" X X X X X X /* X * ERROR KEYWORDS X * X * The actual numbers used in the error codes are not sacred, they can be X * changed under the condition that the codes for the nonfatal errors are X * less than the code for spFATAL and similarly the codes for the fatal X * errors are greater than that for spFATAL. X * X * >>> Error descriptions: X * spOKAY X * No error has occurred. X * spSMALL_PIVOT X * When reordering the matrix, no element was found which satisfies the X * threshold criteria. The largest element in the matrix was chosen X * as pivot. Non-fatal. X * spZERO_DIAG X * Fatal error. A zero was encountered on the diagonal the matrix. This X * does not necessarily imply that the matrix is singular. When this X * error occurs, the matrix should be reconstructed and factored using X * spOrderAndFactor(). In spCOMPATIBILITY mode, spZERO_DIAG is X * indistinguishable from spSINGULAR. X * spSINGULAR X * Fatal error. Matrix is singular, so no unique solution exists. X * spNO_MEMORY X * Fatal error. Indicates that not enough memory is available to handle X * the matrix. X * spPANIC X * Fatal error indicating that the routines are not prepared to X * handle the matrix that has been requested. This may occur when X * the matrix is specified to be real and the routines are not X * compiled for real matrices, or when the matrix is specified to X * be complex and the routines are not compiled to handle complex X * matrices. X * spFATAL X * Not an error flag, but rather the dividing line between fatal errors X * and warnings. X */ X #include "../include/SPerror.h" /* Spice error definitions. */ X /* Begin error macros. */ #define spOKAY OK #define spSMALL_PIVOT OK #define spZERO_DIAG E_SINGULAR #define spSINGULAR E_SINGULAR #define spNO_MEMORY E_NOMEM #define spPANIC E_BADMATRIX X #define spFATAL E_BADMATRIX X X #if spCOMPATIBILITY #define NO_ERROR spOKAY #define UNDER_FLOW spOKAY #define OVER_FLOW spOKAY #define ILL_CONDITIONED spSMALL_PIVOT #define SINGULAR spSINGULAR #define NO_MEMORY spNO_MEMORY #define RANGE spPANIC X #define FATAL spFATAL X #undef spZERO_DIAG #define spZERO_DIAG spSINGULAR #endif /* spCOMPATIBILITY */ X X X X X /* X * KEYWORD DEFINITIONS X * X * Here we define what precision arithmetic Sparse will use. Double X * precision is suggested as being most appropriate for circuit X * simulation and for C. However, it is possible to change spREAL X * to a float for single precision arithmetic. Note that in C, single X * precision arithmetic is often slower than double precision. Sparse X * internally refers to spREALs as RealNumbers. X * X * Some C compilers, notably the old VMS compiler, do not handle the keyword X * "void" correctly. If this is true for your compiler, remove the X * comment delimiters from the redefinition of void to int below. X */ X #define spREAL double /* #define void int */ X #if spCOMPATIBILITY #define SPARSE_REAL spREAL #endif X X X /* X * PARTITION TYPES X * X * When factoring a previously ordered matrix using spFactor(), Sparse X * operates on a row-at-a-time basis. For speed, on each step, the row X * being updated is copied into a full vector and the operations are X * performed on that vector. This can be done one of two ways, either X * using direct addressing or indirect addressing. Direct addressing X * is fastest when the matrix is relatively dense and indirect addressing X * is quite sparse. The user can select which partitioning mode is used. X * The following keywords are passed to spPartition() and indicate that X * Sparse should use only direct addressing, only indirect addressing, or X * that it should choose the best mode on a row-by-row basis. The time X * required to choose a partition is of the same order of the cost to factor X * the matrix. X * X * If you plan to factor a large number of matrices with the same structure, X * it is best to let Sparse choose the partition. Otherwise, you should X * choose the partition based on the predicted density of the matrix. X */ X /* Begin partition keywords. */ X #define spDEFAULT_PARTITION 0 #define spDIRECT_PARTITION 1 #define spINDIRECT_PARTITION 2 #define spAUTO_PARTITION 3 X X X X X /* X * MACRO FUNCTION DEFINITIONS X * X * >>> Macro descriptions: X * spADD_REAL_ELEMENT X * Macro function that adds data to a real element in the matrix by a X * pointer. X * spADD_IMAG_ELEMENT X * Macro function that adds data to a imaginary element in the matrix by X * a pointer. X * spADD_COMPLEX_ELEMENT X * Macro function that adds data to a complex element in the matrix by a X * pointer. X * spADD_REAL_QUAD X * Macro function that adds data to each of the four real matrix elements X * specified by the given template. X * spADD_IMAG_QUAD X * Macro function that adds data to each of the four imaginary matrix X * elements specified by the given template. X * spADD_COMPLEX_QUAD X * Macro function that adds data to each of the four complex matrix X * elements specified by the given template. X */ X /* Begin Macros. */ #define spADD_REAL_ELEMENT(element,real) *(element) += real X #define spADD_IMAG_ELEMENT(element,imag) *(element+1) += imag X #define spADD_COMPLEX_ELEMENT(element,real,imag) \ { *(element) += real; \ X *(element+1) += imag; \ } X #define spADD_REAL_QUAD(template,real) \ { *((template).Element1) += real; \ X *((template).Element2) += real; \ X *((template).Element3Negated) -= real; \ X *((template).Element4Negated) -= real; \ } X #define spADD_IMAG_QUAD(template,imag) \ { *((template).Element1+1) += imag; \ X *((template).Element2+1) += imag; \ X *((template).Element3Negated+1) -= imag; \ X *((template).Element4Negated+1) -= imag; \ } X #define spADD_COMPLEX_QUAD(template,real,imag) \ { *((template).Element1) += real; \ X *((template).Element2) += real; \ X *((template).Element3Negated) -= real; \ X *((template).Element4Negated) -= real; \ X *((template).Element1+1) += imag; \ X *((template).Element2+1) += imag; \ X *((template).Element3Negated+1) -= imag; \ X *((template).Element4Negated+1) -= imag; \ } X #if spCOMPATIBILITY #define ADD_REAL_ELEMENT_TO_MATRIX spADD_REAL_ELEMENT #define ADD_IMAG_ELEMENT_TO_MATRIX spADD_IMAG_ELEMENT #define ADD_COMPLEX_ELEMENT_TO_MATRIX spADD_COMPLEX_ELEMENT #define ADD_REAL_QUAD_ELEMENT_TO_MATRIX spADD_REAL_ELEMENT #define ADD_IMAG_QUAD_ELEMENT_TO_MATRIX spADD_IMAG_ELEMENT #define ADD_COMPLEX_QUAD_ELEMENT_TO_MATRIX spADD_COMPLEX_ELEMENT #endif X X X X X X /* X * TYPE DEFINITION FOR COMPONENT TEMPLATE X * X * This data structure is used to hold pointers to four related elements in X * matrix. It is used in conjunction with the routines X * spGetAdmittance X * spGetQuad X * spGetOnes X * These routines stuff the structure which is later used by the spADD_QUAD X * macro functions above. It is also possible for the user to collect four X * pointers returned by spGetElement and stuff them into the template. X * The spADD_QUAD routines stuff data into the matrix in locations specified X * by Element1 and Element2 without changing the data. The data is negated X * before being placed in Element3 and Element4. X */ X #if spCOMPATIBILITY #define spTemplate TemplateStruct #endif X /* Begin `spTemplate'. */ struct spTemplate { spREAL *Element1 ; X spREAL *Element2 ; X spREAL *Element3Negated; X spREAL *Element4Negated; }; X X X X X /* X * FUNCTION TYPE DEFINITIONS X * X * The type of every user accessible function is declared here. X */ X /* Begin function declarations. */ X spcEXTERN void spClear( char* ); spcEXTERN spREAL spCondition( char*, spREAL, int* ); spcEXTERN char *spCreate( int, int, int* ); spcEXTERN void spDeleteRowAndCol( char*, int, int ); spcEXTERN void spDestroy( char* ); spcEXTERN int spElementCount( char* ); spcEXTERN int spError( char* ); spcEXTERN int spFactor( char* ); spcEXTERN int spFileMatrix( char*, char*, char*, int, int, int ); spcEXTERN int spFileStats( char*, char*, char* ); spcEXTERN int spFillinCount( char* ); spcEXTERN int spGetAdmittance( char*, int, int, X struct spTemplate* ); spcEXTERN spREAL *spGetElement( char*, int, int ); spcEXTERN char *spGetInitInfo( spREAL* ); spcEXTERN int spGetOnes( char*, int, int, int, X struct spTemplate* ); spcEXTERN int spGetQuad( char*, int, int, int, int, X struct spTemplate* ); spcEXTERN int spGetSize( char*, int ); spcEXTERN int spInitialize( char*, int (*)() ); spcEXTERN void spInstallInitInfo( spREAL*, char* ); spcEXTERN spREAL spLargestElement( char* ); spcEXTERN void spMNA_Preorder( char* ); spcEXTERN spREAL spNorm( char* ); spcEXTERN int spOrderAndFactor( char*, spREAL[], spREAL, X spREAL, int ); spcEXTERN void spPartition( char*, int ); spcEXTERN void spPrint( char*, int, int, int ); spcEXTERN spREAL spPseudoCondition( char* ); spcEXTERN spREAL spRoundoff( char*, spREAL ); spcEXTERN void spScale( char*, spREAL[], spREAL[] ); spcEXTERN void spSetComplex( char* ); spcEXTERN void spSetReal( char* ); spcEXTERN void spStripFills( char* ); spcEXTERN void spWhereSingular( char*, int*, int* ); X /* Functions with argument lists that are dependent on options. */ X #if spCOMPLEX spcEXTERN void spDeterminant( char*, int*, spREAL*, spREAL* ); #else /* NOT spCOMPLEX */ spcEXTERN void spDeterminant( char*, int*, spREAL* ); #endif /* NOT spCOMPLEX */ #if spCOMPLEX && spSEPARATED_COMPLEX_VECTORS spcEXTERN int spFileVector( char*, char* , spREAL[], spREAL[]); spcEXTERN void spMultiply( char*, spREAL[], spREAL[], spREAL[], X spREAL[] ); spcEXTERN void spMultTransposed( char*, spREAL[], spREAL[], X spREAL[], spREAL[] ); spcEXTERN void spSolve( char*, spREAL[], spREAL[], spREAL[], X spREAL[] ); spcEXTERN void spSolveTransposed( char*, spREAL[], spREAL[], X spREAL[], spREAL[] ); #else /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */ spcEXTERN int spFileVector( char*, char* , spREAL[] ); spcEXTERN void spMultiply( char*, spREAL[], spREAL[] ); spcEXTERN void spMultTransposed( char*, spREAL[], spREAL[] ); spcEXTERN void spSolve( char*, spREAL[], spREAL[] ); spcEXTERN void spSolveTransposed( char*, spREAL[], spREAL[] ); #endif /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */ #endif /* spOKAY */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spSpice3.h'; eval "$shar_touch") && chmod 0600 'sparse/spSpice3.h' if test $? -ne 0 then ${echo} 'restore of sparse/spSpice3.h failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spSpice3.h: MD5 check failed' ) << \SHAR_EOF 797a57330d34a47fc2934b567695c2c9 sparse/spSpice3.h SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spSpice3.h'` -ne 12073 && \ ${echo} 'restoration warning: size of sparse/spSpice3.h is not 12073' fi fi # ============= sparse/spTest.c ============== if test -f 'sparse/spTest.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spTest.c (file already exists)' else ${echo} 'x - extracting sparse/spTest.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spTest.c' && /* X * TEST MODULE for the sparse matrix routines X * X * Author: Advisor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X * X * This file contains the test routine for the sparse matrix routines. X * They are able to read matrices from files and solve them. X * X * >>> Functions contained in this file: X * main X * ReadMatrixFromFile X * Init X * CheckOutComplexArray X * CheckInAllComplexArrays X * PrintMatrixErrorMessage X * InterpretCommandLine X * GetProgramName X * Usage X * EnlargeVectors X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spTest.c,v 1.5 2003/06/30 19:40:51 kundert Exp $"; #endif X X X /* X * IMPORTS X * X * >>> Import descriptions: X * stdio.h math.h ctype.h X * Standard C libraries. X * spConfig.h X * Macros that customize the sparse matrix package. It is not normally X * necessary, nor is normally particularly desirable to include this X * file into the calling routines. Nor should spINSIDE_SPARSE be defined. X * It is done in this test file so that the complex test routines may be X * removed when they don't exist in Sparse. X * spMatrix.h X * Macros and declarations to be imported by the user. X */ X #define YES 1 #define NO 0 X #include #include #include #include #include #include #define spINSIDE_SPARSE #include "spConfig.h" #undef spINSIDE_SPARSE #include "spMatrix.h" X X /* Declarations that should be in strings.h. extern int strcmp(), strncmp(), strlen();*/ X #ifdef lint #undef MULTIPLICATION #undef DETERMINANT X #define MULTIPLICATION YES #define DETERMINANT YES X #define LINT YES #else /* not lint */ #define LINT NO #endif /* not lint */ X X X X X X X /* X * TIMING X */ X X #ifndef vms # include #endif #ifndef HZ # ifdef __STDC__ # include # include # define HZ CLK_TCK # else # ifdef mips # include # define HZ CLK_TCK # endif # endif #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 # ifdef hpux # ifdef hp300 # define HZ 50 # endif # ifdef hp500 # define HZ 60 # endif # endif #endif X /* Routine that queries the system to find the process time. */ double Time() { X struct time {long user, system, childuser, childsystem;} time; X (void)times(&time); X return (double)time.user / (double)HZ; } X X X X X /* X * MACROS X */ X #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)) X X X X X X /* X * IMAGINARY VECTORS X * X * The imaginary vectors iRHS and iSolution are only needed when the X * options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set. The following X * macro makes it easy to include or exclude these vectors as needed. X */ X #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS #define IMAG_VECTORS , iRHS, iSolution #else #define IMAG_VECTORS #endif X X X X X X X /* X * GLOBAL DECLARATIONS X * X * These variables, types, and macros are used throughout this file. X * X * >>> Macros X * PRINT_LIMIT X * The maximum number of terms to be printed form the solution vector. X * May be overridden by the -n option. X * X * >>> Variable types: X * ElementRecord X * A structure for holding data for each matrix element. X * X * >>> Global variables: X * ProgramName (char *) X * The name of the test program with any path stripped off. X * FileName (char *) X * The name of the current file name. X * Complex (BOOLEAN) X * The flag that indicates whether the matrix is complex or real. X * Element (ElementRecord[]) X * Array used to hold information about all the elements. X * Matrix (char *) X * The pointer to the matrix. X * Size (int) X * The size of the matrix. X * RHS (RealVector) X * The right-hand side vector (b in Ax = b). X * Solution (RealVector) X * The solution vector (x in Ax = b). X * RHS_Verif (RealVector) X * The calculated RHS vector. X */ X /* Begin global declarations. */ #define PRINT_LIMIT 9 X typedef spREAL RealNumber, *RealVector; X typedef struct { RealNumber Real; X RealNumber Imag; } ComplexNumber, *ComplexVector; X 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; X X X X X X /* X * Function declarations X */ X static int ReadMatrixFromFile(); static int Init(); static ComplexVector CheckOutComplexArray(); static void CheckInAllComplexArrays(); static void PrintMatrixErrorMessage(); static int InterpretCommandLine(); static char *GetProgramName(); static void Usage(); static void EnlargeVectors(); X X X X X X X /* X * MAIN TEST ROUTINE for sparse matrix routines X * X * This routine reads a matrix from a file and solves it several X * times. The solution is printed along with some statistics. X * The format expected for the input matrix is the same as what is output by X * spFileMatrix and spFileVector. X * X * >>> Local variables: X * Determinant (RealNumber) X * Real portion of the determinant of the matrix. X * iDeterminant (RealNumber) X * Imaginary portion of the determinant of the matrix. X * Error (int) X * Holds error status. X * Last (int) X * The index of the last term to be printed of the solution. X * Iterations (int) X * The number of times that the matrix will be factored and solved. X * MaxRHS (RealNumber) X * The largest term in the given RHS vector. X * Residual (RealNumber) X * The sum of the magnitude of the differences in each corresponding X * term of the given and calculated RHS vector. X * Exponent (int) X * Exponent for the determinant. X * Growth (RealNumber) X * The growth that has occurred in the matrix during the factorization. X * X * >>> Timing variables: X * BuildTime (double) X * The time required to build up the matrix including the time to clear X * the matrix. X * ConditionTime (double) X * The time required to compute the condition number. X * DeterminantTime (double) X * The time required to compute the determinant. X * FactorTime (double) X * The time required to factor the matrix without ordering. X * InitialFactorTime (double) X * The time required to factor the matrix with ordering. X * SolveTime (double) X * The time required to perform the forward and backward elimination. X * StartTime (double) X * The time that a timing interval was started. X * X * >>> Global variables: X * Complex X * Matrix X * Size X * RHS X * iRHS X */ X X int main( 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(); X /* Begin `main'. */ X X BeginTime = Time(); X ArgCount = InterpretCommandLine( ArgCount, Args ); X /* Assure that the Sparse is compatible with this test program.*/ # if NOT EXPANDABLE OR NOT INITIALIZE OR NOT ARRAY_OFFSET X fprintf(stderr, X "%s: Sparse is configured inappropriately for test program.\n", X ProgramName); X fprintf(stderr, X " Enable EXPANDABLE, INITIALIZE, and ARRAY_OFFSET in `spConfig.h'.\n"); X exit(1); # endif X /* Print copyright notice. */ X printf("Sparse1.4\nCopyright (c) 2003, Kenneth S. Kundert.\nAll rights reserved.\n\n"); X X do X { /* Initialization. */ X BuildTime = FactorTime = SolveTime = 0.0; X ExpansionWarningGiven = NO; X /* Create matrix. */ X Matrix = spCreate( 0, spCOMPLEX, &Error ); X if (Matrix == NULL) X { fprintf(stderr, "%s: insufficient memory available.\n", X ProgramName); X exit(1); X } X if( Error >= spFATAL ) goto End; X /* Read matrix. */ X if (ArgCount == 0) X FileName = NULL; X else X FileName = *(++Args); X Error = ReadMatrixFromFile(); X if( Error) goto End; X StandardInput = (FileName == NULL); X /* Clear solution vector if row and column numbers are not densely packed. */ X if (spGetSize(Matrix, YES) != spGetSize(Matrix, NO)) X { if (Complex OR RealAsComplex) X { for (I = Size; I > 0; I--) X cSolution[I].Real = cSolution[I].Imag = 0.0; X } X else X { for (I = Size; I > 0; I--) X Solution[I] = 0.0; X } X } X /* Perform initial build, factor, and solve. */ X (void)spInitialize(Matrix, Init); X #if MODIFIED_NODAL X spMNA_Preorder( Matrix ); #endif #if DOCUMENTATION X if (CreatePlotFiles) X { if (StandardInput) X (void)sprintf(PlotFile, "bef"); X else X (void)sprintf(PlotFile, "%s.bef", FileName); X if (NOT spFileMatrix( Matrix, PlotFile, FileName, NO, NO, NO )) X { (void)sprintf(ErrMsg,"%s: plotfile `%s'",ProgramName,PlotFile); X perror( ErrMsg ); X } X } #if NO X spPrint( Matrix, NO /*reodered*/, NO /*data*/, NO /*header*/ ); #endif #endif /* DOCUMENTATION */ #if STABILITY X if (NOT SolutionOnly) LargestBefore = spLargestElement(Matrix); #endif #if CONDITION X if (NOT SolutionOnly) InfNorm = spNorm(Matrix); #endif X X StartTime = Time(); X Error = spOrderAndFactor( Matrix, RHS, RelThreshold, AbsThreshold, X DiagPivoting ); X InitialFactorTime = Time() - StartTime; X if (Error != spOKAY) spErrorMessage( Matrix, stderr, ProgramName ); X if( Error >= spFATAL ) X goto End; X #if DOCUMENTATION X if (CreatePlotFiles) X { if (StandardInput) X (void)sprintf(PlotFile, "aft"); X else X (void)sprintf(PlotFile, "%s.aft", FileName); X if (NOT spFileMatrix( Matrix, PlotFile, FileName, YES, NO, NO )) X { (void)sprintf(ErrMsg,"%s: plotfile `%s'",ProgramName,PlotFile); X perror( ErrMsg ); X } X } #if NO X spFileStats( Matrix, FileName, "stats" ); #endif #endif /* DOCUMENTATION */ X /* X * IMAG_VECTORS is a macro that replaces itself with `, iRHS, iSolution' X * if the options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set, X * otherwise it disappears without a trace. X */ #if TRANSPOSE X if (Transposed) X spSolveTransposed( Matrix, RHS, Solution IMAG_VECTORS ); X else #endif X spSolve( Matrix, RHS, Solution IMAG_VECTORS ); X X if (SolutionOnly) X Iterations = 0; X else X { #if STABILITY X LargestAfter = spLargestElement(Matrix); X Roundoff = spRoundoff(Matrix, LargestAfter); #endif #if CONDITION X StartTime = Time(); X ConditionNumber = spCondition(Matrix, InfNorm, &Error); X ConditionTime = Time() - StartTime; X spErrorMessage( Matrix, stderr, ProgramName ); #endif #if PSEUDOCONDITION X PseudoCondition = spPseudoCondition(Matrix); #endif X X StartTime = Time(); X spPartition( Matrix, spDEFAULT_PARTITION ); X PartitionTime = Time() - StartTime; X } X /* Solve system of equations Iterations times. */ X for(I = 1; I <= Iterations; I++) X { StartTime = Time(); X (void)spInitialize(Matrix, Init); X BuildTime += Time() - StartTime; X X StartTime = Time(); X Error = spFactor( Matrix ); X FactorTime += Time() - StartTime; X if (Error != spOKAY) spErrorMessage( Matrix, stderr, ProgramName ); X if (Error >= spFATAL) goto End; X X StartTime = Time(); /* X * IMAG_VECTORS is a macro that replaces itself with `, iRHS, iSolution' X * if the options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set, X * otherwise it disappears without a trace. X */ #if TRANSPOSE X if (Transposed) X spSolveTransposed(Matrix, RHS, Solution IMAG_VECTORS ); X else #endif X spSolve( Matrix, RHS, Solution IMAG_VECTORS ); X SolveTime += Time() - StartTime; X } X /* Print Solution. */ X if (SolutionOnly) X { if (PrintLimitSet) X Last = MIN( PrintLimit, Size ); X else X Last = Size; X j = ' '; X } X else X { Last = (PrintLimit > Size) ? Size : PrintLimit; X if (Last > 0) printf("Solution:\n"); X j = 'j'; X } X X if (Complex OR RealAsComplex) X { #if spSEPARATED_COMPLEX_VECTORS X for (I = 1; I <= Last; I++) X { printf("%-16.9g %-.9g%c\n", (double)Solution[I], X (double)iSolution[I], j); X } #else X for (I = 1; I <= Last; I++) X { printf("%-16.9g %-.9g%c\n", (double)cSolution[I].Real, X (double)cSolution[I].Imag, j); X } #endif X } X else X { for (I = 1; I <= Last; I++) X printf("%-.9g\n", (double)Solution[I]); X } X if (Last < Size AND Last != 0) X printf("Solution list truncated.\n"); X printf("\n"); X #if DETERMINANT /* Calculate determinant. */ X if (NOT SolutionOnly) X { StartTime = Time(); #if spCOMPLEX X spDeterminant( Matrix, &Exponent, &Determinant, &iDeterminant ); #else X spDeterminant( Matrix, &Exponent, &Determinant ); #endif X DeterminantTime = Time() - StartTime; X if (Complex OR RealAsComplex) X { Determinant = hypot( Determinant, iDeterminant ); X while (Determinant >= 10.0) X { Determinant *= 0.1; X Exponent++; X } X } X } #else X Determinant = 0.0; Exponent = 0; #endif X #if MULTIPLICATION X if (NOT SolutionOnly) X { /* Calculate difference between actual RHS vector and RHS vector X * calculated from solution. */ X /* Find the largest element in the given RHS vector. */ X MaxRHS = 0.0; X X if (Complex OR RealAsComplex) X { #if spSEPARATED_COMPLEX_VECTORS X for (I = 1; I <= Size; I++) X { if (ABS(RHS[I]) > MaxRHS) X MaxRHS = ABS(RHS[I]); X if (ABS(iRHS[I]) > MaxRHS) X MaxRHS = ABS(iRHS[I]); X } #else X for (I = 1; I <= Size; I++) X { if (ABS(cRHS[I].Real) > MaxRHS) X MaxRHS = ABS(cRHS[I].Real); X if (ABS(cRHS[I].Imag) > MaxRHS) X MaxRHS = ABS(cRHS[I].Imag); X } #endif X } X else X { for (I = 1; I <= Size; I++) X { if (ABS(RHS[I]) > MaxRHS) X MaxRHS = ABS(RHS[I]); X } X } X /* Rebuild matrix. */ X (void)spInitialize(Matrix, Init); #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X if (Transposed) X { spMultTransposed( Matrix, RHS_Verif, Solution, X iRHS_Verif, iSolution); X } X else spMultiply(Matrix, RHS_Verif, Solution, iRHS_Verif, iSolution); #else X if (Transposed) X spMultTransposed( Matrix, RHS_Verif, Solution ); X else X spMultiply( Matrix, RHS_Verif, Solution ); #endif X /* Calculate residual. */ X Residual = 0.0; X if (Complex OR RealAsComplex) X { #if spSEPARATED_COMPLEX_VECTORS X for (I = 1; I <= Size; I++) X { Residual += ABS(RHS[I] - RHS_Verif[I]) X + ABS(iRHS[I] - iRHS_Verif[I]); X } #else X for (I = 1; I <= Size; I++) X { Residual += ABS(cRHS[I].Real - cRHS_Verif[I].Real) X + ABS(cRHS[I].Imag - cRHS_Verif[I].Imag); X } #endif X } X else X { for (I = 1; I <= Size; I++) X Residual += ABS(RHS[I] - RHS_Verif[I]); X } X } #endif X /* Print statistics. */ X if (NOT SolutionOnly) X { Elements = spElementCount(Matrix); X Fillins = spFillinCount(Matrix); X X printf("Initial factor time = %.2f.\n", InitialFactorTime); X printf("Partition time = %.2f.\n", PartitionTime); X if (Iterations > 0) X { printf("Build time = %.3f.\n", (BuildTime/Iterations)); X printf("Factor time = %.3f.\n",(FactorTime/Iterations)); X printf("Solve time = %.3f.\n", (SolveTime/Iterations)); X } #if STABILITY X printf("Condition time = %.2f.\n", ConditionTime); #endif #if DETERMINANT X printf("Determinant time = %.2f.\n", DeterminantTime); #endif X printf("\nTotal number of elements = %d.\n", Elements); X printf("Average number of elements per row initially = %.2f.\n", X (double)(Elements - Fillins) / X (double)spGetSize(Matrix, NO)); X printf("Total number of fill-ins = %d.\n", Fillins); #if DETERMINANT OR MULTIPLICATION OR PSEUDOCONDITION OR CONDITION OR STABILITY X putchar('\n'); #endif #if STABILITY X if (LargestBefore != 0.0) X printf("Growth = %.2g.\n", LargestAfter / LargestBefore); X printf("Max error in matrix = %.2g.\n", Roundoff); #endif #if STABILITY X if(ABS(ConditionNumber) > 10 * SMALLEST_REAL); X printf("Condition number = %.2g.\n", 1.0 / ConditionNumber); #endif #if CONDITION AND STABILITY X printf("Estimated upper bound of error in solution = %.2g.\n", X Roundoff / ConditionNumber); #endif #if PSEUDOCONDITION X printf("PseudoCondition = %.2g.\n", PseudoCondition); #endif #if DETERMINANT X printf("Determinant = %.3g", (double)Determinant ); X if (Determinant != 0.0 AND Exponent != 0) X printf("e%d", Exponent); X putchar('.'); putchar('\n'); #endif #if MULTIPLICATION X if (MaxRHS != 0.0) X printf("Normalized residual = %.2g.\n", (Residual / MaxRHS)); #endif X } X End:; X (void)fflush(stdout); X CheckInAllComplexArrays(); X spDestroy(Matrix); X Matrix = NULL; X } while( --ArgCount > 0 ); X X if (NOT SolutionOnly) X { printf("\nAggregate resource usage:\n"); X printf(" Time required = %.2f seconds.\n", Time() - BeginTime); X printf(" Virtual memory used = %d kBytes.\n\n", ((int)sbrk(0))/1000); X } X exit (0); } X X X X X X X X X X X /* X * READ MATRIX FROM FILE X * X * This function reads the input file for the matrix and the RHS vector. X * If no RHS vector exists, one is created. If there is an error in the X * file, the appropriate error messages are delivered to standard output. X * X * >>> Returned: X * The error status is returned. If no error occurred, a zero is returned. X * Otherwise, a one is returned. X * X * >>> Local variables: X * pMatrixFile (FILE *) X * The pointer to the file that holds the matrix. X * InputString (char []) X * String variable for holding input from the matrix file. X * Message (char []) X * String variable that contains a one line descriptor of the matrix. X * Descriptor is taken from matrix file. X * X * >>> Global variables: X * Complex X * Size X * Element X * RHS X * iRHS X */ X 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"; X /* Begin `ReadMatrixFromFile'. */ X /* Open matrix file in read mode. */ X if (NOT SolutionOnly) putchar('\n'); X X if (FileName == NULL) X { FileName = "standard input"; X pMatrixFile = stdin; X } X else X { pMatrixFile = fopen(FileName, "r"); X if (pMatrixFile == NULL) X { fprintf(stderr, "%s: file %s was not found.\n", X ProgramName, FileName); X return 1; X } X } X X Complex = NO; X LineNumber = 1; X /* Read and print label. */ X if (NULL == fgets( Message, BUFSIZ, pMatrixFile )) X { fprintf(stderr, EndOfFile, ProgramName, FileName, LineNumber); X return 1; X } X /* For compatibility with the old file syntax. */ X if (!strncmp( Message, "Starting", 8 )) X { /* Test for complex matrix. */ X if (strncmp( Message, "Starting complex", 15 ) == 0) X Complex = YES; X LineNumber++; X if (NULL == fgets( Message, BUFSIZ, pMatrixFile )) X { fprintf(stderr, EndOfFile, ProgramName, FileName, LineNumber); X return 1; X } X } X if (NOT SolutionOnly) printf("%-s\n", Message); X /* Read size of matrix and determine type of matrix. */ X LineNumber++; X if (NULL == fgets( sInput, BUFSIZ, pMatrixFile )) X { fprintf(stderr, EndOfFile, ProgramName, FileName, LineNumber); X return 1; X } X if ((Reads = sscanf( sInput,"%d %s", &Size, sType )) < 1) X { fprintf(stderr, Syntax, ProgramName, FileName, LineNumber); X return 1; X } X if (Reads == 2) X { for (p = sType; *p != '\0'; p++) X if (isupper(*p)) *p += 'a'-'A'; X if (strncmp( sType, "complex", 7 ) == 0) X Complex = YES; X else if (strncmp( sType, "real", 7 ) == 0) X Complex = NO; X else X { fprintf(stderr, Syntax, ProgramName, FileName, LineNumber); X return 1; X } X } X EnlargeVectors( Size, YES ); X RHS_Col = MIN( Size, ColumnAsRHS ); X #if NOT spCOMPLEX X if (Complex) X { fprintf(stderr, X "%s: Sparse is not configured to solve complex matrices.\n", X ProgramName); X fprintf(stderr," Enable spCOMPLEX in `spConfig.h'.\n"); X return 1; X } #endif #if NOT REAL X if (NOT (Complex OR RealAsComplex)) X { fprintf(stderr, X "%s: Sparse is not configured to solve real matrices.\n", X ProgramName); X fprintf(stderr," Enable REAL in `spConfig.h'.\n"); X return 1; X } #endif X /* Read matrix elements. */ X do X { if (Count == 0) X pValue = CheckOutComplexArray( Count = 1000 ); X X LineNumber++; X if (NULL == fgets( sInput, BUFSIZ, pMatrixFile )) X { fprintf(stderr, "%s: unexpected end of file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X X if (Complex) X { Reads = sscanf( sInput,"%d%d%lf%lf", &Row, &Col, &Real, &Imag ); X if (Reads != 4) X { fprintf(stderr, "%s: syntax error in file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X } X else X { Reads = sscanf( sInput,"%d%d%lf", &Row, &Col, &Real ); X if (Reads != 3) X { fprintf(stderr, "%s: syntax error in file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X } X if(Row < 0 OR Col < 0) X { fprintf(stderr, "%s: index not positive in file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X if(Row > Size OR Col > Size) X { if (NOT ExpansionWarningGiven) X { fprintf( stderr, X "%s: computed and given matrix size differ in file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X ExpansionWarningGiven = YES; X } X Size = MAX(Row, Col); X EnlargeVectors( Size, NO ); X } X pElement = spGetElement( Matrix, Row, Col ); X if (pElement == NULL) X { fprintf(stderr, "%s: insufficient memory available.\n", X ProgramName); X exit(1); X } X pInitInfo = (ComplexNumber *)spGetInitInfo( pElement ); X if (pInitInfo == NULL) X { pValue[--Count].Real = Real; X pValue[Count].Imag = Imag; X spInstallInitInfo( pElement, (char *)(pValue + Count) ); X } X else X { pInitInfo->Real += Real; X pInitInfo->Imag += Imag; X } X /* Save into RHS vector if in desired column. */ X if (Col == RHS_Col) X { if (Complex OR RealAsComplex) X { #if spSEPARATED_COMPLEX_VECTORS X RHS[Row] = Real; X iRHS[Row] = Imag; #else X cRHS[Row].Real = Real; X cRHS[Row].Imag = Imag; #endif X } X else RHS[Row] = Real; X } X } while (Row != 0 AND Col != 0); X X Size = spGetSize( Matrix, YES ); X if (Error = spErrorState( Matrix ) != spOKAY) X { spErrorMessage( Matrix, stderr, ProgramName ); X if( Error >= spFATAL ) return 1; X } X /* Read RHS vector. */ X if (NOT UseColumnAsRHS AND (NULL != fgets( sInput, BUFSIZ, pMatrixFile ))) X { /* RHS vector exists, read it. */ X LineNumber++; X for (I = 1; I <= Size; I++) X { if (I != 1 OR (strncmp( sInput, "Beginning", 8 ) == 0)) X { LineNumber++; X if (NULL == fgets( sInput, BUFSIZ, pMatrixFile )) X { fprintf(stderr, X "%s: unexpected end of file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X } X X if (Complex) X { #if spSEPARATED_COMPLEX_VECTORS X Reads = sscanf( sInput,"%lf%lf", &RHS[I], &iRHS[I] ); #else X Reads = sscanf( sInput, "%lf%lf", &cRHS[I].Real, X &cRHS[I].Imag ); #endif X if (Reads != 2) X { fprintf(stderr, X "%s: syntax error in file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X } X else /* Not complex. */ X { Reads = sscanf( sInput, "%lf", &RHS[I] ); X if (Reads != 1) X { fprintf(stderr, X "%s: syntax error in file `%s' at line %d.\n", X ProgramName, FileName, LineNumber); X return 1; X } X } X } X X if (RealAsComplex AND NOT Complex) X { #if spSEPARATED_COMPLEX_VECTORS X for (I = 1; I <= Size; I++) iRHS[I] = 0.0; #else X for (I = Size; I > 0; I--) X { cRHS[I].Real = RHS[I]; X cRHS[I].Imag = 0.0; X } #endif X } X } X /* Print out the size and the type of the matrix. */ X if (NOT SolutionOnly) X { IntSize = spGetSize( Matrix, NO ); X printf("Matrix is %d x %d ", IntSize, IntSize); X if (IntSize != Size) X printf("(external size is %d x %d) ", Size, Size); X if (Complex OR RealAsComplex) X printf("and complex.\n"); X else X printf("and real.\n"); X } X X X if (Complex OR RealAsComplex) X spSetComplex( Matrix ); X else X spSetReal( Matrix ); X X (void)fclose(pMatrixFile); X return 0; } X X X X X X X X X X /* X * INITIALIZE MATRIX ELEMENT X * X * This function copys the InitInfo to the Real and Imag matrix element X * values. X * X * >>> Returned: X * A zero is returns, signifying that no error can be made. X */ X /*ARGSUSED*/ X static int Init( RealNumber *pElement, char *pInitInfo, int Row, int Col ) { /* Begin `Init'. */ X *pElement = *(RealNumber *)pInitInfo; X if (Complex OR RealAsComplex) X *(pElement+1) = *((RealNumber *)pInitInfo+1); X return 0; } X X X X X X X X /* X * COMPLEX ARRAY ALLOCATION X * X * These routines are used to check out and in arrays of complex numbers. X */ X static struct Bin { X ComplexVector Array; X struct Bin *Next; } *FirstArray, *CurrentArray = NULL; X X static ComplexVector CheckOutComplexArray( int Count ) { struct Bin Bin, *pBin; ComplexVector Temp; X /* Begin `CheckOutComplexArray'. */ X X if (CurrentArray == NULL OR CurrentArray->Next == NULL) X { pBin = ALLOC( struct Bin, 1); X Temp = ALLOC( ComplexNumber, Count ); X if (pBin == NULL OR Temp == NULL) X { fprintf( stderr, "%s: insufficient memory available.\n", X ProgramName); X exit(1); X } X Bin.Array = Temp; X Bin.Next = NULL; X *pBin = Bin; X if (CurrentArray == NULL) X FirstArray = CurrentArray = pBin; X else X { CurrentArray->Next = pBin; X CurrentArray = pBin; X } X } X else X { pBin = CurrentArray; X CurrentArray = pBin->Next; X } X X return pBin->Array; } X X static void CheckInAllComplexArrays() { X /* Begin `CheckInAllComplexArrays'. */ X if (CurrentArray != NULL) X CurrentArray = FirstArray; X return; } X X X X X /* X * INTERPRET THE COMMAND LINE ARGUMENTS X */ X static int InterpretCommandLine( int ArgCount, char *Args[] ) { int I, FileCount = 0; char *GetProgramName(); X /* Begin `InterpretCommandLine'. */ X /* Determine the name of the program. */ X ProgramName = GetProgramName(Args[0]); X /* Step through the argument list, interpreting and deleting the options. */ X for (I = 1; I < ArgCount; I++) X { if (!strcmp(Args[I], "-a")) X { if (ArgCount == I OR (sscanf(Args[I+1],"%lf", &AbsThreshold) != 1)) X { AbsThreshold = 0.0; X Usage(Args[I]); X } X else I++; X } X else if (!strcmp(Args[I], "-r")) X { if (ArgCount == I OR (sscanf(Args[I+1],"%lf", &RelThreshold) != 1)) X { RelThreshold = 0.0; X Usage(Args[I]); X } X else I++; X } X else if (!strcmp(Args[I], "-x")) X { #if spCOMPLEX X RealAsComplex = YES; #else X fprintf(stderr, X "%s: Sparse is not configured to solve complex matrices.\n", X ProgramName); X fprintf(stderr," Enable spCOMPLEX in `spConfig.h'.\n"); #endif X } X else if (!strcmp(Args[I], "-s")) X SolutionOnly = YES; X else if (!strcmp(Args[I], "-c")) X DiagPivoting = NO; X else if (!strcmp(Args[I], "-t")) X { #if TRANSPOSE X Transposed = YES; #else X fprintf(stderr, X "%s: Sparse is not configured to solve transposed system.\n", X ProgramName); X fprintf(stderr," Enable TRANSPOSE in `spConfig.h'.\n"); #endif X } X else if (!strcmp(Args[I], "-n")) X { if (ArgCount == I OR (sscanf(Args[I+1],"%d", &PrintLimit) != 1)) X { PrintLimit = PRINT_LIMIT; X Usage(Args[I]); X } X else X { PrintLimitSet = YES; X I++; X } X } X else if (!strcmp(Args[I], "-i")) X { if (ArgCount == I OR (sscanf(Args[I+1],"%d", &Iterations) != 1)) X { Iterations = 1; X Usage(Args[I]); X } X else I++; X } X else if (!strcmp(Args[I], "-b")) X { if (ArgCount == I OR (sscanf(Args[I+1],"%d", &ColumnAsRHS) != 1)) X { ColumnAsRHS = 1; X UseColumnAsRHS = YES; X } X else X { UseColumnAsRHS = YES; X ColumnAsRHS = MAX( ColumnAsRHS, 1 ); X I++; X } X } X else if (!strcmp(Args[I], "-p")) X { #if DOCUMENTATION X CreatePlotFiles = YES; #else X fprintf(stderr, X "%s: Sparse is not configured to generate plot files.\n", X ProgramName); X fprintf(stderr," Enable DOCUMENTATION in `spConfig.h'.\n"); #endif X } X else if (!strcmp(Args[I], "-u")) X Usage( (char *)NULL ); X else if (Args[I][0] == '-') X Usage(Args[I]); X else Args[++FileCount] = Args[I]; X } X X return FileCount; } X X X X X X X /* X * PROGRAM NAME X * X * Removes path from argv[0] and returns program name. X * Assumes UNIX style path names. X */ X static char * GetProgramName( char *Arg0 ) { char *pTail; /* Begin `GetProgramName'. */ X # ifdef vms X return "sparse"; # else X { pTail = Arg0 + strlen(Arg0)-1; X while (pTail != Arg0 AND *(pTail-1) != '/') X --pTail; X return pTail; X } # endif } X X X X X /* X * USAGE WARNING X * X * Sends a warning to the user when the command line syntax is wrong. X */ X static void Usage( char *sBadOpt ) { /* Begin `Usage'. */ X X if (sBadOpt != NULL) X { fprintf(stderr, "%s: unknown or deformed option `%s'.\n", X ProgramName, sBadOpt); X } X X fprintf(stderr, "\nUsage: %s [options] [file names]\n\n", ProgramName); X fprintf(stderr, "Options:\n"); X fprintf(stderr, X " -s Print solution rather than run statistics.\n"); X fprintf( stderr, " -r x Use x as relative threshold.\n"); X fprintf( stderr, " -a x Use x as absolute threshold.\n"); X fprintf( stderr, " -n n Print first n terms of solution vector.\n"); X fprintf( stderr, " -i n Repeat build/factor/solve n times.\n"); X fprintf( stderr, " -b n Use n'th column of matrix as b in Ax=b.\n"); #if DIAGONAL_PIVOTING X fprintf( stderr, X " -c Use complete (as opposed to diagonal) pivoting.\n"); #endif #if DOCUMENTATION X fprintf( stderr, X " -p Create plot files `filename.bef' and `filename.aft'.\n"); #endif #if spCOMPLEX X fprintf( stderr, " -x Treat real matrix as complex with imaginary part zero.\n"); #endif #if TRANSPOSE X fprintf( stderr, " -t Solve transposed system.\n"); #endif X fprintf( stderr, " -u Print usage message.\n"); X exit(1); } X X X X X X /* X * ENLARGE VECTORS X * X * Allocate or enlarge vectors. X */ X static void EnlargeVectors( int NewSize, int Clear ) { int I, PrevSize = MaxSize; RealVector OldRHS = RHS, iOldRHS = iRHS; ComplexVector cOldRHS = cRHS; #if spCOMPLEX # define SCALE 2 #else # define SCALE 1 #endif X /* Begin `EnlargeVectors'. */ X if (NewSize > PrevSize) X { if (MaxSize != 0) X { free( (char *)Solution ); X free( (char *)RHS_Verif ); X } X RHS = ALLOC( RealNumber, SCALE*(NewSize+1) ); X Solution = ALLOC( RealNumber, SCALE*(NewSize+1) ); X RHS_Verif = ALLOC( RealNumber, SCALE*(NewSize+1) ); X if (NOT RHS OR NOT Solution OR NOT RHS_Verif) X { fprintf(stderr, "%s: insufficient memory available.\n", X ProgramName); X exit(1); X } X cRHS = (ComplexVector)RHS; X cSolution = (ComplexVector)Solution; X cRHS_Verif = (ComplexVector)RHS_Verif; X iRHS = RHS + NewSize + 1; X iSolution = Solution + NewSize + 1; X iRHS_Verif = RHS_Verif + NewSize + 1; X /* Copy data from old RHS to new RHS. */ X if (NOT Clear) X { /* Copy old RHS vector to new. */ X if (Complex OR RealAsComplex) X { for (I = PrevSize; I > 0; I--) X { #if spSEPARATED_COMPLEX_VECTORS OR LINT X RHS[I] = OldRHS[I]; X iRHS[I] = iOldRHS[I]; #endif #if (NOT spSEPARATED_COMPLEX_VECTORS) OR LINT X cRHS[I] = cOldRHS[I]; #endif X } X } X else X { for (I = PrevSize; I > 0; I--) X RHS[I] = OldRHS[I]; X } X } X if (MaxSize != 0) free( (char *)OldRHS ); X MaxSize = NewSize; X } X /* Either completely clear or clear remaining portion of RHS vector. */ X if ((NewSize > PrevSize) OR Clear) X { if (Clear) X { NewSize = MAX( NewSize, PrevSize ); X PrevSize = 0; X } X X if (Complex OR RealAsComplex) X { for (I = NewSize; I > PrevSize; I--) X { #if spSEPARATED_COMPLEX_VECTORS OR LINT X RHS[I] = 0.0; X iRHS[I] = 0.0; #endif #if NOT spSEPARATED_COMPLEX_VECTORS OR LINT X cRHS[I].Real = 0.0; X cRHS[I].Imag = 0.0; #endif X } X } X else X { for (I = NewSize; I > PrevSize; I--) X RHS[I] = 0.0; X } X } X X return; } X SHAR_EOF (set 20 16 10 12 10 44 44 'sparse/spTest.c'; eval "$shar_touch") && chmod 0600 'sparse/spTest.c' if test $? -ne 0 then ${echo} 'restore of sparse/spTest.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spTest.c: MD5 check failed' ) << \SHAR_EOF 2daa948475f9e5da22b3953a8c6ce606 sparse/spTest.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spTest.c'` -ne 38455 && \ ${echo} 'restoration warning: size of sparse/spTest.c is not 38455' fi fi # ============= sparse/spUtils.c ============== if test -f 'sparse/spUtils.c' && test "$first_param" != -c; then ${echo} 'x -SKIPPING sparse/spUtils.c (file already exists)' else ${echo} 'x - extracting sparse/spUtils.c (text)' sed 's/^X//' << 'SHAR_EOF' > 'sparse/spUtils.c' && /* X * MATRIX UTILITY MODULE X * X * Author: Advising professor: X * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli X * UC Berkeley X */ /*! \file X * This file contains various optional utility routines. X * X * Objects that begin with the \a spc prefix are considered private X * and should not be used. X * X * \author X * Kenneth S. Kundert X */ /* >>> User accessible functions contained in this file: X * spMNA_Preorder X * spScale X * spMultiply X * spMultTransposed X * spDeterminant X * spStrip X * spDeleteRowAndCol X * spPseudoCondition X * spCondition X * spNorm X * spLargestElement X * spRoundoff X * spErrorMessage X * X * >>> Other functions contained in this file: X * CountTwins X * SwapCols X * ScaleComplexMatrix X * ComplexMatrixMultiply X * ComplexCondition X */ X X /* X * Revision and copyright information. X * X * Copyright (c) 1985-2003 by Kenneth S. Kundert X */ X #ifndef lint static char copyright[] = X "Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert"; static char RCSid[] = X "@(#)$Header: /cvsroot/sparse/src/spUtils.c,v 1.4 2003/06/30 19:40:52 kundert Exp $"; #endif X X X /* X * IMPORTS X * X * >>> Import descriptions: X * spConfig.h X * Macros that customize the sparse matrix routines. X * spMatrix.h X * Macros and declarations to be imported by the user. X * spDefs.h X * Matrix type and macro definitions for the sparse matrix routines. X */ X #define spINSIDE_SPARSE #include #include "spConfig.h" #include "spMatrix.h" #include "spDefs.h" X X X X X /* X * Function declarations X */ X static int CountTwins( MatrixPtr, int, ElementPtr*, ElementPtr* ); static void SwapCols( MatrixPtr, ElementPtr, ElementPtr ); static void ScaleComplexMatrix( MatrixPtr, RealVector, RealVector ); #if spSEPARATED_COMPLEX_VECTORS static void ComplexMatrixMultiply( MatrixPtr, X RealVector, RealVector, RealVector, RealVector ); static void ComplexTransposedMatrixMultiply( MatrixPtr, X RealVector, RealVector, RealVector, RealVector ); #else static void ComplexMatrixMultiply( MatrixPtr, X RealVector, RealVector ); static void ComplexTransposedMatrixMultiply( MatrixPtr, X RealVector, RealVector ); #endif static RealNumber ComplexCondition( MatrixPtr, RealNumber, int* ); X X X X X X #if MODIFIED_NODAL /*! X * This routine massages modified node admittance matrices to remove X * zeros from the diagonal. It takes advantage of the fact that the X * row and column associated with a zero diagonal usually have X * structural ones placed symmetricly. This routine should be used X * only on modified node admittance matrices and should be executed X * after the matrix has been built but before the factorization X * begins. It should be executed for the initial factorization only X * and should be executed before the rows have been linked. Thus it X * should be run before using spScale(), spMultiply(), X * spDeleteRowAndCol(), or spNorm(). X * X * This routine exploits the fact that the structural ones are placed X * in the matrix in symmetric twins. For example, the stamps for X * grounded and a floating voltage sources are \code X * grounded: floating: X * [ x x 1 ] [ x x 1 ] X * [ x x ] [ x x -1 ] X * [ 1 ] [ 1 -1 ] X * \endcode X * Notice for the grounded source, there is one set of twins, and for X * the floating, there are two sets. We remove the zero from the diagonal X * by swapping the rows associated with a set of twins. For example: \code X * grounded: floating 1: floating 2: X * [ 1 ] [ 1 -1 ] [ x x 1 ] X * [ x x ] [ x x -1 ] [ 1 -1 ] X * [ x x 1 ] [ x x 1 ] [ x x -1 ] X * \endcode X * X * It is important to deal with any zero diagonals that only have one X * set of twins before dealing with those that have more than one because X * swapping row destroys the symmetry of any twins in the rows being X * swapped, which may limit future moves. Consider \code X * [ x x 1 ] X * [ x x -1 1 ] X * [ 1 -1 ] X * [ 1 ] X * \endcode X * There is one set of twins for diagonal 4 and two for diagonal 3. X * Dealing with diagonal 4 first requires swapping rows 2 and 4. \code X * [ x x 1 ] X * [ 1 ] X * [ 1 -1 ] X * [ x x -1 1 ] X * \endcode X * We can now deal with diagonal 3 by swapping rows 1 and 3. \code X * [ 1 -1 ] X * [ 1 ] X * [ x x 1 ] X * [ x x -1 1 ] X * \endcode X * And we are done, there are no zeros left on the diagonal. However, if X * we originally dealt with diagonal 3 first, we could swap rows 2 and 3 \code X * [ x x 1 ] X * [ 1 -1 ] X * [ x x -1 1 ] X * [ 1 ] X * \endcode X * Diagonal 4 no longer has a symmetric twin and we cannot continue. X * X * So we always take care of lone twins first. When none remain, we X * choose arbitrarily a set of twins for a diagonal with more than one set X * and swap the rows corresponding to that twin. We then deal with any X * lone twins that were created and repeat the procedure until no X * zero diagonals with symmetric twins remain. X * X * In this particular implementation, columns are swapped rather than rows. X * The algorithm used in this function was developed by Ken Kundert and X * Tom Quarles. X * X * \param * eMatrix X * Pointer to the matrix to be preordered. X */ /* >>> Local variables; X * J (int) X * Column with zero diagonal being currently considered. X * pTwin1 (ElementPtr) X * Pointer to the twin found in the column belonging to the zero diagonal. X * pTwin2 (ElementPtr) X * Pointer to the twin found in the row belonging to the zero diagonal. X * belonging to the zero diagonal. X * AnotherPassNeeded (BOOLEAN) X * Flag indicating that at least one zero diagonal with symmetric twins X * remain. X * StartAt (int) X * Column number of first zero diagonal with symmetric twins. X * Swapped (BOOLEAN) X * Flag indicating that columns were swapped on this pass. X * Twins (int) X * Number of symmetric twins corresponding to current zero diagonal. X */ X void spMNA_Preorder( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int J, Size; ElementPtr pTwin1, pTwin2; int Twins, StartAt = 1; BOOLEAN Swapped, AnotherPassNeeded; X /* Begin `spMNA_Preorder'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X X if (Matrix->RowsLinked) return; X Size = Matrix->Size; X Matrix->Reordered = YES; X X do X { AnotherPassNeeded = Swapped = NO; X /* Search for zero diagonals with lone twins. */ X for (J = StartAt; J <= Size; J++) X { if (Matrix->Diag[J] == NULL) X { Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 ); X if (Twins == 1) X { /* Lone twins found, swap rows. */ X SwapCols( Matrix, pTwin1, pTwin2 ); X Swapped = YES; X } X else if ((Twins > 1) AND NOT AnotherPassNeeded) X { AnotherPassNeeded = YES; X StartAt = J; X } X } X } X /* All lone twins are gone, look for zero diagonals with multiple twins. */ X if (AnotherPassNeeded) X { for (J = StartAt; NOT Swapped AND (J <= Size); J++) X { if (Matrix->Diag[J] == NULL) X { Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 ); X SwapCols( Matrix, pTwin1, pTwin2 ); X Swapped = YES; X } X } X } X } while (AnotherPassNeeded); X return; } X X X X /* X * COUNT TWINS X * X * This function counts the number of symmetric twins associated with X * a zero diagonal and returns one set of twins if any exist. The X * count is terminated early at two. X */ X static int CountTwins( X MatrixPtr Matrix, X int Col, X ElementPtr *ppTwin1, X ElementPtr *ppTwin2 ) { int Row, Twins = 0; ElementPtr pTwin1, pTwin2; X /* Begin `CountTwins'. */ X X pTwin1 = Matrix->FirstInCol[Col]; X while (pTwin1 != NULL) X { if (ABS(pTwin1->Real) == 1.0) X { Row = pTwin1->Row; X pTwin2 = Matrix->FirstInCol[Row]; X while ((pTwin2 != NULL) AND (pTwin2->Row != Col)) X pTwin2 = pTwin2->NextInCol; X if ((pTwin2 != NULL) AND (ABS(pTwin2->Real) == 1.0)) X { /* Found symmetric twins. */ X if (++Twins >= 2) return Twins; X (*ppTwin1 = pTwin1)->Col = Col; X (*ppTwin2 = pTwin2)->Col = Row; X } X } X pTwin1 = pTwin1->NextInCol; X } X return Twins; } X X X X /* X * SWAP COLUMNS X * X * This function swaps two columns and is applicable before the rows are X * linked. X */ X static void SwapCols( X MatrixPtr Matrix, X ElementPtr pTwin1, X ElementPtr pTwin2 ) { int Col1 = pTwin1->Col, Col2 = pTwin2->Col; X /* Begin `SwapCols'. */ X X SWAP (ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]); X SWAP (int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]); #if TRANSLATE X Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col2]]=Col2; X Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col1]]=Col1; #endif X X Matrix->Diag[Col1] = pTwin2; X Matrix->Diag[Col2] = pTwin1; X Matrix->NumberOfInterchangesIsOdd = NOT Matrix->NumberOfInterchangesIsOdd; X return; } #endif /* MODIFIED_NODAL */ X X X X X X X X X #if SCALING /*! X * This function scales the matrix to enhance the possibility of X * finding a good pivoting order. Note that scaling enhances accuracy X * of the solution only if it affects the pivoting order, so it makes X * no sense to scale the matrix before spFactor(). If scaling is X * desired it should be done before spOrderAndFactor(). There X * are several things to take into account when choosing the scale X * factors. First, the scale factors are directly multiplied against X * the elements in the matrix. To prevent roundoff, each scale factor X * should be equal to an integer power of the number base of the X * machine. Since most machines operate in base two, scale factors X * should be a power of two. Second, the matrix should be scaled such X * that the matrix of element uncertainties is equilibrated. Third, X * this function multiplies the scale factors by the elements, so if X * one row tends to have uncertainties 1000 times smaller than the X * other rows, then its scale factor should be 1024, not 1/1024. X * Fourth, to save time, this function does not scale rows or columns X * if their scale factors are equal to one. Thus, the scale factors X * should be normalized to the most common scale factor. Rows and X * columns should be normalized separately. For example, if the size X * of the matrix is 100 and 10 rows tend to have uncertainties near X * 1e-6 and the remaining 90 have uncertainties near 1e-12, then the X * scale factor for the 10 should be 1/1,048,576 and the scale factors X * for the remaining 90 should be 1. Fifth, since this routine X * directly operates on the matrix, it is necessary to apply the scale X * factors to the RHS and Solution vectors. It may be easier to X * simply use spOrderAndFactor() on a scaled matrix to choose the X * pivoting order, and then throw away the matrix. Subsequent X * factorizations, performed with spFactor(), will not need to have X * the RHS and Solution vectors descaled. Lastly, this function X * should not be executed before the function spMNA_Preorder(). X * X * \param eMatrix X * Pointer to the matrix to be scaled. X * \param SolutionScaleFactors X * The array of Solution scale factors. These factors scale the columns. X * All scale factors are real valued. X * \param RHS_ScaleFactors X * The array of RHS scale factors. These factors scale the rows. X * All scale factors are real valued. X */ /* >>> Local variables: X * lSize (int) X * Local version of the size of the matrix. X * pElement (ElementPtr) X * Pointer to an element in the matrix. X * pExtOrder (int *) X * Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to X * compensate for any row or column swaps that have been performed. X * ScaleFactor (RealNumber) X * The scale factor being used on the current row or column. X */ X void spScale( X spMatrix eMatrix, X spREAL RHS_ScaleFactors[], X spREAL SolutionScaleFactors[] ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register int I, lSize, *pExtOrder; RealNumber ScaleFactor; void ScaleComplexMatrix(); X /* Begin `spScale'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X if (NOT Matrix->RowsLinked) spcLinkRows( Matrix ); X #if spCOMPLEX X if (Matrix->Complex) X { ScaleComplexMatrix( Matrix, RHS_ScaleFactors, SolutionScaleFactors ); X return; X } #endif X #if REAL X lSize = Matrix->Size; X /* Correct pointers to arrays for ARRAY_OFFSET */ #if NOT ARRAY_OFFSET X --RHS_ScaleFactors; X --SolutionScaleFactors; #endif X /* Scale Rows */ X pExtOrder = &Matrix->IntToExtRowMap[1]; X for (I = 1; I <= lSize; I++) X { if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0) X { pElement = Matrix->FirstInRow[I]; X X while (pElement != NULL) X { pElement->Real *= ScaleFactor; X pElement = pElement->NextInRow; X } X } X } X /* Scale Columns */ X pExtOrder = &Matrix->IntToExtColMap[1]; X for (I = 1; I <= lSize; I++) X { if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0) X { pElement = Matrix->FirstInCol[I]; X X while (pElement != NULL) X { pElement->Real *= ScaleFactor; X pElement = pElement->NextInCol; X } X } X } X return; X #endif /* REAL */ } #endif /* SCALING */ X X X X X X X X X #if spCOMPLEX AND SCALING /* X * SCALE COMPLEX MATRIX X * X * This function scales the matrix to enhance the possibility of X * finding a good pivoting order. Note that scaling enhances accuracy X * of the solution only if it affects the pivoting order, so it makes X * no sense to scale the matrix before spFactor(). If scaling is X * desired it should be done before spOrderAndFactor(). There X * are several things to take into account when choosing the scale X * factors. First, the scale factors are directly multiplied against X * the elements in the matrix. To prevent roundoff, each scale factor X * should be equal to an integer power of the number base of the X * machine. Since most machines operate in base two, scale factors X * should be a power of two. Second, the matrix should be scaled such X * that the matrix of element uncertainties is equilibrated. Third, X * this function multiplies the scale factors by the elements, so if X * one row tends to have uncertainties 1000 times smaller than the X * other rows, then its scale factor should be 1024, not 1/1024. X * Fourth, to save time, this function does not scale rows or columns X * if their scale factors are equal to one. Thus, the scale factors X * should be normalized to the most common scale factor. Rows and X * columns should be normalized separately. For example, if the size X * of the matrix is 100 and 10 rows tend to have uncertainties near X * 1e-6 and the remaining 90 have uncertainties near 1e-12, then the X * scale factor for the 10 should be 1/1,048,576 and the scale factors X * for the remaining 90 should be 1. Fifth, since this routine X * directly operates on the matrix, it is necessary to apply the scale X * factors to the RHS and Solution vectors. It may be easier to X * simply use spOrderAndFactor() on a scaled matrix to choose the X * pivoting order, and then throw away the matrix. Subsequent X * factorizations, performed with spFactor(), will not need to have X * the RHS and Solution vectors descaled. Lastly, this function X * should not be executed before the function spMNA_Preorder. X * X * >>> Arguments: X * Matrix (char *) X * Pointer to the matrix to be scaled. X * SolutionScaleFactors (RealVector) X * The array of Solution scale factors. These factors scale the columns. X * All scale factors are real valued. X * RHS_ScaleFactors (RealVector) X * The array of RHS scale factors. These factors scale the rows. X * All scale factors are real valued. X * X * >>> Local variables: X * lSize (int) X * Local version of the size of the matrix. X * pElement (ElementPtr) X * Pointer to an element in the matrix. X * pExtOrder (int *) X * Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to X * compensate for any row or column swaps that have been performed. X * ScaleFactor (RealNumber) X * The scale factor being used on the current row or column. X */ X static void ScaleComplexMatrix( X MatrixPtr Matrix, X register RealVector RHS_ScaleFactors, X register RealVector SolutionScaleFactors ) { register ElementPtr pElement; register int I, lSize, *pExtOrder; RealNumber ScaleFactor; X /* Begin `ScaleComplexMatrix'. */ X lSize = Matrix->Size; X /* Correct pointers to arrays for ARRAY_OFFSET */ #if NOT ARRAY_OFFSET X --RHS_ScaleFactors; X --SolutionScaleFactors; #endif X /* Scale Rows */ X pExtOrder = &Matrix->IntToExtRowMap[1]; X for (I = 1; I <= lSize; I++) X { if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0) X { pElement = Matrix->FirstInRow[I]; X X while (pElement != NULL) X { pElement->Real *= ScaleFactor; X pElement->Imag *= ScaleFactor; X pElement = pElement->NextInRow; X } X } X } X /* Scale Columns */ X pExtOrder = &Matrix->IntToExtColMap[1]; X for (I = 1; I <= lSize; I++) X { if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0) X { pElement = Matrix->FirstInCol[I]; X X while (pElement != NULL) X { pElement->Real *= ScaleFactor; X pElement->Imag *= ScaleFactor; X pElement = pElement->NextInCol; X } X } X } X return; } #endif /* SCALING AND spCOMPLEX */ X X X X X X X X #if MULTIPLICATION /*! X * Multiplies matrix by solution vector to find source vector. X * Assumes matrix has not been factored. This routine can be used X * as a test to see if solutions are correct. It should not be used X * before spMNA_Preorder(). X * X * \param eMatrix X * Pointer to the matrix. X * \param RHS X * RHS is the right hand side. This is what is being solved for. X * \param Solution X * Solution is the vector being multiplied by the matrix. X * \param iRHS X * iRHS is the imaginary portion of the right hand side. This is X * what is being solved for. This is only necessary if the matrix is X * complex and \a spSEPARATED_COMPLEX_VECTORS is true. X * \param iSolution X * iSolution is the imaginary portion of the vector being multiplied X * by the matrix. This is only necessary if the matrix is X * complex and \a spSEPARATED_COMPLEX_VECTORS is true. X */ X void spMultiply( X spMatrix eMatrix, X spREAL RHS[], X spREAL Solution[] #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] #endif ) { register ElementPtr pElement; register RealVector Vector; register RealNumber Sum; register int I, *pExtOrder; MatrixPtr Matrix = (MatrixPtr)eMatrix; extern void ComplexMatrixMultiply(); X /* Begin `spMultiply'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X if (NOT Matrix->RowsLinked) X spcLinkRows(Matrix); X if (NOT Matrix->InternalVectorsAllocated) X spcCreateInternalVectors( Matrix ); X #if spCOMPLEX X if (Matrix->Complex) X { ComplexMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS ); X return; X } #endif X #if REAL #if NOT ARRAY_OFFSET /* Correct array pointers for ARRAY_OFFSET. */ X --RHS; X --Solution; #endif X /* Initialize Intermediate vector with reordered Solution vector. */ X Vector = Matrix->Intermediate; X pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; X for (I = Matrix->Size; I > 0; I--) X Vector[I] = Solution[*(pExtOrder--)]; X X pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; X for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInRow[I]; X Sum = 0.0; X X while (pElement != NULL) X { Sum += pElement->Real * Vector[pElement->Col]; X pElement = pElement->NextInRow; X } X RHS[*pExtOrder--] = Sum; X } X return; #endif /* REAL */ } #endif /* MULTIPLICATION */ X X X X X X X #if spCOMPLEX AND MULTIPLICATION /* X * COMPLEX MATRIX MULTIPLICATION X * X * Multiplies matrix by solution vector to find source vector. X * Assumes matrix has not been factored. This routine can be used X * as a test to see if solutions are correct. X * X * >>> Arguments: X * Matrix (char *) X * Pointer to the matrix. X * RHS (RealVector) X * RHS is the right hand side. This is what is being solved for. X * This is only the real portion of the right-hand side if the matrix X * is complex and spSEPARATED_COMPLEX_VECTORS is set true. X * Solution (RealVector) X * Solution is the vector being multiplied by the matrix. This is only X * the real portion if the matrix is complex and X * spSEPARATED_COMPLEX_VECTORS is set true. X * iRHS (RealVector) X * iRHS is the imaginary portion of the right hand side. This is X * what is being solved for. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X * iSolution (RealVector) X * iSolution is the imaginary portion of the vector being multiplied X * by the matrix. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X */ X static void ComplexMatrixMultiply( X MatrixPtr Matrix, X RealVector RHS, X RealVector Solution #if spSEPARATED_COMPLEX_VECTORS X , RealVector iRHS X , RealVector iSolution #endif ) { register ElementPtr pElement; register ComplexVector Vector; ComplexNumber Sum; register int I, *pExtOrder; X /* Begin `ComplexMatrixMultiply'. */ X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET #if spSEPARATED_COMPLEX_VECTORS X --RHS; --iRHS; X --Solution; --iSolution; #else X RHS -= 2; Solution -= 2; #endif #endif X /* Initialize Intermediate vector with reordered Solution vector. */ X Vector = (ComplexVector)Matrix->Intermediate; X pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; X #if spSEPARATED_COMPLEX_VECTORS X for (I = Matrix->Size; I > 0; I--) X { Vector[I].Real = Solution[*pExtOrder]; X Vector[I].Imag = iSolution[*(pExtOrder--)]; X } #else X for (I = Matrix->Size; I > 0; I--) X Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)]; #endif X X pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; X for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInRow[I]; X Sum.Real = Sum.Imag = 0.0; X X while (pElement != NULL) X { /* Cmplx expression : Sum += Element * Vector[Col] */ X CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Col] ); X pElement = pElement->NextInRow; X } X #if spSEPARATED_COMPLEX_VECTORS X RHS[*pExtOrder] = Sum.Real; X iRHS[*pExtOrder--] = Sum.Imag; #else X ((ComplexVector)RHS)[*pExtOrder--] = Sum; #endif X } X return; } #endif /* spCOMPLEX AND MULTIPLICATION */ X X X X X X X X #if MULTIPLICATION AND TRANSPOSE /*! X * Multiplies transposed matrix by solution vector to find source vector. X * Assumes matrix has not been factored. This routine can be used X * as a test to see if solutions are correct. It should not be used X * before spMNA_Preorder(). X * X * \param eMatrix X * Pointer to the matrix. X * \param RHS X * RHS is the right hand side. This is what is being solved for. X * \param Solution X * Solution is the vector being multiplied by the matrix. X * \param iRHS X * iRHS is the imaginary portion of the right hand side. This is X * what is being solved for. This is only necessary if the matrix is X * complex and \a spSEPARATED_COMPLEX_VECTORS is true. X * \param iSolution X * iSolution is the imaginary portion of the vector being multiplied X * by the matrix. This is only necessary if the matrix is X * complex and \a spSEPARATED_COMPLEX_VECTORS is true. X */ X void spMultTransposed( X spMatrix eMatrix, X spREAL RHS[], X spREAL Solution[] #if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS X , spREAL iRHS[] X , spREAL iSolution[] #endif ) { register ElementPtr pElement; register RealVector Vector; register RealNumber Sum; register int I, *pExtOrder; MatrixPtr Matrix = (MatrixPtr)eMatrix; extern void ComplexTransposedMatrixMultiply(); X /* Begin `spMultTransposed'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X if (NOT Matrix->InternalVectorsAllocated) X spcCreateInternalVectors( Matrix ); X #if spCOMPLEX X if (Matrix->Complex) X { ComplexTransposedMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS ); X return; X } #endif X #if REAL #if NOT ARRAY_OFFSET /* Correct array pointers for ARRAY_OFFSET. */ X --RHS; X --Solution; #endif X /* Initialize Intermediate vector with reordered Solution vector. */ X Vector = Matrix->Intermediate; X pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; X for (I = Matrix->Size; I > 0; I--) X Vector[I] = Solution[*(pExtOrder--)]; X X pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; X for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInCol[I]; X Sum = 0.0; X X while (pElement != NULL) X { Sum += pElement->Real * Vector[pElement->Row]; X pElement = pElement->NextInCol; X } X RHS[*pExtOrder--] = Sum; X } X return; #endif /* REAL */ } #endif /* MULTIPLICATION AND TRANSPOSE */ X X X X X X X #if spCOMPLEX AND MULTIPLICATION AND TRANSPOSE /* X * COMPLEX TRANSPOSED MATRIX MULTIPLICATION X * X * Multiplies transposed matrix by solution vector to find source vector. X * Assumes matrix has not been factored. This routine can be used X * as a test to see if solutions are correct. X * X * >>> Arguments: X * Matrix (char *) X * Pointer to the matrix. X * RHS (RealVector) X * RHS is the right hand side. This is what is being solved for. X * This is only the real portion of the right-hand side if the matrix X * is complex and spSEPARATED_COMPLEX_VECTORS is set true. X * Solution (RealVector) X * Solution is the vector being multiplied by the matrix. This is only X * the real portion if the matrix is complex and X * spSEPARATED_COMPLEX_VECTORS is set true. X * iRHS (RealVector) X * iRHS is the imaginary portion of the right hand side. This is X * what is being solved for. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X * iSolution (RealVector) X * iSolution is the imaginary portion of the vector being multiplied X * by the matrix. This is only necessary if the matrix is X * complex and spSEPARATED_COMPLEX_VECTORS is true. X * X * >>> Obscure Macros X * IMAG_VECTORS X * Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and X * spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears X * without a trace. X */ X static void ComplexTransposedMatrixMultiply( X MatrixPtr Matrix, X RealVector RHS, X RealVector Solution #if spSEPARATED_COMPLEX_VECTORS X , RealVector iRHS X , RealVector iSolution #endif ) { register ElementPtr pElement; register ComplexVector Vector; ComplexNumber Sum; register int I, *pExtOrder; X /* Begin `ComplexTransposedMatrixMultiply'. */ X /* Correct array pointers for ARRAY_OFFSET. */ #if NOT ARRAY_OFFSET #if spSEPARATED_COMPLEX_VECTORS X --RHS; --iRHS; X --Solution; --iSolution; #else X RHS -= 2; Solution -= 2; #endif #endif X /* Initialize Intermediate vector with reordered Solution vector. */ X Vector = (ComplexVector)Matrix->Intermediate; X pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; X #if spSEPARATED_COMPLEX_VECTORS X for (I = Matrix->Size; I > 0; I--) X { Vector[I].Real = Solution[*pExtOrder]; X Vector[I].Imag = iSolution[*(pExtOrder--)]; X } #else X for (I = Matrix->Size; I > 0; I--) X Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)]; #endif X X pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; X for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInCol[I]; X Sum.Real = Sum.Imag = 0.0; X X while (pElement != NULL) X { /* Cmplx expression : Sum += Element * Vector[Row] */ X CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Row] ); X pElement = pElement->NextInCol; X } X #if spSEPARATED_COMPLEX_VECTORS X RHS[*pExtOrder] = Sum.Real; X iRHS[*pExtOrder--] = Sum.Imag; #else X ((ComplexVector)RHS)[*pExtOrder--] = Sum; #endif X } X return; } #endif /* spCOMPLEX AND MULTIPLICATION AND TRANSPOSE */ X X X X X X X X #if DETERMINANT /*! X * This routine in capable of calculating the determinant of the X * matrix once the LU factorization has been performed. Hence, only X * use this routine after spFactor() and before spClear(). X * The determinant equals the product of all the diagonal elements of X * the lower triangular matrix L, except that this product may need X * negating. Whether the product or the negative product equals the X * determinant is determined by the number of row and column X * interchanges performed. Note that the determinants of matrices can X * be very large or very small. On large matrices, the determinant X * can be far larger or smaller than can be represented by a floating X * point number. For this reason the determinant is scaled to a X * reasonable value and the logarithm of the scale factor is returned. X * X * \param eMatrix X * A pointer to the matrix for which the determinant is desired. X * \param pExponent X * The logarithm base 10 of the scale factor for the determinant. To find X * the actual determinant, Exponent should be added to the exponent of X * Determinant. X * \param pDeterminant X * The real portion of the determinant. This number is scaled to be X * greater than or equal to 1.0 and less than 10.0. X * \param piDeterminant X * The imaginary portion of the determinant. When the matrix is real X * this pointer need not be supplied, nothing will be returned. This X * number is scaled to be greater than or equal to 1.0 and less than 10.0. X */ /* >>> Local variables: X * Norm (RealNumber) X * L-infinity norm of a complex number. X * Size (int) X * Local storage for Matrix->Size. Placed in a register for speed. X * Temp (RealNumber) X * Temporary storage for real portion of determinant. X */ X void spDeterminant( X spMatrix eMatrix, X int *pExponent, X spREAL *pDeterminant #if spCOMPLEX X , spREAL *piDeterminant #endif ) { register MatrixPtr Matrix = (MatrixPtr)eMatrix; register int I, Size; RealNumber Norm, nr, ni; ComplexNumber Pivot, cDeterminant; X #define NORM(a) (nr = ABS((a).Real), ni = ABS((a).Imag), MAX (nr,ni)) X /* Begin `spDeterminant'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_FACTORED( Matrix ); X *pExponent = 0; X X if (Matrix->Error == spSINGULAR) X { *pDeterminant = 0.0; #if spCOMPLEX X if (Matrix->Complex) *piDeterminant = 0.0; #endif X return; X } X X Size = Matrix->Size; X I = 0; X #if spCOMPLEX X if (Matrix->Complex) /* Complex Case. */ X { cDeterminant.Real = 1.0; X cDeterminant.Imag = 0.0; X X while (++I <= Size) X { CMPLX_RECIPROCAL( Pivot, *Matrix->Diag[I] ); X CMPLX_MULT_ASSIGN( cDeterminant, Pivot ); X /* Scale Determinant. */ X Norm = NORM( cDeterminant ); X if (Norm != 0.0) X { while (Norm >= 1.0e12) X { cDeterminant.Real *= 1.0e-12; X cDeterminant.Imag *= 1.0e-12; X *pExponent += 12; X Norm = NORM( cDeterminant ); X } X while (Norm < 1.0e-12) X { cDeterminant.Real *= 1.0e12; X cDeterminant.Imag *= 1.0e12; X *pExponent -= 12; X Norm = NORM( cDeterminant ); X } X } X } X /* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */ X Norm = NORM( cDeterminant ); X if (Norm != 0.0) X { while (Norm >= 10.0) X { cDeterminant.Real *= 0.1; X cDeterminant.Imag *= 0.1; X (*pExponent)++; X Norm = NORM( cDeterminant ); X } X while (Norm < 1.0) X { cDeterminant.Real *= 10.0; X cDeterminant.Imag *= 10.0; X (*pExponent)--; X Norm = NORM( cDeterminant ); X } X } X if (Matrix->NumberOfInterchangesIsOdd) X CMPLX_NEGATE( cDeterminant ); X X *pDeterminant = cDeterminant.Real; X *piDeterminant = cDeterminant.Imag; X } #endif /* spCOMPLEX */ #if REAL AND spCOMPLEX X else #endif #if REAL X { /* Real Case. */ X *pDeterminant = 1.0; X X while (++I <= Size) X { *pDeterminant /= Matrix->Diag[I]->Real; X /* Scale Determinant. */ X if (*pDeterminant != 0.0) X { while (ABS(*pDeterminant) >= 1.0e12) X { *pDeterminant *= 1.0e-12; X *pExponent += 12; X } X while (ABS(*pDeterminant) < 1.0e-12) X { *pDeterminant *= 1.0e12; X *pExponent -= 12; X } X } X } X /* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */ X if (*pDeterminant != 0.0) X { while (ABS(*pDeterminant) >= 10.0) X { *pDeterminant *= 0.1; X (*pExponent)++; X } X while (ABS(*pDeterminant) < 1.0) X { *pDeterminant *= 10.0; X (*pExponent)--; X } X } X if (Matrix->NumberOfInterchangesIsOdd) X *pDeterminant = -*pDeterminant; X } #endif /* REAL */ } #endif /* DETERMINANT */ X X X X X X X X #if STRIP X /*! X * Strips the matrix of all fill-ins. X * X * \param eMatrix X * Pointer to the matrix to be stripped. X */ /* >>> Local variables: X * pElement (ElementPtr) X * Pointer that is used to step through the matrix. X * ppElement (ElementPtr *) X * Pointer to the location of an ElementPtr. This location will be X * updated if a fill-in is stripped from the matrix. X * pFillin (ElementPtr) X * Pointer used to step through the lists of fill-ins while marking them. X * pLastFillin (ElementPtr) X * A pointer to the last fill-in in the list. Used to terminate a loop. X * pListNode (struct FillinListNodeStruct *) X * A pointer to a node in the FillinList linked-list. X */ X void spStripFills( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; struct FillinListNodeStruct *pListNode; X /* Begin `spStripFills'. */ X ASSERT_IS_SPARSE( Matrix ); X if (Matrix->Fillins == 0) return; X Matrix->NeedsOrdering = YES; X Matrix->Elements -= Matrix->Fillins; X Matrix->Fillins = 0; X /* Mark the fill-ins. */ X { register ElementPtr pFillin, pLastFillin; X X pListNode = Matrix->LastFillinListNode = Matrix->FirstFillinListNode; X Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList; X Matrix->NextAvailFillin = pListNode->pFillinList; X X while (pListNode != NULL) X { pFillin = pListNode->pFillinList; X pLastFillin = &(pFillin[ pListNode->NumberOfFillinsInList - 1 ]); X while (pFillin <= pLastFillin) X (pFillin++)->Row = 0; X pListNode = pListNode->Next; X } X } X /* Unlink fill-ins by searching for elements marked with Row = 0. */ X { register ElementPtr pElement, *ppElement; X register int I, Size = Matrix->Size; X /* Unlink fill-ins in all columns. */ X for (I = 1; I <= Size; I++) X { ppElement = &(Matrix->FirstInCol[I]); X while ((pElement = *ppElement) != NULL) X { if (pElement->Row == 0) X { *ppElement = pElement->NextInCol; /* Unlink fill-in. */ X if (Matrix->Diag[pElement->Col] == pElement) X Matrix->Diag[pElement->Col] = NULL; X } X else X ppElement = &pElement->NextInCol; /* Skip element. */ X } X } X /* Unlink fill-ins in all rows. */ X for (I = 1; I <= Size; I++) X { ppElement = &(Matrix->FirstInRow[I]); X while ((pElement = *ppElement) != NULL) X { if (pElement->Row == 0) X *ppElement = pElement->NextInRow; /* Unlink fill-in. */ X else X ppElement = &pElement->NextInRow; /* Skip element. */ X } X } X } X return; } #endif X X X X X X X #if TRANSLATE AND DELETE /*! X * Deletes a row and a column from a matrix. X * X * Sparse will abort if an attempt is made to delete a row or column that X * doesn't exist. X * X * \param eMatrix X * Pointer to the matrix in which the row and column are to be deleted. X * \param Row X * Row to be deleted. X * \param Col X * Column to be deleted. X */ /* >>> Local variables: X * ExtCol (int) X * The external column that is being deleted. X * ExtRow (int) X * The external row that is being deleted. X * pElement (ElementPtr) X * Pointer to an element in the matrix. Used when scanning rows and X * columns in order to eliminate elements from the last row or column. X * ppElement (ElementPtr *) X * Pointer to the location of an ElementPtr. This location will be X * filled with a NULL pointer if it is the new last element in its row X * or column. X * pElement (ElementPtr) X * Pointer to an element in the last row or column of the matrix. X * Size (int) X * The local version Matrix->Size, the size of the matrix. X */ X void spDeleteRowAndCol( X spMatrix eMatrix, X int Row, X int Col ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement, *ppElement, pLastElement; int Size, ExtRow, ExtCol; X /* Begin `spDeleteRowAndCol'. */ X ASSERT_IS_SPARSE( Matrix ); X vASSERT( (Row > 0) AND (Col > 0), "Nonpositive row or column number" ); X vASSERT( (Row <= Matrix->ExtSize) AND (Col <= Matrix->ExtSize), X "Row or column number too large" ); X X Size = Matrix->Size; X ExtRow = Row; X ExtCol = Col; X if (NOT Matrix->RowsLinked) spcLinkRows( Matrix ); X X Row = Matrix->ExtToIntRowMap[Row]; X Col = Matrix->ExtToIntColMap[Col]; X ASSERT( Row > 0 AND Col > 0 ); X /* Move Row so that it is the last row in the matrix. */ X if (Row != Size) spcRowExchange( Matrix, Row, Size ); X /* Move Col so that it is the last column in the matrix. */ X if (Col != Size) spcColExchange( Matrix, Col, Size ); X /* Correct Diag pointers. */ X if (Row == Col) X SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Size] ) X else X { Matrix->Diag[Row] = spcFindDiag( Matrix, Row ); X Matrix->Diag[Col] = spcFindDiag( Matrix, Col ); X } X /* X * Delete last row and column of the matrix. X */ /* Break the column links to every element in the last row. */ X pLastElement = Matrix->FirstInRow[ Size ]; X while (pLastElement != NULL) X { ppElement = &(Matrix->FirstInCol[ pLastElement->Col ]); X while ((pElement = *ppElement) != NULL) X { if (pElement == pLastElement) X *ppElement = NULL; /* Unlink last element in column. */ X else X ppElement = &pElement->NextInCol; /* Skip element. */ X } X pLastElement = pLastElement->NextInRow; X } X /* Break the row links to every element in the last column. */ X pLastElement = Matrix->FirstInCol[ Size ]; X while (pLastElement != NULL) X { ppElement = &(Matrix->FirstInRow[ pLastElement->Row ]); X while ((pElement = *ppElement) != NULL) X { if (pElement == pLastElement) X *ppElement = NULL; /* Unlink last element in row. */ X else X ppElement = &pElement->NextInRow; /* Skip element. */ X } X pLastElement = pLastElement->NextInCol; X } X /* Clean up some details. */ X Matrix->Size = Size - 1; X Matrix->Diag[Size] = NULL; X Matrix->FirstInRow[Size] = NULL; X Matrix->FirstInCol[Size] = NULL; X Matrix->CurrentSize--; X Matrix->ExtToIntRowMap[ExtRow] = -1; X Matrix->ExtToIntColMap[ExtCol] = -1; X Matrix->NeedsOrdering = YES; X X return; } #endif X X X X X X X X #if PSEUDOCONDITION /*! X * Computes the magnitude of the ratio of the largest to the smallest X * pivots. This quantity is an indicator of ill-conditioning in the X * matrix. If this ratio is large, and if the matrix is scaled such X * that uncertainties in the RHS and the matrix entries are X * equilibrated, then the matrix is ill-conditioned. However, a small X * ratio does not necessarily imply that the matrix is X * well-conditioned. This routine must only be used after a matrix has X * been factored by spOrderAndFactor() or spFactor() and before it is X * cleared by spClear() or spInitialize(). The pseudocondition is X * faster to compute than the condition number calculated by X * spCondition(), but is not as informative. X * X * \return X * The magnitude of the ratio of the largest to smallest pivot used during X * previous factorization. If the matrix was singular, zero is returned. X * X * \param eMatrix X * Pointer to the matrix. X */ X spREAL spPseudoCondition( spMatrix eMatrix ) { X MatrixPtr Matrix = (MatrixPtr)eMatrix; X register int I; X register ArrayOfElementPtrs Diag; X RealNumber MaxPivot, MinPivot, Mag; X X /* Begin `spPseudoCondition'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_FACTORED( Matrix ); X if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG) X return 0.0; X X Diag = Matrix->Diag; X MaxPivot = MinPivot = ELEMENT_MAG( Diag[1] ); X for (I = 2; I <= Matrix->Size; I++) X { Mag = ELEMENT_MAG( Diag[I] ); X if (Mag > MaxPivot) X MaxPivot = Mag; X else if (Mag < MinPivot) X MinPivot = Mag; X } X ASSERT( MaxPivot > 0.0 ); X return MaxPivot / MinPivot; } #endif X X X X X X X X #if CONDITION /*! X * Computes an estimate of the condition number using a variation on X * the LINPACK condition number estimation algorithm. This quantity is X * an indicator of ill-conditioning in the matrix. To avoid problems X * with overflow, the reciprocal of the condition number is returned. X * If this number is small, and if the matrix is scaled such that X * uncertainties in the RHS and the matrix entries are equilibrated, X * then the matrix is ill-conditioned. If the this number is near X * one, the matrix is well conditioned. This routine must only be X * used after a matrix has been factored by spOrderAndFactor() or X * spFactor() and before it is cleared by spClear() or spInitialize(). X * X * Unlike the LINPACK condition number estimator, this routines X * returns the L infinity condition number. This is an artifact of X * Sparse placing ones on the diagonal of the upper triangular matrix X * rather than the lower. This difference should be of no importance. X * X * \b References: X * X * A.K. Cline, C.B. Moler, G.W. Stewart, J.H. Wilkinson. An estimate X * for the condition number of a matrix. SIAM Journal on Numerical X * Analysis. Vol. 16, No. 2, pages 368-375, April 1979. X * X * J.J. Dongarra, C.B. Moler, J.R. Bunch, G.W. Stewart. LINPACK X * User's Guide. SIAM, 1979. X * X * Roger G. Grimes, John G. Lewis. Condition number estimation for X * sparse matrices. SIAM Journal on Scientific and Statistical X * Computing. Vol. 2, No. 4, pages 384-388, December 1981. X * X * Dianne Prost O'Leary. Estimating matrix condition numbers. SIAM X * Journal on Scientific and Statistical Computing. Vol. 1, No. 2, X * pages 205-209, June 1980. X * X * \return X * The reciprocal of the condition number. If the matrix was singular, X * zero is returned. X * X * \param eMatrix X * Pointer to the matrix. X * \param NormOfMatrix X * The L-infinity norm of the unfactored matrix as computed by X * spNorm(). X * \param pError X * Used to return error code. Possible errors include \a spSINGULAR X * or \a spNO_MEMORY. X */ X spREAL spCondition( X spMatrix eMatrix, X spREAL NormOfMatrix, X int *pError ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register RealVector T, Tm; register int I, K, Row; ElementPtr pPivot; int Size; RealNumber E, Em, Wp, Wm, ASp, ASm, ASw, ASy, ASv, ASz, MaxY, ScaleFactor; RealNumber Linpack, OLeary, InvNormOfInverse, ComplexCondition(); #define SLACK 1e4 X /* Begin `spCondition'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_FACTORED( Matrix ); X *pError = Matrix->Error; X if (Matrix->Error >= spFATAL) return 0.0; X if (NormOfMatrix == 0.0) X { *pError = spSINGULAR; X return 0.0; X } X #if spCOMPLEX X if (Matrix->Complex) X return ComplexCondition( Matrix, NormOfMatrix, pError ); #endif X #if REAL X Size = Matrix->Size; X T = Matrix->Intermediate; #if spCOMPLEX X Tm = Matrix->Intermediate + Size; #else X Tm = ALLOC( RealNumber, Size+1 ); X if (Tm == NULL) X { *pError = spNO_MEMORY; X return 0.0; X } #endif X for (I = Size; I > 0; I--) T[I] = 0.0; X /* X * Part 1. Ay = e. X * Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign X * chosen to maximize the size of w in Lw = e. Since the terms in w can X * get very large, scaling is used to avoid overflow. X */ X /* Forward elimination. Solves Lw = e while choosing e. */ X E = 1.0; X for (I = 1; I <= Size; I++) X { pPivot = Matrix->Diag[I]; X if (T[I] < 0.0) Em = -E; else Em = E; X Wm = (Em + T[I]) * pPivot->Real; X if (ABS(Wm) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(Wm) ); X for (K = Size; K > 0; K--) T[K] *= ScaleFactor; X E *= ScaleFactor; X Em *= ScaleFactor; X Wm = (Em + T[I]) * pPivot->Real; X } X Wp = (T[I] - Em) * pPivot->Real; X ASp = ABS(T[I] - Em); X ASm = ABS(Em + T[I]); X /* Update T for both values of W, minus value is placed in Tm. */ X pElement = pPivot->NextInCol; X while (pElement != NULL) X { Row = pElement->Row; X Tm[Row] = T[Row] - (Wm * pElement->Real); X T[Row] -= (Wp * pElement->Real); X ASp += ABS(T[Row]); X ASm += ABS(Tm[Row]); X pElement = pElement->NextInCol; X } X /* If minus value causes more growth, overwrite T with its values. */ X if (ASm > ASp) X { T[I] = Wm; X pElement = pPivot->NextInCol; X while (pElement != NULL) X { T[pElement->Row] = Tm[pElement->Row]; X pElement = pElement->NextInCol; X } X } X else T[I] = Wp; X } X /* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK. */ X for (ASw = 0.0, I = Size; I > 0; I--) ASw += ABS(T[I]); X ScaleFactor = 1.0 / (SLACK * ASw); X if (ScaleFactor < 0.5) X { for (I = Size; I > 0; I--) T[I] *= ScaleFactor; X E *= ScaleFactor; X } X /* Backward Substitution. Solves Uy = w.*/ X for (I = Size; I >= 1; I--) X { pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { T[I] -= pElement->Real * T[pElement->Col]; X pElement = pElement->NextInRow; X } X if (ABS(T[I]) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) ); X for (K = Size; K > 0; K--) T[K] *= ScaleFactor; X E *= ScaleFactor; X } X } X /* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK. */ X for (ASy = 0.0, I = Size; I > 0; I--) ASy += ABS(T[I]); X ScaleFactor = 1.0 / (SLACK * ASy); X if (ScaleFactor < 0.5) X { for (I = Size; I > 0; I--) T[I] *= ScaleFactor; X ASy = 1.0 / SLACK; X E *= ScaleFactor; X } X /* Compute infinity-norm of T for O'Leary's estimate. */ X for (MaxY = 0.0, I = Size; I > 0; I--) X if (MaxY < ABS(T[I])) MaxY = ABS(T[I]); X /* X * Part 2. A* z = y where the * represents the transpose. X * Recall that A = LU implies A* = U* L*. X */ X /* Forward elimination, U* v = y. */ X for (I = 1; I <= Size; I++) X { pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { T[pElement->Col] -= T[I] * pElement->Real; X pElement = pElement->NextInRow; X } X if (ABS(T[I]) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) ); X for (K = Size; K > 0; K--) T[K] *= ScaleFactor; X ASy *= ScaleFactor; X } X } X /* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK. */ X for (ASv = 0.0, I = Size; I > 0; I--) ASv += ABS(T[I]); X ScaleFactor = 1.0 / (SLACK * ASv); X if (ScaleFactor < 0.5) X { for (I = Size; I > 0; I--) T[I] *= ScaleFactor; X ASy *= ScaleFactor; X } X /* Backward Substitution, L* z = v. */ X for (I = Size; I >= 1; I--) X { pPivot = Matrix->Diag[I]; X pElement = pPivot->NextInCol; X while (pElement != NULL) X { T[I] -= pElement->Real * T[pElement->Row]; X pElement = pElement->NextInCol; X } X T[I] *= pPivot->Real; X if (ABS(T[I]) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) ); X for (K = Size; K > 0; K--) T[K] *= ScaleFactor; X ASy *= ScaleFactor; X } X } X /* Compute 1-norm of T, which now contains z. */ X for (ASz = 0.0, I = Size; I > 0; I--) ASz += ABS(T[I]); X #if NOT spCOMPLEX X FREE( Tm ); #endif X X Linpack = ASy / ASz; X OLeary = E / MaxY; X InvNormOfInverse = MIN( Linpack, OLeary ); X return InvNormOfInverse / NormOfMatrix; #endif /* REAL */ } X X X X X #if spCOMPLEX /* X * ESTIMATE CONDITION NUMBER X * X * Complex version of spCondition(). X * X * >>> Returns: X * The reciprocal of the condition number. X * X * >>> Arguments: X * Matrix (MatrixPtr) X * Pointer to the matrix. X * NormOfMatrix (RealNumber) X * The L-infinity norm of the unfactored matrix as computed by X * spNorm(). X * pError (int *) X * Used to return error code. X * X * >>> Possible errors: X * spNO_MEMORY X */ X static RealNumber ComplexCondition( X MatrixPtr Matrix, X RealNumber NormOfMatrix, X int *pError ) { register ElementPtr pElement; register ComplexVector T, Tm; register int I, K, Row; ElementPtr pPivot; int Size; RealNumber E, Em, ASp, ASm, ASw, ASy, ASv, ASz, MaxY, ScaleFactor; RealNumber Linpack, OLeary, InvNormOfInverse; ComplexNumber Wp, Wm; X /* Begin `ComplexCondition'. */ X X Size = Matrix->Size; X T = (ComplexVector)Matrix->Intermediate; X Tm = ALLOC( ComplexNumber, Size+1 ); X if (Tm == NULL) X { *pError = spNO_MEMORY; X return 0.0; X } X for (I = Size; I > 0; I--) T[I].Real = T[I].Imag = 0.0; X /* X * Part 1. Ay = e. X * Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign X * chosen to maximize the size of w in Lw = e. Since the terms in w can X * get very large, scaling is used to avoid overflow. X */ X /* Forward elimination. Solves Lw = e while choosing e. */ X E = 1.0; X for (I = 1; I <= Size; I++) X { pPivot = Matrix->Diag[I]; X if (T[I].Real < 0.0) Em = -E; else Em = E; X Wm = T[I]; X Wm.Real += Em; X ASm = CMPLX_1_NORM( Wm ); X CMPLX_MULT_ASSIGN( Wm, *pPivot ); X if (CMPLX_1_NORM(Wm) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(Wm) ); X for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); X E *= ScaleFactor; X Em *= ScaleFactor; X ASm *= ScaleFactor; X SCLR_MULT_ASSIGN( Wm, ScaleFactor ); X } X Wp = T[I]; X Wp.Real -= Em; X ASp = CMPLX_1_NORM( Wp ); X CMPLX_MULT_ASSIGN( Wp, *pPivot ); X /* Update T for both values of W, minus value is placed in Tm. */ X pElement = pPivot->NextInCol; X while (pElement != NULL) X { Row = pElement->Row; X /* Cmplx expr: Tm[Row] = T[Row] - (Wp * *pElement). */ X CMPLX_MULT_SUBT( Tm[Row], Wm, *pElement, T[Row] ); X /* Cmplx expr: T[Row] -= Wp * *pElement. */ X CMPLX_MULT_SUBT_ASSIGN( T[Row], Wm, *pElement ); X ASp += CMPLX_1_NORM(T[Row]); X ASm += CMPLX_1_NORM(Tm[Row]); X pElement = pElement->NextInCol; X } X /* If minus value causes more growth, overwrite T with its values. */ X if (ASm > ASp) X { T[I] = Wm; X pElement = pPivot->NextInCol; X while (pElement != NULL) X { T[pElement->Row] = Tm[pElement->Row]; X pElement = pElement->NextInCol; X } X } X else T[I] = Wp; X } X /* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK. */ X for (ASw = 0.0, I = Size; I > 0; I--) ASw += CMPLX_1_NORM(T[I]); X ScaleFactor = 1.0 / (SLACK * ASw); X if (ScaleFactor < 0.5) X { for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor ); X E *= ScaleFactor; X } X /* Backward Substitution. Solves Uy = w.*/ X for (I = Size; I >= 1; I--) X { pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { /* Cmplx expr: T[I] -= T[pElement->Col] * *pElement. */ X CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Col], *pElement ); X pElement = pElement->NextInRow; X } X if (CMPLX_1_NORM(T[I]) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) ); X for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); X E *= ScaleFactor; X } X } X /* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK. */ X for (ASy = 0.0, I = Size; I > 0; I--) ASy += CMPLX_1_NORM(T[I]); X ScaleFactor = 1.0 / (SLACK * ASy); X if (ScaleFactor < 0.5) X { for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor ); X ASy = 1.0 / SLACK; X E *= ScaleFactor; X } X /* Compute infinity-norm of T for O'Leary's estimate. */ X for (MaxY = 0.0, I = Size; I > 0; I--) X if (MaxY < CMPLX_1_NORM(T[I])) MaxY = CMPLX_1_NORM(T[I]); X /* X * Part 2. A* z = y where the * represents the transpose. X * Recall that A = LU implies A* = U* L*. X */ X /* Forward elimination, U* v = y. */ X for (I = 1; I <= Size; I++) X { pElement = Matrix->Diag[I]->NextInRow; X while (pElement != NULL) X { /* Cmplx expr: T[pElement->Col] -= T[I] * *pElement. */ X CMPLX_MULT_SUBT_ASSIGN( T[pElement->Col], T[I], *pElement ); X pElement = pElement->NextInRow; X } X if (CMPLX_1_NORM(T[I]) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) ); X for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); X ASy *= ScaleFactor; X } X } X /* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK. */ X for (ASv = 0.0, I = Size; I > 0; I--) ASv += CMPLX_1_NORM(T[I]); X ScaleFactor = 1.0 / (SLACK * ASv); X if (ScaleFactor < 0.5) X { for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor ); X ASy *= ScaleFactor; X } X /* Backward Substitution, L* z = v. */ X for (I = Size; I >= 1; I--) X { pPivot = Matrix->Diag[I]; X pElement = pPivot->NextInCol; X while (pElement != NULL) X { /* Cmplx expr: T[I] -= T[pElement->Row] * *pElement. */ X CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Row], *pElement ); X pElement = pElement->NextInCol; X } X CMPLX_MULT_ASSIGN( T[I], *pPivot ); X if (CMPLX_1_NORM(T[I]) > SLACK) X { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) ); X for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); X ASy *= ScaleFactor; X } X } X /* Compute 1-norm of T, which now contains z. */ X for (ASz = 0.0, I = Size; I > 0; I--) ASz += CMPLX_1_NORM(T[I]); X X FREE( Tm ); X X Linpack = ASy / ASz; X OLeary = E / MaxY; X InvNormOfInverse = MIN( Linpack, OLeary ); X return InvNormOfInverse / NormOfMatrix; } #endif /* spCOMPLEX */ X X X X X /*! X * Computes the L-infinity norm of an unfactored matrix. It is a fatal X * error to pass this routine a factored matrix. X * X * \return X * The largest absolute row sum of matrix. X * X * \param eMatrix X * Pointer to the matrix. X */ X spREAL spNorm( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register int I; RealNumber Max = 0.0, AbsRowSum; X /* Begin `spNorm'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_NOT_FACTORED( Matrix ); X if (NOT Matrix->RowsLinked) spcLinkRows( Matrix ); X /* Compute row sums. */ #if REAL X if (NOT Matrix->Complex) X { for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInRow[I]; X AbsRowSum = 0.0; X while (pElement != NULL) X { AbsRowSum += ABS( pElement->Real ); X pElement = pElement->NextInRow; X } X if (Max < AbsRowSum) Max = AbsRowSum; X } X } #endif #if spCOMPLEX X if (Matrix->Complex) X { for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInRow[I]; X AbsRowSum = 0.0; X while (pElement != NULL) X { AbsRowSum += CMPLX_1_NORM( *pElement ); X pElement = pElement->NextInRow; X } X if (Max < AbsRowSum) Max = AbsRowSum; X } X } #endif X return Max; } #endif /* CONDITION */ X X X X X X #if STABILITY /*! X * This routine, along with spRoundoff(), are used to gauge the stability of a X * factorization. If the factorization is determined to be too unstable, X * then the matrix should be reordered. The routines compute quantities X * that are needed in the computation of a bound on the error attributed X * to any one element in the matrix during the factorization. In other X * words, there is a matrix \f$ E = [e_{ij}] \f$ of error terms such that X * \f$ A+E = LU \f$. This routine finds a bound on \f$ |e_{ij}| \f$. X * Erisman & Reid [1] showed that \f$ |e_{ij}| < 3.01 u \rho m_{ij} \f$, X * where \f$ u \f$ is the machine rounding unit, X * \f$ \rho = \max a_{ij} \f$ where the max is taken over every row \f$ i \f$, X * column \f$ j \f$, and step \f$ k \f$, and \f$ m_{ij} \f$ is the number X * of multiplications required in the computation of \f$ l_{ij} \f$ if X * \f$ i > j \f$ or \f$ u_{ij} \f$ otherwise. Barlow [2] showed that X * \f$ \rho < \max_i || l_i ||_p \max_j || u_j ||_q \f$ where X * \f$ 1/p + 1/q = 1 \f$. X * X * spLargestElement() finds the magnitude on the largest element in the X * matrix. If the matrix has not yet been factored, the largest X * element is found by direct search. If the matrix is factored, a X * bound on the largest element in any of the reduced submatrices is X * computed using Barlow with \f$ p = \infty \f$ and \f$ q = 1 \f$. X * The ratio of these X * two numbers is the growth, which can be used to determine if the X * pivoting order is adequate. A large growth implies that X * considerable error has been made in the factorization and that it X * is probably a good idea to reorder the matrix. If a large growth X * in encountered after using spFactor(), reconstruct the matrix and X * refactor using spOrderAndFactor(). If a large growth is X * encountered after using spOrderAndFactor(), refactor using X * spOrderAndFactor() with the pivot threshold increased, say to 0.1. X * X * Using only the size of the matrix as an upper bound on \f$ m_{ij} \f$ and X * Barlow's bound, the user can estimate the size of the matrix error X * terms \f$ e_{ij} \f$ using the bound of Erisman and Reid. spRoundoff() X * computes a tighter bound (with more work) based on work by Gear X * [3], \f$ |e_{ij}| < 1.01 u \rho (t c^3 + (1 + t)c^2) \f$ where X * \f$ t \f$ is the threshold and \f$ c \f$ is the maximum number of X * off-diagonal elements in any row of \f$ L \f$. The expensive part X * of computing this bound is determining the maximum number of X * off-diagonals in \f$ L \f$, which changes X * only when the order of the matrix changes. This number is computed X * and saved, and only recomputed if the matrix is reordered. X * X * [1] A. M. Erisman, J. K. Reid. Monitoring the stability of the X * triangular factorization of a sparse matrix. Numerische X * Mathematik. Vol. 22, No. 3, 1974, pp 183-186. X * X * [2] J. L. Barlow. A note on monitoring the stability of triangular X * decomposition of sparse matrices. "SIAM Journal of Scientific X * and Statistical Computing." Vol. 7, No. 1, January 1986, pp 166-168. X * X * [3] I. S. Duff, A. M. Erisman, J. K. Reid. "Direct Methods for Sparse X * Matrices." Oxford 1986. pp 99. X * X * \return X * If matrix is not factored, returns the magnitude of the largest element in X * the matrix. If the matrix is factored, a bound on the magnitude of the X * largest element in any of the reduced submatrices is returned. X * X * \param eMatrix X * Pointer to the matrix. X */ X spREAL spLargestElement( spMatrix eMatrix ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register int I; RealNumber Mag, AbsColSum, Max = 0.0, MaxRow = 0.0, MaxCol = 0.0; RealNumber Pivot; ComplexNumber cPivot; register ElementPtr pElement, pDiag; X /* Begin `spLargestElement'. */ X ASSERT_IS_SPARSE( Matrix ); X #if REAL X if (Matrix->Factored AND NOT Matrix->Complex) X { if (Matrix->Error == spSINGULAR) return 0.0; X /* Find the bound on the size of the largest element over all factorization. */ X for (I = 1; I <= Matrix->Size; I++) X { pDiag = Matrix->Diag[I]; X /* Lower triangular matrix. */ X Pivot = 1.0 / pDiag->Real; X Mag = ABS( Pivot ); X if (Mag > MaxRow) MaxRow = Mag; X pElement = Matrix->FirstInRow[I]; X while (pElement != pDiag) X { Mag = ABS( pElement->Real ); X if (Mag > MaxRow) MaxRow = Mag; X pElement = pElement->NextInRow; X } X /* Upper triangular matrix. */ X pElement = Matrix->FirstInCol[I]; X AbsColSum = 1.0; /* Diagonal of U is unity. */ X while (pElement != pDiag) X { AbsColSum += ABS( pElement->Real ); X pElement = pElement->NextInCol; X } X if (AbsColSum > MaxCol) MaxCol = AbsColSum; X } X } X else if (NOT Matrix->Complex) X { for (I = 1; I <= Matrix->Size; I++) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { Mag = ABS( pElement->Real ); X if (Mag > Max) Max = Mag; X pElement = pElement->NextInCol; X } X } X return Max; X } #endif #if spCOMPLEX X if (Matrix->Factored AND Matrix->Complex) X { if (Matrix->Error == spSINGULAR) return 0.0; X /* Find the bound on the size of the largest element over all factorization. */ X for (I = 1; I <= Matrix->Size; I++) X { pDiag = Matrix->Diag[I]; X /* Lower triangular matrix. */ X CMPLX_RECIPROCAL( cPivot, *pDiag ); X Mag = CMPLX_INF_NORM( cPivot ); X if (Mag > MaxRow) MaxRow = Mag; X pElement = Matrix->FirstInRow[I]; X while (pElement != pDiag) X { Mag = CMPLX_INF_NORM( *pElement ); X if (Mag > MaxRow) MaxRow = Mag; X pElement = pElement->NextInRow; X } X /* Upper triangular matrix. */ X pElement = Matrix->FirstInCol[I]; X AbsColSum = 1.0; /* Diagonal of U is unity. */ X while (pElement != pDiag) X { AbsColSum += CMPLX_INF_NORM( *pElement ); X pElement = pElement->NextInCol; X } X if (AbsColSum > MaxCol) MaxCol = AbsColSum; X } X } X else if (Matrix->Complex) X { for (I = 1; I <= Matrix->Size; I++) X { pElement = Matrix->FirstInCol[I]; X while (pElement != NULL) X { Mag = CMPLX_INF_NORM( *pElement ); X if (Mag > Max) Max = Mag; X pElement = pElement->NextInCol; X } X } X return Max; X } #endif X return MaxRow * MaxCol; } X X X X /*! X * This routine, along with spLargestElement(), are used to gauge the X * stability of a factorization. See description of spLargestElement() X * for more information. X * X * \return X * Returns a bound on the magnitude of the largest element in X * \f$ E = A - LU \f$. X * X * \param eMatrix X * Pointer to the matrix. X * \param Rho X * The bound on the magnitude of the largest element in any of the X * reduced submatrices. This is the number computed by the function X * spLargestElement() when given a factored matrix. If this number is X * negative, the bound will be computed automatically. X */ X spREAL spRoundoff( X spMatrix eMatrix, X spREAL Rho ) { MatrixPtr Matrix = (MatrixPtr)eMatrix; register ElementPtr pElement; register int Count, I, MaxCount = 0; RealNumber Reid, Gear; X /* Begin `spRoundoff'. */ X ASSERT_IS_SPARSE( Matrix ); X ASSERT_NO_ERRORS( Matrix ); X ASSERT_IS_FACTORED( Matrix ); X /* Compute Barlow's bound if it is not given. */ X if (Rho < 0.0) Rho = spLargestElement( eMatrix ); X /* Find the maximum number of off-diagonals in L if not previously computed. */ X if (Matrix->MaxRowCountInLowerTri < 0) X { for (I = Matrix->Size; I > 0; I--) X { pElement = Matrix->FirstInRow[I]; X Count = 0; X while (pElement->Col < I) X { Count++; X pElement = pElement->NextInRow; X } X if (Count > MaxCount) MaxCount = Count; X } X Matrix->MaxRowCountInLowerTri = MaxCount; X } X else MaxCount = Matrix->MaxRowCountInLowerTri; X /* Compute error bound. */ X Gear = 1.01*((MaxCount + 1) * Matrix->RelThreshold + 1.0) * SQR(MaxCount); X Reid = 3.01 * Matrix->Size; X X if (Gear < Reid) X return (MACHINE_RESOLUTION * Rho * Gear); X else X return (MACHINE_RESOLUTION * Rho * Reid); } #endif X X X X X X X #if DOCUMENTATION /*! X * This routine prints a short message describing the error error state X * of sparse. No message is produced if there is no error. X * The error state is cleared. X * X * \param eMatrix X * Matrix for which the error message is to be printed. X * \param Stream X * Stream to which the error message is to be printed. X * \param Originator X * Name of originator of error message. If NULL, `sparse' is used. X * If zero-length string, no originator is printed. X */ X void spErrorMessage( X spMatrix eMatrix, X FILE *Stream, X char *Originator ) { int Row, Col, Error; X /* Begin `spErrorMessage'. */ X if (eMatrix == NULL) X Error = spNO_MEMORY; X else X { ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); X Error = ((MatrixPtr)eMatrix)->Error; X } X X if (Error == spOKAY) return; X if (Originator == NULL) Originator = "sparse"; X if (Stream == NULL) Stream = stderr; X if (Originator[0] != '\0') fprintf( Stream, "%s: ", Originator ); X if (Error >= spFATAL) X fprintf( Stream, "fatal error: "); X else X fprintf( Stream, "warning: "); /* X * Print particular error message. X * Do not use switch statement because error codes may not be unique. X */ X if (Error == spPANIC) X fprintf( Stream, "Sparse called improperly.\n"); X else if (Error == spNO_MEMORY) X fprintf( Stream, "insufficient memory available.\n"); X else if (Error == spMANGLED) X fprintf( Stream, "matrix is mangled.\n"); X else if (Error == spSINGULAR) X { spWhereSingular( eMatrix, &Row, &Col ); X fprintf( Stream, "singular matrix detected at row %d and column %d.\n", X Row, Col); X } X else if (Error == spZERO_DIAG) X { spWhereSingular( eMatrix, &Row, &Col ); X fprintf( Stream, "zero diagonal detected at row %d and column %d.\n", X Row, Col); X } X else if (Error == spSMALL_PIVOT) X { fprintf( Stream, X "unable to find a pivot that is larger than absolute threshold.\n"); X } X else ABORT(); X X ((MatrixPtr)eMatrix)->Error = spOKAY; X return; } #endif /* DOCUMENTATION */ SHAR_EOF (set 20 03 06 30 16 12 39 'sparse/spUtils.c'; eval "$shar_touch") && chmod 0600 'sparse/spUtils.c' if test $? -ne 0 then ${echo} 'restore of sparse/spUtils.c failed' fi if ${md5check} then ( ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'sparse/spUtils.c: MD5 check failed' ) << \SHAR_EOF dd8d95f756acb3b1ebc24b765b120d3c sparse/spUtils.c SHAR_EOF else test `LC_ALL=C wc -c < 'sparse/spUtils.c'` -ne 68909 && \ ${echo} 'restoration warning: size of sparse/spUtils.c is not 68909' fi fi if rm -fr ${lock_dir} then ${echo} 'x - removed lock directory `'${lock_dir}\''.' else ${echo} 'x - failed to remove lock directory `'${lock_dir}\''.' exit 1 fi exit 0