|
Agar: r7562 - in trunk: . agar-math-config math math/SPARSE: msg#00002lib.agar.scm
Author: vedge Date: 2008-09-01 13:13:21 -0300 (Mon, 01 Sep 2008) New Revision: 7562 Added: trunk/agar-math-config/ trunk/agar-math-config/Makefile trunk/agar-math-config/agar-math-config.c trunk/math/ trunk/math/.manlinks.mk trunk/math/M_Matrix.3 trunk/math/Makefile trunk/math/SPARSE/ trunk/math/SPARSE/CHANGES trunk/math/SPARSE/LICENSE trunk/math/SPARSE/Makefile trunk/math/SPARSE/spallocate.c trunk/math/SPARSE/spbuild.c trunk/math/SPARSE/spconfig.h trunk/math/SPARSE/spdefs.h trunk/math/SPARSE/spedacious.c trunk/math/SPARSE/spfactor.c trunk/math/SPARSE/spmatrix.h trunk/math/SPARSE/spoutput.c trunk/math/SPARSE/spsolve.c trunk/math/SPARSE/sputils.c trunk/math/m.h trunk/math/m_begin.h trunk/math/m_bitstring.h trunk/math/m_circle.c trunk/math/m_circle.h trunk/math/m_close.h trunk/math/m_color.c trunk/math/m_color.h trunk/math/m_complex.c trunk/math/m_complex.h trunk/math/m_convexhull.c trunk/math/m_coordinates.c trunk/math/m_coordinates.h trunk/math/m_gaussj.c trunk/math/m_gaussj.h trunk/math/m_geometry.h trunk/math/m_gui.c trunk/math/m_gui.h trunk/math/m_heapsort.c trunk/math/m_int_vector.c trunk/math/m_int_vector.h trunk/math/m_line.c trunk/math/m_line.h trunk/math/m_lu.c trunk/math/m_lu.h trunk/math/m_math.c trunk/math/m_math.h trunk/math/m_matrix.c trunk/math/m_matrix.h trunk/math/m_matrix44_fpu.c trunk/math/m_matrix44_fpu.h trunk/math/m_matrix44_sse.c trunk/math/m_matrix44_sse.h trunk/math/m_matrix_fpu.c trunk/math/m_matrix_fpu.h trunk/math/m_matrix_sparse.c trunk/math/m_matrix_sparse.h trunk/math/m_matview.c trunk/math/m_matview.h trunk/math/m_mergesort.c trunk/math/m_plane.c trunk/math/m_plane.h trunk/math/m_plotter.c trunk/math/m_plotter.h trunk/math/m_point_set.c trunk/math/m_point_set.h trunk/math/m_polygon.c trunk/math/m_polygon.h trunk/math/m_qsort.c trunk/math/m_quaternion.c trunk/math/m_quaternion.h trunk/math/m_radixsort.c trunk/math/m_rectangle.c trunk/math/m_rectangle.h trunk/math/m_triangle.c trunk/math/m_triangle.h trunk/math/m_vector.c trunk/math/m_vector.h trunk/math/m_vector2_fpu.c trunk/math/m_vector2_fpu.h trunk/math/m_vector3_fpu.c trunk/math/m_vector3_fpu.h trunk/math/m_vector3_sse.c trunk/math/m_vector3_sse.h trunk/math/m_vector3_sse3.c trunk/math/m_vector3_sse3.h trunk/math/m_vector4_fpu.c trunk/math/m_vector4_fpu.h trunk/math/m_vector_fpu.c trunk/math/m_vector_fpu.h trunk/math/math_pub.h Modified: trunk/Makefile Log: merge M library from FreeSG. Modified: trunk/Makefile =================================================================== --- trunk/Makefile 2008-09-01 14:37:42 UTC (rev 7561) +++ trunk/Makefile 2008-09-01 16:13:21 UTC (rev 7562) @@ -13,6 +13,7 @@ vg agar-vg-config \ rg agar-rg-config \ map agar-map-config \ + math agar-math-config \ dev agar-dev-config \ net agar-net-config \ po @@ -22,6 +23,7 @@ rg \ vg \ map \ + math \ dev \ net @@ -97,6 +99,8 @@ ${INCLDIR}/agar/net.h; \ ${SUDO} ${INSTALL_INCL} ${SRC}/map/map_pub.h \ ${INCLDIR}/agar/map.h; \ + ${SUDO} ${INSTALL_INCL} ${SRC}/math/math_pub.h \ + ${INCLDIR}/agar/math.h; \ ${SUDO} ${INSTALL_INCL} ${SRC}/dev/dev_pub.h \ ${INCLDIR}/agar/dev.h; \ else \ @@ -114,6 +118,7 @@ ${SUDO} ${INSTALL_INCL} rg/rg_pub.h ${INCLDIR}/agar/rg.h; \ ${SUDO} ${INSTALL_INCL} net/net_pub.h ${INCLDIR}/agar/net.h; \ ${SUDO} ${INSTALL_INCL} map/map_pub.h ${INCLDIR}/agar/map.h; \ + ${SUDO} ${INSTALL_INCL} math/math_pub.h ${INCLDIR}/agar/math.h;\ ${SUDO} ${INSTALL_INCL} dev/dev_pub.h ${INCLDIR}/agar/dev.h; \ fi Added: trunk/agar-math-config/Makefile =================================================================== --- trunk/agar-math-config/Makefile (rev 0) +++ trunk/agar-math-config/Makefile 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,11 @@ +TOP= .. +include ${TOP}/Makefile.config + +PROG= agar-math-config +PROG_TYPE= "CLI" +PROG_GUID= "fb9d278f-50c6-49bb-98fb-631b55c2790f" + +SRCS= agar-math-config.c +CFLAGS+=-I.. + +include ${TOP}/mk/build.prog.mk Added: trunk/agar-math-config/agar-math-config.c =================================================================== --- trunk/agar-math-config/agar-math-config.c (rev 0) +++ trunk/agar-math-config/agar-math-config.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2008 Hypertriton, Inc. <http://hypertriton.com/> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +#include <config/version.h> +#include <config/release.h> +#include <config/prefix.h> +#include <config/sysconfdir.h> +#include <config/incldir.h> +#include <config/libdir.h> +#include <config/sharedir.h> + +#include <config/have_math.h> +#include <config/have_altivec.h> +#include <config/have_sse.h> +#include <config/have_sse2.h> +#include <config/have_sse3.h> + +#ifdef HAVE_MATH +#include <config/math_cflags.h> +#include <config/math_libs.h> +#endif +#ifdef HAVE_ALTIVEC +#include <config/altivec_cflags.h> +#endif +#ifdef HAVE_SSE +#include <config/sse_cflags.h> +#endif +#ifdef HAVE_SSE2 +#include <config/sse2_cflags.h> +#endif +#ifdef HAVE_SSE3 +#include <config/sse3_cflags.h> +#endif + +#include <stdio.h> +#include <string.h> + +int +main(int argc, char *argv[]) +{ + int i; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--version") == 0) { + printf("%s\n", VERSION); + } else if (strcmp(argv[i], "--release") == 0) { + printf("%s\n", RELEASE); + } else if (strcmp(argv[i], "--prefix") == 0) { + printf("%s\n", PREFIX); + } else if (strcmp(argv[i], "--sysconfdir") == 0) { + printf("%s\n", SYSCONFDIR); + } else if (strcmp(argv[i], "--incldir") == 0) { + printf("%s\n", INCLDIR); + } else if (strcmp(argv[i], "--libdir") == 0) { + printf("%s\n", LIBDIR); + } else if (strcmp(argv[i], "--sharedir") == 0) { + printf("%s\n", SHAREDIR); + } else if (strcmp(argv[i], "--cflags") == 0) { + printf("-I%s ", INCLDIR); +#ifdef HAVE_MATH + printf("%s ", MATH_CFLAGS); +#endif +#ifdef HAVE_ALTIVEC + printf("%s ", ALTIVEC_CFLAGS); +#endif +#ifdef HAVE_SSE + printf("%s ", SSE_CFLAGS); +#endif +#ifdef HAVE_SSE2 + printf("%s ", SSE2_CFLAGS); +#endif +#ifdef HAVE_SSE3 + printf("%s ", SSE3_CFLAGS); +#endif + printf("\n"); + } else if (strcmp(argv[i], "--libs") == 0) { + printf("-L%s ", LIBDIR); + printf("-lag_math "); +#ifdef HAVE_MATH + printf("%s ", MATH_LIBS); +#endif + printf("\n"); + } + } + if (i <= 1) { + fprintf(stderr, + "Usage: %s [--version] [--prefix] [--sysconfdir] " + "[--incldir] [--libdir] [--sharedir] " + "[--cflags] [--libs]\n", argv[0]); + return (1); + } + return (0); +} + Added: trunk/math/.manlinks.mk =================================================================== --- trunk/math/.manlinks.mk (rev 0) +++ trunk/math/.manlinks.mk 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,8 @@ +MANLINKS+=M_Matrix.3:M_MatIdentity44.3 +CATLINKS+=M_Matrix.cat3:M_MatIdentity44.cat3 +MANLINKS+=M_Matrix.3:M_MatIdentity44v.3 +CATLINKS+=M_Matrix.cat3:M_MatIdentity44v.cat3 +MANLINKS+=M_Matrix.3:M_MatZero44.3 +CATLINKS+=M_Matrix.cat3:M_MatZero44.cat3 +MANLINKS+=M_Matrix.3:M_MatZero44v.3 +CATLINKS+=M_Matrix.cat3:M_MatZero44v.cat3 Added: trunk/math/M_Matrix.3 =================================================================== --- trunk/math/M_Matrix.3 (rev 0) +++ trunk/math/M_Matrix.3 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,114 @@ +.\" +.\" Copyright (c) 2006-2008 Hypertriton, Inc. <http://hypertriton.com/> +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +.\" +.Dd July 22, 2006 +.Dt M_MATRIX 3 +.Os +.ds vT Agar-Math API Reference +.ds oS Agar 1.3.3 +.Sh NAME +.Nm M_Matrix +.Nd Agar-Math matrix-related functions +.Sh SYNOPSIS +.Bd -literal +#include <agar/math.h> +.Ed +.Sh DESCRIPTION +The +.Nm +interface provides basic linear algebra routines specific to matrices. +Similarly to +.Xr M_Vector 3 , +a consistent interface to different +.Em backends +is provided, allowing different numerical solvers and memory representations. +Selection between multiple backends is possible at run-time, or the Agar-Math +library can be compiled to provide inline expansions of these operations under +a specific backend. +.Sh M-BY-N MATRICES +The following routines operate on dynamically-allocated m-by-n matrices. +Individual entries are not directly accessible since the +.Em sparse +backend does not actually store them in an array. +.Pp +.Bl -tag -width "sparse " -compact +.It fpu +Native scalar floating point methods. +.It sparse +Methods optimized for large, sparse matrices. +Based on the excellent Sparse 1.4 package by Kenneth Kundert at UC Berkeley +(http://sparse.sourceforge.net/). +.El +.Pp +.nr nS 1 +.nr nS 0 +.Sh 4-BY-4 MATRICES +.Pp +The following routines are optimized for 4x4 matrices, as frequently +encountered in computer graphics. +Contrary to m-by-n matrices, the entries are not dynamically allocated and +are directly accessible via the +.Va m +member of the +.Ft M_Matrix44 +structure (row-major format). +Available backends include: +.Pp +.Bl -tag -width "fpu " -compact +.It fpu +Native scalar floating point methods. +.It sse +Accelerate operations using Streaming SIMD Extensions (SSE). +.El +.Pp +.nr nS 1 +.Ft "M_Matrix44" +.Fn M_MatIdentity44 "void" +.Pp +.Ft "void" +.Fn M_MatIdentity44v "M_Matrix44 *A" +.Pp +.Ft "M_Matrix44" +.Fn M_MatZero44 "void" +.Pp +.Ft "void" +.Fn M_MatZero44v "M_Matrix44 *A" +.Pp +.nr nS 0 +The functions +.Fn M_MatIdentity44 +and +.Fn M_MatIdentity44v +return the identity matrix. +.Fn M_MatZero44 +and +.Fn M_MatZero44v +return the zero matrix. +.Sh SEE ALSO +.Xr AG_Intro 3 , +.Xr M_Vector 3 +.Sh HISTORY +The +.Nm +interface first appeared in Agar 1.3.3. Added: trunk/math/Makefile =================================================================== --- trunk/math/Makefile (rev 0) +++ trunk/math/Makefile 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,39 @@ +TOP= .. +include ${TOP}/Makefile.config + +SUBDIR= SPARSE +LIBS= SPARSE/*.lo + +LIB= ag_math +LIB_INSTALL= Yes +LIB_SHARED= Yes +LIB_GUID= "ba35a553-bd88-4e5e-8c4b-d17f60ae7439" +LIB_LINKS= ag_core_static ag_gui_static \ + pthreads SDL opengl freetype + +SRCS= m_math.c m_complex.c m_quaternion.c \ + m_int_vector.c m_vector.c m_vector_fpu.c \ + m_vector2_fpu.c m_vector3_fpu.c m_vector4_fpu.c \ + m_vector3_sse.c m_vector3_sse3.c \ + m_matrix.c m_matrix_fpu.c m_matrix44_fpu.c m_matrix44_sse.c \ + m_gaussj.c m_lu.c \ + m_gui.c m_plotter.c m_matview.c \ + m_line.c m_circle.c m_triangle.c m_rectangle.c m_polygon.c m_plane.c \ + m_coordinates.c m_heapsort.c m_mergesort.c m_qsort.c m_radixsort.c \ + m_point_set.c m_color.c \ + m_matrix_sparse.c + +LIB_XOBJS=SPARSE + +MAN3= M_Matrix.3 + +CFLAGS+=${SDL_CFLAGS} ${PTHREADS_XOPEN_CFLAGS} ${ALTIVEC_CFLAGS} ${SSE_CFLAGS} \ + ${SSE2_CFLAGS} ${SSE3_CFLAGS} ${GLU_CFLAGS} ${MATH_CFLAGS} \ + -D_M_INTERNAL + +# For gcc warnings in m_matrix44_sse.c. +CFLAGS+=${WNO_UNINITIALIZED} + +include .manlinks.mk +include ${TOP}/mk/build.lib.mk +include ${TOP}/mk/build.man.mk Added: trunk/math/SPARSE/CHANGES =================================================================== --- trunk/math/SPARSE/CHANGES (rev 0) +++ trunk/math/SPARSE/CHANGES 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,7 @@ +This documents the changes we made to SPARSE library : http://sparse.sourceforge.net/ +We currently use SPARSE 1.4b. +Changes to vanilla SPARSE : +* changed the includes (basically, include "../m.h" in every .c, and include nothing in .h) +* #define spREAL M_Real to use M's real type +* uncapitalized file names +* removed some unused prototypes, and silenced some warnings Added: trunk/math/SPARSE/LICENSE =================================================================== --- trunk/math/SPARSE/LICENSE (rev 0) +++ trunk/math/SPARSE/LICENSE 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,25 @@ +Sparse1.4 is distributed as open-source software under the Berkeley +license model. Redistribution and use in source and binary forms, with +or without modification, are permitted provided that the following +conditions are met: + +Redistributions of source code must retain the original 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. + +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. Added: trunk/math/SPARSE/Makefile =================================================================== --- trunk/math/SPARSE/Makefile (rev 0) +++ trunk/math/SPARSE/Makefile 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,23 @@ +TOP= ../.. +include ${TOP}/Makefile.config + +LIB= SPARSE +LIB_INSTALL= No +LIB_SHARED= Yes +LIB_GUID= "6051cbce-2498-406d-ae46-cf6ac89d05cf" +LIB_LINKS= ag_core_static ag_gui_static \ + pthreads SDL opengl freetype + +SRCS= spallocate.c \ + spbuild.c \ + spfactor.c \ + spoutput.c \ + spsolve.c \ + sputils.c \ + spedacious.c + +CFLAGS+=${SDL_CFLAGS} ${PTHREADS_XOPEN_CFLAGS} ${ALTIVEC_CFLAGS} ${SSE_CFLAGS} \ + ${SSE2_CFLAGS} ${SSE3_CFLAGS} ${GLU_CFLAGS} ${MATH_CFLAGS} \ + -D_M_INTERNAL + +include ${TOP}/mk/build.lib.mk Added: trunk/math/SPARSE/spallocate.c =================================================================== --- trunk/math/SPARSE/spallocate.c (rev 0) +++ trunk/math/SPARSE/spallocate.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,841 @@ +/* + * MATRIX ALLOCATION MODULE + * + * Author: Advising professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + */ +/*!\file + * This file contains functions for allocating and freeing matrices, configuring them, and for + * accessing global information about the matrix (size, error status, etc.). + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ +/* >>> User accessible functions contained in this file: + * spCreate + * spDestroy + * spErrorState + * spWhereSingular + * spGetSize + * spSetReal + * spSetComplex + * spFillinCount + * spElementCount + * + * >>> Other functions contained in this file: + * spcGetElement + * InitializeElementBlocks + * spcGetFillin + * RecordAllocation + * AllocateBlockOfAllocationList + * EnlargeMatrix + * ExpandTranslationArrays + */ + + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + * spMatrix.h + * Macros and declarations to be imported by the user. + * spDefs.h + * Matrix type and macro definitions for the sparse matrix routines.. + */ + +#define spINSIDE_SPARSE +#include <core/core.h> +#include "../m.h" +#include "spconfig.h" +#include "spmatrix.h" +#include "spdefs.h" + + + + + +/* + * Global strings + */ + +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"; + + + + +/* + * Function declarations + */ + +static void InitializeElementBlocks( MatrixPtr, int, int ); +static void RecordAllocation( MatrixPtr, void* ); +static void AllocateBlockOfAllocationList( MatrixPtr ); + + + +/*! + * Allocates and initializes the data structures associated with a matrix. + * + * \return + * A pointer to the matrix is returned cast into \a spMatrix (typically a + * pointer to a void). This pointer is then passed and used by the other + * matrix routines to refer to a particular matrix. If an error occurs, + * the \a NULL pointer is returned. + * + * \param Size + * Size of matrix or estimate of size of matrix if matrix is \a EXPANDABLE. + * \param Complex + * Type of matrix. If \a Complex is 0 then the matrix is real, otherwise + * the matrix will be complex. Note that if the routines are not set up + * to handle the type of matrix requested, then an \a spPANIC error will occur. + * Further note that if a matrix will be both real and complex, it must + * be specified here as being complex. + * \param pError + * Returns error flag, needed because function \a spErrorState() will + * not work correctly if \a spCreate() returns \a NULL. Possible errors + * include \a spNO_MEMORY and \a spPANIC. + */ +/* >>> Local variables: + * AllocatedSize (int) + * The size of the matrix being allocated. + * Matrix (MatrixPtr) + * A pointer to the matrix frame being created. + */ + +spMatrix +spCreate( + int Size, + int Complex, + spError *pError +) +{ +register unsigned SizePlusOne; +register MatrixPtr Matrix; +register int I; +int AllocatedSize; + +/* Begin `spCreate'. */ +/* Clear error flag. */ + *pError = spOKAY; + +/* Test for valid size. */ + vASSERT( (Size >= 0) AND (Size != 0 OR EXPANDABLE), "Invalid size" ); + +/* Test for valid type. */ +#if NOT spCOMPLEX + ASSERT( NOT Complex ); +#endif +#if NOT REAL + ASSERT( Complex ); +#endif + +/* Create Matrix. */ + AllocatedSize = MAX( Size, MINIMUM_ALLOCATED_SIZE ); + SizePlusOne = (unsigned)(AllocatedSize + 1); + + if ((Matrix = ALLOC(struct MatrixFrame, 1)) == NULL) + { *pError = spNO_MEMORY; + return NULL; + } + +/* Initialize matrix */ + Matrix->ID = SPARSE_ID; + Matrix->Complex = Complex; + Matrix->PreviousMatrixWasComplex = Complex; + Matrix->Factored = NO; + Matrix->Elements = 0; + Matrix->Error = *pError; + Matrix->Fillins = 0; + Matrix->Reordered = NO; + Matrix->NeedsOrdering = YES; + Matrix->NumberOfInterchangesIsOdd = NO; + Matrix->Partitioned = NO; + Matrix->RowsLinked = NO; + Matrix->InternalVectorsAllocated = NO; + Matrix->SingularCol = 0; + Matrix->SingularRow = 0; + Matrix->Size = Size; + Matrix->AllocatedSize = AllocatedSize; + Matrix->ExtSize = Size; + Matrix->AllocatedExtSize = AllocatedSize; + Matrix->CurrentSize = 0; + Matrix->ExtToIntColMap = NULL; + Matrix->ExtToIntRowMap = NULL; + Matrix->IntToExtColMap = NULL; + Matrix->IntToExtRowMap = NULL; + Matrix->MarkowitzRow = NULL; + Matrix->MarkowitzCol = NULL; + Matrix->MarkowitzProd = NULL; + Matrix->DoCmplxDirect = NULL; + Matrix->DoRealDirect = NULL; + Matrix->Intermediate = NULL; + Matrix->RelThreshold = DEFAULT_THRESHOLD; + Matrix->AbsThreshold = 0.0; + + Matrix->TopOfAllocationList = NULL; + Matrix->RecordsRemaining = 0; + Matrix->ElementsRemaining = 0; + Matrix->FillinsRemaining = 0; + + RecordAllocation( Matrix, (void *)Matrix ); + if (Matrix->Error == spNO_MEMORY) goto MemoryError; + +/* Take out the trash. */ + Matrix->TrashCan.Real = 0.0; +#if spCOMPLEX + Matrix->TrashCan.Imag = 0.0; +#endif + Matrix->TrashCan.Row = 0; + Matrix->TrashCan.Col = 0; + Matrix->TrashCan.NextInRow = NULL; + Matrix->TrashCan.NextInCol = NULL; +#if INITIALIZE + Matrix->TrashCan.pInitInfo = NULL; +#endif + +/* Allocate space in memory for Diag pointer vector. */ + CALLOC( Matrix->Diag, ElementPtr, SizePlusOne); + if (Matrix->Diag == NULL) + goto MemoryError; + +/* Allocate space in memory for FirstInCol pointer vector. */ + CALLOC( Matrix->FirstInCol, ElementPtr, SizePlusOne); + if (Matrix->FirstInCol == NULL) + goto MemoryError; + +/* Allocate space in memory for FirstInRow pointer vector. */ + CALLOC( Matrix->FirstInRow, ElementPtr, SizePlusOne); + if (Matrix->FirstInRow == NULL) + goto MemoryError; + +/* Allocate space in memory for IntToExtColMap vector. */ + if (( Matrix->IntToExtColMap = ALLOC(int, SizePlusOne)) == NULL) + goto MemoryError; + +/* Allocate space in memory for IntToExtRowMap vector. */ + if (( Matrix->IntToExtRowMap = ALLOC(int, SizePlusOne)) == NULL) + goto MemoryError; + +/* Initialize MapIntToExt vectors. */ + for (I = 1; I <= AllocatedSize; I++) + { Matrix->IntToExtRowMap[I] = I; + Matrix->IntToExtColMap[I] = I; + } + +#if TRANSLATE +/* Allocate space in memory for ExtToIntColMap vector. */ + if (( Matrix->ExtToIntColMap = ALLOC(int, SizePlusOne)) == NULL) + goto MemoryError; + +/* Allocate space in memory for ExtToIntRowMap vector. */ + if (( Matrix->ExtToIntRowMap = ALLOC(int, SizePlusOne)) == NULL) + goto MemoryError; + +/* Initialize MapExtToInt vectors. */ + for (I = 1; I <= AllocatedSize; I++) + { Matrix->ExtToIntColMap[I] = -1; + Matrix->ExtToIntRowMap[I] = -1; + } + Matrix->ExtToIntColMap[0] = 0; + Matrix->ExtToIntRowMap[0] = 0; +#endif + +/* Allocate space for fill-ins and initial set of elements. */ + InitializeElementBlocks( Matrix, SPACE_FOR_ELEMENTS*AllocatedSize, + SPACE_FOR_FILL_INS*AllocatedSize ); + if (Matrix->Error == spNO_MEMORY) + goto MemoryError; + + return (char *)Matrix; + +MemoryError: + +/* Deallocate matrix and return no pointer to matrix if there is not enough + memory. */ + *pError = spNO_MEMORY; + spDestroy( (char *)Matrix); + return NULL; +} + + + + + + + + + +/* + * ELEMENT ALLOCATION + * + * This routine allocates space for matrix elements. It requests large blocks + * of storage from the system and doles out individual elements as required. + * This technique, as opposed to allocating elements individually, tends to + * speed the allocation process. + * + * >>> Returned: + * A pointer to an element. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * + * >>> Local variables: + * pElement (ElementPtr) + * A pointer to the first element in the group of elements being allocated. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +ElementPtr +spcGetElement( MatrixPtr Matrix ) +{ +ElementPtr pElement; + +/* Begin `spcGetElement'. */ + +/* Allocate block of MatrixElements if necessary. */ + if (Matrix->ElementsRemaining == 0) + { pElement = ALLOC(struct MatrixElement, ELEMENTS_PER_ALLOCATION); + RecordAllocation( Matrix, (void *)pElement ); + if (Matrix->Error == spNO_MEMORY) return NULL; + Matrix->ElementsRemaining = ELEMENTS_PER_ALLOCATION; + Matrix->NextAvailElement = pElement; + } + +/* Update Element counter and return pointer to Element. */ + Matrix->ElementsRemaining--; + return Matrix->NextAvailElement++; +} + + + + + + + + +/* + * ELEMENT ALLOCATION INITIALIZATION + * + * This routine allocates space for matrix fill-ins and an initial set of + * elements. Besides being faster than allocating space for elements one + * at a time, it tends to keep the fill-ins physically close to the other + * matrix elements in the computer memory. This keeps virtual memory paging + * to a minimum. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * InitialNumberOfElements <input> (int) + * This number is used as the size of the block of memory, in + * MatrixElements, reserved for elements. If more than this number of + * elements are generated, then more space is allocated later. + * NumberOfFillinsExpected <input> (int) + * This number is used as the size of the block of memory, in + * MatrixElements, reserved for fill-ins. If more than this number of + * fill-ins are generated, then more space is allocated, but they may + * not be physically close in computer's memory. + * + * >>> Local variables: + * pElement (ElementPtr) + * A pointer to the first element in the group of elements being allocated. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +static void +InitializeElementBlocks( + MatrixPtr Matrix, + int InitialNumberOfElements, + int NumberOfFillinsExpected +) +{ +ElementPtr pElement; + +/* Begin `InitializeElementBlocks'. */ + +/* Allocate block of MatrixElements for elements. */ + pElement = ALLOC(struct MatrixElement, InitialNumberOfElements); + RecordAllocation( Matrix, (void *)pElement ); + if (Matrix->Error == spNO_MEMORY) return; + Matrix->ElementsRemaining = InitialNumberOfElements; + Matrix->NextAvailElement = pElement; + +/* Allocate block of MatrixElements for fill-ins. */ + pElement = ALLOC(struct MatrixElement, NumberOfFillinsExpected); + RecordAllocation( Matrix, (void *)pElement ); + if (Matrix->Error == spNO_MEMORY) return; + Matrix->FillinsRemaining = NumberOfFillinsExpected; + Matrix->NextAvailFillin = pElement; + +/* Allocate a fill-in list structure. */ + Matrix->FirstFillinListNode = ALLOC(struct FillinListNodeStruct,1); + RecordAllocation( Matrix, (void *)Matrix->FirstFillinListNode ); + if (Matrix->Error == spNO_MEMORY) return; + Matrix->LastFillinListNode = Matrix->FirstFillinListNode; + + Matrix->FirstFillinListNode->pFillinList = pElement; + Matrix->FirstFillinListNode->NumberOfFillinsInList =NumberOfFillinsExpected; + Matrix->FirstFillinListNode->Next = NULL; + + return; +} + + + + + + + + + + +/* + * FILL-IN ALLOCATION + * + * This routine allocates space for matrix fill-ins. It requests large blocks + * of storage from the system and doles out individual elements as required. + * This technique, as opposed to allocating elements individually, tends to + * speed the allocation process. + * + * >>> Returned: + * A pointer to the fill-in. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +ElementPtr +spcGetFillin( MatrixPtr Matrix ) +{ +struct FillinListNodeStruct *pListNode; +ElementPtr pFillins; + +/* Begin `spcGetFillin'. */ + +#if NOT STRIP OR LINT + if (Matrix->FillinsRemaining == 0) + return spcGetElement( Matrix ); +#endif +#if STRIP OR LINT + + if (Matrix->FillinsRemaining == 0) + { pListNode = Matrix->LastFillinListNode; + +/* First see if there are any stripped fill-ins left. */ + if (pListNode->Next != NULL) + { Matrix->LastFillinListNode = pListNode = pListNode->Next; + Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList; + Matrix->NextAvailFillin = pListNode->pFillinList; + } + else + { +/* Allocate block of fill-ins. */ + pFillins = ALLOC(struct MatrixElement, ELEMENTS_PER_ALLOCATION); + RecordAllocation( Matrix, (void *)pFillins ); + if (Matrix->Error == spNO_MEMORY) return NULL; + Matrix->FillinsRemaining = ELEMENTS_PER_ALLOCATION; + Matrix->NextAvailFillin = pFillins; + +/* Allocate a fill-in list structure. */ + pListNode->Next = ALLOC(struct FillinListNodeStruct,1); + RecordAllocation( Matrix, (void *)pListNode->Next ); + if (Matrix->Error == spNO_MEMORY) return NULL; + Matrix->LastFillinListNode = pListNode = pListNode->Next; + + pListNode->pFillinList = pFillins; + pListNode->NumberOfFillinsInList = ELEMENTS_PER_ALLOCATION; + pListNode->Next = NULL; + } + } +#endif + +/* Update Fill-in counter and return pointer to Fill-in. */ + Matrix->FillinsRemaining--; + return Matrix->NextAvailFillin++; +} + + + + + + + + + +/* + * RECORD A MEMORY ALLOCATION + * + * This routine is used to record all memory allocations so that the memory + * can be freed later. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * AllocatedPtr <input> (void *) + * The pointer returned by malloc or calloc. These pointers are saved in + * a list so that they can be easily freed. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +static void +RecordAllocation( + MatrixPtr Matrix, + void *AllocatedPtr +) +{ +/* Begin `RecordAllocation'. */ +/* + * If Allocated pointer is NULL, assume that malloc returned a NULL pointer, + * which indicates a spNO_MEMORY error. + */ + if (AllocatedPtr == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + +/* Allocate block of MatrixElements if necessary. */ + if (Matrix->RecordsRemaining == 0) + { AllocateBlockOfAllocationList( Matrix ); + if (Matrix->Error == spNO_MEMORY) + { FREE(AllocatedPtr); + return; + } + } + +/* Add Allocated pointer to Allocation List. */ + (++Matrix->TopOfAllocationList)->AllocatedPtr = AllocatedPtr; + Matrix->RecordsRemaining--; + return; + +} + + + + + + + + +/* + * ADD A BLOCK OF SLOTS TO ALLOCATION LIST + * + * This routine increases the size of the allocation list. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * + * >>> Local variables: + * ListPtr (AllocationListPtr) + * Pointer to the list that contains the pointers to segments of memory + * that were allocated by the operating system for the current matrix. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +static void +AllocateBlockOfAllocationList( MatrixPtr Matrix ) +{ +register int I; +register AllocationListPtr ListPtr; + +/* Begin `AllocateBlockOfAllocationList'. */ +/* Allocate block of records for allocation list. */ + ListPtr = ALLOC(struct AllocationRecord, (ELEMENTS_PER_ALLOCATION+1)); + if (ListPtr == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + +/* String entries of allocation list into singly linked list. List is linked + such that any record points to the one before it. */ + + ListPtr->NextRecord = Matrix->TopOfAllocationList; + Matrix->TopOfAllocationList = ListPtr; + ListPtr += ELEMENTS_PER_ALLOCATION; + for (I = ELEMENTS_PER_ALLOCATION; I > 0; I--) + { ListPtr->NextRecord = ListPtr - 1; + ListPtr--; + } + +/* Record allocation of space for allocation list on allocation list. */ + Matrix->TopOfAllocationList->AllocatedPtr = (void *)ListPtr; + Matrix->RecordsRemaining = ELEMENTS_PER_ALLOCATION; + + return; +} + + + + + + + + +/*! + * Destroys a matrix and frees all memory associated with it. + * + * \param eMatrix + * Pointer to the matrix frame which is to be destroyed. + */ +/* >>> Local variables: + * ListPtr (AllocationListPtr) + * Pointer into the linked list of pointers to allocated data structures. + * Points to pointer to structure to be freed. + * NextListPtr (AllocationListPtr) + * Pointer into the linked list of pointers to allocated data structures. + * Points to the next pointer to structure to be freed. This is needed + * because the data structure to be freed could include the current node + * in the allocation list. + */ + +void +spDestroy( spMatrix eMatrix ) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register AllocationListPtr ListPtr, NextListPtr; + +/* Begin `spDestroy'. */ + ASSERT_IS_SPARSE( Matrix ); + +/* Deallocate the vectors that are located in the matrix frame. */ + FREE( Matrix->IntToExtColMap ); + FREE( Matrix->IntToExtRowMap ); + FREE( Matrix->ExtToIntColMap ); + FREE( Matrix->ExtToIntRowMap ); + FREE( Matrix->Diag ); + FREE( Matrix->FirstInRow ); + FREE( Matrix->FirstInCol ); + FREE( Matrix->MarkowitzRow ); + FREE( Matrix->MarkowitzCol ); + FREE( Matrix->MarkowitzProd ); + FREE( Matrix->DoCmplxDirect ); + FREE( Matrix->DoRealDirect ); + FREE( Matrix->Intermediate ); + +/* Sequentially step through the list of allocated pointers freeing pointers + * along the way. */ + ListPtr = Matrix->TopOfAllocationList; + while (ListPtr != NULL) + { NextListPtr = ListPtr->NextRecord; + free( ListPtr->AllocatedPtr ); + ListPtr = NextListPtr; + } + return; +} + + + + + + + +/*! + * This function returns the error status of the given matrix. + * + * \return + * The error status of the given matrix. + * + * \param eMatrix + * The pointer to the matrix for which the error status is desired. + */ + +spError +spErrorState( spMatrix eMatrix ) +{ +/* Begin `spErrorState'. */ + + if (eMatrix != NULL) + { ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); + return ((MatrixPtr)eMatrix)->Error; + } + else return spNO_MEMORY; /* This error may actually be spPANIC, + * no way to tell. */ +} + + + + + + + + + +/*! + * This function returns the row and column number where the matrix was + * detected as singular (if pivoting was allowed on the last factorization) + * or where a zero was detected on the diagonal (if pivoting was not + * allowed on the last factorization). Pivoting is performed only in + * spOrderAndFactor(). + * + * \param eMatrix + * The matrix for which the error status is desired. + * \param pRow + * The row number. + * \param pCol + * The column number. + */ + +void +spWhereSingular( + spMatrix eMatrix, + int *pRow, + int *pCol +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; + +/* Begin `spWhereSingular'. */ + ASSERT_IS_SPARSE( Matrix ); + + if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG) + { *pRow = Matrix->SingularRow; + *pCol = Matrix->SingularCol; + } + else *pRow = *pCol = 0; + return; +} + + + + + + +/*! + * Returns the size of the matrix. Either the internal or external size of + * the matrix is returned. + * + * \param eMatrix + * Pointer to matrix. + * \param External + * If \a External is set true, the external size , i.e., the value of the + * largest external row or column number encountered is returned. + * Otherwise the true size of the matrix is returned. These two sizes + * may differ if the \a TRANSLATE option is set true. + */ + +int +spGetSize( + spMatrix eMatrix, + int External +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; + +/* Begin `spGetSize'. */ + ASSERT_IS_SPARSE( Matrix ); + +#if TRANSLATE + if (External) + return Matrix->ExtSize; + else + return Matrix->Size; +#else + return Matrix->Size; +#endif +} + + + + + + + + +/*! + * Forces matrix to be real. + * + * \param eMatrix + * Pointer to matrix. + */ + +void +spSetReal( spMatrix eMatrix ) +{ +/* Begin `spSetReal'. */ + + ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); + vASSERT( REAL, "Sparse not compiled to handle real matrices" ); + ((MatrixPtr)eMatrix)->Complex = NO; + return; +} + + +/*! + * Forces matrix to be complex. + * + * \param eMatrix + * Pointer to matrix. + */ + +void +spSetComplex( spMatrix eMatrix ) +{ +/* Begin `spSetComplex'. */ + + ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); + vASSERT( spCOMPLEX, "Sparse not compiled to handle complex matrices" ); + ((MatrixPtr)eMatrix)->Complex = YES; + return; +} + + + + + + + + + +/*! + * This function returns the number of fill-ins that currently exists in a matrix. + * + * \param eMatrix + * Pointer to matrix. + */ + +int +spFillinCount( spMatrix eMatrix ) +{ +/* Begin `spFillinCount'. */ + + ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); + return ((MatrixPtr)eMatrix)->Fillins; +} + + +/*! + * This function returns the total number of elements (including fill-ins) that currently exists in a matrix. + * + * \param eMatrix + * Pointer to matrix. + */ + +int +spElementCount( spMatrix eMatrix ) +{ +/* Begin `spElementCount'. */ + + ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); + return ((MatrixPtr)eMatrix)->Elements; +} Added: trunk/math/SPARSE/spbuild.c =================================================================== --- trunk/math/SPARSE/spbuild.c (rev 0) +++ trunk/math/SPARSE/spbuild.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,1226 @@ +/* + * MATRIX BUILD MODULE + * + * Author: Advising professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + */ +/*!\file + * This file contains the routines associated with clearing, loading and + * preprocessing the matrix. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ +/* >>> User accessible functions contained in this file: + * spClear + * spFindElement + * spGetElement + * spGetAdmittance + * spGetQuad + * spGetOnes + * spInstallInitInfo + * spGetInitInfo + * spInitialize + * + * >>> Other functions contained in this file: + * Translate + * spcFindDiag + * spcCreateElement + * spcLinkRows + * EnlargeMatrix + * ExpandTranslationArrays + */ + + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + * spMatrix.h + * Macros and declarations to be imported by the user. + * spDefs.h + * Matrix type and macro definitions for the sparse matrix routines. + */ + +#define spINSIDE_SPARSE +#include <core/core.h> +#include "../m.h" +#include "spconfig.h" +#include "spmatrix.h" +#include "spdefs.h" + + + + + +/* + * Function declarations + */ + +static void Translate( MatrixPtr, int*, int* ); +static void EnlargeMatrix( MatrixPtr, int ); +static void ExpandTranslationArrays( MatrixPtr, int ); + + + + + + +/*! + * Sets every element of the matrix to zero and clears the error flag. + * + * \param eMatrix + * Pointer to matrix that is to be cleared. + */ +/* >>> Local variables: + * pElement (ElementPtr) + * A pointer to the element being cleared. + */ + +void +spClear( spMatrix eMatrix ) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register int I; + +/* Begin `spClear'. */ + ASSERT_IS_SPARSE( Matrix ); + +/* Clear matrix. */ +#if spCOMPLEX + if (Matrix->PreviousMatrixWasComplex OR Matrix->Complex) + { for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { pElement->Real = 0.0; + pElement->Imag = 0.0; + pElement = pElement->NextInCol; + } + } + } + else +#endif + { for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { pElement->Real = 0.0; + pElement = pElement->NextInCol; + } + } + } + +/* Empty the trash. */ + Matrix->TrashCan.Real = 0.0; +#if spCOMPLEX + Matrix->TrashCan.Imag = 0.0; +#endif + + Matrix->Error = spOKAY; + Matrix->Factored = NO; + Matrix->SingularCol = 0; + Matrix->SingularRow = 0; + Matrix->PreviousMatrixWasComplex = Matrix->Complex; + return; +} + + + + + + + + + + +/*! + * This routine is used to find an element given its indices. It will not + * create it if it does not exist. + * + * \return + * A pointer to the desired element, or \a NULL if it does not exist. + * + * \param eMatrix + * Pointer to matrix. + * \param Row + * Row index for element. + * \param Col + * Column index for element. + * + * \see spGetElement() + */ +/* >>> Local variables: + * pElement (ElementPtr) + * Pointer to an element in the matrix. + */ + +spElement * +spFindElement( + spMatrix eMatrix, + int Row, + int Col +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +int StartAt = 0, Min = AG_INT_MAX; +#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. */ + +ASSERT_IS_SPARSE( Matrix ); +vASSERT( Row >= 0 AND Col >= 0, "Negative row or column number" ); + +#if TRANSLATE +Translate( Matrix, &Row, &Col ); +if (Matrix->Error == spNO_MEMORY) return NULL; +#endif + +#if NOT TRANSLATE +#if NOT EXPANDABLE +vASSERT( (Row <= Matrix->Size) AND (Col <= Matrix->Size), + "Row or column number too large" ); +#endif +#endif + +/* Begin `spFindElement'. */ + if (Row == Col) return &Matrix->Diag[Row]->Real; + +/* Determine where to start the search. */ + if (Matrix->RowsLinked) + { if ((Col >= Row) AND Matrix->Diag[Row]) + { Min = Col - Row; + StartAt = DiagRight; + } + else + { Min = Col; + StartAt = BorderRight; + } + } + if ((Row >= Col) AND Matrix->Diag[Col]) + { if (Row - Col < Min) + StartAt = DiagDown; + } + else if (Row < Min) + StartAt = BorderDown; + +/* Search column for element. */ + if ((StartAt == BorderDown) OR (StartAt == DiagDown)) + { if (StartAt == BorderDown) + pElement = Matrix->FirstInCol[Col]; + else + pElement = Matrix->Diag[Col]; + + while ((pElement != NULL) AND (pElement->Row < Row)) + pElement = pElement->NextInCol; + if (pElement AND (pElement->Row == Row)) + return &pElement->Real; + else + return NULL; + } + +/* Search row for element. */ + if (StartAt == BorderRight) + pElement = Matrix->FirstInRow[Row]; + else + pElement = Matrix->Diag[Row]; + + while ((pElement != NULL) AND (pElement->Col < Col)) + pElement = pElement->NextInRow; + if (pElement AND (pElement->Col == Col)) + return &pElement->Real; + else + return NULL; +} + + + + + + + + + +/*! + * Finds element [Row,Col] and returns a pointer to it. If element is + * not found then it is created and spliced into matrix. This routine + * is only to be used after spCreate() and before spMNA_Preorder(), + * spFactor() or spOrderAndFactor(). Returns a pointer to the + * real portion of an \a spElement. This pointer is later used by + * \a spADD_xxx_ELEMENT to directly access element. + * + * \return + * Returns a pointer to the element. This pointer is then used to directly + * access the element during successive builds. + * + * \param eMatrix + * Pointer to the matrix that the element is to be added to. + * \param Row + * Row index for element. Must be in the range of [0..Size] unless + * the options \a EXPANDABLE or \a TRANSLATE are used. Elements placed in + * row zero are discarded. In no case may \a Row be less than zero. + * \param Col + * Column index for element. Must be in the range of [0..Size] unless + * the options \a EXPANDABLE or \a TRANSLATE are used. Elements placed in + * column zero are discarded. In no case may \a Col be less than zero. + + * \see spFindElement() + */ +/* >>> Local variables: + * pElement (RealNumber *) + * Pointer to the element. + * + * >>> Possible errors: + * spNO_MEMORY + * Error is not cleared in this routine. + */ + +spElement * +spGetElement( + spMatrix eMatrix, + int Row, + int Col +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +ElementPtr pElement; + +/* Begin `spGetElement'. */ + ASSERT_IS_SPARSE( Matrix ); + vASSERT( Row >= 0 AND Col >= 0, "Negative row or column number" ); + + if ((Row == 0) OR (Col == 0)) + return &Matrix->TrashCan.Real; + +#if NOT TRANSLATE + vASSERT( NOT Matrix->Reordered, + "Set TRANSLATE to add elements to a reordered matrix" ); +#endif + +#if TRANSLATE + Translate( Matrix, &Row, &Col ); + if (Matrix->Error == spNO_MEMORY) return NULL; +#endif + +#if NOT TRANSLATE +#if NOT EXPANDABLE + vASSERT( (Row <= Matrix->Size) AND (Col <= Matrix->Size), + "Row or column number too large" ); +#endif + +#if EXPANDABLE +/* Re-size Matrix if necessary. */ + if ((Row > Matrix->Size) OR (Col > Matrix->Size)) + EnlargeMatrix( Matrix, MAX(Row, Col) ); + if (Matrix->Error == spNO_MEMORY) return NULL; +#endif +#endif + + if ((Row != Col) OR ((pElement = Matrix->Diag[Row]) == NULL)) + { /* + * Element does not exist or does not reside along diagonal. Search + * for element and if it does not exist, create it. + */ + pElement = spcCreateElement( Matrix, Row, Col, + &(Matrix->FirstInRow[Row]), + &(Matrix->FirstInCol[Col]), NO ); + } +/* + * Cast pointer into a pointer to a RealNumber. This requires that Real + * be the first record in the MatrixElement structure. + */ + return &pElement->Real; +} + + + + + + + +#if TRANSLATE + +/* + * TRANSLATE EXTERNAL INDICES TO INTERNAL + * + * Convert internal row and column numbers to internal row and column numbers. + * Also updates Ext/Int maps. + * + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * Row <input/output> (int *) + * Upon entry Row is either a external row number of an external node + * number. Upon entry, the internal equivalent is supplied. + * Col <input/output> (int *) + * Upon entry Column is either a external column number of an external node + * number. Upon entry, the internal equivalent is supplied. + * + * >>> Local variables: + * ExtCol (int) + * Temporary variable used to hold the external column or node number + * during the external to internal column number translation. + * ExtRow (int) + * Temporary variable used to hold the external row or node number during + * the external to internal row number translation. + * IntCol (int) + * Temporary variable used to hold the internal column or node number + * during the external to internal column number translation. + * IntRow (int) + * Temporary variable used to hold the internal row or node number during + * the external to internal row number translation. + */ + +static void +Translate( + MatrixPtr Matrix, + int *Row, + int *Col +) +{ +register int IntRow, IntCol, ExtRow, ExtCol; + +/* Begin `Translate'. */ + ExtRow = *Row; + ExtCol = *Col; + +/* Expand translation arrays if necessary. */ + if ((ExtRow > Matrix->AllocatedExtSize) OR + (ExtCol > Matrix->AllocatedExtSize)) + { + ExpandTranslationArrays( Matrix, MAX(ExtRow, ExtCol) ); + if (Matrix->Error == spNO_MEMORY) return; + } + +/* Set ExtSize if necessary. */ + if ((ExtRow > Matrix->ExtSize) OR (ExtCol > Matrix->ExtSize)) + Matrix->ExtSize = MAX(ExtRow, ExtCol); + +/* Translate external row or node number to internal row or node number. */ + if ((IntRow = Matrix->ExtToIntRowMap[ExtRow]) == -1) + { Matrix->ExtToIntRowMap[ExtRow] = ++Matrix->CurrentSize; + Matrix->ExtToIntColMap[ExtRow] = Matrix->CurrentSize; + IntRow = Matrix->CurrentSize; + +#if NOT EXPANDABLE + vASSERT( IntRow <= Matrix->Size, "Matrix size fixed" ); +#endif + +#if EXPANDABLE +/* Re-size Matrix if necessary. */ + if (IntRow > Matrix->Size) + EnlargeMatrix( Matrix, IntRow ); + if (Matrix->Error == spNO_MEMORY) return; +#endif + + Matrix->IntToExtRowMap[IntRow] = ExtRow; + Matrix->IntToExtColMap[IntRow] = ExtRow; + } + +/* Translate external column or node number to internal column or node number.*/ + if ((IntCol = Matrix->ExtToIntColMap[ExtCol]) == -1) + { Matrix->ExtToIntRowMap[ExtCol] = ++Matrix->CurrentSize; + Matrix->ExtToIntColMap[ExtCol] = Matrix->CurrentSize; + IntCol = Matrix->CurrentSize; + +#if NOT EXPANDABLE + vASSERT( IntCol <= Matrix->Size, "Matrix size fixed" ); +#endif + +#if EXPANDABLE +/* Re-size Matrix if necessary. */ + if (IntCol > Matrix->Size) + EnlargeMatrix( Matrix, IntCol ); + if (Matrix->Error == spNO_MEMORY) return; +#endif + + Matrix->IntToExtRowMap[IntCol] = ExtCol; + Matrix->IntToExtColMap[IntCol] = ExtCol; + } + + *Row = IntRow; + *Col = IntCol; + return; +} +#endif + + + + + + +#if QUAD_ELEMENT +/*! + * Performs same function as spGetElement() except rather than one + * element, all four matrix elements for a floating two terminal + * admittance component are added. This routine also works if component + * is grounded. Positive elements are placed at [Node1,Node2] and + * [Node2,Node1]. This routine is only to be used after spCreate() + * and before spMNA_Preorder(), spFactor() or spOrderAndFactor(). + * + * \return + * Error code. Possible errors include \a spNO_MEMORY. + * Error is not cleared in this routine. + * + * \param Matrix + * Pointer to the matrix that component is to be entered in. + * \param Node1 + * Row and column indices for elements. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Node zero is the + * ground node. In no case may \a Node1 be less than zero. + * \param Node2 + * Row and column indices for elements. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Node zero is the + * ground node. In no case may \a Node2 be less than zero. + * \param Template + * Collection of pointers to four elements that are later used to directly + * address elements. User must supply the template, this routine will + * fill it. + */ + +spError +spGetAdmittance( + spMatrix Matrix, + int Node1, + int Node2, + struct spTemplate *Template +) +{ + +/* Begin `spGetAdmittance'. */ + Template->Element1 = spGetElement(Matrix, Node1, Node1 ); + Template->Element2 = spGetElement(Matrix, Node2, Node2 ); + Template->Element3Negated = spGetElement( Matrix, Node2, Node1 ); + Template->Element4Negated = spGetElement( Matrix, Node1, Node2 ); + if + ( (Template->Element1 == NULL) + OR (Template->Element2 == NULL) + OR (Template->Element3Negated == NULL) + OR (Template->Element4Negated == NULL) + ) return spNO_MEMORY; + + if (Node1 == 0) + SWAP( RealNumber*, Template->Element1, Template->Element2 ); + + return spOKAY; +} +#endif /* QUAD_ELEMENT */ + + + + + + + + + +#if QUAD_ELEMENT +/*! + * Similar to spGetAdmittance(), except that spGetAdmittance() only + * handles 2-terminal components, whereas spGetQuad() handles simple + * 4-terminals as well. These 4-terminals are simply generalized + * 2-terminals with the option of having the sense terminals different + * from the source and sink terminals. spGetQuad() adds four + * elements to the matrix. Positive elements occur at [Row1,Col1] + * [Row2,Col2] while negative elements occur at [Row1,Col2] and [Row2,Col1]. + * The routine works fine if any of the rows and columns are zero. + * This routine is only to be used after spCreate() and before + * spMNA_Preorder(), spFactor() or spOrderAndFactor() + * unless \a TRANSLATE is set true. + * + * \return + * Error code. Possible errors include \a spNO_MEMORY. + * Error is not cleared in this routine. + * + * \param Matrix + * Pointer to the matrix that component is to be entered in. + * \param Row1 + * First row index for elements. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground row. In no case may Row1 be less than zero. + * \param Row2 + * Second row index for elements. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground row. In no case may Row2 be less than zero. + * \param Col1 + * First column index for elements. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground column. In no case may Col1 be less than zero. + * \param Col2 + * Second column index for elements. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground column. In no case may Col2 be less than zero. + * \param Template + * Collection of pointers to four elements that are later used to directly + * address elements. User must supply the template, this routine will + * fill it. + */ + +spError +spGetQuad( + spMatrix Matrix, + int Row1, + int Row2, + int Col1, + int Col2, + struct spTemplate *Template +) +{ +/* Begin `spGetQuad'. */ + Template->Element1 = spGetElement( Matrix, Row1, Col1); + Template->Element2 = spGetElement( Matrix, Row2, Col2 ); + Template->Element3Negated = spGetElement( Matrix, Row2, Col1 ); + Template->Element4Negated = spGetElement( Matrix, Row1, Col2 ); + if + ( (Template->Element1 == NULL) + OR (Template->Element2 == NULL) + OR (Template->Element3Negated == NULL) + OR (Template->Element4Negated == NULL) + ) return spNO_MEMORY; + + if (Template->Element1 == &((MatrixPtr)Matrix)->TrashCan.Real) + SWAP( RealNumber *, Template->Element1, Template->Element2 ); + + return spOKAY; +} +#endif /* QUAD_ELEMENT */ + + + + + + + + + +#if QUAD_ELEMENT +/*! + * Addition of four structural ones to matrix by index. + * Performs similar function to spGetQuad() except this routine is + * meant for components that do not have an admittance representation. + * + * The following stamp is used: \code + * Pos Neg Eqn + * Pos [ . . 1 ] + * Neg [ . . -1 ] + * Eqn [ 1 -1 . ] + * \endcode + * + * \return + * Error code. Possible errors include \a spNO_MEMORY. + * Error is not cleared in this routine. + * + * \param Matrix + * Pointer to the matrix that component is to be entered in. + * \param Pos + * See stamp above. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground row. In no case may \a Pos be less than zero. + * \param Neg + * See stamp above. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground row. In no case may \a Neg be less than zero. + * \param Eqn + * See stamp above. Must be in the range of [0..Size] + * unless the options \a EXPANDABLE or \a TRANSLATE are used. Zero is the + * ground row. In no case may \a Eqn be less than zero. + * \param Template + * Collection of pointers to four elements that are later used to directly + * address elements. User must supply the template, this routine will + * fill it. + */ + +spError +spGetOnes( + spMatrix Matrix, + int Pos, + int Neg, + int Eqn, + struct spTemplate *Template +) +{ +/* Begin `spGetOnes'. */ + Template->Element4Negated = spGetElement( Matrix, Neg, Eqn ); + Template->Element3Negated = spGetElement( Matrix, Eqn, Neg ); + Template->Element2 = spGetElement( Matrix, Pos, Eqn ); + Template->Element1 = spGetElement( Matrix, Eqn, Pos ); + if + ( (Template->Element1 == NULL) + OR (Template->Element2 == NULL) + OR (Template->Element3Negated == NULL) + OR (Template->Element4Negated == NULL) + ) return spNO_MEMORY; + + spADD_REAL_QUAD( *Template, 1.0 ); + return spOKAY; +} +#endif /* QUAD_ELEMENT */ + + + + + + + +/* + * FIND DIAGONAL + * + * This routine is used to find a diagonal element. It will not + * create it if it does not exist. + * + * >>> Returned: + * A pointer to the desired element, or NULL if it does not exist. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Index <input> (int) + * Row, Col index for diagonal element. + * + * >>> Local variables: + * pElement (ElementPtr) + * Pointer to an element in the matrix. + */ + +ElementPtr +spcFindDiag( + MatrixPtr Matrix, + register int Index +) +{ +register ElementPtr pElement; + +/* Begin `spcFindDiag'. */ + pElement = Matrix->FirstInCol[Index]; + +/* Search column for element. */ + while ((pElement != NULL) AND (pElement->Row < Index)) + pElement = pElement->NextInCol; + if (pElement AND (pElement->Row == Index)) + return pElement; + else + return NULL; +} + + + + + + + + +/* + * CREATE AND SPLICE ELEMENT INTO MATRIX + * + * This routine is used to create new matrix elements and splice them into the + * matrix. + * + * >>> Returned: + * A pointer to the element that was created is returned. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Row <input> (int) + * Row index for element. + * Col <input> (int) + * Column index for element. + * ppToLeft <input-output> (ElementPtr *) + * This contains the address of the pointer to an element to the left + * of the one being created. It is used to speed the search and if it + * is immediately to the left, it is updated with address of the + * created element. + * ppAbove <input-output> (ElementPtr *) + * This contains the address of the pointer to an element above the + * one being created. It is used to speed the search and it if it + * is immediatley above, it is updated with address of the created + * element. + * Fillin <input> (BOOLEAN) + * Flag that indicates if created element is to be a fill-in. + * + * >>> Local variables: + * pElement (ElementPtr) + * Pointer to an element in the matrix. + * pCreatedElement (ElementPtr) + * Pointer to the desired element, the one that was just created. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +ElementPtr +spcCreateElement( + MatrixPtr Matrix, + int Row, + register int Col, + register ElementPtr *ppToLeft, + register ElementPtr *ppAbove, + BOOLEAN Fillin +) +{ +register ElementPtr pElement, pCreatedElement; + +/* Begin `spcCreateElement'. */ + +/* Find element immediately above the desired element. */ + pElement = *ppAbove; + while ((pElement != NULL) AND (pElement->Row < Row)) + { ppAbove = &pElement->NextInCol; + pElement = *ppAbove; + } + if ((pElement != NULL) AND (pElement->Row == Row)) + return pElement; + +/* The desired element does not exist, create it. */ + if (Fillin) + { pCreatedElement = spcGetFillin( Matrix ); + Matrix->Fillins++; + +/* Update Markowitz counts and products. */ + ++Matrix->MarkowitzRow[Row]; + spcMarkoProd( Matrix->MarkowitzProd[Row], + Matrix->MarkowitzRow[Row], + Matrix->MarkowitzCol[Row] ); + if ((Matrix->MarkowitzRow[Row] == 1) AND + (Matrix->MarkowitzCol[Row] != 0)) + { + Matrix->Singletons--; + } + ++Matrix->MarkowitzCol[Col]; + spcMarkoProd( Matrix->MarkowitzProd[Col], + Matrix->MarkowitzCol[Col], + Matrix->MarkowitzRow[Col] ); + if ((Matrix->MarkowitzRow[Col] != 0) AND + (Matrix->MarkowitzCol[Col] == 1)) + { + Matrix->Singletons--; + } + } + else + { pCreatedElement = spcGetElement( Matrix ); + Matrix->NeedsOrdering = YES; + } + if (pCreatedElement == NULL) return NULL; + Matrix->Elements++; + +/* Initialize Element. */ + pCreatedElement->Row = Row; + pCreatedElement->Col = Col; + pCreatedElement->Real = 0.0; +#if spCOMPLEX + pCreatedElement->Imag = 0.0; +#endif +#if INITIALIZE + pCreatedElement->pInitInfo = NULL; +#endif + +/* If element is on diagonal, store pointer in Diag. */ + if (Row == Col) Matrix->Diag[Row] = pCreatedElement; + +/* Splice element into column. */ + pCreatedElement->NextInCol = *ppAbove; + *ppAbove = pCreatedElement; + +/* Find Element immediately to the left of the fill-in. */ + if (Matrix->RowsLinked) + { pElement = *ppToLeft; + while (pElement != NULL) + { if (pElement->Col < Col) + { ppToLeft = &pElement->NextInRow; + pElement = *ppToLeft; + } + else break; /* while loop */ + } + +/* Splice element into row. */ + pCreatedElement->NextInRow = *ppToLeft; + *ppToLeft = pCreatedElement; + } + return pCreatedElement; +} + + + + + + + + +/* + * + * LINK ROWS + * + * This routine is used to generate the row links. The spGetElement() + * routines do not create row links, which are needed by the spFactor() + * routines. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * + * >>> Local variables: + * pElement (ElementPtr) + * Pointer to an element in the matrix. + * FirstInRowEntry (ElementPtr *) + * A pointer into the FirstInRow array. Points to the FirstInRow entry + * currently being operated upon. + * FirstInRowArray (ArrayOfElementPtrs) + * A pointer to the FirstInRow array. Same as Matrix->FirstInRow but + * resides in a register and requires less indirection so is faster to + * use. + * Col (int) + * Column currently being operated upon. + */ + +void +spcLinkRows( MatrixPtr Matrix ) +{ +register ElementPtr pElement, *FirstInRowEntry; +register ArrayOfElementPtrs FirstInRowArray; +register int Col; + +/* Begin `spcLinkRows'. */ + FirstInRowArray = Matrix->FirstInRow; + for (Col = Matrix->Size; Col >= 1; Col--) + FirstInRowArray[Col] = NULL; + + for (Col = Matrix->Size; Col >= 1; Col--) + { +/* Generate row links for the elements in the Col'th column. */ + pElement = Matrix->FirstInCol[Col]; + + while (pElement != NULL) + { pElement->Col = Col; + FirstInRowEntry = &FirstInRowArray[pElement->Row]; + pElement->NextInRow = *FirstInRowEntry; + *FirstInRowEntry = pElement; + pElement = pElement->NextInCol; + } + } + Matrix->RowsLinked = YES; + return; +} + + + + + + + + +/* + * ENLARGE MATRIX + * + * Increases the size of the matrix. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * NewSize <input> (int) + * The new size of the matrix. + * + * >>> Local variables: + * OldAllocatedSize (int) + * The allocated size of the matrix before it is expanded. + */ + +static void +EnlargeMatrix( + MatrixPtr Matrix, + register int NewSize +) +{ +register int I, OldAllocatedSize = Matrix->AllocatedSize; + +/* Begin `EnlargeMatrix'. */ + Matrix->Size = NewSize; + + if (NewSize <= OldAllocatedSize) + return; + +/* Expand the matrix frame. */ + NewSize = MAX( NewSize, (int)(EXPANSION_FACTOR * OldAllocatedSize) ); + Matrix->AllocatedSize = NewSize; + + if (( REALLOC(Matrix->IntToExtColMap, int, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + if (( REALLOC(Matrix->IntToExtRowMap, int, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + if (( REALLOC(Matrix->Diag, ElementPtr, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + if (( REALLOC(Matrix->FirstInCol, ElementPtr, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + if (( REALLOC(Matrix->FirstInRow, ElementPtr, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + +/* + * Destroy the Markowitz and Intermediate vectors, they will be recreated + * in spOrderAndFactor(). + */ + FREE( Matrix->MarkowitzRow ); + FREE( Matrix->MarkowitzCol ); + FREE( Matrix->MarkowitzProd ); + FREE( Matrix->DoRealDirect ); + FREE( Matrix->DoCmplxDirect ); + FREE( Matrix->Intermediate ); + Matrix->InternalVectorsAllocated = NO; + +/* Initialize the new portion of the vectors. */ + for (I = OldAllocatedSize+1; I <= NewSize; I++) + { Matrix->IntToExtColMap[I] = I; + Matrix->IntToExtRowMap[I] = I; + Matrix->Diag[I] = NULL; + Matrix->FirstInRow[I] = NULL; + Matrix->FirstInCol[I] = NULL; + } + + return; +} + + + + + + + + +#if TRANSLATE + +/* + * EXPAND TRANSLATION ARRAYS + * + * Increases the size arrays that are used to translate external to internal + * row and column numbers. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * NewSize <input> (int) + * The new size of the translation arrays. + * + * >>> Local variables: + * OldAllocatedSize (int) + * The allocated size of the translation arrays before being expanded. + */ + +static void +ExpandTranslationArrays( + MatrixPtr Matrix, + register int NewSize +) +{ +register int I, OldAllocatedSize = Matrix->AllocatedExtSize; + +/* Begin `ExpandTranslationArrays'. */ + Matrix->ExtSize = NewSize; + + if (NewSize <= OldAllocatedSize) + return; + +/* Expand the translation arrays ExtToIntRowMap and ExtToIntColMap. */ + NewSize = MAX( NewSize, (int)(EXPANSION_FACTOR * OldAllocatedSize) ); + Matrix->AllocatedExtSize = NewSize; + + if (( REALLOC(Matrix->ExtToIntRowMap, int, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + if (( REALLOC(Matrix->ExtToIntColMap, int, NewSize+1)) == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + +/* Initialize the new portion of the vectors. */ + for (I = OldAllocatedSize+1; I <= NewSize; I++) + { Matrix->ExtToIntRowMap[I] = -1; + Matrix->ExtToIntColMap[I] = -1; + } + + return; +} +#endif + + + + + + + + + +#if INITIALIZE +/*! + * Initialize the matrix. + * + * With the \a INITIALIZE compiler option (see spConfig.h) set true, + * Sparse allows the user to keep initialization information with each + * structurally nonzero matrix element. Each element has a pointer + * that is set and used by the user. The user can set this pointer + * using spInstallInitInfo() and may be read using spGetInitInfo(). Both + * may be used only after the element exists. The function + * spInitialize() is a user customizable way to initialize the matrix. + * Passed to this routine is a function pointer. spInitialize() sweeps + * through every element in the matrix and checks the \a pInitInfo + * pointer (the user supplied pointer). If the \a pInitInfo is \a NULL, + * which is true unless the user changes it (almost always true for + * fill-ins), then the element is zeroed. Otherwise, the function + * pointer is called and passed the \a pInitInfo pointer as well as the + * element pointer and the external row and column numbers. If the + * user sets the value of each element, then spInitialize() replaces + * spClear(). + * + * The user function is expected to return a nonzero integer if there + * is a fatal error and zero otherwise. Upon encountering a nonzero + * return code, spInitialize() terminates, sets the error state of + * the matrix to be \a spMANGLED, and returns the error code. + * + * \return + * Returns the return value of the \a pInit() function. + * \param eMatrix + * Pointer to matrix. + * \param pInit + * Pointer to a function that initializes an element. + + * \see spClear() + */ + +int +spInitialize( + spMatrix eMatrix, + int (*pInit)( + spElement *pElement, + spGenericPtr pInitInfo, + int Row, + int Col + ) +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +int J, Error, Col; + +/* Begin `spInitialize'. */ + ASSERT_IS_SPARSE( Matrix ); + +#if spCOMPLEX +/* Clear imaginary part of matrix if matrix is real but was complex. */ + if (Matrix->PreviousMatrixWasComplex AND NOT Matrix->Complex) + { for (J = Matrix->Size; J > 0; J--) + { pElement = Matrix->FirstInCol[J]; + while (pElement != NULL) + { pElement->Imag = 0.0; + pElement = pElement->NextInCol; + } + } + } +#endif /* spCOMPLEX */ + +/* Initialize the matrix. */ + for (J = Matrix->Size; J > 0; J--) + { pElement = Matrix->FirstInCol[J]; + Col = Matrix->IntToExtColMap[J]; + while (pElement != NULL) + { if (pElement->pInitInfo == NULL) + { pElement->Real = 0.0; +# if spCOMPLEX + pElement->Imag = 0.0; +# endif + } + else + { Error = (*pInit)((RealNumber *)pElement, pElement->pInitInfo, + Matrix->IntToExtRowMap[pElement->Row], Col); + if (Error) + { Matrix->Error = spMANGLED; + return Error; + } + + } + pElement = pElement->NextInCol; + } + } + +/* Empty the trash. */ + Matrix->TrashCan.Real = 0.0; +#if spCOMPLEX + Matrix->TrashCan.Imag = 0.0; +#endif + + Matrix->Error = spOKAY; + Matrix->Factored = NO; + Matrix->SingularCol = 0; + Matrix->SingularRow = 0; + Matrix->PreviousMatrixWasComplex = Matrix->Complex; + return 0; +} + + + + +/*! + * This function installs a pointer to a data structure that is used + * to contain initialization information to a matrix element. It is + * is then used by spInitialize() to initialize the matrix. + * + * \param pElement + * Pointer to matrix element. + * \param pInitInfo + * Pointer to the data structure that will contain initialiation + * information. + * \see spInitialize() + */ + +void +spInstallInitInfo( + spElement *pElement, + spGenericPtr pInitInfo +) +{ +/* Begin `spInstallInitInfo'. */ + vASSERT( pElement != NULL, "Invalid element pointer" ); + + ((ElementPtr)pElement)->pInitInfo = pInitInfo; +} + + +/*! + * This function returns a pointer to a data structure that is used + * to contain initialization information to a matrix element. + * + * \return + * The pointer to the initialiation information data structure + * that is associated with a particular matrix element. + * + * \param pElement + * Pointer to the matrix element. + * + * \see spInitialize() + */ +spGenericPtr +spGetInitInfo( + spElement *pElement +) +{ +/* Begin `spGetInitInfo'. */ + vASSERT( pElement != NULL, "Invalid element pointer" ); + + return (spGenericPtr)((ElementPtr)pElement)->pInitInfo; +} +#endif /* INITIALIZE */ Added: trunk/math/SPARSE/spconfig.h =================================================================== --- trunk/math/SPARSE/spconfig.h (rev 0) +++ trunk/math/SPARSE/spconfig.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,568 @@ +/* CONFIGURATION MACRO DEFINITIONS for sparse matrix routines */ +/*! + * \file + * + * This file contains macros for the sparse matrix routines that are used + * to define the personality of the routines. The user is expected to + * modify this file to maximize the performance of the routines with + * his/her matrices. + * + * Macros are distinguished by using solely capital letters in their + * identifiers. This contrasts with C defined identifiers which are + * strictly lower case, and program variable and procedure names which use + * both upper and lower case. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ + + +/* + * Revision and copyright information. + * + * Copyright (c) 1985-2003 by Kenneth S. Kundert + * + * $Date: 2003/06/30 19:41:29 $ + * $Revision: 1.5 $ + */ + + +#ifndef spCONFIG_DEFS +#define spCONFIG_DEFS + + + + +#ifdef spINSIDE_SPARSE +/* + * OPTIONS + * + * These are compiler options. Set each option to one to compile that + * section of the code. If a feature is not desired, set the macro + * to NO. + */ + +/* Begin options. */ + +/* Arithmetic Precision + * + * The precision of the arithmetic used by Sparse can be set by + * changing changing the spREAL macro. This macro is + * contained in the file spMatrix.h. It is strongly suggested to + * used double precision with circuit simulators. Note that + * because C always performs arithmetic operations in double + * precision, the only benefit to using single precision is that + * less storage is required. There is often a noticeable speed + * penalty when using single precision. Sparse internally refers + * to a spREAL as a RealNumber. + */ + +/*! + * This specifies that the routines are expected to handle real + * systems of equations. The routines can be compiled to handle + * both real and complex systems at the same time, but there is a + * slight speed and memory advantage if the routines are complied + * to handle only real systems of equations. + */ +#define REAL YES + +/*! + * Setting this compiler flag true (1) makes the matrix + * expandable before it has been factored. If the matrix is + * expandable, then if an element is added that would be + * considered out of bounds in the current matrix, the size of + * the matrix is increased to hold that element. As a result, + * the size of the matrix need not be known before the matrix is + * built. The matrix can be allocated with size zero and expanded. + */ +#define EXPANDABLE YES + +/*! + * This option allows the set of external row and column numbers + * to be non-packed. In other words, the row and column numbers + * do not have to be contiguous. The priced paid for this + * flexibility is that when \a TRANSLATE is set true, the time + * required to initially build the matrix will be greater because + * the external row and column number must be translated into + * internal equivalents. This translation brings about other + * benefits though. First, the spGetElement() and + * spGetAdmittance() routines may be used after the matrix has + * been factored. Further, elements, and even rows and columns, + * may be added to the matrix, and row and columns may be deleted + * from the matrix, after it has been factored. Note that when + * the set of row and column number is not a packed set, neither + * are the \a RHS and \a Solution vectors. Thus the size of these + * vectors must be at least as large as the external size, which + * is the value of the largest given row or column numbers. + */ +#define TRANSLATE YES + +/*! + * Causes the spInitialize(), spGetInitInfo(), and + * spInstallInitInfo() routines to be compiled. These routines + * allow the user to store and read one pointer in each nonzero + * element in the matrix. spInitialize() then calls a user + * specified function for each structural nonzero in the matrix, + * and includes this pointer as well as the external row and + * column numbers as arguments. This allows the user to write + * custom matrix initialization routines. + */ +#define INITIALIZE YES + +/*! + * Many matrices, and in particular node- and modified-node + * admittance matrices, tend to be nearly symmetric and nearly + * diagonally dominant. For these matrices, it is a good idea to + * select pivots from the diagonal. With this option enabled, + * this is exactly what happens, though if no satisfactory pivot + * can be found on the diagonal, an off-diagonal pivot will be + * used. If this option is disabled, Sparse does not + * preferentially search the diagonal. Because of this, Sparse + * has a wider variety of pivot candidates available, and so + * presumably fewer fill-ins will be created. However, the + * initial pivot selection process will take considerably longer. + * If working with node admittance matrices, or other matrices + * with a strong diagonal, it is probably best to use + * \a DIAGONAL_PIVOTING for two reasons. First, accuracy will be + * better because pivots will be chosen from the large diagonal + * elements, thus reducing the chance of growth. Second, a near + * optimal ordering will be chosen quickly. If the class of + * matrices you are working with does not have a strong diagonal, + * do not use \a DIAGONAL_PIVOTING, but consider using a larger + * threshold. When \a DIAGONAL_PIVOTING is turned off, the following + * options and constants are not used: \a MODIFIED_MARKOWITZ, + * \a MAX_MARKOWITZ_TIES, and \a TIES_MULTIPLIER. + */ +#define DIAGONAL_PIVOTING YES + +/*! + * This determines whether arrays start at an index of zero or one. + * This option is necessitated by the fact that standard C + * convention dictates that arrays begin with an index of zero but + * the standard mathematic convention states that arrays begin with + * an index of one. So if you prefer to start your arrays with + * zero, or your calling Sparse from FORTRAN, set ARRAY_OFFSET to + * NO or 0. Otherwise, set ARRAY_OFFSET to YES or 1. Note that if + * you use an offset of one, the arrays that you pass to Sparse + * must have an allocated length of one plus the size of the + * matrix. ARRAY_OFFSET must be either 0 or 1, no other offsets + * are valid. + */ +#define ARRAY_OFFSET NOT FORTRAN + +/*! + * This specifies that the modified Markowitz method of pivot + * selection is to be used. The modified Markowitz method differs + * from standard Markowitz in two ways. First, under modified + * Markowitz, the search for a pivot can be terminated early if a + * adequate (in terms of sparsity) pivot candidate is found. + * Thus, when using modified Markowitz, the initial factorization + * can be faster, but at the expense of a suboptimal pivoting + * order that may slow subsequent factorizations. The second + * difference is in the way modified Markowitz breaks Markowitz + * ties. When two or more elements are pivot candidates and they + * all have the same Markowitz product, then the tie is broken by + * choosing the element that is best numerically. The numerically + * best element is the one with the largest ratio of its magnitude + * to the magnitude of the largest element in the same column, + * excluding itself. The modified Markowitz method results in + * marginally better accuracy. This option is most appropriate + * for use when working with very large matrices where the initial + * factor time represents an unacceptable burden. \a NO is recommended. + */ +#define MODIFIED_MARKOWITZ NO + +/*! + * This specifies that the spDeleteRowAndCol() routine + * should be compiled. Note that for this routine to be + * compiled, both \a DELETE and \a TRANSLATE should be set true. + */ +#define DELETE YES + +/*! + * This specifies that the spStripFills() routine should be compiled. + */ +#define STRIP YES + +/*! + * This specifies that the routine that preorders modified node + * admittance matrices should be compiled. This routine results + * in greater speed and accuracy if used with this type of + * matrix. + */ +#define MODIFIED_NODAL YES + +/*! + * This specifies that the routines that allow four related + * elements to be entered into the matrix at once should be + * compiled. These elements are usually related to an + * admittance. The routines affected by \a QUAD_ELEMENT are the + * spGetAdmittance(), spGetQuad() and spGetOnes() routines. + */ +#define QUAD_ELEMENT YES + +/*! + * This specifies that the routines that solve the matrix as if + * it was transposed should be compiled. These routines are + * useful when performing sensitivity analysis using the adjoint + * method. + */ +#define TRANSPOSE YES + +/*! + * This specifies that the routine that performs scaling on the + * matrix should be complied. Scaling is not strongly + * supported. The routine to scale the matrix is provided, but + * no routines are provided to scale and descale the RHS and + * Solution vectors. It is suggested that if scaling is desired, + * it only be preformed when the pivot order is being chosen [in + * spOrderAndFactor()]. This is the only time scaling has + * an effect. The scaling may then either be removed from the + * solution by the user or the scaled factors may simply be + * thrown away. \a NO is recommended. + */ +#define SCALING YES + +/*! + * This specifies that routines that are used to document the + * matrix, such as spPrint() and spFileMatrix(), should be + * compiled. + */ +#define DOCUMENTATION YES + +/*! + * This specifies that routines that are used to multily the + * matrix by a vector, such as spMultiply() and spMultTransposed(), should be + * compiled. + */ +#define MULTIPLICATION YES + +/*! + * This specifies that the routine spDeterminant() should be complied. + */ +#define DETERMINANT YES + +/*! + * This specifies that spLargestElement() and spRoundoff() should + * be compiled. These routines are used to check the stability (and + * hence the quality of the pivoting) of the factorization by + * computing a bound on the size of the element is the matrix + * \f$ E = A - LU \f$. If this bound is very high after applying + * spOrderAndFactor(), then the pivot threshold should be raised. + * If the bound increases greatly after using spFactor(), then the + * matrix should probably be reordered. Recomend \a NO. + */ +#define STABILITY YES + +/*! + * This specifies that spCondition() and spNorm(), the code that + * computes a good estimate of the condition number of the matrix, + * should be compiled. Recomend \a NO. + */ +#define CONDITION YES + +/*! + * This specifies that spPseudoCondition(), the code that computes + * a crude and easily fooled indicator of ill-conditioning in the + * matrix, should be compiled. Recomend \a NO. + */ +#define PSEUDOCONDITION YES + +/*! + * This specifies that the \a FORTRAN interface routines should be + * compiled. When interfacing to \a FORTRAN programs, the \a ARRAY_OFFSET + * options should be set to NO. + */ +#define FORTRAN NO + +/*! + * This specifies that additional error checking will be compiled. + * The type of error checked are those that are common when the + * matrix routines are first integrated into a user's program. Once + * the routines have been integrated in and are running smoothly, this + * option should be turned off. \a YES is recommended. + */ +#define spDEBUG YES + +#endif /* spINSIDE_SPARSE */ + +/* + * The following options affect Sparse exports and so are exported as a + * side effect. For this reason they use the `sp' prefix. The boolean + * constants YES an NO are not defined in spMatrix.h to avoid conflicts + * with user code, so use 0 for NO and 1 for YES. + */ + +/*! + * This specifies that the routines will be complied to handle + * complex systems of equations. + */ +#define spCOMPLEX 1 + +/*! + * This specifies the format for complex vectors. If this is set + * false then a complex vector is made up of one double sized + * array of RealNumber's in which the real and imaginary numbers + * are placed alternately in the array. In other + * words, the first entry would be Complex[1].Real, then comes + * Complex[1].Imag, then Complex[2].Real, etc. If + * \a spSEPARATED_COMPLEX_VECTORS is set true, then each complex + * vector is represented by two arrays of \a spREALs, one with + * the real terms, the other with the imaginary. \a NO is recommended. + */ +#define spSEPARATED_COMPLEX_VECTORS 0 + +#ifdef spINSIDE_SPARSE + + + + + + + +/* + * MATRIX CONSTANTS + * + * These constants are used throughout the sparse matrix routines. They + * should be set to suit the type of matrix being solved. + */ + +/* Begin constants. */ + +/*! + * The relative threshold used if the user enters an invalid + * threshold. Also the threshold used by spFactor() when + * calling spOrderAndFactor(). The default threshold should + * not be less than or equal to zero nor larger than one. + * 0.001 is recommended. + */ +#define DEFAULT_THRESHOLD 1.0e-3 + +/*! + * This indicates whether spOrderAndFactor() should use diagonal + * pivoting as default. This issue only arises when + * spOrderAndFactor() is called from spFactor(). \a YES is recommended. + */ +#define DIAG_PIVOTING_AS_DEFAULT YES + +/*! + * This number multiplied by the size of the matrix equals the number + * of elements for which memory is initially allocated in spCreate(). + * 6 is recommended. + */ +#define SPACE_FOR_ELEMENTS 6 + +/*! + * This number multiplied by the size of the matrix equals the number + * of elements for which memory is initially allocated and specifically + * reserved for fill-ins in spCreate(). 4 is recommended. + */ +#define SPACE_FOR_FILL_INS 4 + +/*! + * The number of matrix elements requested from the malloc utility on + * each call to it. Setting this value greater than 1 reduces the + * amount of overhead spent in this system call. On a virtual memory + * machine, its good to allocate slightly less than a page worth of + * elements at a time (or some multiple thereof). + * 31 is recommended. + */ +#define ELEMENTS_PER_ALLOCATION 31 + +/*! + * The minimum allocated size of a matrix. Note that this does not + * limit the minimum size of a matrix. This just prevents having to + * resize a matrix many times if the matrix is expandable, large and + * allocated with an estimated size of zero. This number should not + * be less than one. + */ +#define MINIMUM_ALLOCATED_SIZE 6 + +/*! + * The amount the allocated size of the matrix is increased when it + * is expanded. + */ +#define EXPANSION_FACTOR 1.5 + +/*! + * Some terminology should be defined. The Markowitz row count is the number + * of non-zero elements in a row excluding the one being considered as pivot. + * There is one Markowitz row count for every row. The Markowitz column + * is defined similarly for columns. The Markowitz product for an element + * is the product of its row and column counts. It is a measure of how much + * work would be required on the next step of the factorization if that + * element were chosen to be pivot. A small Markowitz product is desirable. + * + * This number is used for two slightly different things, both of which + * relate to the search for the best pivot. First, it is the maximum + * number of elements that are Markowitz tied that will be sifted + * through when trying to find the one that is numerically the best. + * Second, it creates an upper bound on how large a Markowitz product + * can be before it eliminates the possibility of early termination + * of the pivot search. In other words, if the product of the smallest + * Markowitz product yet found and \a TIES_MULTIPLIER is greater than + * \a MAX_MARKOWITZ_TIES, then no early termination takes place. + * Set \a MAX_MARKOWITZ_TIES to some small value if no early termination of + * the pivot search is desired. An array of RealNumbers is allocated + * of size \a MAX_MARKOWITZ_TIES so it must be positive and shouldn't + * be too large. Active when MODIFIED_MARKOWITZ is 1 (YES). + * 100 is recommended. + * \see TIES_MULTIPLIER + */ +#define MAX_MARKOWITZ_TIES 100 + +/*! + * Specifies the number of Markowitz ties that are allowed to occur + * before the search for the pivot is terminated early. Set to some + * large value if no early termination of the pivot search is desired. + * This number is multiplied times the Markowitz product to determine + * how many ties are required for early termination. This means that + * more elements will be searched before early termination if a large + * number of fill-ins could be created by accepting what is currently + * considered the best choice for the pivot. Active when + * \a MODIFIED_MARKOWITZ is 1 (YES). Setting this number to zero + * effectively eliminates all pivoting, which should be avoided. + * This number must be positive. \a TIES_MULTIPLIER is also used when + * diagonal pivoting breaks down. 5 is recommended. + * \see MAX_MARKOWITZ_TIES + */ +#define TIES_MULTIPLIER 5 + +/*! + * Which partition mode is used by spPartition() as default. + * Possibilities include \a spDIRECT_PARTITION (each row used direct + * addressing, best for a few relatively dense matrices), + * \a spINDIRECT_PARTITION (each row used indirect addressing, best + * for a few very sparse matrices), and \a spAUTO_PARTITION (direct or + * indirect addressing is chosen on a row-by-row basis, carries a large + * overhead, but speeds up both dense and sparse matrices, best if there + * is a large number of matrices that can use the same ordering. + */ +#define DEFAULT_PARTITION spAUTO_PARTITION + +/*! + * The number of characters per page width. Set to 80 for terminal, + * 132 for line printer. Controls how many columns printed by + * spPrint() per page width. + */ +#define PRINTER_WIDTH 80 + + + + + + + + + + +#endif /* spINSIDE_SPARSE */ +/* + * PORTABILITY MACROS + */ + +#ifdef __STDC__ +# define spcCONCAT(prefix,suffix) prefix ## suffix +# define spcQUOTE(x) # x +# define spcFUNC_NEEDS_FILE(func,file) \ + func ## _requires_ ## file ## _to_be_included_ +#else +# define spcCONCAT(prefix,suffix) prefix/**/suffix +# define spcQUOTE(x) "x" +# define spcFUNC_NEEDS_FILE(func,file) \ + func/**/_requires_/**/file/**/_to_be_included_ +#endif + +#if defined(__cplusplus) || defined(c_plusplus) + /* + * Definitions for C++ + */ +# define spcEXTERN extern "C" +# define spcNO_ARGS +# define spcCONST const + typedef void *spGenericPtr; +#else +#ifdef __STDC__ + /* + * Definitions for ANSI C + */ +# define spcEXTERN extern +# define spcNO_ARGS void +# define spcCONST const + typedef void *spGenericPtr; +# else + /* + * Definitions for K&R C -- ignore function prototypes + */ +# define spcEXTERN extern +# define spcNO_ARGS +# define spcCONST + typedef char *spGenericPtr; +#endif +#endif + +#ifdef spINSIDE_SPARSE + + + + + + + +/* + * MACHINE CONSTANTS + * + * These numbers must be updated when the program is ported to a new machine. + */ + +/*! The resolution of spREAL. */ +#define MACHINE_RESOLUTION AG_DBL_EPSILON + +/*! The largest possible value of spREAL. */ +#define LARGEST_REAL AG_DBL_MAX + +/*! The smalles possible positive value of spREAL. */ +#define SMALLEST_REAL AG_DBL_MIN + +/*! The largest possible value of shorts. */ +#define LARGEST_SHORT_INTEGER (0xffff-1) + +/*! The largest possible value of longs. */ +#define LARGEST_LONG_INTEGER AG_LONG_MAX + + + + + + +/* ANNOTATION */ +/*! + * This macro changes the amount of annotation produced by the matrix + * routines. The annotation is used as a debugging aid. Change the number + * associated with \a ANNOTATE to change the amount of annotation produced by + * the program. Possible values include \a NONE, \a ON_STRANGE_BEHAVIOR, and + * \a FULL. \a NONE is recommended. + */ +#define ANNOTATE NONE + +/*! + * A possible value for \a ANNOTATE. Disables all annotation. + */ +#define NONE 0 + +/*! + * A possible value for \a ANNOTATE. Causes annotation to be produce + * upon unusual occurances only. + */ +#define ON_STRANGE_BEHAVIOR 1 + +/*! + * A possible value for \a ANNOTATE. Enables full annotation. + */ +#define FULL 2 + +#endif /* spINSIDE_SPARSE */ +#endif /* spCONFIG_DEFS */ Added: trunk/math/SPARSE/spdefs.h =================================================================== --- trunk/math/SPARSE/spdefs.h (rev 0) +++ trunk/math/SPARSE/spdefs.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,945 @@ +/* + * DATA STRUCTURE AND MACRO DEFINITIONS for Sparse. + * + * Author: Advising professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + * + * This file contains common type definitions and macros for the sparse + * matrix routines. These definitions are of no interest to the user. + */ + + +/* + * Revision and copyright information. + * + * Copyright (c) 1985-2003 by Kenneth S. Kundert + * + * $Date: 2003/06/29 04:19:52 $ + * $Revision: 1.2 $ + */ + + + + +/* + * If running lint, change some of the compiler options to get a more + * complete inspection. + */ + +#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 spDEBUG + +#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 spDEBUG YES + +#define LINT YES +#else /* not lint */ +#define LINT NO +#endif /* not lint */ + + + + + + + +/* + * MACRO DEFINITIONS + * + * Macros are distinguished by using solely capital letters in their + * identifiers. This contrasts with C defined identifiers which are strictly + * lower case, and program variable and procedure names which use both upper + * and lower case. + */ + +/* Begin macros. */ + +/* Boolean data type */ +#define BOOLEAN int +#define NO 0 +#define YES 1 +#define NOT ! +#define AND && +#define OR || + +/* NULL pointer */ +#ifndef NULL +#define NULL 0 +#endif + +/* Define macros for validating matrix. */ +#define SPARSE_ID 0xDeadBeef /* Arbitrary. */ +#define IS_SPARSE(matrix) (((matrix) != NULL) AND \ + ((matrix)->ID == SPARSE_ID)) +#define NO_ERRORS(matrix) (((matrix)->Error >= spOKAY) AND \ + ((matrix)->Error < spFATAL)) +#define IS_FACTORED(matrix) ((matrix)->Factored AND \ + NOT (matrix)->NeedsOrdering) + +#define ASSERT_IS_SPARSE(matrix) vASSERT( IS_SPARSE(matrix), \ + spcMatrixIsNotValid ) +#define ASSERT_NO_ERRORS(matrix) vASSERT( NO_ERRORS(matrix), \ + spcErrorsMustBeCleared ) +#define ASSERT_IS_FACTORED(matrix) vASSERT( IS_FACTORED(matrix), \ + spcMatrixMustBeFactored ) +#define ASSERT_IS_NOT_FACTORED(matrix) vASSERT( NOT (matrix)->Factored, \ + spcMatrixMustNotBeFactored ) + +/* Macro commands */ +/* Macro functions that return the maximum or minimum independent of type. */ +#if 0 +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +/* Macro function that returns the absolute value of a floating point number. */ +#define ABS(a) ((a) < 0 ? -(a) : (a)) + +/* Macro function that returns the square of a number. */ +#define SQR(a) ((a)*(a)) + +/* Macro procedure that swaps two entities. */ +#define SWAP(type, a, b) {type swapx; swapx = a; a = b; b = swapx;} + + +/* + * COMPLEX OPERATION MACROS + */ + +/* 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 + +/* Complex assignment statements. */ +#define CMPLX_ASSIGN(to,from) \ +{ (to).Real = (from).Real; \ + (to).Imag = (from).Imag; \ +} +#define CMPLX_CONJ_ASSIGN(to,from) \ +{ (to).Real = (from).Real; \ + (to).Imag = -(from).Imag; \ +} +#define CMPLX_NEGATE_ASSIGN(to,from) \ +{ (to).Real = -(from).Real; \ + (to).Imag = -(from).Imag; \ +} +#define CMPLX_CONJ_NEGATE_ASSIGN(to,from) \ +{ (to).Real = -(from).Real; \ + (to).Imag = (from).Imag; \ +} +#define CMPLX_CONJ(a) (a).Imag = -(a).Imag +#define CMPLX_NEGATE(a) \ +{ (a).Real = -(a).Real; \ + (a).Imag = -(a).Imag; \ +} + +/* Macro that returns the approx magnitude (L-1 norm) of a complex number. */ +#define CMPLX_1_NORM(a) (ABS((a).Real) + ABS((a).Imag)) + +/* Macro that returns the approx magnitude (L-infinity norm) of a complex. */ +#define CMPLX_INF_NORM(a) (MAX (ABS((a).Real),ABS((a).Imag))) + +/* 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)) + +/* Macro function that performs complex addition. */ +#define CMPLX_ADD(to,from_a,from_b) \ +{ (to).Real = (from_a).Real + (from_b).Real; \ + (to).Imag = (from_a).Imag + (from_b).Imag; \ +} + +/* Macro function that performs complex subtraction. */ +#define CMPLX_SUBT(to,from_a,from_b) \ +{ (to).Real = (from_a).Real - (from_b).Real; \ + (to).Imag = (from_a).Imag - (from_b).Imag; \ +} + +/* Macro function that is equivalent to += operator for complex numbers. */ +#define CMPLX_ADD_ASSIGN(to,from) \ +{ (to).Real += (from).Real; \ + (to).Imag += (from).Imag; \ +} + +/* Macro function that is equivalent to -= operator for complex numbers. */ +#define CMPLX_SUBT_ASSIGN(to,from) \ +{ (to).Real -= (from).Real; \ + (to).Imag -= (from).Imag; \ +} + +/* Macro function that multiplies a complex number by a scalar. */ +#define SCLR_MULT(to,sclr,cmplx) \ +{ (to).Real = (sclr) * (cmplx).Real; \ + (to).Imag = (sclr) * (cmplx).Imag; \ +} + +/* Macro function that multiply-assigns a complex number by a scalar. */ +#define SCLR_MULT_ASSIGN(to,sclr) \ +{ (to).Real *= (sclr); \ + (to).Imag *= (sclr); \ +} + +/* Macro function that multiplies two complex numbers. */ +#define CMPLX_MULT(to,from_a,from_b) \ +{ (to).Real = (from_a).Real * (from_b).Real - \ + (from_a).Imag * (from_b).Imag; \ + (to).Imag = (from_a).Real * (from_b).Imag + \ + (from_a).Imag * (from_b).Real; \ +} + +/* Macro function that implements to *= from for complex numbers. */ +#define CMPLX_MULT_ASSIGN(to,from) \ +{ RealNumber to_real_ = (to).Real; \ + (to).Real = to_real_ * (from).Real - \ + (to).Imag * (from).Imag; \ + (to).Imag = to_real_ * (from).Imag + \ + (to).Imag * (from).Real; \ +} + +/* Macro function that multiplies two complex numbers, the first of which is + * conjugated. */ +#define CMPLX_CONJ_MULT(to,from_a,from_b) \ +{ (to).Real = (from_a).Real * (from_b).Real + \ + (from_a).Imag * (from_b).Imag; \ + (to).Imag = (from_a).Real * (from_b).Imag - \ + (from_a).Imag * (from_b).Real; \ +} + +/* Macro function that multiplies two complex numbers and then adds them + * 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 - \ + (mult_a).Imag * (mult_b).Imag + (add).Real; \ + (to).Imag = (mult_a).Real * (mult_b).Imag + \ + (mult_a).Imag * (mult_b).Real + (add).Imag; \ +} + +/* Macro function that subtracts the product of two complex numbers from + * 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 + \ + (mult_a).Imag * (mult_b).Imag; \ + (to).Imag = (subt).Imag - (mult_a).Real * (mult_b).Imag - \ + (mult_a).Imag * (mult_b).Real; \ +} + +/* Macro function that multiplies two complex numbers and then adds them + * to another. to = add + mult_a* * mult_b where mult_a* represents mult_a + * conjugate. */ +#define CMPLX_CONJ_MULT_ADD(to,mult_a,mult_b,add) \ +{ (to).Real = (mult_a).Real * (mult_b).Real + \ + (mult_a).Imag * (mult_b).Imag + (add).Real; \ + (to).Imag = (mult_a).Real * (mult_b).Imag - \ + (mult_a).Imag * (mult_b).Real + (add).Imag; \ +} + +/* Macro function that multiplies two complex numbers and then adds them + * 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 - \ + (from_a).Imag * (from_b).Imag; \ + (to).Imag += (from_a).Real * (from_b).Imag + \ + (from_a).Imag * (from_b).Real; \ +} + +/* Macro function that multiplies two complex numbers and then subtracts them + * from another. */ +#define CMPLX_MULT_SUBT_ASSIGN(to,from_a,from_b) \ +{ (to).Real -= (from_a).Real * (from_b).Real - \ + (from_a).Imag * (from_b).Imag; \ + (to).Imag -= (from_a).Real * (from_b).Imag + \ + (from_a).Imag * (from_b).Real; \ +} + +/* Macro function that multiplies two complex numbers and then adds them + * to the destination. to += from_a* * from_b where from_a* represents from_a + * conjugate. */ +#define CMPLX_CONJ_MULT_ADD_ASSIGN(to,from_a,from_b) \ +{ (to).Real += (from_a).Real * (from_b).Real + \ + (from_a).Imag * (from_b).Imag; \ + (to).Imag += (from_a).Real * (from_b).Imag - \ + (from_a).Imag * (from_b).Real; \ +} + +/* Macro function that multiplies two complex numbers and then subtracts them + * from the destination. to -= from_a* * from_b where from_a* represents from_a + * conjugate. */ +#define CMPLX_CONJ_MULT_SUBT_ASSIGN(to,from_a,from_b) \ +{ (to).Real -= (from_a).Real * (from_b).Real + \ + (from_a).Imag * (from_b).Imag; \ + (to).Imag -= (from_a).Real * (from_b).Imag - \ + (from_a).Imag * (from_b).Real; \ +} + +/* + * Macro functions that provide complex division. + */ + +/* Complex division: to = num / den */ +#define CMPLX_DIV(to,num,den) \ +{ RealNumber r_, s_; \ + if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \ + ((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \ + { r_ = (den).Imag / (den).Real; \ + s_ = (den).Real + r_*(den).Imag; \ + (to).Real = ((num).Real + r_*(num).Imag)/s_; \ + (to).Imag = ((num).Imag - r_*(num).Real)/s_; \ + } \ + else \ + { r_ = (den).Real / (den).Imag; \ + s_ = (den).Imag + r_*(den).Real; \ + (to).Real = (r_*(num).Real + (num).Imag)/s_; \ + (to).Imag = (r_*(num).Imag - (num).Real)/s_; \ + } \ +} + +/* Complex division and assignment: num /= den */ +#define CMPLX_DIV_ASSIGN(num,den) \ +{ RealNumber r_, s_, t_; \ + if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \ + ((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \ + { r_ = (den).Imag / (den).Real; \ + s_ = (den).Real + r_*(den).Imag; \ + t_ = ((num).Real + r_*(num).Imag)/s_; \ + (num).Imag = ((num).Imag - r_*(num).Real)/s_; \ + (num).Real = t_; \ + } \ + else \ + { r_ = (den).Real / (den).Imag; \ + s_ = (den).Imag + r_*(den).Real; \ + t_ = (r_*(num).Real + (num).Imag)/s_; \ + (num).Imag = (r_*(num).Imag - (num).Real)/s_; \ + (num).Real = t_; \ + } \ +} + +/* Complex reciprocation: to = 1.0 / den */ +#define CMPLX_RECIPROCAL(to,den) \ +{ RealNumber r_; \ + if (((den).Real >= (den).Imag AND (den).Real > -(den).Imag) OR \ + ((den).Real < (den).Imag AND (den).Real <= -(den).Imag)) \ + { r_ = (den).Imag / (den).Real; \ + (to).Imag = -r_*((to).Real = 1.0/((den).Real + r_*(den).Imag)); \ + } \ + else \ + { r_ = (den).Real / (den).Imag; \ + (to).Real = -r_*((to).Imag = -1.0/((den).Imag + r_*(den).Real));\ + } \ +} + + + + + + +/* + * ASSERT and ABORT + * + * Macro used to assert that if the code is working correctly, then + * a condition must be true. If not, then execution is terminated + * and an error message is issued stating that there is an internal + * error and giving the file and line number. These assertions are + * not evaluated unless the spDEBUG flag is true. + */ + +#if spDEBUG +#define ASSERT(condition) \ +{ if (NOT(condition)) \ + { (void)fflush(stdout); \ + (void)fprintf(stderr, "sparse: internal error detected in file `%s' at line %d.\n assertion `%s' failed.\n",\ + __FILE__, __LINE__, spcQUOTE(condition) ); \ + (void)fflush(stderr); \ + abort(); \ + } \ +} +#else +#define ASSERT(condition) +#endif + +#if spDEBUG +#define vASSERT(condition,message) \ +{ if (NOT(condition)) \ + vABORT(message); \ +} +#else +#define vASSERT(condition,message) +#endif + +#if spDEBUG +#define vABORT(message) \ +{ (void)fflush(stdout); \ + (void)fprintf(stderr, "sparse: internal error detected in file `%s' at line %d.\n %s.\n", __FILE__, __LINE__, message );\ + (void)fflush(stderr); \ + abort(); \ +} + +#define ABORT() \ +{ (void)fflush(stdout); \ + (void)fprintf(stderr, "sparse: internal error detected in file `%s' at line %d.\n", __FILE__, __LINE__ ); \ + (void)fflush(stderr); \ + abort(); \ +} +#else +#define vABORT(message) abort() +#define ABORT() abort() +#endif + + + + + + +/* + * IMAGINARY VECTORS + * + * The imaginary vectors iRHS and iSolution are only needed when the + * options spCOMPLEX and spSEPARATED_COMPLEX_VECTORS are set. The following + * macro makes it easy to include or exclude these vectors as needed. + */ + +#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS +#define IMAG_VECTORS , iRHS, iSolution +#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 + + + + + + +/* + * MEMORY ALLOCATION + */ + +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); + +#define ALLOC(type,number) ((type *)malloc((unsigned)(sizeof(type)*(number)))) +#define REALLOC(ptr,type,number) \ + ptr = (type *)realloc((char *)ptr,(unsigned)(sizeof(type)*(number))) +#define FREE(ptr) { if ((ptr) != NULL) free((char *)(ptr)); (ptr) = NULL; } + + +/* Calloc that properly handles allocating a cleared vector. */ +#define CALLOC(ptr,type,number) \ +{ int i; ptr = ALLOC(type, number); \ + if (ptr != (type *)NULL) \ + for(i=(number)-1;i>=0; i--) ptr[i] = (type) 0; \ +} + + + + + + + +/* + * Utility Functions + */ +/* + * Compute the product of two intergers while avoiding overflow. + * Used when computing Markowitz products. + */ + +#define spcMarkoProd(product, op1, op2) \ + if (( (op1) > LARGEST_SHORT_INTEGER AND (op2) != 0) OR \ + ( (op2) > LARGEST_SHORT_INTEGER AND (op1) != 0)) \ + { double fProduct = (double)(op1) * (double)(op2); \ + if (fProduct >= LARGEST_LONG_INTEGER) \ + (product) = LARGEST_LONG_INTEGER; \ + else \ + (product) = (long)fProduct; \ + } \ + else (product) = (op1)*(op2); + + + + + + +/* + * REAL NUMBER + */ + +/* Begin `RealNumber'. */ + +typedef spREAL RealNumber, *RealVector; + + + + + + + + +/* + * COMPLEX NUMBER DATA STRUCTURE + * + * >>> Structure fields: + * Real (RealNumber) + * The real portion of the number. Real must be the first + * field in this structure. + * Imag (RealNumber) + * The imaginary portion of the number. This field must follow + * immediately after Real. + */ + +/* Begin `ComplexNumber'. */ + +typedef struct +{ RealNumber Real; + RealNumber Imag; +} ComplexNumber, *ComplexVector; + + + + + + + + +/* + * MATRIX ELEMENT DATA STRUCTURE + * + * Every nonzero element in the matrix is stored in a dynamically allocated + * MatrixElement structure. These structures are linked together in an + * orthogonal linked list. Two different MatrixElement structures exist. + * One is used when only real matrices are expected, it is missing an entry + * for imaginary data. The other is used if complex matrices are expected. + * It contains an entry for imaginary data. + * + * >>> Structure fields: + * Real (RealNumber) + * The real portion of the value of the element. Real must be the first + * field in this structure. + * Imag (RealNumber) + * The imaginary portion of the value of the element. If the matrix + * routines are not compiled to handle complex matrices, then this + * field does not exist. If it exists, it must follow immediately after + * Real. + * Row (int) + * The row number of the element. + * Col (int) + * The column number of the element. + * NextInRow (struct MatrixElement *) + * NextInRow contains a pointer to the next element in the row to the + * right of this element. If this element is the last nonzero in the + * row then NextInRow contains NULL. + * NextInCol (struct MatrixElement *) + * NextInCol contains a pointer to the next element in the column below + * this element. If this element is the last nonzero in the column then + * NextInCol contains NULL. + * pInitInfo (spGenericPtr) + * Pointer to user data used for initialization of the matrix element. + * Initialized to NULL. + * + * >>> Type definitions: + * ElementPtr + * A pointer to a MatrixElement. + * ArrayOfElementPtrs + * An array of ElementPtrs. Used for FirstInRow, FirstInCol and + * Diag pointer arrays. + */ + +/* Begin `MatrixElement'. */ + +struct MatrixElement +{ RealNumber Real; +#if spCOMPLEX + RealNumber Imag; +#endif + int Row; + int Col; + struct MatrixElement *NextInRow; + struct MatrixElement *NextInCol; +#if INITIALIZE + spGenericPtr pInitInfo; +#endif +}; + +typedef struct MatrixElement *ElementPtr; +typedef ElementPtr *ArrayOfElementPtrs; + + + + + + + + +/* + * ALLOCATION DATA STRUCTURE + * + * The sparse matrix routines keep track of all memory that is allocated by + * the operating system so the memory can later be freed. This is done by + * saving the pointers to all the chunks of memory that are allocated to a + * particular matrix in an allocation list. That list is organized as a + * linked list so that it can grow without a priori bounds. + * + * >>> Structure fields: + * AllocatedPtr (void *) + * Pointer to chunk of memory that has been allocated for the matrix. + * NextRecord (struct AllocationRecord *) + * Pointer to the next allocation record. + */ + +/* Begin `AllocationRecord'. */ +struct AllocationRecord +{ void *AllocatedPtr; + struct AllocationRecord *NextRecord; +}; + +typedef struct AllocationRecord *AllocationListPtr; + + + + + + + + + +/* + * FILL-IN LIST DATA STRUCTURE + * + * The sparse matrix routines keep track of all fill-ins separately from + * user specified elements so they may be removed by spStripFills(). Fill-ins + * are allocated in bunched in what is called a fill-in lists. The data + * structure defined below is used to organize these fill-in lists into a + * linked-list. + * + * >>> Structure fields: + * pFillinList (ElementPtr) + * Pointer to a fill-in list, or a bunch of fill-ins arranged contiguously + * in memory. + * NumberOfFillinsInList (int) + * Seems pretty self explanatory to me. + * Next (struct FillinListNodeStruct *) + * Pointer to the next fill-in list structures. + */ + +/* Begin `FillinListNodeStruct'. */ +struct FillinListNodeStruct +{ ElementPtr pFillinList; + int NumberOfFillinsInList; + struct FillinListNodeStruct *Next; +}; + + + + + + + + + + +/* + * MATRIX FRAME DATA STRUCTURE + * + * This structure contains all the pointers that support the orthogonal + * linked list that contains the matrix elements. Also included in this + * structure are other numbers and pointers that are used globally by the + * sparse matrix routines and are associated with one particular matrix.. + * + * >>> Type definitions: + * MatrixPtr + * A pointer to MatrixFrame. Essentially, a pointer to the matrix. + * + * >>> Structure fields: + * AbsThreshold (RealNumber) + * The absolute magnitude an element must have to be considered as a + * pivot candidate, except as a last resort. + * AllocatedExtSize (int) + * The allocated size of the arrays used to translate external row and + * column numbers to their internal values. + * AllocatedSize (int) + * The currently allocated size of the matrix; the size the matrix can + * grow to when EXPANDABLE is set true and AllocatedSize is the largest + * the matrix can get without requiring that the matrix frame be + * reallocated. + * Complex (BOOLEAN) + * The flag which indicates whether the matrix is complex (true) or + * real. + * CurrentSize (int) + * This number is used during the building of the matrix when the + * TRANSLATE option is set true. It indicates the number of internal + * rows and columns that have elements in them. + * Diag (ArrayOfElementPtrs) + * Array of pointers that points to the diagonal elements. + * DoCmplxDirect (BOOLEAN *) + * Array of flags, one for each column in matrix. If a flag is true + * then corresponding column in a complex matrix should be eliminated + * in spFactor() using direct addressing (rather than indirect + * addressing). + * DoRealDirect (BOOLEAN *) + * Array of flags, one for each column in matrix. If a flag is true + * then corresponding column in a real matrix should be eliminated + * in spFactor() using direct addressing (rather than indirect + * addressing). + * Elements (int) + * The number of original elements (total elements minus fill ins) + * present in matrix. + * Error (int) + * The error status of the sparse matrix package. + * ExtSize (int) + * The value of the largest external row or column number encountered. + * ExtToIntColMap (int []) + * An array that is used to convert external columns number to internal + * external column numbers. Present only if TRANSLATE option is set true. + * ExtToIntRowMap (int []) + * An array that is used to convert external row numbers to internal + * external row numbers. Present only if TRANSLATE option is set true. + * Factored (BOOLEAN) + * Indicates if matrix has been factored. This flag is set true in + * spFactor() and spOrderAndFactor() and set false in spCreate() + * and spClear(). + * Fillins (int) + * The number of fill-ins created during the factorization the matrix. + * FirstInCol (ArrayOfElementPtrs) + * Array of pointers that point to the first nonzero element of the + * column corresponding to the index. + * FirstInRow (ArrayOfElementPtrs) + * Array of pointers that point to the first nonzero element of the row + * corresponding to the index. + * ID (unsigned long int) + * A constant that provides the sparse data structure with a signature. + * When spDEBUG is true, all externally available sparse routines check + * this signature to assure they are operating on a valid matrix. + * Intermediate (RealVector) + * Temporary storage used in the spSolve routines. Intermediate is an + * array used during forward and backward substitution. It is + * commonly called y when the forward and backward substitution process is + * denoted Ax = b => Ly = b and Ux = y. + * InternalVectorsAllocated (BOOLEAN) + * A flag that indicates whether theMmarkowitz vectors and the + * Intermediate vector have been created. + * These vectors are created in spcCreateInternalVectors(). + * IntToExtColMap (int []) + * An array that is used to convert internal column numbers to external + * external column numbers. + * IntToExtRowMap (int []) + * An array that is used to convert internal row numbers to external + * external row numbers. + * MarkowitzCol (int []) + * An array that contains the count of the non-zero elements excluding + * the pivots for each column. Used to generate and update MarkowitzProd. + * MarkowitzProd (long []) + * The array of the products of the Markowitz row and column counts.. The + * element with the smallest product is the best pivot to use to maintain + * sparsity. + * MarkowitzRow (int []) + * An array that contains the count of the non-zero elements excluding + * the pivots for each row. Used to generate and update MarkowitzProd. + * MaxRowCountInLowerTri (int) + * The maximum number of off-diagonal element in the rows of L, the + * lower triangular matrix. This quantity is used when computing an + * estimate of the roundoff error in the matrix. + * NeedsOrdering (BOOLEAN) + * This is a flag that signifies that the matrix needs to be ordered + * or reordered. NeedsOrdering is set true in spCreate() and + * spGetElement() or spGetAdmittance() if new elements are added to the + * matrix after it has been previously factored. It is set false in + * spOrderAndFactor(). + * NumberOfInterchangesIsOdd (BOOLEAN) + * Flag that indicates the sum of row and column interchange counts + * is an odd number. Used when determining the sign of the determinant. + * Partitioned (BOOLEAN) + * This flag indicates that the columns of the matrix have been + * partitioned into two groups. Those that will be addressed directly + * and those that will be addressed indirectly in spFactor(). + * PivotsOriginalCol (int) + * Column pivot was chosen from. + * PivotsOriginalRow (int) + * Row pivot was chosen from. + * PivotSelectionMethod (char) + * Character that indicates which pivot search method was successful. + * PreviousMatrixWasComplex (BOOLEAN) + * This flag in needed to determine how to clear the matrix. When + * dealing with real matrices, it is important that the imaginary terms + * in the matrix elements be zero. Thus, if the previous matrix was + * complex, then the current matrix will be cleared as if it were complex + * even if it is real. + * RelThreshold (RealNumber) + * The magnitude an element must have relative to others in its row + * to be considered as a pivot candidate, except as a last resort. + * Reordered (BOOLEAN) + * This flag signifies that the matrix has been reordered. It + * is cleared in spCreate(), set in spMNA_Preorder() and + * spOrderAndFactor() and is used in spPrint(). + * RowsLinked (BOOLEAN) + * A flag that indicates whether the row pointers exist. The AddByIndex + * routines do not generate the row pointers, which are needed by some + * of the other routines, such as spOrderAndFactor() and spScale(). + * The row pointers are generated in the function spcLinkRows(). + * SingularCol (int) + * Normally zero, but if matrix is found to be singular, SingularCol is + * assigned the external column number of pivot that was zero. + * SingularRow (int) + * Normally zero, but if matrix is found to be singular, SingularRow is + * assigned the external row number of pivot that was zero. + * Singletons (int) + * The number of singletons available for pivoting. Note that if row I + * and column I both contain singletons, only one of them is counted. + * Size (int) + * Number of rows and columns in the matrix. Does not change as matrix + * is factored. + * TrashCan (MatrixElement) + * This is a dummy MatrixElement that is used to by the user to stuff + * data related to the zero row or column. In other words, when the user + * adds an element in row zero or column zero, then the matrix returns + * a pointer to TrashCan. In this way the user can have a uniform way + * data into the matrix independent of whether a component is connected + * to ground. + * + * >>> The remaining fields are related to memory allocation. + * TopOfAllocationList (AllocationListPtr) + * Pointer which points to the top entry in a list. The list contains + * all the pointers to the segments of memory that have been allocated + * to this matrix. This is used when the memory is to be freed on + * deallocation of the matrix. + * RecordsRemaining (int) + * Number of slots left in the list of allocations. + * NextAvailElement (ElementPtr) + * Pointer to the next available element which has been allocated but as + * yet is unused. Matrix elements are allocated in groups of + * ELEMENTS_PER_ALLOCATION in order to speed element allocation and + * freeing. + * ElementsRemaining (int) + * Number of unused elements left in last block of elements allocated. + * NextAvailFillin (ElementPtr) + * Pointer to the next available fill-in which has been allocated but + * as yet is unused. Fill-ins are allocated in a group in order to keep + * them physically close in memory to the rest of the matrix. + * FillinsRemaining (int) + * Number of unused fill-ins left in the last block of fill-ins + * allocated. + * FirstFillinListNode (FillinListNodeStruct *) + * A pointer to the head of the linked-list that keeps track of the + * lists of fill-ins. + * LastFillinListNode (FillinListNodeStruct *) + * A pointer to the tail of the linked-list that keeps track of the + * lists of fill-ins. + */ + +/* Begin `MatrixFrame'. */ +struct MatrixFrame +{ RealNumber AbsThreshold; + int AllocatedSize; + int AllocatedExtSize; + BOOLEAN Complex; + int CurrentSize; + ArrayOfElementPtrs Diag; + BOOLEAN *DoCmplxDirect; + BOOLEAN *DoRealDirect; + int Elements; + int Error; + int ExtSize; + int *ExtToIntColMap; + int *ExtToIntRowMap; + BOOLEAN Factored; + int Fillins; + ArrayOfElementPtrs FirstInCol; + ArrayOfElementPtrs FirstInRow; + unsigned long ID; + RealVector Intermediate; + BOOLEAN InternalVectorsAllocated; + int *IntToExtColMap; + int *IntToExtRowMap; + int *MarkowitzRow; + int *MarkowitzCol; + long *MarkowitzProd; + int MaxRowCountInLowerTri; + BOOLEAN NeedsOrdering; + BOOLEAN NumberOfInterchangesIsOdd; + BOOLEAN Partitioned; + int PivotsOriginalCol; + int PivotsOriginalRow; + char PivotSelectionMethod; + BOOLEAN PreviousMatrixWasComplex; + RealNumber RelThreshold; + BOOLEAN Reordered; + BOOLEAN RowsLinked; + int SingularCol; + int SingularRow; + int Singletons; + int Size; + struct MatrixElement TrashCan; + + AllocationListPtr TopOfAllocationList; + int RecordsRemaining; + ElementPtr NextAvailElement; + int ElementsRemaining; + ElementPtr NextAvailFillin; + int FillinsRemaining; + struct FillinListNodeStruct *FirstFillinListNode; + struct FillinListNodeStruct *LastFillinListNode; +}; +typedef struct MatrixFrame *MatrixPtr; + + + + +/* + * Declarations + */ + +spcEXTERN ElementPtr spcGetElement( MatrixPtr ); +spcEXTERN ElementPtr spcGetFillin( MatrixPtr ); +spcEXTERN ElementPtr spcFindDiag( MatrixPtr, int ); +spcEXTERN ElementPtr spcCreateElement( MatrixPtr, int, int, + ElementPtr*, ElementPtr*, int ); +spcEXTERN void spcCreateInternalVectors( MatrixPtr ); +spcEXTERN void spcLinkRows( MatrixPtr ); +spcEXTERN void spcColExchange( MatrixPtr, int, int ); +spcEXTERN void spcRowExchange( MatrixPtr, int, int ); + +spcEXTERN char spcMatrixIsNotValid[]; +spcEXTERN char spcErrorsMustBeCleared[]; +spcEXTERN char spcMatrixMustBeFactored[]; +spcEXTERN char spcMatrixMustNotBeFactored[]; Added: trunk/math/SPARSE/spedacious.c =================================================================== --- trunk/math/SPARSE/spedacious.c (rev 0) +++ trunk/math/SPARSE/spedacious.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,45 @@ + /* + * Copyright (c) 2008 + * + * Antoine Levitt (smeuuh-Re5JQEeQqe8AvxtiuMwx3w@xxxxxxxxxxxxxxxx) + * Steven Herbst (herbst-3s7WtUTddSA@xxxxxxxxxxxxxxxx) + * + * Hypertriton, Inc. <http://hypertriton.com/> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + + +/* This file contains extra functions used in edacious */ + +#include <core/core.h> +#include "../m.h" +#include "spdefs.h" + +void spAddToReorderedDiag(spMatrix M, spREAL g) +{ + MatrixPtr Matrix = (MatrixPtr)M; + int i; + for(i = 0; i < Matrix->Size ; i++) + Matrix->Diag[i]->Real += g; +} Added: trunk/math/SPARSE/spfactor.c =================================================================== --- trunk/math/SPARSE/spfactor.c (rev 0) +++ trunk/math/SPARSE/spfactor.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,3048 @@ +/* + * MATRIX FACTORIZATION MODULE + * + * Author: Advising Professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + */ +/*! \file + * This file contains the routines to factor the matrix into LU form. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ +/* >>> User accessible functions contained in this file: + * spOrderAndFactor + * spFactor + * spPartition + * + * >>> Other functions contained in this file: + * FactorComplexMatrix spcCreateInternalVectors + * CountMarkowitz MarkowitzProducts + * SearchForPivot SearchForSingleton + * QuicklySearchDiagonal SearchDiagonal + * SearchEntireMatrix FindLargestInCol + * FindBiggestInColExclude ExchangeRowsAndCols + * spcRowExchange spcColExchange + * ExchangeColElements ExchangeRowElements + * RealRowColElimination ComplexRowColElimination + * UpdateMarkowitzNumbers MatrixIsSingular + * ZeroPivot WriteStatus + */ + + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + * spMatrix.h + * Macros and declarations to be imported by the user. + * spDefs.h + * Matrix type and macro definitions for the sparse matrix routines. + */ + +#define spINSIDE_SPARSE +#include <core/core.h> +#include "../m.h" +#include "spconfig.h" +#include "spmatrix.h" +#include "spdefs.h" + + + + + +/* + * Function declarations + */ + +static int FactorComplexMatrix( 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, + ElementPtr, int ); +static void ExchangeRowElements( MatrixPtr, int, ElementPtr, int, + 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 ); + + + + + +/*! + * This routine chooses a pivot order for the matrix and factors it + * into \a LU form. It handles both the initial factorization and subsequent + * factorizations when a reordering is desired. This is handled in a manner + * that is transparent to the user. The routine uses a variation of + * Gauss's method where the pivots are associated with \a L and the + * diagonal terms of \a U are one. + * + * \return + * The error code is returned. Possible errors are \a spNO_MEMORY, + * \a spSINGULAR and \a spSMALL_PIVOT. + * Error is cleared upon entering this function. + * + * \param eMatrix + * Pointer to the matrix. + * \param RHS + * Representative right-hand side vector that is used to determine + * pivoting order when the right hand side vector is sparse. If + * RHS is a NULL pointer then the RHS vector is assumed to + * be full and it is not used when determining the pivoting + * order. + * \param RelThreshold + * This number determines what the pivot relative threshold will + * be. It should be between zero and one. If it is one then the + * pivoting method becomes complete pivoting, which is very slow + * and tends to fill up the matrix. If it is set close to zero + * the pivoting method becomes strict Markowitz with no + * threshold. The pivot threshold is used to eliminate pivot + * candidates that would cause excessive element growth if they + * were used. Element growth is the cause of roundoff error. + * Element growth occurs even in well-conditioned matrices. + * Setting the \a RelThreshold large will reduce element growth and + * roundoff error, but setting it too large will cause execution + * time to be excessive and will result in a large number of + * fill-ins. If this occurs, accuracy can actually be degraded + * because of the large number of operations required on the + * matrix due to the large number of fill-ins. A good value seems + * to be 0.001. The default is chosen by giving a value larger + * than one or less than or equal to zero. This value should be + * increased and the matrix resolved if growth is found to be + * excessive. Changing the pivot threshold does not improve + * performance on matrices where growth is low, as is often the + * case with ill-conditioned matrices. Once a valid threshold is + * given, it becomes the new default. The default value of + * \a RelThreshold was choosen for use with nearly diagonally + * dominant matrices such as node- and modified-node admittance + * matrices. For these matrices it is usually best to use + * diagonal pivoting. For matrices without a strong diagonal, it + * is usually best to use a larger threshold, such as 0.01 or + * 0.1. + * \param AbsThreshold + * The absolute magnitude an element must have to be considered + * as a pivot candidate, except as a last resort. This number + * should be set significantly smaller than the smallest diagonal + * element that is is expected to be placed in the matrix. If + * there is no reasonable prediction for the lower bound on these + * elements, then \a AbsThreshold should be set to zero. + * \a AbsThreshold is used to reduce the possibility of choosing as a + * pivot an element that has suffered heavy cancellation and as a + * result mainly consists of roundoff error. Once a valid + * threshold is given, it becomes the new default. + * \param DiagPivoting + * A flag indicating that pivot selection should be confined to the + * diagonal if possible. If \a DiagPivoting is nonzero and if + * \a DIAGONAL_PIVOTING is enabled pivots will be chosen only from + * the diagonal unless there are no diagonal elements that satisfy + * the threshold criteria. Otherwise, the entire reduced + * submatrix is searched when looking for a pivot. The diagonal + * pivoting in Sparse is efficient and well refined, while the + * off-diagonal pivoting is not. For symmetric and near symmetric + * matrices, it is best to use diagonal pivoting because it + * results in the best performance when reordering the matrix and + * when factoring the matrix without ordering. If there is a + * considerable amount of nonsymmetry in the matrix, then + * off-diagonal pivoting may result in a better equation ordering + * simply because there are more pivot candidates to choose from. + * A better ordering results in faster subsequent factorizations. + * However, the initial pivot selection process takes considerably + * longer for off-diagonal pivoting. + * + * \see spFactor() + */ +/* >>> Local variables: + * pPivot (ElementPtr) + * Pointer to the element being used as a pivot. + * + */ + +spError +spOrderAndFactor( + spMatrix eMatrix, + spREAL RHS[], + spREAL RelThreshold, + spREAL AbsThreshold, + int DiagPivoting +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +ElementPtr pPivot; +int Step, Size; +RealNumber LargestInCol; + +/* Begin `spOrderAndFactor'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + + Matrix->Error = spOKAY; + Size = Matrix->Size; + if (RelThreshold <= 0.0) RelThreshold = Matrix->RelThreshold; + if (RelThreshold > 1.0) RelThreshold = Matrix->RelThreshold; + Matrix->RelThreshold = RelThreshold; + if (AbsThreshold < 0.0) AbsThreshold = Matrix->AbsThreshold; + Matrix->AbsThreshold = AbsThreshold; + + if (NOT Matrix->NeedsOrdering) + { +/* Matrix has been factored before and reordering is not required. */ + for (Step = 1; Step <= Size; Step++) + { pPivot = Matrix->Diag[Step]; + LargestInCol = FindLargestInCol(pPivot->NextInCol); + if ((LargestInCol * RelThreshold < ELEMENT_MAG(pPivot))) + { if (Matrix->Complex) + ComplexRowColElimination( Matrix, pPivot ); + else + RealRowColElimination( Matrix, pPivot ); + } + else + { Matrix->NeedsOrdering = YES; + break; /* for loop */ + } + } + if (NOT Matrix->NeedsOrdering) + goto Done; + else + { +/* + * A pivot was not large enough to maintain accuracy, + * so a partial reordering is required. + */ + +#if (ANNOTATE >= ON_STRANGE_BEHAVIOR) + printf("Reordering, Step = %1d\n", Step); +#endif + } + } /* End of if(NOT Matrix->NeedsOrdering) */ + else + { +/* + * This is the first time the matrix has been factored. These few statements + * indicate to the rest of the code that a full reodering is required rather + * than a partial reordering, which occurs during a failure of a fast + * factorization. + */ + Step = 1; + if (NOT Matrix->RowsLinked) + spcLinkRows( Matrix ); + if (NOT Matrix->InternalVectorsAllocated) + spcCreateInternalVectors( Matrix ); + if (Matrix->Error >= spFATAL) + return Matrix->Error; + } + +/* Form initial Markowitz products. */ + CountMarkowitz( Matrix, RHS, Step ); + MarkowitzProducts( Matrix, Step ); + Matrix->MaxRowCountInLowerTri = -1; + +/* Perform reordering and factorization. */ + for (; Step <= Size; Step++) + { pPivot = SearchForPivot( Matrix, Step, DiagPivoting ); + if (pPivot == NULL) return MatrixIsSingular( Matrix, Step ); + ExchangeRowsAndCols( Matrix, pPivot, Step ); + + if (Matrix->Complex) + ComplexRowColElimination( Matrix, pPivot ); + else + RealRowColElimination( Matrix, pPivot ); + + if (Matrix->Error >= spFATAL) return Matrix->Error; + UpdateMarkowitzNumbers( Matrix, pPivot ); + +#if (ANNOTATE == FULL) + WriteStatus( Matrix, Step ); +#endif + } + +Done: + Matrix->NeedsOrdering = NO; + Matrix->Reordered = YES; + Matrix->Factored = YES; + + return Matrix->Error; +} + + + + + + + +/*! + * This routine is the companion routine to spOrderAndFactor(). + * Unlike spOrderAndFactor(), spFactor() cannot change the ordering. + * It is also faster than spOrderAndFactor(). The standard way of + * using these two routines is to first use spOrderAndFactor() for the + * initial factorization. For subsequent factorizations, spFactor() + * is used if there is some assurance that little growth will occur + * (say for example, that the matrix is diagonally dominant). If + * spFactor() is called for the initial factorization of the matrix, + * then spOrderAndFactor() is automatically called with the default + * threshold. This routine uses "row at a time" \a LU factorization. + * Pivots are associated with the lower triangular matrix and the + * diagonals of the upper triangular matrix are ones. + * + * \return + * The error code is returned. Possible errors are + * \a spNO_MEMORY, \a spSINGULAR, \a spZERO_DIAG and \a spSMALL_PIVOT. + * Error is cleared upon entering this function. + * + * \param eMatrix + * Pointer to matrix. + * \see spOrderAndFactor() + */ + +spError +spFactor( spMatrix eMatrix ) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register ElementPtr pColumn; +register int Step, Size; +RealNumber Mult; + +/* Begin `spFactor'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + + if (Matrix->NeedsOrdering) + { return spOrderAndFactor( eMatrix, (RealVector)NULL, + 0.0, 0.0, DIAG_PIVOTING_AS_DEFAULT ); + } + if (NOT Matrix->Partitioned) spPartition( eMatrix, spDEFAULT_PARTITION ); +#if spCOMPLEX + if (Matrix->Complex) return FactorComplexMatrix( Matrix ); +#endif + +#if REAL + Size = Matrix->Size; + + if (Matrix->Diag[1]->Real == 0.0) return ZeroPivot( Matrix, 1 ); + Matrix->Diag[1]->Real = 1.0 / Matrix->Diag[1]->Real; + +/* Start factorization. */ + for (Step = 2; Step <= Size; Step++) + { if (Matrix->DoRealDirect[Step]) + { /* Update column using direct addressing scatter-gather. */ + register RealNumber *Dest = (RealNumber *)Matrix->Intermediate; + +/* Scatter. */ + pElement = Matrix->FirstInCol[Step]; + while (pElement != NULL) + { Dest[pElement->Row] = pElement->Real; + pElement = pElement->NextInCol; + } + +/* Update column. */ + pColumn = Matrix->FirstInCol[Step]; + while (pColumn->Row < Step) + { pElement = Matrix->Diag[pColumn->Row]; + pColumn->Real = Dest[pColumn->Row] * pElement->Real; + while ((pElement = pElement->NextInCol) != NULL) + Dest[pElement->Row] -= pColumn->Real * pElement->Real; + pColumn = pColumn->NextInCol; + } + +/* Gather. */ + pElement = Matrix->Diag[Step]->NextInCol; + while (pElement != NULL) + { pElement->Real = Dest[pElement->Row]; + pElement = pElement->NextInCol; + } + +/* Check for singular matrix. */ + if (Dest[Step] == 0.0) return ZeroPivot( Matrix, Step ); + Matrix->Diag[Step]->Real = 1.0 / Dest[Step]; + } + else + { /* Update column using indirect addressing scatter-gather. */ + register RealNumber **pDest = (RealNumber **)Matrix->Intermediate; + +/* Scatter. */ + pElement = Matrix->FirstInCol[Step]; + while (pElement != NULL) + { pDest[pElement->Row] = &pElement->Real; + pElement = pElement->NextInCol; + } + +/* Update column. */ + pColumn = Matrix->FirstInCol[Step]; + while (pColumn->Row < Step) + { pElement = Matrix->Diag[pColumn->Row]; + Mult = (*pDest[pColumn->Row] *= pElement->Real); + while ((pElement = pElement->NextInCol) != NULL) + *pDest[pElement->Row] -= Mult * pElement->Real; + pColumn = pColumn->NextInCol; + } + +/* Check for singular matrix. */ + if (Matrix->Diag[Step]->Real == 0.0) + return ZeroPivot( Matrix, Step ); + Matrix->Diag[Step]->Real = 1.0 / Matrix->Diag[Step]->Real; + } + } + + Matrix->Factored = YES; + return (Matrix->Error = spOKAY); +#endif /* REAL */ +} + + + + + + +#if spCOMPLEX +/* + * FACTOR COMPLEX MATRIX + * + * This routine is the companion routine to spFactor(), it + * handles complex matrices. It is otherwise identical. + * + * >>> Returned: + * The error code is returned. Possible errors are listed below. + * + * >>> Arguments: + * Matrix <input> (char *) + * Pointer to matrix. + * + * >>> Possible errors: + * spSINGULAR + * Error is cleared in this function. + */ + +static int +FactorComplexMatrix( MatrixPtr Matrix ) +{ +register ElementPtr pElement; +register ElementPtr pColumn; +register int Step, Size; +ComplexNumber Mult, Pivot; + +/* Begin `FactorComplexMatrix'. */ + ASSERT(Matrix->Complex); + + Size = Matrix->Size; + pElement = Matrix->Diag[1]; + if (ELEMENT_MAG(pElement) == 0.0) return ZeroPivot( Matrix, 1 ); +/* Cmplx expr: *pPivot = 1.0 / *pPivot. */ + CMPLX_RECIPROCAL( *pElement, *pElement ); + +/* Start factorization. */ + for (Step = 2; Step <= Size; Step++) + { if (Matrix->DoCmplxDirect[Step]) + { /* Update column using direct addressing scatter-gather. */ + register ComplexNumber *Dest; + Dest = (ComplexNumber *)Matrix->Intermediate; + +/* Scatter. */ + pElement = Matrix->FirstInCol[Step]; + while (pElement != NULL) + { Dest[pElement->Row] = *(ComplexNumber *)pElement; + pElement = pElement->NextInCol; + } + +/* Update column. */ + pColumn = Matrix->FirstInCol[Step]; + while (pColumn->Row < Step) + { pElement = Matrix->Diag[pColumn->Row]; + /* Cmplx expr: Mult = Dest[pColumn->Row] * (1.0 / *pPivot). */ + CMPLX_MULT(Mult, Dest[pColumn->Row], *pElement); + CMPLX_ASSIGN(*pColumn, Mult); + while ((pElement = pElement->NextInCol) != NULL) + { /* Cmplx expr: Dest[pElement->Row] -= Mult * pElement */ + CMPLX_MULT_SUBT_ASSIGN(Dest[pElement->Row],Mult,*pElement); + } + pColumn = pColumn->NextInCol; + } + +/* Gather. */ + pElement = Matrix->Diag[Step]->NextInCol; + while (pElement != NULL) + { *(ComplexNumber *)pElement = Dest[pElement->Row]; + pElement = pElement->NextInCol; + } + +/* Check for singular matrix. */ + Pivot = Dest[Step]; + if (CMPLX_1_NORM(Pivot) == 0.0) return ZeroPivot( Matrix, Step ); + CMPLX_RECIPROCAL( *Matrix->Diag[Step], Pivot ); + } + else + { /* Update column using direct addressing scatter-gather. */ + register ComplexNumber **pDest; + pDest = (ComplexNumber **)Matrix->Intermediate; + +/* Scatter. */ + pElement = Matrix->FirstInCol[Step]; + while (pElement != NULL) + { pDest[pElement->Row] = (ComplexNumber *)pElement; + pElement = pElement->NextInCol; + } + +/* Update column. */ + pColumn = Matrix->FirstInCol[Step]; + while (pColumn->Row < Step) + { pElement = Matrix->Diag[pColumn->Row]; + /* Cmplx expr: Mult = *pDest[pColumn->Row] * (1.0 / *pPivot). */ + CMPLX_MULT(Mult, *pDest[pColumn->Row], *pElement); + CMPLX_ASSIGN(*pDest[pColumn->Row], Mult); + while ((pElement = pElement->NextInCol) != NULL) + { /* Cmplx expr: *pDest[pElement->Row] -= Mult * pElement */ + CMPLX_MULT_SUBT_ASSIGN(*pDest[pElement->Row],Mult,*pElement); + } + pColumn = pColumn->NextInCol; + } + +/* Check for singular matrix. */ + pElement = Matrix->Diag[Step]; + if (ELEMENT_MAG(pElement) == 0.0) return ZeroPivot( Matrix, Step ); + CMPLX_RECIPROCAL( *pElement, *pElement ); + } + } + + Matrix->Factored = YES; + return (Matrix->Error = spOKAY); +} +#endif /* spCOMPLEX */ + + + + + + +/*! + * This routine determines the cost to factor each row using both + * direct and indirect addressing and decides, on a row-by-row basis, + * which addressing mode is fastest. This information is used in + * spFactor() to speed the factorization. + * + * When factoring a previously ordered matrix using spFactor(), Sparse + * operates on a row-at-a-time basis. For speed, on each step, the + * row being updated is copied into a full vector and the operations + * are performed on that vector. This can be done one of two ways, + * either using direct addressing or indirect addressing. Direct + * addressing is fastest when the matrix is relatively dense and + * indirect addressing is best when the matrix is quite sparse. The + * user selects the type of partition used with \a Mode. If \a Mode is set + * to \a spDIRECT_PARTITION, then the all rows are placed in the direct + * addressing partition. Similarly, if \a Mode is set to + * \a spINDIRECT_PARTITION, then the all rows are placed in the indirect + * addressing partition. By setting \a Mode to \a spAUTO_PARTITION, the + * user allows Sparse to select the partition for each row + * individually. spFactor() generally runs faster if Sparse is + * allowed to choose its own partitioning, however choosing a + * partition is expensive. The time required to choose a partition is + * of the same order of the cost to factor the matrix. If you plan to + * factor a large number of matrices with the same structure, it is + * best to let Sparse choose the partition. Otherwise, you should + * choose the partition based on the predicted density of the matrix. + * + * \param eMatrix + * Pointer to matrix. + * \param Mode + * Mode must be one of three special codes: \a spDIRECT_PARTITION, + * \a spINDIRECT_PARTITION, or \a spAUTO_PARTITION. + */ + +void +spPartition( + spMatrix eMatrix, + int Mode +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement, pColumn; +register int Step, Size; +register int *Nc, *No; +register long *Nm; +BOOLEAN *DoRealDirect, *DoCmplxDirect; + +/* Begin `spPartition'. */ + ASSERT_IS_SPARSE( Matrix ); + + if (Matrix->Partitioned) return; + Size = Matrix->Size; + DoRealDirect = Matrix->DoRealDirect; + DoCmplxDirect = Matrix->DoCmplxDirect; + Matrix->Partitioned = YES; + +/* If partition is specified by the user, this is easy. */ + if (Mode == spDEFAULT_PARTITION) Mode = DEFAULT_PARTITION; + if (Mode == spDIRECT_PARTITION) + { for (Step = 1; Step <= Size; Step++) +#if REAL + DoRealDirect[Step] = YES; +#endif +#if spCOMPLEX + DoCmplxDirect[Step] = YES; +#endif + return; + } + else if (Mode == spINDIRECT_PARTITION) + { for (Step = 1; Step <= Size; Step++) +#if REAL + DoRealDirect[Step] = NO; +#endif +#if spCOMPLEX + DoCmplxDirect[Step] = NO; +#endif + return; + } + else vASSERT( Mode == spAUTO_PARTITION, "Invalid partition code" );; + +/* Otherwise, count all operations needed in when factoring matrix. */ + Nc = (int *)Matrix->MarkowitzRow; + No = (int *)Matrix->MarkowitzCol; + Nm = (long *)Matrix->MarkowitzProd; + +/* Start mock-factorization. */ + for (Step = 1; Step <= Size; Step++) + { Nc[Step] = No[Step] = Nm[Step] = 0; + + pElement = Matrix->FirstInCol[Step]; + while (pElement != NULL) + { Nc[Step]++; + pElement = pElement->NextInCol; + } + + pColumn = Matrix->FirstInCol[Step]; + while (pColumn->Row < Step) + { pElement = Matrix->Diag[pColumn->Row]; + Nm[Step]++; + while ((pElement = pElement->NextInCol) != NULL) + No[Step]++; + pColumn = pColumn->NextInCol; + } + } + + for (Step = 1; Step <= Size; Step++) + { +/* + * The following are just estimates based on a count on the number of + * machine instructions used on each machine to perform the various + * tasks. It was assumed that each machine instruction required the + * same amount of time (I don't believe this is true for the VAX, and + * have no idea if this is true for the 68000 family). For optimum + * performance, these numbers should be tuned to the machine. + * Nc is the number of nonzero elements in the column. + * Nm is the number of multipliers in the column. + * No is the number of operations in the inner loop. + */ + +#define generic +#ifdef hp9000s300 +#if REAL + DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]); +#endif +#if spCOMPLEX + /* On the hp350, it is never profitable to use direct for complex. */ + DoCmplxDirect[Step] = NO; +#endif +#undef generic +#endif + +#ifdef vax +#if REAL + DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]); +#endif +#if spCOMPLEX + DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7*Nc[Step] - 4*Nm[Step]); +#endif +#undef generic +#endif + +#ifdef generic +#if REAL + DoRealDirect[Step] = (Nm[Step] + No[Step] > 3*Nc[Step] - 2*Nm[Step]); +#endif +#if spCOMPLEX + DoCmplxDirect[Step] = (Nm[Step] + No[Step] > 7*Nc[Step] - 4*Nm[Step]); +#endif +#undef generic +#endif + } + +#if (ANNOTATE == FULL) + { int Ops = 0; + for (Step = 1; Step <= Size; Step++) + Ops += No[Step]; + printf("Operation count for inner loop of factorization = %d.\n", Ops); + } +#endif + return; +} + + + + + + + +/* + * CREATE INTERNAL VECTORS + * + * Creates the Markowitz and Intermediate vectors. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +void +spcCreateInternalVectors( MatrixPtr Matrix ) +{ +int Size; + +/* Begin `spcCreateInternalVectors'. */ +/* Create Markowitz arrays. */ + Size= Matrix->Size; + + if (Matrix->MarkowitzRow == NULL) + { if (( Matrix->MarkowitzRow = ALLOC(int, Size+1)) == NULL) + Matrix->Error = spNO_MEMORY; + } + if (Matrix->MarkowitzCol == NULL) + { if (( Matrix->MarkowitzCol = ALLOC(int, Size+1)) == NULL) + Matrix->Error = spNO_MEMORY; + } + if (Matrix->MarkowitzProd == NULL) + { if (( Matrix->MarkowitzProd = ALLOC(long, Size+2)) == NULL) + Matrix->Error = spNO_MEMORY; + } + +/* Create DoDirect vectors for use in spFactor(). */ +#if REAL + if (Matrix->DoRealDirect == NULL) + { if (( Matrix->DoRealDirect = ALLOC(BOOLEAN, Size+1)) == NULL) + Matrix->Error = spNO_MEMORY; + } +#endif +#if spCOMPLEX + if (Matrix->DoCmplxDirect == NULL) + { if (( Matrix->DoCmplxDirect = ALLOC(BOOLEAN, Size+1)) == NULL) + Matrix->Error = spNO_MEMORY; + } +#endif + +/* Create Intermediate vectors for use in MatrixSolve. */ +#if spCOMPLEX + if (Matrix->Intermediate == NULL) + { if ((Matrix->Intermediate = ALLOC(RealNumber,2*(Size+1))) == NULL) + Matrix->Error = spNO_MEMORY; + } +#else + if (Matrix->Intermediate == NULL) + { if ((Matrix->Intermediate = ALLOC(RealNumber, Size+1)) == NULL) + Matrix->Error = spNO_MEMORY; + } +#endif + + if (Matrix->Error != spNO_MEMORY) + Matrix->InternalVectorsAllocated = YES; + return; +} + + + + + + + +/* + * COUNT MARKOWITZ + * + * Scans Matrix to determine the Markowitz counts for each row and column. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * RHS <input> (RealVector) + * Representative right-hand side vector that is used to determine + * pivoting order when the right hand side vector is sparse. If + * RHS is a NULL pointer then the RHS vector is assumed to be full + * and it is not used when determining the pivoting order. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * Count (int) + * Temporary counting variable. + * ExtRow (int) + * The external row number that corresponds to I. + * pElement (ElementPtr) + * Pointer to matrix elements. + * Size (int) + * The size of the matrix. + */ + +static void +CountMarkowitz( + MatrixPtr Matrix, + register RealVector RHS, + int Step +) +{ +register int Count, I, Size = Matrix->Size; +register ElementPtr pElement; +int ExtRow; + +/* Begin `CountMarkowitz'. */ + +/* Correct array pointer for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET +#if spSEPARATED_COMPLEX_VECTORS OR NOT spCOMPLEX + if (RHS != NULL) --RHS; +#else + if (RHS != NULL) + { if (Matrix->Complex) RHS -= 2; + else --RHS; + } +#endif +#endif + +/* Generate MarkowitzRow Count for each row. */ + for (I = Step; I <= Size; I++) + { +/* Set Count to -1 initially to remove count due to pivot element. */ + Count = -1; + pElement = Matrix->FirstInRow[I]; + while (pElement != NULL AND pElement->Col < Step) + pElement = pElement->NextInRow; + while (pElement != NULL) + { Count++; + pElement = pElement->NextInRow; + } + +/* Include nonzero elements in the RHS vector. */ + ExtRow = Matrix->IntToExtRowMap[I]; + +#if spSEPARATED_COMPLEX_VECTORS OR NOT spCOMPLEX + if (RHS != NULL) + if (RHS[ExtRow] != 0.0) Count++; +#else + if (RHS != NULL) + { if (Matrix->Complex) + { if ((RHS[2*ExtRow] != 0.0) OR (RHS[2*ExtRow+1] != 0.0)) + Count++; + } + else if (RHS[I] != 0.0) Count++; + } +#endif + Matrix->MarkowitzRow[I] = Count; + } + +/* Generate the MarkowitzCol count for each column. */ + for (I = Step; I <= Size; I++) + { +/* Set Count to -1 initially to remove count due to pivot element. */ + Count = -1; + pElement = Matrix->FirstInCol[I]; + while (pElement != NULL AND pElement->Row < Step) + pElement = pElement->NextInCol; + while (pElement != NULL) + { Count++; + pElement = pElement->NextInCol; + } + Matrix->MarkowitzCol[I] = Count; + } + return; +} + + + + + + + + + + +/* + * MARKOWITZ PRODUCTS + * + * Calculates MarkowitzProduct for each diagonal element from the Markowitz + * counts. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local Variables: + * pMarkowitzProduct (long *) + * Pointer that points into MarkowitzProduct array. Is used to + * sequentially access entries quickly. + * pMarkowitzRow (int *) + * Pointer that points into MarkowitzRow array. Is used to sequentially + * access entries quickly. + * pMarkowitzCol (int *) + * Pointer that points into MarkowitzCol array. Is used to sequentially + * access entries quickly. + * Product (long) + * Temporary storage for Markowitz product./ + * Size (int) + * The size of the matrix. + */ + +static void +MarkowitzProducts( + MatrixPtr Matrix, + int Step +) +{ +register int I, *pMarkowitzRow, *pMarkowitzCol; +register long Product, *pMarkowitzProduct; +register int Size = Matrix->Size; +double fProduct; + +/* Begin `MarkowitzProducts'. */ + Matrix->Singletons = 0; + + pMarkowitzProduct = &(Matrix->MarkowitzProd[Step]); + pMarkowitzRow = &(Matrix->MarkowitzRow[Step]); + pMarkowitzCol = &(Matrix->MarkowitzCol[Step]); + + for (I = Step; I <= Size; I++) + { +/* If chance of overflow, use real numbers. */ + if ((*pMarkowitzRow > LARGEST_SHORT_INTEGER AND *pMarkowitzCol != 0) OR + (*pMarkowitzCol > LARGEST_SHORT_INTEGER AND *pMarkowitzRow != 0)) + { fProduct = (double)(*pMarkowitzRow++) * (double)(*pMarkowitzCol++); + if (fProduct >= LARGEST_LONG_INTEGER) + *pMarkowitzProduct++ = LARGEST_LONG_INTEGER; + else + *pMarkowitzProduct++ = (long)fProduct; + } + else + { Product = *pMarkowitzRow++ * *pMarkowitzCol++; + if ((*pMarkowitzProduct++ = Product) == 0) + Matrix->Singletons++; + } + } + return; +} + + + + + + + + + + + +/* + * SEARCH FOR BEST PIVOT + * + * Performs a search to determine the element with the lowest Markowitz + * Product that is also acceptable. An acceptable element is one that is + * larger than the AbsThreshold and at least as large as RelThreshold times + * the largest element in the same column. The first step is to look for + * singletons if any exist. If none are found, then all the diagonals are + * searched. The diagonal is searched once quickly using the assumption that + * elements on the diagonal are large compared to other elements in their + * column, and so the pivot can be chosen only on the basis of the Markowitz + * criterion. After a element has been chosen to be pivot on the basis of + * its Markowitz product, it is checked to see if it is large enough. + * Waiting to the end of the Markowitz search to check the size of a pivot + * candidate saves considerable time, but is not guaranteed to find an + * acceptable pivot. Thus if unsuccessful a second pass of the diagonal is + * made. This second pass checks to see if an element is large enough during + * the search, not after it. If still no acceptable pivot candidate has + * been found, the search expands to cover the entire matrix. + * + * >>> Returned: + * A pointer to the element chosen to be pivot. If every element in the + * matrix is zero, then NULL is returned. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * The row and column number of the beginning of the reduced submatrix. + * + * >>> Local variables: + * ChosenPivot (ElementPtr) + * Pointer to element that has been chosen to be the pivot. + * + * >>> Possible errors: + * spSINGULAR + * spSMALL_PIVOT + */ + +static ElementPtr +SearchForPivot( + MatrixPtr Matrix, + int Step, + BOOLEAN DiagPivoting +) +{ +register ElementPtr ChosenPivot; + +/* Begin `SearchForPivot'. */ + +/* If singletons exist, look for an acceptable one to use as pivot. */ + if (Matrix->Singletons) + { ChosenPivot = SearchForSingleton( Matrix, Step ); + if (ChosenPivot != NULL) + { Matrix->PivotSelectionMethod = 's'; + return ChosenPivot; + } + } + +#if DIAGONAL_PIVOTING + if (DiagPivoting) + { +/* + * Either no singletons exist or they weren't acceptable. Take quick first + * pass at searching diagonal. First search for element on diagonal of + * remaining submatrix with smallest Markowitz product, then check to see + * if it okay numerically. If not, QuicklySearchDiagonal fails. + */ + ChosenPivot = QuicklySearchDiagonal( Matrix, Step ); + if (ChosenPivot != NULL) + { Matrix->PivotSelectionMethod = 'q'; + return ChosenPivot; + } + +/* + * Quick search of diagonal failed, carefully search diagonal and check each + * pivot candidate numerically before even tentatively accepting it. + */ + ChosenPivot = SearchDiagonal( Matrix, Step ); + if (ChosenPivot != NULL) + { Matrix->PivotSelectionMethod = 'd'; + return ChosenPivot; + } + } +#endif /* DIAGONAL_PIVOTING */ + +/* No acceptable pivot found yet, search entire matrix. */ + ChosenPivot = SearchEntireMatrix( Matrix, Step ); + Matrix->PivotSelectionMethod = 'e'; + + return ChosenPivot; +} + + + + + + + + + +/* + * SEARCH FOR SINGLETON TO USE AS PIVOT + * + * Performs a search to find a singleton to use as the pivot. The + * first acceptable singleton is used. A singleton is acceptable if + * it is larger in magnitude than the AbsThreshold and larger + * than RelThreshold times the largest of any other elements in the same + * column. It may seem that a singleton need not satisfy the + * relative threshold criterion, however it is necessary to prevent + * excessive growth in the RHS from resulting in overflow during the + * forward and backward substitution. A singleton does not need to + * be on the diagonal to be selected. + * + * >>> Returned: + * A pointer to the singleton chosen to be pivot. In no singleton is + * acceptable, return NULL. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * ChosenPivot (ElementPtr) + * Pointer to element that has been chosen to be the pivot. + * PivotMag (RealNumber) + * Magnitude of ChosenPivot. + * Singletons (int) + * The count of the number of singletons that can be used as pivots.. + * A local version of Matrix->Singletons. + * pMarkowitzProduct (long *) + * Pointer that points into MarkowitzProduct array. It is used to quickly + * access successive Markowitz products. + */ + +static ElementPtr +SearchForSingleton( + MatrixPtr Matrix, + int Step +) +{ +register ElementPtr ChosenPivot; +register int I; +register long *pMarkowitzProduct; +int Singletons; +RealNumber PivotMag; + +/* Begin `SearchForSingleton'. */ +/* Initialize pointer that is to scan through MarkowitzProduct vector. */ + pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+1]); + Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step]; + +/* Decrement the count of available singletons, on the assumption that an + * acceptable one will be found. */ + Singletons = Matrix->Singletons--; + +/* + * Assure that following while loop will always terminate, this is just + * preventive medicine, if things are working right this should never + * be needed. + */ + Matrix->MarkowitzProd[Step-1] = 0; + + while (Singletons-- > 0) + { +/* Singletons exist, find them. */ + +/* + * This is tricky. Am using a pointer to sequentially step through the + * MarkowitzProduct array. Search terminates when singleton (Product = 0) + * is found. Note that the conditional in the while statement + * ( *pMarkowitzProduct ) is true as long as the MarkowitzProduct is not + * equal to zero. The row (and column) index on the diagonal is then + * calculated by subtracting the pointer to the Markowitz product of + * the first diagonal from the pointer to the Markowitz product of the + * desired element, the singleton. + * + * Search proceeds from the end (high row and column numbers) to the + * beginning (low row and column numbers) so that rows and columns with + * large Markowitz products will tend to be move to the bottom of the + * matrix. However, choosing Diag[Step] is desirable because it would + * require no row and column interchanges, so inspect it first by + * putting its Markowitz product at the end of the MarkowitzProd + * vector. + */ + + while ( *pMarkowitzProduct-- ) + { /* + * N bottles of beer on the wall; + * N bottles of beer. + * you take one down and pass it around; + * N-1 bottles of beer on the wall. + */ + } + I = pMarkowitzProduct - Matrix->MarkowitzProd + 1; + +/* Assure that I is valid. */ + if (I < Step) break; /* while (Singletons-- > 0) */ + if (I > Matrix->Size) I = Step; + +/* Singleton has been found in either/both row or/and column I. */ + if ((ChosenPivot = Matrix->Diag[I]) != NULL) + { +/* Singleton lies on the diagonal. */ + PivotMag = ELEMENT_MAG(ChosenPivot); + if + ( PivotMag > Matrix->AbsThreshold AND + PivotMag > Matrix->RelThreshold * + FindBiggestInColExclude( Matrix, ChosenPivot, Step ) + ) return ChosenPivot; + } + else + { +/* Singleton does not lie on diagonal, find it. */ + if (Matrix->MarkowitzCol[I] == 0) + { ChosenPivot = Matrix->FirstInCol[I]; + while ((ChosenPivot != NULL) AND (ChosenPivot->Row < Step)) + ChosenPivot = ChosenPivot->NextInCol; + if (ChosenPivot == NULL) + { /* Reduced column has no elements, matrix is singular. */ + break; + } + PivotMag = ELEMENT_MAG( ChosenPivot ); + if + ( PivotMag > Matrix->AbsThreshold AND + PivotMag > Matrix->RelThreshold * + FindBiggestInColExclude( Matrix, ChosenPivot, + Step ) + ) return ChosenPivot; + else + { if (Matrix->MarkowitzRow[I] == 0) + { ChosenPivot = Matrix->FirstInRow[I]; + while((ChosenPivot != NULL) AND (ChosenPivot->Col<Step)) + ChosenPivot = ChosenPivot->NextInRow; + if (ChosenPivot == NULL) + {/* Reduced row has no elements, matrix is singular. */ + break; + } + PivotMag = ELEMENT_MAG(ChosenPivot); + if + ( PivotMag > Matrix->AbsThreshold AND + PivotMag > Matrix->RelThreshold * + FindBiggestInColExclude( Matrix, + ChosenPivot, + Step ) + ) return ChosenPivot; + } + } + } + else + { ChosenPivot = Matrix->FirstInRow[I]; + while ((ChosenPivot != NULL) AND (ChosenPivot->Col < Step)) + ChosenPivot = ChosenPivot->NextInRow; + if (ChosenPivot == NULL) + { /* Reduced row has no elements, matrix is singular. */ + break; + } + PivotMag = ELEMENT_MAG(ChosenPivot); + if + ( PivotMag > Matrix->AbsThreshold AND + PivotMag > Matrix->RelThreshold * + FindBiggestInColExclude( Matrix, ChosenPivot, + Step ) + ) return ChosenPivot; + } + } +/* Singleton not acceptable (too small), try another. */ + } /* end of while(lSingletons>0) */ + +/* + * All singletons were unacceptable. Restore Matrix->Singletons count. + * Initial assumption that an acceptable singleton would be found was wrong. + */ + Matrix->Singletons++; + return NULL; +} + + + + + + + + + + + + +#if DIAGONAL_PIVOTING +#if MODIFIED_MARKOWITZ +/* + * QUICK SEARCH OF DIAGONAL FOR PIVOT WITH MODIFIED MARKOWITZ CRITERION + * + * Searches the diagonal looking for the best pivot. For a pivot to be + * acceptable it must be larger than the pivot RelThreshold times the largest + * element in its reduced column. Among the acceptable diagonals, the + * one with the smallest MarkowitzProduct is sought. Search terminates + * early if a diagonal is found with a MarkowitzProduct of one and its + * magnitude is larger than the other elements in its row and column. + * Since its MarkowitzProduct is one, there is only one other element in + * both its row and column, and, as a condition for early termination, + * these elements must be located symmetricly in the matrix. If a tie + * occurs between elements of equal MarkowitzProduct, then the element with + * the largest ratio between its magnitude and the largest element in its + * column is used. The search will be terminated after a given number of + * ties have occurred and the best (largest ratio) of the tied element will + * be used as the pivot. The number of ties that will trigger an early + * termination is MinMarkowitzProduct * TIES_MULTIPLIER. + * + * >>> Returned: + * A pointer to the diagonal element chosen to be pivot. If no diagonal is + * acceptable, a NULL is returned. + * + * >>> Arguments: + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * ChosenPivot (ElementPtr) + * Pointer to the element that has been chosen to be the pivot. + * LargestOffDiagonal (RealNumber) + * Magnitude of the largest of the off-diagonal terms associated with + * a diagonal with MarkowitzProduct equal to one. + * Magnitude (RealNumber) + * Absolute value of diagonal element. + * MaxRatio (RealNumber) + * Among the elements tied with the smallest Markowitz product, MaxRatio + * is the best (smallest) ratio of LargestInCol to the diagonal Magnitude + * found so far. The smaller the ratio, the better numerically the + * element will be as pivot. + * MinMarkowitzProduct (long) + * Smallest Markowitz product found of pivot candidates that lie along + * diagonal. + * NumberOfTies (int) + * A count of the number of Markowitz ties that have occurred at current + * MarkowitzProduct. + * pDiag (ElementPtr) + * Pointer to current diagonal element. + * pMarkowitzProduct (long *) + * Pointer that points into MarkowitzProduct array. It is used to quickly + * access successive Markowitz products. + * Ratio (RealNumber) + * For the current pivot candidate, Ratio is the ratio of the largest + * element in its column (excluding itself) to its magnitude. + * TiedElements (ElementPtr[]) + * Array of pointers to the elements with the minimum Markowitz + * product. + * pOtherInCol (ElementPtr) + * When there is only one other element in a column other than the + * diagonal, pOtherInCol is used to point to it. Used when Markowitz + * product is to determine if off diagonals are placed symmetricly. + * pOtherInRow (ElementPtr) + * When there is only one other element in a row other than the diagonal, + * pOtherInRow is used to point to it. Used when Markowitz product is + * to determine if off diagonals are placed symmetricly. + */ + +static ElementPtr +QuicklySearchDiagonal( + MatrixPtr Matrix, + int Step +) +{ +register long MinMarkowitzProduct, *pMarkowitzProduct; +register ElementPtr pDiag, pOtherInRow, pOtherInCol; +int I, NumberOfTies = 0; +ElementPtr ChosenPivot, TiedElements[MAX_MARKOWITZ_TIES + 1]; +RealNumber Magnitude, LargestInCol, Ratio, MaxRatio; +RealNumber LargestOffDiagonal; +RealNumber FindBiggestInColExclude(); + +/* Begin `QuicklySearchDiagonal'. */ + NumberOfTies = -1; + MinMarkowitzProduct = LARGEST_LONG_INTEGER; + pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+2]); + Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step]; + +/* Assure that following while loop will always terminate. */ + Matrix->MarkowitzProd[Step-1] = -1; + +/* + * This is tricky. Am using a pointer in the inner while loop to + * sequentially step through the MarkowitzProduct array. Search + * terminates when the Markowitz product of zero placed at location + * Step-1 is found. The row (and column) index on the diagonal is then + * calculated by subtracting the pointer to the Markowitz product of + * the first diagonal from the pointer to the Markowitz product of the + * desired element. The outer for loop is infinite, broken by using + * break. + * + * Search proceeds from the end (high row and column numbers) to the + * beginning (low row and column numbers) so that rows and columns with + * large Markowitz products will tend to be move to the bottom of the + * matrix. However, choosing Diag[Step] is desirable because it would + * require no row and column interchanges, so inspect it first by + * putting its Markowitz product at the end of the MarkowitzProd + * vector. + */ + + for(;;) /* Endless for loop. */ + { while (MinMarkowitzProduct < *(--pMarkowitzProduct)) + { /* + * N bottles of beer on the wall; + * N bottles of beer. + * You take one down and pass it around; + * N-1 bottles of beer on the wall. + */ + } + + I = pMarkowitzProduct - Matrix->MarkowitzProd; + +/* Assure that I is valid; if I < Step, terminate search. */ + if (I < Step) break; /* Endless for loop */ + if (I > Matrix->Size) I = Step; + + if ((pDiag = Matrix->Diag[I]) == NULL) + continue; /* Endless for loop */ + if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold) + continue; /* Endless for loop */ + + if (*pMarkowitzProduct == 1) + { +/* Case where only one element exists in row and column other than diagonal. */ + +/* Find off diagonal elements. */ + pOtherInRow = pDiag->NextInRow; + pOtherInCol = pDiag->NextInCol; + if (pOtherInRow == NULL AND pOtherInCol == NULL) + { pOtherInRow = Matrix->FirstInRow[I]; + while(pOtherInRow != NULL) + { if (pOtherInRow->Col >= Step AND pOtherInRow->Col != I) + break; + pOtherInRow = pOtherInRow->NextInRow; + } + pOtherInCol = Matrix->FirstInCol[I]; + while(pOtherInCol != NULL) + { if (pOtherInCol->Row >= Step AND pOtherInCol->Row != I) + break; + pOtherInCol = pOtherInCol->NextInCol; + } + } + +/* Accept diagonal as pivot if diagonal is larger than off diagonals and the + * off diagonals are placed symmetricly. */ + if (pOtherInRow != NULL AND pOtherInCol != NULL) + { if (pOtherInRow->Col == pOtherInCol->Row) + { LargestOffDiagonal = MAX(ELEMENT_MAG(pOtherInRow), + ELEMENT_MAG(pOtherInCol)); + if (Magnitude >= LargestOffDiagonal) + { +/* Accept pivot, it is unlikely to contribute excess error. */ + return pDiag; + } + } + } + } + + if (*pMarkowitzProduct < MinMarkowitzProduct) + { +/* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */ + TiedElements[0] = pDiag; + MinMarkowitzProduct = *pMarkowitzProduct; + NumberOfTies = 0; + } + else + { +/* This case handles Markowitz ties. */ + if (NumberOfTies < MAX_MARKOWITZ_TIES) + { TiedElements[++NumberOfTies] = pDiag; + if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER) + break; /* Endless for loop */ + } + } + } /* End of endless for loop. */ + +/* Test to see if any element was chosen as a pivot candidate. */ + if (NumberOfTies < 0) + return NULL; + +/* Determine which of tied elements is best numerically. */ + ChosenPivot = NULL; + MaxRatio = 1.0 / Matrix->RelThreshold; + + for (I = 0; I <= NumberOfTies; I++) + { pDiag = TiedElements[I]; + Magnitude = ELEMENT_MAG(pDiag); + LargestInCol = FindBiggestInColExclude( Matrix, pDiag, Step ); + Ratio = LargestInCol / Magnitude; + if (Ratio < MaxRatio) + { ChosenPivot = pDiag; + MaxRatio = Ratio; + } + } + return ChosenPivot; +} + + + + + + + + + + +#else /* Not MODIFIED_MARKOWITZ */ +/* + * QUICK SEARCH OF DIAGONAL FOR PIVOT WITH CONVENTIONAL MARKOWITZ + * CRITERION + * + * Searches the diagonal looking for the best pivot. For a pivot to be + * acceptable it must be larger than the pivot RelThreshold times the largest + * element in its reduced column. Among the acceptable diagonals, the + * one with the smallest MarkowitzProduct is sought. Search terminates + * early if a diagonal is found with a MarkowitzProduct of one and its + * magnitude is larger than the other elements in its row and column. + * Since its MarkowitzProduct is one, there is only one other element in + * both its row and column, and, as a condition for early termination, + * these elements must be located symmetricly in the matrix. + * + * >>> Returned: + * A pointer to the diagonal element chosen to be pivot. If no diagonal is + * acceptable, a NULL is returned. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * ChosenPivot (ElementPtr) + * Pointer to the element that has been chosen to be the pivot. + * LargestOffDiagonal (RealNumber) + * Magnitude of the largest of the off-diagonal terms associated with + * a diagonal with MarkowitzProduct equal to one. + * Magnitude (RealNumber) + * Absolute value of diagonal element. + * MinMarkowitzProduct (long) + * Smallest Markowitz product found of pivot candidates which are + * acceptable. + * pDiag (ElementPtr) + * Pointer to current diagonal element. + * pMarkowitzProduct (long *) + * Pointer that points into MarkowitzProduct array. It is used to quickly + * access successive Markowitz products. + * pOtherInCol (ElementPtr) + * When there is only one other element in a column other than the + * diagonal, pOtherInCol is used to point to it. Used when Markowitz + * product is to determine if off diagonals are placed symmetricly. + * pOtherInRow (ElementPtr) + * When there is only one other element in a row other than the diagonal, + * pOtherInRow is used to point to it. Used when Markowitz product is + * to determine if off diagonals are placed symmetricly. + */ + +static ElementPtr +QuicklySearchDiagonal( + MatrixPtr Matrix, + int Step +) +{ +register long MinMarkowitzProduct, *pMarkowitzProduct; +register ElementPtr pDiag; +int I; +ElementPtr ChosenPivot, pOtherInRow, pOtherInCol; +RealNumber Magnitude, LargestInCol, LargestOffDiagonal; +RealNumber FindBiggestInColExclude(); + +/* Begin `QuicklySearchDiagonal'. */ + ChosenPivot = NULL; + MinMarkowitzProduct = LARGEST_LONG_INTEGER; + pMarkowitzProduct = &(Matrix->MarkowitzProd[Matrix->Size+2]); + Matrix->MarkowitzProd[Matrix->Size+1] = Matrix->MarkowitzProd[Step]; + +/* Assure that following while loop will always terminate. */ + Matrix->MarkowitzProd[Step-1] = -1; + +/* + * This is tricky. Am using a pointer in the inner while loop to + * sequentially step through the MarkowitzProduct array. Search + * terminates when the Markowitz product of zero placed at location + * Step-1 is found. The row (and column) index on the diagonal is then + * calculated by subtracting the pointer to the Markowitz product of + * the first diagonal from the pointer to the Markowitz product of the + * desired element. The outer for loop is infinite, broken by using + * break. + * + * Search proceeds from the end (high row and column numbers) to the + * beginning (low row and column numbers) so that rows and columns with + * large Markowitz products will tend to be move to the bottom of the + * matrix. However, choosing Diag[Step] is desirable because it would + * require no row and column interchanges, so inspect it first by + * putting its Markowitz product at the end of the MarkowitzProd + * vector. + */ + + for (;;) /* Endless for loop. */ + { while (*(--pMarkowitzProduct) >= MinMarkowitzProduct) + { /* Just passing through. */ + } + + I = pMarkowitzProduct - Matrix->MarkowitzProd; + +/* Assure that I is valid; if I < Step, terminate search. */ + if (I < Step) break; /* Endless for loop */ + if (I > Matrix->Size) I = Step; + + if ((pDiag = Matrix->Diag[I]) == NULL) + continue; /* Endless for loop */ + if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold) + continue; /* Endless for loop */ + + if (*pMarkowitzProduct == 1) + { +/* Case where only one element exists in row and column other than diagonal. */ + +/* Find off-diagonal elements. */ + pOtherInRow = pDiag->NextInRow; + pOtherInCol = pDiag->NextInCol; + if (pOtherInRow == NULL AND pOtherInCol == NULL) + { pOtherInRow = Matrix->FirstInRow[I]; + while(pOtherInRow != NULL) + { if (pOtherInRow->Col >= Step AND pOtherInRow->Col != I) + break; + pOtherInRow = pOtherInRow->NextInRow; + } + pOtherInCol = Matrix->FirstInCol[I]; + while(pOtherInCol != NULL) + { if (pOtherInCol->Row >= Step AND pOtherInCol->Row != I) + break; + pOtherInCol = pOtherInCol->NextInCol; + } + } + +/* Accept diagonal as pivot if diagonal is larger than off-diagonals and the + * off-diagonals are placed symmetricly. */ + if (pOtherInRow != NULL AND pOtherInCol != NULL) + { if (pOtherInRow->Col == pOtherInCol->Row) + { LargestOffDiagonal = MAX(ELEMENT_MAG(pOtherInRow), + ELEMENT_MAG(pOtherInCol)); + if (Magnitude >= LargestOffDiagonal) + { +/* Accept pivot, it is unlikely to contribute excess error. */ + return pDiag; + } + } + } + } + + MinMarkowitzProduct = *pMarkowitzProduct; + ChosenPivot = pDiag; + } /* End of endless for loop. */ + + if (ChosenPivot != NULL) + { LargestInCol = FindBiggestInColExclude( Matrix, ChosenPivot, Step ); + if( ELEMENT_MAG(ChosenPivot) <= Matrix->RelThreshold * LargestInCol ) + ChosenPivot = NULL; + } + return ChosenPivot; +} +#endif /* Not MODIFIED_MARKOWITZ */ + + + + + + + + + +/* + * SEARCH DIAGONAL FOR PIVOT WITH MODIFIED MARKOWITZ CRITERION + * + * Searches the diagonal looking for the best pivot. For a pivot to be + * acceptable it must be larger than the pivot RelThreshold times the largest + * element in its reduced column. Among the acceptable diagonals, the + * one with the smallest MarkowitzProduct is sought. If a tie occurs + * between elements of equal MarkowitzProduct, then the element with + * the largest ratio between its magnitude and the largest element in its + * column is used. The search will be terminated after a given number of + * ties have occurred and the best (smallest ratio) of the tied element will + * be used as the pivot. The number of ties that will trigger an early + * termination is MinMarkowitzProduct * TIES_MULTIPLIER. + * + * >>> Returned: + * A pointer to the diagonal element chosen to be pivot. If no diagonal is + * acceptable, a NULL is returned. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * ChosenPivot (ElementPtr) + * Pointer to the element that has been chosen to be the pivot. + * Size (int) + * Local version of size which is placed in a register to increase speed. + * Magnitude (RealNumber) + * Absolute value of diagonal element. + * MinMarkowitzProduct (long) + * Smallest Markowitz product found of those pivot candidates which are + * acceptable. + * NumberOfTies (int) + * A count of the number of Markowitz ties that have occurred at current + * MarkowitzProduct. + * pDiag (ElementPtr) + * Pointer to current diagonal element. + * pMarkowitzProduct (long*) + * Pointer that points into MarkowitzProduct array. It is used to quickly + * access successive Markowitz products. + * Ratio (RealNumber) + * For the current pivot candidate, Ratio is the + * Ratio of the largest element in its column to its magnitude. + * RatioOfAccepted (RealNumber) + * For the best pivot candidate found so far, RatioOfAccepted is the + * Ratio of the largest element in its column to its magnitude. + */ + +static ElementPtr +SearchDiagonal( + MatrixPtr Matrix, + register int Step +) +{ +register int J; +register long MinMarkowitzProduct, *pMarkowitzProduct; +register int I; +register ElementPtr pDiag; +int NumberOfTies = 0, Size = Matrix->Size; +ElementPtr ChosenPivot; +RealNumber Magnitude, Ratio, RatioOfAccepted = 0.0, LargestInCol; +RealNumber FindBiggestInColExclude(); + +/* Begin `SearchDiagonal'. */ + ChosenPivot = NULL; + MinMarkowitzProduct = LARGEST_LONG_INTEGER; + pMarkowitzProduct = &(Matrix->MarkowitzProd[Size+2]); + Matrix->MarkowitzProd[Size+1] = Matrix->MarkowitzProd[Step]; + +/* Start search of diagonal. */ + for (J = Size+1; J > Step; J--) + { + if (*(--pMarkowitzProduct) > MinMarkowitzProduct) + continue; /* for loop */ + if (J > Matrix->Size) + I = Step; + else + I = J; + if ((pDiag = Matrix->Diag[I]) == NULL) + continue; /* for loop */ + if ((Magnitude = ELEMENT_MAG(pDiag)) <= Matrix->AbsThreshold) + continue; /* for loop */ + +/* Test to see if diagonal's magnitude is acceptable. */ + LargestInCol = FindBiggestInColExclude( Matrix, pDiag, Step ); + if (Magnitude <= Matrix->RelThreshold * LargestInCol) + continue; /* for loop */ + + if (*pMarkowitzProduct < MinMarkowitzProduct) + { +/* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */ + ChosenPivot = pDiag; + MinMarkowitzProduct = *pMarkowitzProduct; + RatioOfAccepted = LargestInCol / Magnitude; + NumberOfTies = 0; + } + else + { +/* This case handles Markowitz ties. */ + NumberOfTies++; + Ratio = LargestInCol / Magnitude; + if (Ratio < RatioOfAccepted) + { ChosenPivot = pDiag; + RatioOfAccepted = Ratio; + } + if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER) + return ChosenPivot; + } + } /* End of for(Step) */ + return ChosenPivot; +} +#endif /* DIAGONAL_PIVOTING */ + + + + + + + + + + +/* + * SEARCH ENTIRE MATRIX FOR BEST PIVOT + * + * Performs a search over the entire matrix looking for the acceptable + * element with the lowest MarkowitzProduct. If there are several that + * are tied for the smallest MarkowitzProduct, the tie is broken by using + * the ratio of the magnitude of the element being considered to the largest + * element in the same column. If no element is acceptable then the largest + * element in the reduced submatrix is used as the pivot and the + * matrix is declared to be spSMALL_PIVOT. If the largest element is + * zero, the matrix is declared to be spSINGULAR. + * + * >>> Returned: + * A pointer to the diagonal element chosen to be pivot. If no element is + * found, then NULL is returned and the matrix is spSINGULAR. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * ChosenPivot (ElementPtr) + * Pointer to the element that has been chosen to be the pivot. + * LargestElementMag (RealNumber) + * Magnitude of the largest element yet found in the reduced submatrix. + * Size (int) + * Local version of Size; placed in a register for speed. + * Magnitude (RealNumber) + * Absolute value of diagonal element. + * MinMarkowitzProduct (long) + * Smallest Markowitz product found of pivot candidates which are + * acceptable. + * NumberOfTies (int) + * A count of the number of Markowitz ties that have occurred at current + * MarkowitzProduct. + * pElement (ElementPtr) + * Pointer to current element. + * pLargestElement (ElementPtr) + * Pointer to the largest element yet found in the reduced submatrix. + * Product (long) + * Markowitz product for the current row and column. + * Ratio (RealNumber) + * For the current pivot candidate, Ratio is the + * Ratio of the largest element in its column to its magnitude. + * RatioOfAccepted (RealNumber) + * For the best pivot candidate found so far, RatioOfAccepted is the + * Ratio of the largest element in its column to its magnitude. + * + * >>> Possible errors: + * spSINGULAR + * spSMALL_PIVOT + */ + +static ElementPtr +SearchEntireMatrix( + MatrixPtr Matrix, + int Step +) +{ +register int I, Size = Matrix->Size; +register ElementPtr pElement; +int NumberOfTies = 0; +long Product, MinMarkowitzProduct; +ElementPtr ChosenPivot, pLargestElement = NULL; +RealNumber Magnitude, LargestElementMag, Ratio, RatioOfAccepted = 0.0, LargestInCol; + +/* Begin `SearchEntireMatrix'. */ + ChosenPivot = NULL; + LargestElementMag = 0.0; + MinMarkowitzProduct = LARGEST_LONG_INTEGER; + +/* Start search of matrix on column by column basis. */ + for (I = Step; I <= Size; I++) + { pElement = Matrix->FirstInCol[I]; + + while (pElement != NULL AND pElement->Row < Step) + pElement = pElement->NextInCol; + + if((LargestInCol = FindLargestInCol(pElement)) == 0.0) + continue; /* for loop */ + + while (pElement != NULL) + { +/* Check to see if element is the largest encountered so far. If so, record + its magnitude and address. */ + if ((Magnitude = ELEMENT_MAG(pElement)) > LargestElementMag) + { LargestElementMag = Magnitude; + pLargestElement = pElement; + } +/* Calculate element's MarkowitzProduct. */ + spcMarkoProd( Product, Matrix->MarkowitzRow[pElement->Row], + Matrix->MarkowitzCol[pElement->Col] ); + +/* Test to see if element is acceptable as a pivot candidate. */ + if ((Product <= MinMarkowitzProduct) AND + (Magnitude > Matrix->RelThreshold * LargestInCol) AND + (Magnitude > Matrix->AbsThreshold)) + { +/* Test to see if element has lowest MarkowitzProduct yet found, or whether it + is tied with an element found earlier. */ + if (Product < MinMarkowitzProduct) + { +/* Notice strict inequality in test. This is a new smallest MarkowitzProduct. */ + ChosenPivot = pElement; + MinMarkowitzProduct = Product; + RatioOfAccepted = LargestInCol / Magnitude; + NumberOfTies = 0; + } + else + { +/* This case handles Markowitz ties. */ + NumberOfTies++; + Ratio = LargestInCol / Magnitude; + if (Ratio < RatioOfAccepted) + { ChosenPivot = pElement; + RatioOfAccepted = Ratio; + } + if (NumberOfTies >= MinMarkowitzProduct * TIES_MULTIPLIER) + return ChosenPivot; + } + } + pElement = pElement->NextInCol; + } /* End of while(pElement != NULL) */ + } /* End of for(Step) */ + + if (ChosenPivot != NULL) return ChosenPivot; + + if (LargestElementMag == 0.0) + { Matrix->Error = spSINGULAR; + return NULL; + } + + Matrix->Error = spSMALL_PIVOT; + return pLargestElement; +} + + + + + + + + + + + + +/* + * DETERMINE THE MAGNITUDE OF THE LARGEST ELEMENT IN A COLUMN + * + * This routine searches a column and returns the magnitude of the largest + * element. This routine begins the search at the element pointed to by + * pElement, the parameter. + * + * The search is conducted by starting at the element specified by a pointer, + * which should be one below the diagonal, and moving down the column. On + * the way down the column, the magnitudes of the elements are tested to see + * if they are the largest yet found. + * + * >>> Returned: + * The magnitude of the largest element in the column below and including + * the one pointed to by the input parameter. + * + * >>> Arguments: + * pElement <input> (ElementPtr) + * The pointer to the first element to be tested. Also, used by the + * routine to access all lower elements in the column. + * + * >>> Local variables: + * Largest (RealNumber) + * The magnitude of the largest element. + * Magnitude (RealNumber) + * The magnitude of the currently active element. + */ + +static RealNumber +FindLargestInCol( register ElementPtr pElement ) +{ +RealNumber Magnitude, Largest = 0.0; + +/* Begin `FindLargestInCol'. */ +/* Search column for largest element beginning at Element. */ + while (pElement != NULL) + { if ((Magnitude = ELEMENT_MAG(pElement)) > Largest) + Largest = Magnitude; + pElement = pElement->NextInCol; + } + + return Largest; +} + + + + + + + + + + +/* + * DETERMINE THE MAGNITUDE OF THE LARGEST ELEMENT IN A COLUMN + * EXCLUDING AN ELEMENT + * + * This routine searches a column and returns the magnitude of the largest + * element. One given element is specifically excluded from the search.. + * + * The search is conducted by starting at the first element in the column + * and moving down the column until the active part of the matrix is entered, + * i.e. the reduced submatrix. The rest of the column is then traversed + * looking for the largest element. + * + * >>> Returned: + * The magnitude of the largest element in the active portion of the column, + * excluding the specified element, is returned. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * pElement <input> (ElementPtr) + * The pointer to the element that is to be excluded from search. Column + * to be searched is one that contains this element. Also used to + * access the elements in the column. + * Step <input> (int) + * Index of the diagonal currently being eliminated. Indicates where + * the active part of the matrix begins. + * + * >>> Local variables: + * Col (int) + * The number of the column to be searched. Also the column number of + * the element to be avoided in the search. + * Largest (RealNumber) + * The magnitude of the largest element. + * Magnitude (RealNumber) + * The magnitude of the currently active element. + * Row (int) + * The row number of element to be excluded from the search. + */ + +static RealNumber +FindBiggestInColExclude( + MatrixPtr Matrix, + register ElementPtr pElement, + register int Step +) +{ +register int Row; +int Col; +RealNumber Largest, Magnitude; + +/* Begin `FindBiggestInColExclude'. */ + Row = pElement->Row; + Col = pElement->Col; + pElement = Matrix->FirstInCol[Col]; + +/* Travel down column until reduced submatrix is entered. */ + while ((pElement != NULL) AND (pElement->Row < Step)) + pElement = pElement->NextInCol; + +/* Initialize the variable Largest. */ + if (pElement->Row != Row) + Largest = ELEMENT_MAG(pElement); + else + Largest = 0.0; + +/* Search rest of column for largest element, avoiding excluded element. */ + while ((pElement = pElement->NextInCol) != NULL) + { if ((Magnitude = ELEMENT_MAG(pElement)) > Largest) + { if (pElement->Row != Row) + Largest = Magnitude; + } + } + + return Largest; +} + + + + + + + + + + +/* + * EXCHANGE ROWS AND COLUMNS + * + * Exchanges two rows and two columns so that the selected pivot is moved to + * the upper left corner of the remaining submatrix. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * pPivot <input> (ElementPtr) + * Pointer to the current pivot. + * Step <input> (int) + * Index of the diagonal currently being eliminated. + * + * >>> Local variables: + * Col (int) + * Column where the pivot was found. + * Row (int) + * Row where the pivot was found. + * OldMarkowitzProd_Col (long) + * Markowitz product associated with the diagonal element in the row + * the pivot was found in. + * OldMarkowitzProd_Row (long) + * Markowitz product associated with the diagonal element in the column + * the pivot was found in. + * OldMarkowitzProd_Step (long) + * Markowitz product associated with the diagonal element that is being + * moved so that the pivot can be placed in the upper left-hand corner + * of the reduced submatrix. + */ + +static void +ExchangeRowsAndCols( + MatrixPtr Matrix, + ElementPtr pPivot, + register int Step +) +{ +register int Row, Col; +long OldMarkowitzProd_Step, OldMarkowitzProd_Row, OldMarkowitzProd_Col; + +/* Begin `ExchangeRowsAndCols'. */ + Row = pPivot->Row; + Col = pPivot->Col; + Matrix->PivotsOriginalRow = Row; + Matrix->PivotsOriginalCol = Col; + + if ((Row == Step) AND (Col == Step)) return; + +/* Exchange rows and columns. */ + if (Row == Col) + { spcRowExchange( Matrix, Step, Row ); + spcColExchange( Matrix, Step, Col ); + SWAP( long, Matrix->MarkowitzProd[Step], Matrix->MarkowitzProd[Row] ); + SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Step] ); + } + else + { + +/* Initialize variables that hold old Markowitz products. */ + OldMarkowitzProd_Step = Matrix->MarkowitzProd[Step]; + OldMarkowitzProd_Row = Matrix->MarkowitzProd[Row]; + OldMarkowitzProd_Col = Matrix->MarkowitzProd[Col]; + +/* Exchange rows. */ + if (Row != Step) + { spcRowExchange( Matrix, Step, Row ); + Matrix->NumberOfInterchangesIsOdd = + NOT Matrix->NumberOfInterchangesIsOdd; + spcMarkoProd( Matrix->MarkowitzProd[Row], + Matrix->MarkowitzRow[Row], + Matrix->MarkowitzCol[Row] ); + +/* Update singleton count. */ + if ((Matrix->MarkowitzProd[Row]==0) != (OldMarkowitzProd_Row==0)) + { if (OldMarkowitzProd_Row == 0) + Matrix->Singletons--; + else + Matrix->Singletons++; + } + } + +/* Exchange columns. */ + if (Col != Step) + { spcColExchange( Matrix, Step, Col ); + Matrix->NumberOfInterchangesIsOdd = + NOT Matrix->NumberOfInterchangesIsOdd; + spcMarkoProd( Matrix->MarkowitzProd[Col], + Matrix->MarkowitzCol[Col], + Matrix->MarkowitzRow[Col] ); + +/* Update singleton count. */ + if ((Matrix->MarkowitzProd[Col]==0) != (OldMarkowitzProd_Col==0)) + { if (OldMarkowitzProd_Col == 0) + Matrix->Singletons--; + else + Matrix->Singletons++; + } + + Matrix->Diag[Col] = spcFindDiag( Matrix, Col ); + } + if (Row != Step) + Matrix->Diag[Row] = spcFindDiag( Matrix, Row ); + Matrix->Diag[Step] = spcFindDiag( Matrix, Step ); + +/* Update singleton count. */ + Matrix->MarkowitzProd[Step] = Matrix->MarkowitzCol[Step] * + Matrix->MarkowitzRow[Step]; + if ((Matrix->MarkowitzProd[Step]==0) != (OldMarkowitzProd_Step==0)) + { if (OldMarkowitzProd_Step == 0) + Matrix->Singletons--; + else + Matrix->Singletons++; + } + } + return; +} + + + + + + + + + +/* + * EXCHANGE ROWS + * + * Performs all required operations to exchange two rows. Those operations + * include: swap FirstInRow pointers, fixing up the NextInCol pointers, + * swapping row indexes in MatrixElements, and swapping Markowitz row + * counts. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * Row1 <input> (int) + * Row index of one of the rows, becomes the smallest index. + * Row2 <input> (int) + * Row index of the other row, becomes the largest index. + * + * Local variables: + * Column (int) + * Column in which row elements are currently being exchanged. + * Row1Ptr (ElementPtr) + * Pointer to an element in Row1. + * Row2Ptr (ElementPtr) + * Pointer to an element in Row2. + * Element1 (ElementPtr) + * Pointer to the element in Row1 to be exchanged. + * Element2 (ElementPtr) + * Pointer to the element in Row2 to be exchanged. + */ + +void +spcRowExchange( + MatrixPtr Matrix, + int Row1, + int Row2 +) +{ +register ElementPtr Row1Ptr, Row2Ptr; +int Column; +ElementPtr Element1, Element2; + +/* Begin `spcRowExchange'. */ + if (Row1 > Row2) SWAP(int, Row1, Row2); + + Row1Ptr = Matrix->FirstInRow[Row1]; + Row2Ptr = Matrix->FirstInRow[Row2]; + while (Row1Ptr != NULL OR Row2Ptr != NULL) + { +/* Exchange elements in rows while traveling from left to right. */ + if (Row1Ptr == NULL) + { Column = Row2Ptr->Col; + Element1 = NULL; + Element2 = Row2Ptr; + Row2Ptr = Row2Ptr->NextInRow; + } + else if (Row2Ptr == NULL) + { Column = Row1Ptr->Col; + Element1 = Row1Ptr; + Element2 = NULL; + Row1Ptr = Row1Ptr->NextInRow; + } + else if (Row1Ptr->Col < Row2Ptr->Col) + { Column = Row1Ptr->Col; + Element1 = Row1Ptr; + Element2 = NULL; + Row1Ptr = Row1Ptr->NextInRow; + } + else if (Row1Ptr->Col > Row2Ptr->Col) + { Column = Row2Ptr->Col; + Element1 = NULL; + Element2 = Row2Ptr; + Row2Ptr = Row2Ptr->NextInRow; + } + else /* Row1Ptr->Col == Row2Ptr->Col */ + { Column = Row1Ptr->Col; + Element1 = Row1Ptr; + Element2 = Row2Ptr; + Row1Ptr = Row1Ptr->NextInRow; + Row2Ptr = Row2Ptr->NextInRow; + } + + ExchangeColElements( Matrix, Row1, Element1, Row2, Element2, Column); + } /* end of while(Row1Ptr != NULL OR Row2Ptr != NULL) */ + + if (Matrix->InternalVectorsAllocated) + SWAP( int, Matrix->MarkowitzRow[Row1], Matrix->MarkowitzRow[Row2]); + SWAP( ElementPtr, Matrix->FirstInRow[Row1], Matrix->FirstInRow[Row2]); + SWAP( int, Matrix->IntToExtRowMap[Row1], Matrix->IntToExtRowMap[Row2]); +#if TRANSLATE + Matrix->ExtToIntRowMap[ Matrix->IntToExtRowMap[Row1] ] = Row1; + Matrix->ExtToIntRowMap[ Matrix->IntToExtRowMap[Row2] ] = Row2; +#endif + + return; +} + + + + + + + + + +/* + * EXCHANGE COLUMNS + * + * Performs all required operations to exchange two columns. Those operations + * include: swap FirstInCol pointers, fixing up the NextInRow pointers, + * swapping column indexes in MatrixElements, and swapping Markowitz + * column counts. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * Col1 <input> (int) + * Column index of one of the columns, becomes the smallest index. + * Col2 <input> (int) + * Column index of the other column, becomes the largest index + * + * Local variables: + * Row (int) + * Row in which column elements are currently being exchanged. + * Col1Ptr (ElementPtr) + * Pointer to an element in Col1. + * Col2Ptr (ElementPtr) + * Pointer to an element in Col2. + * Element1 (ElementPtr) + * Pointer to the element in Col1 to be exchanged. + * Element2 (ElementPtr) + * Pointer to the element in Col2 to be exchanged. + */ + +void +spcColExchange( + MatrixPtr Matrix, + int Col1, + int Col2 +) +{ +register ElementPtr Col1Ptr, Col2Ptr; +int Row; +ElementPtr Element1, Element2; + +/* Begin `spcColExchange'. */ + if (Col1 > Col2) SWAP(int, Col1, Col2); + + Col1Ptr = Matrix->FirstInCol[Col1]; + Col2Ptr = Matrix->FirstInCol[Col2]; + while (Col1Ptr != NULL OR Col2Ptr != NULL) + { +/* Exchange elements in rows while traveling from top to bottom. */ + if (Col1Ptr == NULL) + { Row = Col2Ptr->Row; + Element1 = NULL; + Element2 = Col2Ptr; + Col2Ptr = Col2Ptr->NextInCol; + } + else if (Col2Ptr == NULL) + { Row = Col1Ptr->Row; + Element1 = Col1Ptr; + Element2 = NULL; + Col1Ptr = Col1Ptr->NextInCol; + } + else if (Col1Ptr->Row < Col2Ptr->Row) + { Row = Col1Ptr->Row; + Element1 = Col1Ptr; + Element2 = NULL; + Col1Ptr = Col1Ptr->NextInCol; + } + else if (Col1Ptr->Row > Col2Ptr->Row) + { Row = Col2Ptr->Row; + Element1 = NULL; + Element2 = Col2Ptr; + Col2Ptr = Col2Ptr->NextInCol; + } + else /* Col1Ptr->Row == Col2Ptr->Row */ + { Row = Col1Ptr->Row; + Element1 = Col1Ptr; + Element2 = Col2Ptr; + Col1Ptr = Col1Ptr->NextInCol; + Col2Ptr = Col2Ptr->NextInCol; + } + + ExchangeRowElements( Matrix, Col1, Element1, Col2, Element2, Row); + } /* end of while(Col1Ptr != NULL OR Col2Ptr != NULL) */ + + if (Matrix->InternalVectorsAllocated) + SWAP( int, Matrix->MarkowitzCol[Col1], Matrix->MarkowitzCol[Col2]); + SWAP( ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]); + SWAP( int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]); +#if TRANSLATE + Matrix->ExtToIntColMap[ Matrix->IntToExtColMap[Col1] ] = Col1; + Matrix->ExtToIntColMap[ Matrix->IntToExtColMap[Col2] ] = Col2; +#endif + + return; +} + + + + + + + +/* + * EXCHANGE TWO ELEMENTS IN A COLUMN + * + * Performs all required operations to exchange two elements in a column. + * Those operations are: restring NextInCol pointers and swapping row indexes + * in the MatrixElements. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * Row1 <input> (int) + * Row of top element to be exchanged. + * Element1 <input> (ElementPtr) + * Pointer to top element to be exchanged. + * Row2 <input> (int) + * Row of bottom element to be exchanged. + * Element2 <input> (ElementPtr) + * Pointer to bottom element to be exchanged. + * Column <input> (int) + * Column that exchange is to take place in. + * + * >>> Local variables: + * ElementAboveRow1 (ElementPtr *) + * Location of pointer which points to the element above Element1. This + * pointer is modified so that it points to correct element on exit.. + * ElementAboveRow2 (ElementPtr *) + * Location of pointer which points to the element above Element2. This + * pointer is modified so that it points to correct element on exit.. + * ElementBelowRow1 (ElementPtr) + * Pointer to element below Element1. + * ElementBelowRow2 (ElementPtr) + * Pointer to element below Element2. + * pElement (ElementPtr) + * Pointer used to traverse the column. + */ + +static void +ExchangeColElements( + MatrixPtr Matrix, + int Row1, + register ElementPtr Element1, + int Row2, + register ElementPtr Element2, + int Column +) +{ +ElementPtr *ElementAboveRow1, *ElementAboveRow2; +ElementPtr ElementBelowRow1, ElementBelowRow2; +register ElementPtr pElement; + +/* Begin `ExchangeColElements'. */ +/* Search to find the ElementAboveRow1. */ + ElementAboveRow1 = &(Matrix->FirstInCol[Column]); + pElement = *ElementAboveRow1; + while (pElement->Row < Row1) + { ElementAboveRow1 = &(pElement->NextInCol); + pElement = *ElementAboveRow1; + } + if (Element1 != NULL) + { ElementBelowRow1 = Element1->NextInCol; + if (Element2 == NULL) + { +/* Element2 does not exist, move Element1 down to Row2. */ + if ( ElementBelowRow1 != NULL AND ElementBelowRow1->Row < Row2 ) + { +/* Element1 must be removed from linked list and moved. */ + *ElementAboveRow1 = ElementBelowRow1; + +/* Search column for Row2. */ + pElement = ElementBelowRow1; + do + { ElementAboveRow2 = &(pElement->NextInCol); + pElement = *ElementAboveRow2; + } while (pElement != NULL AND pElement->Row < Row2); + +/* Place Element1 in Row2. */ + *ElementAboveRow2 = Element1; + Element1->NextInCol = pElement; + *ElementAboveRow1 =ElementBelowRow1; + } + Element1->Row = Row2; + } + else + { +/* Element2 does exist, and the two elements must be exchanged. */ + if ( ElementBelowRow1->Row == Row2) + { +/* Element2 is just below Element1, exchange them. */ + Element1->NextInCol = Element2->NextInCol; + Element2->NextInCol = Element1; + *ElementAboveRow1 = Element2; + } + else + { +/* Element2 is not just below Element1 and must be searched for. */ + pElement = ElementBelowRow1; + do + { ElementAboveRow2 = &(pElement->NextInCol); + pElement = *ElementAboveRow2; + } while (pElement->Row < Row2); + + ElementBelowRow2 = Element2->NextInCol; + +/* Switch Element1 and Element2. */ + *ElementAboveRow1 = Element2; + Element2->NextInCol = ElementBelowRow1; + *ElementAboveRow2 = Element1; + Element1->NextInCol = ElementBelowRow2; + } + Element1->Row = Row2; + Element2->Row = Row1; + } + } + else + { +/* Element1 does not exist. */ + ElementBelowRow1 = pElement; + +/* Find Element2. */ + if (ElementBelowRow1->Row != Row2) + { do + { ElementAboveRow2 = &(pElement->NextInCol); + pElement = *ElementAboveRow2; + } while (pElement->Row < Row2); + + ElementBelowRow2 = Element2->NextInCol; + +/* Move Element2 to Row1. */ + *ElementAboveRow2 = Element2->NextInCol; + *ElementAboveRow1 = Element2; + Element2->NextInCol = ElementBelowRow1; + } + Element2->Row = Row1; + } + return; +} + + + + + + + +/* + * EXCHANGE TWO ELEMENTS IN A ROW + * + * Performs all required operations to exchange two elements in a row. + * Those operations are: restring NextInRow pointers and swapping column + * indexes in the MatrixElements. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * Col1 <input> (int) + * Col of left-most element to be exchanged. + * Element1 <input> (ElementPtr) + * Pointer to left-most element to be exchanged. + * Col2 <input> (int) + * Col of right-most element to be exchanged. + * Element2 <input> (ElementPtr) + * Pointer to right-most element to be exchanged. + * Row <input> (int) + * Row that exchange is to take place in. + * + * >>> Local variables: + * ElementLeftOfCol1 (ElementPtr *) + * Location of pointer which points to the element to the left of + * Element1. This pointer is modified so that it points to correct + * element on exit. + * ElementLeftOfCol2 (ElementPtr *) + * Location of pointer which points to the element to the left of + * Element2. This pointer is modified so that it points to correct + * element on exit. + * ElementRightOfCol1 (ElementPtr) + * Pointer to element right of Element1. + * ElementRightOfCol2 (ElementPtr) + * Pointer to element right of Element2. + * pElement (ElementPtr) + * Pointer used to traverse the row. + */ + +static void +ExchangeRowElements( + MatrixPtr Matrix, + int Col1, + register ElementPtr Element1, + int Col2, + register ElementPtr Element2, + int Row +) +{ +ElementPtr *ElementLeftOfCol1, *ElementLeftOfCol2; +ElementPtr ElementRightOfCol1, ElementRightOfCol2; +register ElementPtr pElement; + +/* Begin `ExchangeRowElements'. */ +/* Search to find the ElementLeftOfCol1. */ + ElementLeftOfCol1 = &(Matrix->FirstInRow[Row]); + pElement = *ElementLeftOfCol1; + while (pElement->Col < Col1) + { ElementLeftOfCol1 = &(pElement->NextInRow); + pElement = *ElementLeftOfCol1; + } + if (Element1 != NULL) + { ElementRightOfCol1 = Element1->NextInRow; + if (Element2 == NULL) + { +/* Element2 does not exist, move Element1 to right to Col2. */ + if ( ElementRightOfCol1 != NULL AND ElementRightOfCol1->Col < Col2 ) + { +/* Element1 must be removed from linked list and moved. */ + *ElementLeftOfCol1 = ElementRightOfCol1; + +/* Search Row for Col2. */ + pElement = ElementRightOfCol1; + do + { ElementLeftOfCol2 = &(pElement->NextInRow); + pElement = *ElementLeftOfCol2; + } while (pElement != NULL AND pElement->Col < Col2); + +/* Place Element1 in Col2. */ + *ElementLeftOfCol2 = Element1; + Element1->NextInRow = pElement; + *ElementLeftOfCol1 =ElementRightOfCol1; + } + Element1->Col = Col2; + } + else + { +/* Element2 does exist, and the two elements must be exchanged. */ + if ( ElementRightOfCol1->Col == Col2) + { +/* Element2 is just right of Element1, exchange them. */ + Element1->NextInRow = Element2->NextInRow; + Element2->NextInRow = Element1; + *ElementLeftOfCol1 = Element2; + } + else + { +/* Element2 is not just right of Element1 and must be searched for. */ + pElement = ElementRightOfCol1; + do + { ElementLeftOfCol2 = &(pElement->NextInRow); + pElement = *ElementLeftOfCol2; + } while (pElement->Col < Col2); + + ElementRightOfCol2 = Element2->NextInRow; + +/* Switch Element1 and Element2. */ + *ElementLeftOfCol1 = Element2; + Element2->NextInRow = ElementRightOfCol1; + *ElementLeftOfCol2 = Element1; + Element1->NextInRow = ElementRightOfCol2; + } + Element1->Col = Col2; + Element2->Col = Col1; + } + } + else + { +/* Element1 does not exist. */ + ElementRightOfCol1 = pElement; + +/* Find Element2. */ + if (ElementRightOfCol1->Col != Col2) + { do + { ElementLeftOfCol2 = &(pElement->NextInRow); + pElement = *ElementLeftOfCol2; + } while (pElement->Col < Col2); + + ElementRightOfCol2 = Element2->NextInRow; + +/* Move Element2 to Col1. */ + *ElementLeftOfCol2 = Element2->NextInRow; + *ElementLeftOfCol1 = Element2; + Element2->NextInRow = ElementRightOfCol1; + } + Element2->Col = Col1; + } + return; +} + + + + + + + + + + + +/* + * PERFORM ROW AND COLUMN ELIMINATION ON REAL MATRIX + * + * Eliminates a single row and column of the matrix and leaves single row of + * the upper triangular matrix and a single column of the lower triangular + * matrix in its wake. Uses Gauss's method. + * + * >>> Argument: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * pPivot <input> (ElementPtr) + * Pointer to the current pivot. + * + * >>> Local variables: + * pLower (ElementPtr) + * Points to matrix element in lower triangular column. + * pSub (ElementPtr) + * Points to elements in the reduced submatrix. + * Row (int) + * Row index. + * pUpper (ElementPtr) + * Points to matrix element in upper triangular row. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +static void +RealRowColElimination( + MatrixPtr Matrix, + register ElementPtr pPivot +) +{ +#if REAL +register ElementPtr pSub, *ppAbove; +register int Row; +register ElementPtr pLower, pUpper; + +/* Begin `RealRowColElimination'. */ + +/* Test for zero pivot. */ + if (ABS(pPivot->Real) == 0.0) + { (void)MatrixIsSingular( Matrix, pPivot->Row ); + return; + } + pPivot->Real = 1.0 / pPivot->Real; + + pUpper = pPivot->NextInRow; + while (pUpper != NULL) + { +/* Calculate upper triangular element. */ + pUpper->Real *= pPivot->Real; + + pSub = pUpper->NextInCol; + pLower = pPivot->NextInCol; + ppAbove = &pUpper->NextInCol; + while (pLower != NULL) + { Row = pLower->Row; + +/* Find element in row that lines up with current lower triangular element. */ + while (pSub != NULL AND pSub->Row < Row) + { ppAbove = &pSub->NextInCol; + pSub = pSub->NextInCol; + } + +/* Test to see if desired element was not found, if not, create fill-in. */ + if (pSub == NULL OR pSub->Row > Row) + { pSub = spcCreateElement( Matrix, Row, pUpper->Col, + &pLower->NextInRow, ppAbove, YES ); + if (pSub == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + } + pSub->Real -= pUpper->Real * pLower->Real; + pSub = pSub->NextInCol; + pLower = pLower->NextInCol; + } + pUpper = pUpper->NextInRow; + } + return; +#endif /* REAL */ +} + + + + + + + + + +/* + * PERFORM ROW AND COLUMN ELIMINATION ON COMPLEX MATRIX + * + * Eliminates a single row and column of the matrix and leaves single row of + * the upper triangular matrix and a single column of the lower triangular + * matrix in its wake. Uses Gauss's method. + * + * >>> Argument: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * pPivot <input> (ElementPtr) + * Pointer to the current pivot. + * + * >>> Local variables: + * pLower (ElementPtr) + * Points to matrix element in lower triangular column. + * pSub (ElementPtr) + * Points to elements in the reduced submatrix. + * Row (int) + * Row index. + * pUpper (ElementPtr) + * Points to matrix element in upper triangular row. + * + * Possible errors: + * spNO_MEMORY + */ + +static void +ComplexRowColElimination( + MatrixPtr Matrix, + register ElementPtr pPivot +) +{ +#if spCOMPLEX +register ElementPtr pSub, *ppAbove; +register int Row; +register ElementPtr pLower, pUpper; + +/* Begin `ComplexRowColElimination'. */ + +/* Test for zero pivot. */ + if (ELEMENT_MAG(pPivot) == 0.0) + { (void)MatrixIsSingular( Matrix, pPivot->Row ); + return; + } + CMPLX_RECIPROCAL(*pPivot, *pPivot); + + pUpper = pPivot->NextInRow; + while (pUpper != NULL) + { +/* Calculate upper triangular element. */ +/* Cmplx expr: *pUpper = *pUpper * (1.0 / *pPivot). */ + CMPLX_MULT_ASSIGN(*pUpper, *pPivot); + + pSub = pUpper->NextInCol; + pLower = pPivot->NextInCol; + ppAbove = &pUpper->NextInCol; + while (pLower != NULL) + { Row = pLower->Row; + +/* Find element in row that lines up with current lower triangular element. */ + while (pSub != NULL AND pSub->Row < Row) + { ppAbove = &pSub->NextInCol; + pSub = pSub->NextInCol; + } + +/* Test to see if desired element was not found, if not, create fill-in. */ + if (pSub == NULL OR pSub->Row > Row) + { pSub = spcCreateElement( Matrix, Row, pUpper->Col, + &pLower->NextInRow, ppAbove, YES ); + if (pSub == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + } + +/* Cmplx expr: pElement -= *pUpper * pLower. */ + CMPLX_MULT_SUBT_ASSIGN(*pSub, *pUpper, *pLower); + pSub = pSub->NextInCol; + pLower = pLower->NextInCol; + } + pUpper = pUpper->NextInRow; + } + return; +#endif /* spCOMPLEX */ +} + + + + + +/* + * UPDATE MARKOWITZ NUMBERS + * + * Updates the Markowitz numbers after a row and column have been eliminated. + * Also updates singleton count. + * + * >>> Argument: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * pPivot <input> (ElementPtr) + * Pointer to the current pivot. + * + * >>> Local variables: + * Row (int) + * Row index. + * Col (int) + * Column index. + * ColPtr (ElementPtr) + * Points to matrix element in upper triangular column. + * RowPtr (ElementPtr) + * Points to matrix element in lower triangular row. + */ + +static void +UpdateMarkowitzNumbers( + MatrixPtr Matrix, + ElementPtr pPivot +) +{ +register int Row, Col; +register ElementPtr ColPtr, RowPtr; +register int *MarkoRow = Matrix->MarkowitzRow, *MarkoCol = Matrix->MarkowitzCol; +double Product; + +/* Begin `UpdateMarkowitzNumbers'. */ + +/* Update Markowitz numbers. */ + for (ColPtr = pPivot->NextInCol; ColPtr != NULL; ColPtr = ColPtr->NextInCol) + { Row = ColPtr->Row; + --MarkoRow[Row]; + +/* Form Markowitz product while being cautious of overflows. */ + if ((MarkoRow[Row] > LARGEST_SHORT_INTEGER AND MarkoCol[Row] != 0) OR + (MarkoCol[Row] > LARGEST_SHORT_INTEGER AND MarkoRow[Row] != 0)) + { Product = (double)MarkoCol[Row] * (double)MarkoRow[Row]; + if (Product >= LARGEST_LONG_INTEGER) + Matrix->MarkowitzProd[Row] = LARGEST_LONG_INTEGER; + else + Matrix->MarkowitzProd[Row] = (long)Product; + } + else Matrix->MarkowitzProd[Row] = MarkoRow[Row] * MarkoCol[Row]; + if (MarkoRow[Row] == 0) + Matrix->Singletons++; + } + + for (RowPtr = pPivot->NextInRow; RowPtr != NULL; RowPtr = RowPtr->NextInRow) + { Col = RowPtr->Col; + --MarkoCol[Col]; + +/* Form Markowitz product while being cautious of overflows. */ + if ((MarkoRow[Col] > LARGEST_SHORT_INTEGER AND MarkoCol[Col] != 0) OR + (MarkoCol[Col] > LARGEST_SHORT_INTEGER AND MarkoRow[Col] != 0)) + { Product = (double)MarkoCol[Col] * (double)MarkoRow[Col]; + if (Product >= LARGEST_LONG_INTEGER) + Matrix->MarkowitzProd[Col] = LARGEST_LONG_INTEGER; + else + Matrix->MarkowitzProd[Col] = (long)Product; + } + else Matrix->MarkowitzProd[Col] = MarkoRow[Col] * MarkoCol[Col]; + if ((MarkoCol[Col] == 0) AND (MarkoRow[Col] != 0)) + Matrix->Singletons++; + } + return; +} + + + + + + +/* + * ZERO PIVOT ENCOUNTERED + * + * This routine is called when a singular matrix is found. It then + * records the current row and column and exits. + * + * >>> Returned: + * The error code spSINGULAR or spZERO_DIAG is returned. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to matrix. + * Step <input> (int) + * Index of diagonal that is zero. + */ + +static int +MatrixIsSingular( + MatrixPtr Matrix, + int Step +) +{ +/* Begin `MatrixIsSingular'. */ + + Matrix->SingularRow = Matrix->IntToExtRowMap[ Step ]; + Matrix->SingularCol = Matrix->IntToExtColMap[ Step ]; + return (Matrix->Error = spSINGULAR); +} + + +static int +ZeroPivot( + MatrixPtr Matrix, + int Step +) +{ +/* Begin `ZeroPivot'. */ + + Matrix->SingularRow = Matrix->IntToExtRowMap[ Step ]; + Matrix->SingularCol = Matrix->IntToExtColMap[ Step ]; + return (Matrix->Error = spZERO_DIAG); +} + + + + + + +#if (ANNOTATE == FULL) + +/* + * + * WRITE STATUS + * + * Write a summary of important variables to standard output. + */ + +static void +WriteStatus( + MatrixPtr Matrix, + int Step +) +{ +int I; + +/* Begin `WriteStatus'. */ + + printf("Step = %1d ", Step); + printf("Pivot found at %1d,%1d using ", Matrix->PivotsOriginalRow, + Matrix->PivotsOriginalCol); + switch(Matrix->PivotSelectionMethod) + { case 's': printf("SearchForSingleton\n"); break; + case 'q': printf("QuicklySearchDiagonal\n"); break; + case 'd': printf("SearchDiagonal\n"); break; + case 'e': printf("SearchEntireMatrix\n"); break; + } + + printf("MarkowitzRow = "); + for (I = 1; I <= Matrix->Size; I++) + printf("%2d ", Matrix->MarkowitzRow[I]); + printf("\n"); + + printf("MarkowitzCol = "); + for (I = 1; I <= Matrix->Size; I++) + printf("%2d ", Matrix->MarkowitzCol[I]); + printf("\n"); + + printf("MarkowitzProduct = "); + for (I = 1; I <= Matrix->Size; I++) + printf("%2d ", Matrix->MarkowitzProd[I]); + printf("\n"); + + printf("Singletons = %2d\n", Matrix->Singletons); + + printf("IntToExtRowMap = "); + for (I = 1; I <= Matrix->Size; I++) + printf("%2d ", Matrix->IntToExtRowMap[I]); + printf("\n"); + + printf("IntToExtColMap = "); + for (I = 1; I <= Matrix->Size; I++) + printf("%2d ", Matrix->IntToExtColMap[I]); + printf("\n"); + + printf("ExtToIntRowMap = "); + for (I = 1; I <= Matrix->ExtSize; I++) + printf("%2d ", Matrix->ExtToIntRowMap[I]); + printf("\n"); + + printf("ExtToIntColMap = "); + for (I = 1; I <= Matrix->ExtSize; I++) + printf("%2d ", Matrix->ExtToIntColMap[I]); + printf("\n\n"); + +/* spPrint((char *)Matrix, NO, YES); */ + + return; + +} +#endif /* ANNOTATE == FULL */ Added: trunk/math/SPARSE/spmatrix.h =================================================================== --- trunk/math/SPARSE/spmatrix.h (rev 0) +++ trunk/math/SPARSE/spmatrix.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,377 @@ +/* EXPORTS for sparse matrix routines. */ +/*! + * \file + * + * This file contains definitions that are useful to the calling + * program. In particular, this file contains error keyword + * definitions, some macro functions that are used to quickly enter + * data into the matrix and the type definition of a data structure + * that acts as a template for entering admittances into the matrix. + * Also included is the type definitions for the various functions + * available to the user. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ + + +/* + * Revision and copyright information. + * + * Copyright (c) 1985-2003 by Kenneth S. Kundert + * + * $Date: 2003/06/29 04:19:52 $ + * $Revision: 1.2 $ + */ + + + + +#ifndef spOKAY + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + */ + +#include "spconfig.h" + + + + + + +/* + * ERROR KEYWORDS + * + * The actual numbers used in the error codes are not sacred, they can be + * changed under the condition that the codes for the nonfatal errors are + * less than the code for spFATAL and similarly the codes for the fatal + * errors are greater than that for spFATAL. + */ + +/* Begin error macros. */ +#define spOKAY 0 /*!< + * Error code that indicates that no error has + * occurred. + */ +#define spSMALL_PIVOT 1 /*!< + * Non-fatal error code that indicates that, when + * reordering the matrix, no element was found that + * satisfies the absolute threshold criteria. The + * largest element in the matrix was chosen as pivot. + */ +#define spZERO_DIAG 2 /*!< + * Fatal error code that indicates that, a zero was + * encountered on the diagonal the matrix. This does + * not necessarily imply that the matrix is singular. + * When this error occurs, the matrix should be + * reconstructed and factored using + * spOrderAndFactor(). + */ +#define spSINGULAR 3 /*!< + * Fatal error code that indicates that, matrix is + * singular, so no unique solution exists. + */ +#define spMANGLED 4 /*!< + * Fatal error code that indicates that, matrix has + * been mangled, results of requested operation are + * garbage. + */ +#define spNO_MEMORY 5 /*!< + * Fatal error code that indicates that not enough + * memory is available. + */ +#define spPANIC 6 /*!< + * Fatal error code that indicates that the routines + * are not prepared to handle the matrix that has + * been requested. This may occur when the matrix + * is specified to be real and the routines are not + * compiled for real matrices, or when the matrix is + * specified to be complex and the routines are not + * compiled to handle complex matrices. + */ +#define spFATAL 2 /*!< + * Error code that is not an error flag, but rather + * the dividing line between fatal errors and + * warnings. + */ + + + + + + +/* + * KEYWORD DEFINITIONS + */ + +#define spREAL M_Real /*!< + * Defines the precision of the arithmetic used by + * \a Sparse will use. Double precision is suggested + * as being most appropriate for circuit simulation + * and for C. However, it is possible to change spREAL + * to a float for single precision arithmetic. Note + * that in C, single precision arithmetic is often + * slower than double precision. Sparse + * internally refers to spREALs as RealNumbers. + */ + + + +/* + * PARTITION TYPES + * + * When factoring a previously ordered matrix using spFactor(), Sparse + * operates on a row-at-a-time basis. For speed, on each step, the row + * being updated is copied into a full vector and the operations are + * performed on that vector. This can be done one of two ways, either + * using direct addressing or indirect addressing. Direct addressing + * is fastest when the matrix is relatively dense and indirect addressing + * is quite sparse. The user can select which partitioning mode is used. + * The following keywords are passed to spPartition() and indicate that + * Sparse should use only direct addressing, only indirect addressing, or + * that it should choose the best mode on a row-by-row basis. The time + * required to choose a partition is of the same order of the cost to factor + * the matrix. + * + * If you plan to factor a large number of matrices with the same structure, + * it is best to let Sparse choose the partition. Otherwise, you should + * choose the partition based on the predicted density of the matrix. + */ + +/* Begin partition keywords. */ + +#define spDEFAULT_PARTITION 0 /*!< + * Partition code for spPartition(). + * Indicates that the default partitioning + * mode should be used. + * \see spPartition() + */ +#define spDIRECT_PARTITION 1 /*!< + * Partition code for spPartition(). + * Indicates that all rows should be placed + * in the direct addressing partition. + * \see spPartition() + */ +#define spINDIRECT_PARTITION 2 /*!< + * Partition code for spPartition(). + * Indicates that all rows should be placed + * in the indirect addressing partition. + * \see spPartition() + */ +#define spAUTO_PARTITION 3 /*!< + * Partition code for spPartition(). + * Indicates that \a Sparse should chose + * the best partition for each row based + * on some simple rules. This is generally + * preferred. + * \see spPartition() + */ + + + + + +/* + * MACRO FUNCTION DEFINITIONS + */ + +/* Begin Macros. */ +/*! + * Macro function that adds data to a real element in the matrix by a pointer. + */ +#define spADD_REAL_ELEMENT(element,real) *(element) += real + +/*! + * Macro function that adds data to a imaginary element in the matrix by + * a pointer. + */ +#define spADD_IMAG_ELEMENT(element,imag) *(element+1) += imag + +/*! + * Macro function that adds data to a complex element in the matrix by + * a pointer. + */ +#define spADD_COMPLEX_ELEMENT(element,real,imag) \ +{ *(element) += real; \ + *(element+1) += imag; \ +} + +/*! + * Macro function that adds data to each of the four real matrix elements + * specified by the given template. + */ +#define spADD_REAL_QUAD(template,real) \ +{ *((template).Element1) += real; \ + *((template).Element2) += real; \ + *((template).Element3Negated) -= real; \ + *((template).Element4Negated) -= real; \ +} + +/*! + * Macro function that adds data to each of the four imaginary matrix + * elements specified by the given template. + */ +#define spADD_IMAG_QUAD(template,imag) \ +{ *((template).Element1+1) += imag; \ + *((template).Element2+1) += imag; \ + *((template).Element3Negated+1) -= imag; \ + *((template).Element4Negated+1) -= imag; \ +} + +/*! + * Macro function that adds data to each of the four complex matrix + * elements specified by the given template. + */ +#define spADD_COMPLEX_QUAD(template,real,imag) \ +{ *((template).Element1) += real; \ + *((template).Element2) += real; \ + *((template).Element3Negated) -= real; \ + *((template).Element4Negated) -= real; \ + *((template).Element1+1) += imag; \ + *((template).Element2+1) += imag; \ + *((template).Element3Negated+1) -= imag; \ + *((template).Element4Negated+1) -= imag; \ +} + + + + + + + +/* + * TYPE DEFINITION FOR EXTERNAL MATRIX ELEMENT REFERENCES + * + * External type definitions for Sparse data objects. + */ + +/*! Declares the type of the a pointer to a matrix. */ +typedef spGenericPtr spMatrix; + +/*! Declares the type of the a pointer to a matrix element. */ +typedef spREAL spElement; + +/*! Declares the type of the Sparse error codes. */ +typedef int spError; + + + + + +/* TYPE DEFINITION FOR COMPONENT TEMPLATE */ +/*! + * This data structure is used to hold pointers to four related elements in + * matrix. It is used in conjunction with the routines spGetAdmittance(), + * spGetQuad(), and spGetOnes(). These routines stuff the structure which + * is later used by the \a spADD_QUAD macro functions above. It is also + * possible for the user to collect four pointers returned by spGetElement() + * and stuff them into the template. The \a spADD_QUAD routines stuff data + * into the matrix in locations specified by \a Element1 and \a Element2 + * without changing the data. The data is negated before being placed in + * \a Element3 and \a Element4. + */ + +/* Begin `spTemplate'. */ +struct spTemplate +{ spElement *Element1; + spElement *Element2; + spElement *Element3Negated; + spElement *Element4Negated; +}; + + + + + +/* + * FUNCTION TYPE DEFINITIONS + * + * The type of every user accessible function is declared here. + */ + +/* Begin function declarations. */ + +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 + spcEXTERN void spErrorMessage( spMatrix, FILE*, char* ); +#else +# define spErrorMessage(a,b,c) spcFUNC_NEEDS_FILE(_spErrorMessage,stdio) +#endif + + +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, + struct spTemplate* ); +spcEXTERN spElement *spGetElement( spMatrix, int, int ); +spcEXTERN spGenericPtr spGetInitInfo( spElement* ); +spcEXTERN spError spGetOnes( spMatrix, int, int, int, + struct spTemplate* ); +spcEXTERN spError spGetQuad( spMatrix, int, int, int, int, + 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, + 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* ); + +/* Functions added for edacious */ +spcEXTERN void spAddToReorderedDiag(spMatrix, spREAL); + +/* Functions with argument lists that are dependent on options. */ + +#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* , + spREAL[], spREAL[]); +spcEXTERN void spMultiply( spMatrix, spREAL[], spREAL[], + spREAL[], spREAL[] ); +spcEXTERN void spMultTransposed( spMatrix, spREAL[], spREAL[], + spREAL[], spREAL[] ); +spcEXTERN void spSolve( spMatrix, spREAL[], spREAL[], spREAL[], + spREAL[] ); +spcEXTERN void spSolveTransposed( spMatrix, spREAL[], spREAL[], + spREAL[], spREAL[] ); +#else /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */ +spcEXTERN int spFileVector( spMatrix, char* , spREAL[] ); +spcEXTERN void spMultiply( spMatrix, spREAL[], spREAL[] ); +spcEXTERN void spMultTransposed( spMatrix, + spREAL[], spREAL[] ); +spcEXTERN void spSolve( spMatrix, spREAL[], spREAL[] ); +spcEXTERN void spSolveTransposed( spMatrix, + spREAL[], spREAL[] ); +#endif /* NOT (spCOMPLEX && spSEPARATED_COMPLEX_VECTORS) */ +#endif /* spOKAY */ Added: trunk/math/SPARSE/spoutput.c =================================================================== --- trunk/math/SPARSE/spoutput.c (rev 0) +++ trunk/math/SPARSE/spoutput.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,745 @@ +/* + * MATRIX OUTPUT MODULE + * + * Author: Advisor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + */ +/*! \file + * + * This file contains the output-to-file and output-to-screen routines for + * the matrix package. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ +/* >>> User accessible functions contained in this file: + * spPrint + * spFileMatrix + * spFileVector + * spFileStats + * + * >>> Other functions contained in this file: + */ + + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + * spMatrix.h + * Macros and declarations to be imported by the user. + * spDefs.h + * Matrix type and macro definitions for the sparse matrix routines. + */ + +#define spINSIDE_SPARSE +#include <core/core.h> +#include "../m.h" +#include "spconfig.h" +#include "spmatrix.h" +#include "spdefs.h" + + + + + +#if DOCUMENTATION + +/*! + * Formats and send the matrix to standard output. Some elementary + * statistics are also output. The matrix is output in a format that is + * readable by people. + * + * \param eMatrix + * Pointer to matrix. + * \param PrintReordered + * Indicates whether the matrix should be printed out in its original + * form, as input by the user, or whether it should be printed in its + * reordered form, as used by the matrix routines. A zero indicates that + * the matrix should be printed as inputed, a one indicates that it + * should be printed reordered. + * \param Data + * Boolean flag that when false indicates that output should be + * compressed such that only the existence of an element should be + * indicated rather than giving the actual value. Thus 11 times as + * many can be printed on a row. A zero signifies that the matrix + * should be printed compressed. A one indicates that the matrix + * should be printed in all its glory. + * \param Header + * Flag indicating that extra information should be given, such as row + * and column numbers. + */ +/* >>> Local variables: + * Col (int) + * Column being printed. + * ElementCount (int) + * Variable used to count the number of nonzero elements in the matrix. + * LargestElement (RealNumber) + * The magnitude of the largest element in the matrix. + * LargestDiag (RealNumber) + * The magnitude of the largest diagonal in the matrix. + * Magnitude (RealNumber) + * The absolute value of the matrix element being printed. + * PrintOrdToIntColMap (int []) + * A translation array that maps the order that columns will be + * printed in (if not PrintReordered) to the internal column numbers. + * PrintOrdToIntRowMap (int []) + * A translation array that maps the order that rows will be + * printed in (if not PrintReordered) to the internal row numbers. + * pElement (ElementPtr) + * Pointer to the element in the matrix that is to be printed. + * pImagElements (ElementPtr [ ]) + * Array of pointers to elements in the matrix. These pointers point + * to the elements whose real values have just been printed. They are + * used to quickly access those same elements so their imaginary values + * can be printed. + * Row (int) + * Row being printed. + * Size (int) + * The size of the matrix. + * SmallestDiag (RealNumber) + * The magnitude of the smallest diagonal in the matrix. + * SmallestElement (RealNumber) + * The magnitude of the smallest element in the matrix excluding zero + * elements. + * StartCol (int) + * The column number of the first column to be printed in the group of + * columns currently being printed. + * StopCol (int) + * The column number of the last column to be printed in the group of + * columns currently being printed. + * Top (int) + * The largest expected external row or column number. + */ + +void +spPrint( + spMatrix eMatrix, + int PrintReordered, + int Data, + 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 = 0.0, SmallestElement = 0.0; +double LargestElement = 0.0, LargestDiag = 0.0; +ElementPtr pElement, pImagElements[PRINTER_WIDTH/10+1]; +int *PrintOrdToIntRowMap, *PrintOrdToIntColMap; + +/* Begin `spPrint'. */ + ASSERT_IS_SPARSE( Matrix ); + Size = Matrix->Size; + +/* Create a packed external to internal row and column translation array.. */ +# if TRANSLATE + Top = Matrix->AllocatedExtSize; +#else + Top = Matrix->AllocatedSize; +#endif + CALLOC( PrintOrdToIntRowMap, int, Top + 1 ); + CALLOC( PrintOrdToIntColMap, int, Top + 1 ); + if ( PrintOrdToIntRowMap == NULL OR PrintOrdToIntColMap == NULL) + { Matrix->Error = spNO_MEMORY; + return; + } + for (I = 1; I <= Size; I++) + { PrintOrdToIntRowMap[ Matrix->IntToExtRowMap[I] ] = I; + PrintOrdToIntColMap[ Matrix->IntToExtColMap[I] ] = I; + } + +/* Pack the arrays. */ + for (J = 1, I = 1; I <= Top; I++) + { if (PrintOrdToIntRowMap[I] != 0) + PrintOrdToIntRowMap[ J++ ] = PrintOrdToIntRowMap[ I ]; + } + for (J = 1, I = 1; I <= Top; I++) + { if (PrintOrdToIntColMap[I] != 0) + PrintOrdToIntColMap[ J++ ] = PrintOrdToIntColMap[ I ]; + } + +/* Print header. */ + if (Header) + { printf("MATRIX SUMMARY\n\n"); + printf("Size of matrix = %1d x %1d.\n", Size, Size); + if ( Matrix->Reordered AND PrintReordered ) + printf("Matrix has been reordered.\n"); + putchar('\n'); + + if ( Matrix->Factored ) + printf("Matrix after factorization:\n"); + else + printf("Matrix before factorization:\n"); + + SmallestElement = LARGEST_REAL; + SmallestDiag = SmallestElement; + } + if (Size == 0) return; + +/* Determine how many columns to use. */ + Columns = PRINTER_WIDTH; + if (Header) Columns -= 5; + if (Data) Columns = (Columns+1) / 10; + +/* + * Print matrix by printing groups of complete columns until all the columns + * are printed. + */ + J = 0; + while ( J <= Size ) + +/* Calculate index of last column to printed in this group. */ + { StopCol = StartCol + Columns - 1; + if (StopCol > Size) + StopCol = Size; + +/* Label the columns. */ + if (Header) + { if (Data) + { printf(" "); + for (I = StartCol; I <= StopCol; I++) + { if (PrintReordered) + Col = I; + else + Col = PrintOrdToIntColMap[I]; + printf(" %9d", Matrix->IntToExtColMap[ Col ]); + } + printf("\n\n"); + } + else + { if (PrintReordered) + printf("Columns %1d to %1d.\n",StartCol,StopCol); + else + { printf("Columns %1d to %1d.\n", + Matrix->IntToExtColMap[ PrintOrdToIntColMap[StartCol] ], + Matrix->IntToExtColMap[ PrintOrdToIntColMap[StopCol] ]); + } + } + } + +/* Print every row ... */ + for (I = 1; I <= Size; I++) + { if (PrintReordered) + Row = I; + else + Row = PrintOrdToIntRowMap[I]; + + if (Header) + { if (PrintReordered AND NOT Data) + printf("%4d", I); + else + printf("%4d", Matrix->IntToExtRowMap[ Row ]); + if (NOT Data) putchar(' '); + } + +/* ... in each column of the group. */ + for (J = StartCol; J <= StopCol; J++) + { if (PrintReordered) + Col = J; + else + Col = PrintOrdToIntColMap[J]; + + pElement = Matrix->FirstInCol[Col]; + while(pElement != NULL AND pElement->Row != Row) + pElement = pElement->NextInCol; + + if (Data) + pImagElements[J - StartCol] = pElement; + + if (pElement != NULL) + +/* Case where element exists */ + { if (Data) + printf(" %9.3g", (double)pElement->Real); + else + putchar('x'); + +/* Update status variables */ + if ( (Magnitude = ELEMENT_MAG(pElement)) > LargestElement ) + LargestElement = Magnitude; + if ((Magnitude < SmallestElement) AND (Magnitude != 0.0)) + SmallestElement = Magnitude; + ElementCount++; + } + +/* Case where element is structurally zero */ + else + { if (Data) + printf(" ..."); + else + putchar('.'); + } + } + putchar('\n'); + +#if spCOMPLEX + if (Matrix->Complex AND Data) + { if (Header) + printf(" "); + for (J = StartCol; J <= StopCol; J++) + { if (pImagElements[J - StartCol] != NULL) + { printf(" %8.2gj", + (double)pImagElements[J-StartCol]->Imag); + } + else printf(" "); + } + putchar('\n'); + } +#endif /* spCOMPLEX */ + } + +/* Calculate index of first column in next group. */ + StartCol = StopCol; + StartCol++; + putchar('\n'); + } + if (Header) + { printf("\nLargest element in matrix = %-1.4g.\n", LargestElement); + printf("Smallest element in matrix = %-1.4g.\n", SmallestElement); + +/* Search for largest and smallest diagonal values */ + for (I = 1; I <= Size; I++) + { if (Matrix->Diag[I] != NULL) + { Magnitude = ELEMENT_MAG( Matrix->Diag[I] ); + if ( Magnitude > LargestDiag ) LargestDiag = Magnitude; + if ( Magnitude < SmallestDiag ) SmallestDiag = Magnitude; + } + } + + /* Print the largest and smallest diagonal values */ + if ( Matrix->Factored ) + { printf("\nLargest diagonal element = %-1.4g.\n", LargestDiag); + printf("Smallest diagonal element = %-1.4g.\n", SmallestDiag); + } + else + { printf("\nLargest pivot element = %-1.4g.\n", LargestDiag); + printf("Smallest pivot element = %-1.4g.\n", SmallestDiag); + } + + /* Calculate and print sparsity and number of fill-ins created. */ + printf("\nDensity = %2.2f%%.\n", ((double)ElementCount * 100.0) + / (((double)Size * (double)Size))); + if (NOT Matrix->NeedsOrdering) + printf("Number of fill-ins = %1d.\n", Matrix->Fillins); + } + putchar('\n'); + (void)fflush(stdout); + + FREE(PrintOrdToIntColMap); + FREE(PrintOrdToIntRowMap); + return; +} + + + + + + + + + + + +/*! + * Writes matrix to file in format suitable to be read back in by the + * matrix test program. + * + * \return + * One is returned if routine was successful, otherwise zero is returned. + * The calling function can query \a errno (the system global error variable) + * as to the reason why this routine failed. + * + * \param eMatrix + * Pointer to matrix. + * \param File + * Name of file into which matrix is to be written. + * \param Label + * String that is transferred to file and is used as a label. + * \param Reordered + * Specifies whether matrix should be output in reordered form, + * or in original order. + * \param Data + * Indicates that the element values should be output along with + * the indices for each element. This parameter must be true if + * matrix is to be read by the sparse test program. + * \param Header + * Indicates that header is desired. This parameter must be true if + * matrix is to be read by the sparse test program. + */ +/* >>> Local variables: + * Col (int) + * The original column number of the element being output. + * pElement (ElementPtr) + * Pointer to an element in the matrix. + * pMatrixFile (FILE *) + * File pointer to the matrix file. + * Row (int) + * The original row number of the element being output. + * Size (int) + * The size of the matrix. + */ + +int +spFileMatrix( + spMatrix eMatrix, + char *File, + char *Label, + int Reordered, + int Data, + int Header +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register int I, Size; +register ElementPtr pElement; +int Row, Col, Err; +FILE *pMatrixFile; + +/* Begin `spFileMatrix'. */ + ASSERT_IS_SPARSE( Matrix ); + +/* Open file matrix file in write mode. */ + if ((pMatrixFile = fopen(File, "w")) == NULL) + return 0; + +/* Output header. */ + Size = Matrix->Size; + if (Header) + { if (Matrix->Factored AND Data) + { Err = fprintf + ( pMatrixFile, + "Warning : The following matrix is factored in to LU form.\n" + ); + if (Err < 0) return 0; + } + if (fprintf(pMatrixFile, "%s\n", Label) < 0) return 0; + Err = fprintf( pMatrixFile, "%d\t%s\n", Size, + (Matrix->Complex ? "complex" : "real")); + if (Err < 0) return 0; + } + if (Size == 0) return 1; + +/* Output matrix. */ + if (NOT Data) + { for (I = 1; I <= Size; I++) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { if (Reordered) + { Row = pElement->Row; + Col = I; + } + else + { Row = Matrix->IntToExtRowMap[pElement->Row]; + Col = Matrix->IntToExtColMap[I]; + } + pElement = pElement->NextInCol; + if (fprintf(pMatrixFile, "%d\t%d\n", Row, Col) < 0) return 0; + } + } +/* Output terminator, a line of zeros. */ + if (Header) + if (fprintf(pMatrixFile, "0\t0\n") < 0) return 0; + } + +#if spCOMPLEX + if (Data AND Matrix->Complex) + { for (I = 1; I <= Size; I++) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { if (Reordered) + { Row = pElement->Row; + Col = I; + } + else + { Row = Matrix->IntToExtRowMap[pElement->Row]; + Col = Matrix->IntToExtColMap[I]; + } + Err = fprintf + ( pMatrixFile,"%d\t%d\t%-.15g\t%-.15g\n", + Row, Col, (double)pElement->Real, (double)pElement->Imag + ); + if (Err < 0) return 0; + pElement = pElement->NextInCol; + } + } +/* Output terminator, a line of zeros. */ + if (Header) + if (fprintf(pMatrixFile,"0\t0\t0.0\t0.0\n") < 0) return 0; + + } +#endif /* spCOMPLEX */ + +#if REAL + if (Data AND NOT Matrix->Complex) + { for (I = 1; I <= Size; I++) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { Row = Matrix->IntToExtRowMap[pElement->Row]; + Col = Matrix->IntToExtColMap[I]; + Err = fprintf + ( pMatrixFile,"%d\t%d\t%-.15g\n", + Row, Col, (double)pElement->Real + ); + if (Err < 0) return 0; + pElement = pElement->NextInCol; + } + } +/* Output terminator, a line of zeros. */ + if (Header) + if (fprintf(pMatrixFile,"0\t0\t0.0\n") < 0) return 0; + + } +#endif /* REAL */ + +/* Close file. */ + if (fclose(pMatrixFile) < 0) return 0; + return 1; +} + + + + + + + +/*! + * Writes vector to file in format suitable to be read back in by the + * matrix test program. This routine should be executed after the function + * spFileMatrix. + * + * \return + * One is returned if routine was successful, otherwise zero is returned. + * The calling function can query \a errno (the system global error variable) + * as to the reason why this routine failed. + * + * \param eMatrix + * Pointer to matrix. + * \param File + * Name of file into which matrix is to be written. + * \param RHS + * Right-hand side vector. This is only the real portion if + * \a spSEPARATED_COMPLEX_VECTORS is true. + * \param iRHS + * Right-hand side vector, imaginary portion. Not necessary if matrix + * is real or if \a spSEPARATED_COMPLEX_VECTORS is set false. + * \a iRHS is a macro that replaces itself with `, iRHS' if the options + * \a spCOMPLEX and \a spSEPARATED_COMPLEX_VECTORS are set, otherwise + * it disappears without a trace. + */ +/* >>> Local variables: + * pMatrixFile (FILE *) + * File pointer to the matrix file. + * Size (int) + * The size of the matrix. + */ + +int +spFileVector( + spMatrix eMatrix, + char *File, + spREAL RHS[] +#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS + , spREAL iRHS[] +#endif +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register int I, Size, Err; +FILE *pMatrixFile; + +/* Begin `spFileVector'. */ + ASSERT_IS_SPARSE( Matrix ); + vASSERT( RHS != NULL, "Vector missing" ); + +/* Open File in append mode. */ + if ((pMatrixFile = fopen(File,"a")) == NULL) + return 0; + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET +#if spCOMPLEX + if (Matrix->Complex) + { +#if spSEPARATED_COMPLEX_VECTORS + vASSERT( iRHS != NULL, "Imaginary vector missing" ); + --RHS; + --iRHS; +#else + RHS -= 2; +#endif + } + else +#endif /* spCOMPLEX */ + --RHS; +#endif /* NOT ARRAY_OFFSET */ + + +/* Output vector. */ + Size = Matrix->Size; + if (Size == 0) return 1; + +#if spCOMPLEX + if (Matrix->Complex) + { +#if spSEPARATED_COMPLEX_VECTORS + for (I = 1; I <= Size; I++) + { Err = fprintf + ( pMatrixFile, "%-.15g\t%-.15g\n", + (double)RHS[I], (double)iRHS[I] + ); + if (Err < 0) return 0; + } +#else + for (I = 1; I <= Size; I++) + { Err = fprintf + ( pMatrixFile, "%-.15g\t%-.15g\n", + (double)RHS[2*I], (double)RHS[2*I+1] + ); + if (Err < 0) return 0; + } +#endif + } +#endif /* spCOMPLEX */ +#if REAL AND spCOMPLEX + else +#endif +#if REAL + { for (I = 1; I <= Size; I++) + { if (fprintf(pMatrixFile, "%-.15g\n", (double)RHS[I]) < 0) + return 0; + } + } +#endif /* REAL */ + +/* Close file. */ + if (fclose(pMatrixFile) < 0) return 0; + return 1; +} + + + + + + + + + +/*! + * Writes useful information concerning the matrix to a file. Should be + * executed after the matrix is factored. + * + * \return + * One is returned if routine was successful, otherwise zero is returned. + * The calling function can query \a errno (the system global error variable) + * as to the reason why this routine failed. + * + * \param eMatrix + * Pointer to matrix. + * \param File + * Name of file into which matrix is to be written. + * \param Label + * String that is transferred to file and is used as a label. + */ +/* >>> Local variables: + * Data (RealNumber) + * The value of the matrix element being output. + * LargestElement (RealNumber) + * The largest element in the matrix. + * NumberOfElements (int) + * Number of nonzero elements in the matrix. + * pElement (ElementPtr) + * Pointer to an element in the matrix. + * pStatsFile (FILE *) + * File pointer to the statistics file. + * Size (int) + * The size of the matrix. + * SmallestElement (RealNumber) + * The smallest element in the matrix excluding zero elements. + */ + +int +spFileStats( + spMatrix eMatrix, + char *File, + char *Label +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register int Size, I; +register ElementPtr pElement; +int NumberOfElements; +RealNumber Data, LargestElement, SmallestElement; +FILE *pStatsFile; + +/* Begin `spFileStats'. */ + ASSERT_IS_SPARSE( Matrix ); + +/* Open File in append mode. */ + if ((pStatsFile = fopen(File, "a")) == NULL) + return 0; + +/* Output statistics. */ + Size = Matrix->Size; + if (NOT Matrix->Factored) + fprintf(pStatsFile, "Matrix has not been factored.\n"); + fprintf(pStatsFile, "||| Starting new matrix |||\n"); + fprintf(pStatsFile, "%s\n", Label); + if (Matrix->Complex) + fprintf(pStatsFile, "Matrix is complex.\n"); + else + fprintf(pStatsFile, "Matrix is real.\n"); + fprintf(pStatsFile," Size = %d\n",Size); + if (Size == 0) return 1; + +/* Search matrix. */ + NumberOfElements = 0; + LargestElement = 0.0; + SmallestElement = LARGEST_REAL; + + for (I = 1; I <= Size; I++) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { NumberOfElements++; + Data = ELEMENT_MAG(pElement); + if (Data > LargestElement) + LargestElement = Data; + if (Data < SmallestElement AND Data != 0.0) + SmallestElement = Data; + pElement = pElement->NextInCol; + } + } + + SmallestElement = MIN( SmallestElement, LargestElement ); + +/* Output remaining statistics. */ + fprintf(pStatsFile, " Initial number of elements = %d\n", + NumberOfElements - Matrix->Fillins); + fprintf(pStatsFile, + " Initial average number of elements per row = %f\n", + (double)(NumberOfElements - Matrix->Fillins) / (double)Size); + fprintf(pStatsFile, " Fill-ins = %d\n",Matrix->Fillins); + fprintf(pStatsFile, " Average number of fill-ins per row = %f%%\n", + (double)Matrix->Fillins / (double)Size); + fprintf(pStatsFile, " Total number of elements = %d\n", + NumberOfElements); + fprintf(pStatsFile, " Average number of elements per row = %f\n", + (double)NumberOfElements / (double)Size); + fprintf(pStatsFile," Density = %f%%\n", + (100.0*(double)NumberOfElements)/((double)Size*(double)Size)); + fprintf(pStatsFile," Relative Threshold = %e\n", Matrix->RelThreshold); + fprintf(pStatsFile," Absolute Threshold = %e\n", Matrix->AbsThreshold); + fprintf(pStatsFile," Largest Element = %e\n", LargestElement); + fprintf(pStatsFile," Smallest Element = %e\n\n\n", SmallestElement); + +/* Close file. */ + (void)fclose(pStatsFile); + return 1; +} +#endif /* DOCUMENTATION */ Added: trunk/math/SPARSE/spsolve.c =================================================================== --- trunk/math/SPARSE/spsolve.c (rev 0) +++ trunk/math/SPARSE/spsolve.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,683 @@ +/* + * MATRIX SOLVE MODULE + * + * Author: Advising professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + */ +/*! \file + * This file contains the forward and backward substitution routines for + * the sparse matrix routines. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ +/* >>> User accessible functions contained in this file: + * spSolve + * spSolveTransposed + * + * >>> Other functions contained in this file: + * SolveComplexMatrix + * SolveComplexTransposedMatrix + */ + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + * spMatrix.h + * Macros and declarations to be imported by the user. + * spDefs.h + * Matrix type and macro definitions for the sparse matrix routines. + */ + +#define spINSIDE_SPARSE +#include <core/core.h> +#include "../m.h" +#include "spconfig.h" +#include "spmatrix.h" +#include "spdefs.h" + + + + +/* + * Function declarations + */ + +#if spSEPARATED_COMPLEX_VECTORS +static void SolveComplexMatrix( MatrixPtr, + RealVector, RealVector, RealVector, RealVector ); +static void SolveComplexTransposedMatrix( MatrixPtr, + RealVector, RealVector, RealVector, RealVector ); +#else +static void SolveComplexMatrix( MatrixPtr, RealVector, RealVector ); +static void SolveComplexTransposedMatrix( MatrixPtr, + RealVector, RealVector ); +#endif + + + + + + + +/*! + * Performs forward elimination and back substitution to find the + * unknown vector from the \a RHS vector and factored matrix. This + * routine assumes that the pivots are associated with the lower + * triangular matrix and that the diagonal of the upper triangular + * matrix consists of ones. This routine arranges the computation + * in different way than is traditionally used in order to exploit the + * sparsity of the right-hand side. See the reference in spRevision. + * + * \param eMatrix + * Pointer to matrix. + * \param RHS + * \a RHS is the input data array, the right hand side. This data is + * undisturbed and may be reused for other solves. + * \param Solution + * \a Solution is the output data array. This routine is constructed + * such that \a RHS and \a Solution can be the same array. + * \param iRHS + * \a iRHS is the imaginary portion of the input data array, the right + * hand side. This data is undisturbed and may be reused for other solves. + * This argument is only necessary if matrix is complex and if + * \a spSEPARATED_COMPLEX_VECTOR is set true. + * \param iSolution + * \a iSolution is the imaginary portion of the output data array. This + * routine is constructed such that \a iRHS and \a iSolution can be + * the same array. This argument is only necessary if matrix is complex + * and if \a spSEPARATED_COMPLEX_VECTOR is set true. + */ +/* >>> Local variables: + * Intermediate (RealVector) + * Temporary storage for use in forward elimination and backward + * substitution. Commonly referred to as c, when the LU factorization + * equations are given as Ax = b, Lc = b, Ux = c Local version of + * Matrix->Intermediate, which was created during the initial + * factorization in function spcCreateInternalVectors() in the matrix + * factorization module. + * pElement (ElementPtr) + * Pointer used to address elements in both the lower and upper triangle + * matrices. + * pExtOrder (int *) + * Pointer used to sequentially access each entry in IntToExtRowMap + * and IntToExtColMap arrays. Used to quickly scramble and unscramble + * RHS and Solution to account for row and column interchanges. + * pPivot (ElementPtr) + * Pointer that points to current pivot or diagonal element. + * Size (int) + * Size of matrix. Made local to reduce indirection. + * Temp (RealNumber) + * Temporary storage for entries in arrays. + * + * >>> Obscure Macros + * IMAG_VECTORS + * Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and + * spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears + * without a trace. + */ + +/*VARARGS3*/ + +void +spSolve( + spMatrix eMatrix, + spREAL RHS[], + spREAL Solution[] +# if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS + , spREAL iRHS[] + , spREAL iSolution[] +# endif +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register RealVector Intermediate; +register RealNumber Temp; +register int I, *pExtOrder, Size; +ElementPtr pPivot; + +/* Begin `spSolve'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_FACTORED( Matrix ); + +#if spCOMPLEX + if (Matrix->Complex) + { SolveComplexMatrix( Matrix, RHS, Solution IMAG_VECTORS ); + return; + } +#endif + +#if REAL + Intermediate = Matrix->Intermediate; + Size = Matrix->Size; + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET + --RHS; + --Solution; +#endif + +/* Initialize Intermediate vector. */ + pExtOrder = &Matrix->IntToExtRowMap[Size]; + for (I = Size; I > 0; I--) + Intermediate[I] = RHS[*(pExtOrder--)]; + +/* Forward elimination. Solves Lc = b.*/ + for (I = 1; I <= Size; I++) + { +/* This step of the elimination is skipped if Temp equals zero. */ + if ((Temp = Intermediate[I]) != 0.0) + { pPivot = Matrix->Diag[I]; + Intermediate[I] = (Temp *= pPivot->Real); + + pElement = pPivot->NextInCol; + while (pElement != NULL) + { Intermediate[pElement->Row] -= Temp * pElement->Real; + pElement = pElement->NextInCol; + } + } + } + +/* Backward Substitution. Solves Ux = c.*/ + for (I = Size; I > 0; I--) + { Temp = Intermediate[I]; + pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { Temp -= pElement->Real * Intermediate[pElement->Col]; + pElement = pElement->NextInRow; + } + Intermediate[I] = Temp; + } + +/* Unscramble Intermediate vector while placing data in to Solution vector. */ + pExtOrder = &Matrix->IntToExtColMap[Size]; + for (I = Size; I > 0; I--) + Solution[*(pExtOrder--)] = Intermediate[I]; + + return; +#endif /* REAL */ +} + + + + + + + + + + + +#if spCOMPLEX +/*! + * Performs forward elimination and back substitution to find the + * unknown vector from the RHS vector and factored matrix. This + * routine assumes that the pivots are associated with the lower + * triangular matrix and that the diagonal of the upper triangular + * matrix consists of ones. This routine arranges the computation + * in different way than is traditionally used in order to exploit the + * sparsity of the right-hand side. See the reference in spRevision. + * + * \param Matrix + * Pointer to matrix. + * \param RHS + * RHS is the real portion of the input data array, the right hand + * side. This data is undisturbed and may be reused for other solves. + * \param Solution + * Solution is the real portion of the output data array. This routine + * is constructed such that RHS and Solution can be the same + * array. + * \param iRHS + * iRHS is the imaginary portion of the input data array, the right + * hand side. This data is undisturbed and may be reused for other solves. + * If spSEPARATED_COMPLEX_VECTOR is set false, there is no need to + * supply this array. + * \param iSolution + * iSolution is the imaginary portion of the output data array. This + * routine is constructed such that iRHS and iSolution can be + * the same array. If spSEPARATED_COMPLEX_VECTOR is set false, there is no + * need to supply this array. + */ +/* >>> Local variables: + * Intermediate (ComplexVector) + * Temporary storage for use in forward elimination and backward + * substitution. Commonly referred to as c, when the LU factorization + * equations are given as Ax = b, Lc = b, Ux = c. + * Local version of Matrix->Intermediate, which was created during + * the initial factorization in function spcCreateInternalVectors() in the + * matrix factorization module. + * pElement (ElementPtr) + * Pointer used to address elements in both the lower and upper triangle + * matrices. + * pExtOrder (int *) + * Pointer used to sequentially access each entry in IntToExtRowMap + * and IntToExtColMap arrays. Used to quickly scramble and unscramble + * RHS and Solution to account for row and column interchanges. + * pPivot (ElementPtr) + * Pointer that points to current pivot or diagonal element. + * Size (int) + * Size of matrix. Made local to reduce indirection. + * Temp (ComplexNumber) + * Temporary storage for entries in arrays. + */ + +static void +SolveComplexMatrix( + MatrixPtr Matrix, + RealVector RHS, + RealVector Solution +# if spSEPARATED_COMPLEX_VECTORS + , RealVector iRHS + , RealVector iSolution +# endif +) +{ +register ElementPtr pElement; +register ComplexVector Intermediate; +register int I, *pExtOrder, Size; +ElementPtr pPivot; +register ComplexVector ExtVector; +ComplexNumber Temp; + +/* Begin `SolveComplexMatrix'. */ + + Size = Matrix->Size; + Intermediate = (ComplexVector)Matrix->Intermediate; + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET +#if spSEPARATED_COMPLEX_VECTORS + --RHS; --iRHS; + --Solution; --iSolution; +#else + RHS -= 2; Solution -= 2; +#endif +#endif + +/* Initialize Intermediate vector. */ + pExtOrder = &Matrix->IntToExtRowMap[Size]; + +#if spSEPARATED_COMPLEX_VECTORS + for (I = Size; I > 0; I--) + { Intermediate[I].Real = RHS[*(pExtOrder)]; + Intermediate[I].Imag = iRHS[*(pExtOrder--)]; + } +#else + ExtVector = (ComplexVector)RHS; + for (I = Size; I > 0; I--) + Intermediate[I] = ExtVector[*(pExtOrder--)]; +#endif + +/* Forward substitution. Solves Lc = b.*/ + for (I = 1; I <= Size; I++) + { Temp = Intermediate[I]; + +/* This step of the substitution is skipped if Temp equals zero. */ + if ((Temp.Real != 0.0) OR (Temp.Imag != 0.0)) + { pPivot = Matrix->Diag[I]; +/* Cmplx expr: Temp *= (1.0 / Pivot). */ + CMPLX_MULT_ASSIGN(Temp, *pPivot); + Intermediate[I] = Temp; + pElement = pPivot->NextInCol; + while (pElement != NULL) + { +/* Cmplx expr: Intermediate[Element->Row] -= Temp * *Element. */ + CMPLX_MULT_SUBT_ASSIGN(Intermediate[pElement->Row], + Temp, *pElement); + pElement = pElement->NextInCol; + } + } + } + +/* Backward Substitution. Solves Ux = c.*/ + for (I = Size; I > 0; I--) + { Temp = Intermediate[I]; + pElement = Matrix->Diag[I]->NextInRow; + + while (pElement != NULL) + { +/* Cmplx expr: Temp -= *Element * Intermediate[Element->Col]. */ + CMPLX_MULT_SUBT_ASSIGN(Temp, *pElement,Intermediate[pElement->Col]); + pElement = pElement->NextInRow; + } + Intermediate[I] = Temp; + } + +/* Unscramble Intermediate vector while placing data in to Solution vector. */ + pExtOrder = &Matrix->IntToExtColMap[Size]; + +#if spSEPARATED_COMPLEX_VECTORS + for (I = Size; I > 0; I--) + { Solution[*(pExtOrder)] = Intermediate[I].Real; + iSolution[*(pExtOrder--)] = Intermediate[I].Imag; + } +#else + ExtVector = (ComplexVector)Solution; + for (I = Size; I > 0; I--) + ExtVector[*(pExtOrder--)] = Intermediate[I]; +#endif + + return; +} +#endif /* spCOMPLEX */ + + + + + + + + + + + + + + +#if TRANSPOSE +/*! + * Performs forward elimination and back substitution to find the + * unknown vector from the RHS vector and transposed factored + * matrix. This routine is useful when performing sensitivity analysis + * on a circuit using the adjoint method. This routine assumes that + * the pivots are associated with the untransposed lower triangular + * matrix and that the diagonal of the untransposed upper + * triangular matrix consists of ones. + * + * \param eMatrix + * Pointer to matrix. + * \param RHS + * \a RHS is the input data array, the right hand side. This data is + * undisturbed and may be reused for other solves. + * \param Solution + * \a Solution is the output data array. This routine is constructed + * such that \a RHS and \a Solution can be the same array. + * \param iRHS + * \a iRHS is the imaginary portion of the input data array, the right + * hand side. This data is undisturbed and may be reused for other solves. + * If \a spSEPARATED_COMPLEX_VECTOR is set false, or if matrix is real, + * there is no need to supply this array. + * \param iSolution + * \a iSolution is the imaginary portion of the output data array. This + * routine is constructed such that \a iRHS and \a iSolution can be + * the same array. If \a spSEPARATED_COMPLEX_VECTOR is set false, or if + * matrix is real, there is no need to supply this array. + */ +/* >>> Local variables: + * Intermediate (RealVector) + * Temporary storage for use in forward elimination and backward + * substitution. Commonly referred to as c, when the LU factorization + * equations are given as Ax = b, Lc = b, Ux = c. Local version of + * Matrix->Intermediate, which was created during the initial + * factorization in function spcCreateInternalVectors() in the matrix + * factorization module. + * pElement (ElementPtr) + * Pointer used to address elements in both the lower and upper triangle + * matrices. + * pExtOrder (int *) + * Pointer used to sequentially access each entry in IntToExtRowMap + * and IntToExtRowMap arrays. Used to quickly scramble and unscramble + * RHS and Solution to account for row and column interchanges. + * pPivot (ElementPtr) + * Pointer that points to current pivot or diagonal element. + * Size (int) + * Size of matrix. Made local to reduce indirection. + * Temp (RealNumber) + * Temporary storage for entries in arrays. + */ + +/*VARARGS3*/ + +void +spSolveTransposed( + spMatrix eMatrix, + spREAL RHS[], + spREAL Solution[] +# if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS + , spREAL iRHS[] + , spREAL iSolution[] +# endif +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register RealVector Intermediate; +register int I, *pExtOrder, Size; +ElementPtr pPivot; +RealNumber Temp; + +/* Begin `spSolveTransposed'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_FACTORED( Matrix ); + +#if spCOMPLEX + if (Matrix->Complex) + { SolveComplexTransposedMatrix( Matrix, RHS, Solution IMAG_VECTORS ); + return; + } +#endif + +#if REAL + Size = Matrix->Size; + Intermediate = Matrix->Intermediate; + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET + --RHS; + --Solution; +#endif + +/* Initialize Intermediate vector. */ + pExtOrder = &Matrix->IntToExtColMap[Size]; + for (I = Size; I > 0; I--) + Intermediate[I] = RHS[*(pExtOrder--)]; + +/* Forward elimination. */ + for (I = 1; I <= Size; I++) + { +/* This step of the elimination is skipped if Temp equals zero. */ + if ((Temp = Intermediate[I]) != 0.0) + { pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { Intermediate[pElement->Col] -= Temp * pElement->Real; + pElement = pElement->NextInRow; + } + + } + } + +/* Backward Substitution. */ + for (I = Size; I > 0; I--) + { pPivot = Matrix->Diag[I]; + Temp = Intermediate[I]; + pElement = pPivot->NextInCol; + while (pElement != NULL) + { Temp -= pElement->Real * Intermediate[pElement->Row]; + pElement = pElement->NextInCol; + } + Intermediate[I] = Temp * pPivot->Real; + } + +/* Unscramble Intermediate vector while placing data in to Solution vector. */ + pExtOrder = &Matrix->IntToExtRowMap[Size]; + for (I = Size; I > 0; I--) + Solution[*(pExtOrder--)] = Intermediate[I]; + + return; +#endif /* REAL */ +} +#endif /* TRANSPOSE */ + + + + + + + + + + +#if TRANSPOSE AND spCOMPLEX +/*! + * Performs forward elimination and back substitution to find the + * unknown vector from the RHS vector and transposed factored + * matrix. This routine is useful when performing sensitivity analysis + * on a circuit using the adjoint method. This routine assumes that + * the pivots are associated with the untransposed lower triangular + * matrix and that the diagonal of the untransposed upper + * triangular matrix consists of ones. + * + * \param Matrix + * Pointer to matrix. + * \param RHS + * \a RHS is the input data array, the right hand + * side. This data is undisturbed and may be reused for other solves. + * This vector is only the real portion if the matrix is complex and + * \a spSEPARATED_COMPLEX_VECTORS is set true. + * \param Solution + * \a Solution is the real portion of the output data array. This routine + * is constructed such that \a RHS and \a Solution can be the same array. + * This vector is only the real portion if the matrix is complex and + * \a spSEPARATED_COMPLEX_VECTORS is set true. + * \param iRHS + * \a iRHS is the imaginary portion of the input data array, the right + * hand side. This data is undisturbed and may be reused for other solves. + * If either \a spCOMPLEX or \a spSEPARATED_COMPLEX_VECTOR is set false, + * there is no need to supply this array. + * \param iSolution + * \a iSolution is the imaginary portion of the output data array. This + * routine is constructed such that \a iRHS and \a iSolution can be + * the same array. If \a spCOMPLEX or \a spSEPARATED_COMPLEX_VECTOR is set + * false, there is no need to supply this array. + */ +/* >>> Local variables: + * Intermediate (ComplexVector) + * Temporary storage for use in forward elimination and backward + * substitution. Commonly referred to as c, when the LU factorization + * equations are given as Ax = b, Lc = b, Ux = c. Local version of + * Matrix->Intermediate, which was created during + * the initial factorization in function spcCreateInternalVectors() in the + * matrix factorization module. + * pElement (ElementPtr) + * Pointer used to address elements in both the lower and upper triangle + * matrices. + * pExtOrder (int *) + * Pointer used to sequentially access each entry in IntToExtRowMap + * and IntToExtColMap arrays. Used to quickly scramble and unscramble + * RHS and Solution to account for row and column interchanges. + * pPivot (ElementPtr) + * Pointer that points to current pivot or diagonal element. + * Size (int) + * Size of matrix. Made local to reduce indirection. + * Temp (ComplexNumber) + * Temporary storage for entries in arrays. + */ + +static void +SolveComplexTransposedMatrix( + MatrixPtr Matrix, + RealVector RHS, + RealVector Solution +# if spSEPARATED_COMPLEX_VECTORS + , RealVector iRHS + , RealVector iSolution +# endif +) +{ +register ElementPtr pElement; +register ComplexVector Intermediate; +register int I, *pExtOrder, Size; +register ComplexVector ExtVector; +ElementPtr pPivot; +ComplexNumber Temp; + +/* Begin `SolveComplexTransposedMatrix'. */ + + Size = Matrix->Size; + Intermediate = (ComplexVector)Matrix->Intermediate; + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET +#if spSEPARATED_COMPLEX_VECTORS + --RHS; --iRHS; + --Solution; --iSolution; +#else + RHS -= 2; Solution -= 2; +#endif +#endif + +/* Initialize Intermediate vector. */ + pExtOrder = &Matrix->IntToExtColMap[Size]; + +#if spSEPARATED_COMPLEX_VECTORS + for (I = Size; I > 0; I--) + { Intermediate[I].Real = RHS[*(pExtOrder)]; + Intermediate[I].Imag = iRHS[*(pExtOrder--)]; + } +#else + ExtVector = (ComplexVector)RHS; + for (I = Size; I > 0; I--) + Intermediate[I] = ExtVector[*(pExtOrder--)]; +#endif + +/* Forward elimination. */ + for (I = 1; I <= Size; I++) + { Temp = Intermediate[I]; + +/* This step of the elimination is skipped if Temp equals zero. */ + if ((Temp.Real != 0.0) OR (Temp.Imag != 0.0)) + { pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { +/* Cmplx expr: Intermediate[Element->Col] -= Temp * *Element. */ + CMPLX_MULT_SUBT_ASSIGN( Intermediate[pElement->Col], + Temp, *pElement); + pElement = pElement->NextInRow; + } + } + } + +/* Backward Substitution. */ + for (I = Size; I > 0; I--) + { pPivot = Matrix->Diag[I]; + Temp = Intermediate[I]; + pElement = pPivot->NextInCol; + + while (pElement != NULL) + { +/* Cmplx expr: Temp -= Intermediate[Element->Row] * *Element. */ + CMPLX_MULT_SUBT_ASSIGN(Temp,Intermediate[pElement->Row],*pElement); + + pElement = pElement->NextInCol; + } +/* Cmplx expr: Intermediate = Temp * (1.0 / *pPivot). */ + CMPLX_MULT(Intermediate[I], Temp, *pPivot); + } + +/* Unscramble Intermediate vector while placing data in to Solution vector. */ + pExtOrder = &Matrix->IntToExtRowMap[Size]; + +#if spSEPARATED_COMPLEX_VECTORS + for (I = Size; I > 0; I--) + { Solution[*(pExtOrder)] = Intermediate[I].Real; + iSolution[*(pExtOrder--)] = Intermediate[I].Imag; + } +#else + ExtVector = (ComplexVector)Solution; + for (I = Size; I > 0; I--) + ExtVector[*(pExtOrder--)] = Intermediate[I]; +#endif + + return; +} +#endif /* TRANSPOSE AND spCOMPLEX */ Added: trunk/math/SPARSE/sputils.c =================================================================== --- trunk/math/SPARSE/sputils.c (rev 0) +++ trunk/math/SPARSE/sputils.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,2188 @@ +/* + * MATRIX UTILITY MODULE + * + * Author: Advising professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + */ +/*! \file + * This file contains various optional utility routines. + * + * Objects that begin with the \a spc prefix are considered private + * and should not be used. + * + * \author + * Kenneth S. Kundert <kundert-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@xxxxxxxxxxxxxxxx> + */ +/* >>> User accessible functions contained in this file: + * spMNA_Preorder + * spScale + * spMultiply + * spMultTransposed + * spDeterminant + * spStrip + * spDeleteRowAndCol + * spPseudoCondition + * spCondition + * spNorm + * spLargestElement + * spRoundoff + * spErrorMessage + * + * >>> Other functions contained in this file: + * CountTwins + * SwapCols + * ScaleComplexMatrix + * ComplexMatrixMultiply + * ComplexCondition + */ + +/* + * IMPORTS + * + * >>> Import descriptions: + * spConfig.h + * Macros that customize the sparse matrix routines. + * spMatrix.h + * Macros and declarations to be imported by the user. + * spDefs.h + * Matrix type and macro definitions for the sparse matrix routines.. + */ + +#define spINSIDE_SPARSE +#include <core/core.h> +#include "../m.h" +#include "spconfig.h" +#include "spmatrix.h" +#include "spdefs.h" + + + + + +/* + * Function declarations + */ + +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, + RealVector, RealVector, RealVector, RealVector ); +static void ComplexTransposedMatrixMultiply( MatrixPtr, + RealVector, RealVector, RealVector, RealVector ); +#else +static void ComplexMatrixMultiply( MatrixPtr, + RealVector, RealVector ); +static void ComplexTransposedMatrixMultiply( MatrixPtr, + RealVector, RealVector ); +#endif +static RealNumber ComplexCondition( MatrixPtr, RealNumber, int* ); + + + + + + +#if MODIFIED_NODAL +/*! + * This routine massages modified node admittance matrices to remove + * zeros from the diagonal. It takes advantage of the fact that the + * row and column associated with a zero diagonal usually have + * structural ones placed symmetricly. This routine should be used + * only on modified node admittance matrices and should be executed + * after the matrix has been built but before the factorization + * begins. It should be executed for the initial factorization only + * and should be executed before the rows have been linked. Thus it + * should be run before using spScale(), spMultiply(), + * spDeleteRowAndCol(), or spNorm(). + * + * This routine exploits the fact that the structural ones are placed + * in the matrix in symmetric twins. For example, the stamps for + * grounded and a floating voltage sources are \code + * grounded: floating: + * [ x x 1 ] [ x x 1 ] + * [ x x ] [ x x -1 ] + * [ 1 ] [ 1 -1 ] + * \endcode + * Notice for the grounded source, there is one set of twins, and for + * the floating, there are two sets. We remove the zero from the diagonal + * by swapping the rows associated with a set of twins. For example: \code + * grounded: floating 1: floating 2: + * [ 1 ] [ 1 -1 ] [ x x 1 ] + * [ x x ] [ x x -1 ] [ 1 -1 ] + * [ x x 1 ] [ x x 1 ] [ x x -1 ] + * \endcode + * + * It is important to deal with any zero diagonals that only have one + * set of twins before dealing with those that have more than one because + * swapping row destroys the symmetry of any twins in the rows being + * swapped, which may limit future moves. Consider \code + * [ x x 1 ] + * [ x x -1 1 ] + * [ 1 -1 ] + * [ 1 ] + * \endcode + * There is one set of twins for diagonal 4 and two for diagonal 3. + * Dealing with diagonal 4 first requires swapping rows 2 and 4. \code + * [ x x 1 ] + * [ 1 ] + * [ 1 -1 ] + * [ x x -1 1 ] + * \endcode + * We can now deal with diagonal 3 by swapping rows 1 and 3. \code + * [ 1 -1 ] + * [ 1 ] + * [ x x 1 ] + * [ x x -1 1 ] + * \endcode + * And we are done, there are no zeros left on the diagonal. However, if + * we originally dealt with diagonal 3 first, we could swap rows 2 and 3 \code + * [ x x 1 ] + * [ 1 -1 ] + * [ x x -1 1 ] + * [ 1 ] + * \endcode + * Diagonal 4 no longer has a symmetric twin and we cannot continue. + * + * So we always take care of lone twins first. When none remain, we + * choose arbitrarily a set of twins for a diagonal with more than one set + * and swap the rows corresponding to that twin. We then deal with any + * lone twins that were created and repeat the procedure until no + * zero diagonals with symmetric twins remain. + * + * In this particular implementation, columns are swapped rather than rows. + * The algorithm used in this function was developed by Ken Kundert and + * Tom Quarles. + * + * \param * eMatrix + * Pointer to the matrix to be preordered. + */ +/* >>> Local variables; + * J (int) + * Column with zero diagonal being currently considered. + * pTwin1 (ElementPtr) + * Pointer to the twin found in the column belonging to the zero diagonal. + * pTwin2 (ElementPtr) + * Pointer to the twin found in the row belonging to the zero diagonal. + * belonging to the zero diagonal. + * AnotherPassNeeded (BOOLEAN) + * Flag indicating that at least one zero diagonal with symmetric twins + * remain. + * StartAt (int) + * Column number of first zero diagonal with symmetric twins. + * Swapped (BOOLEAN) + * Flag indicating that columns were swapped on this pass. + * Twins (int) + * Number of symmetric twins corresponding to current zero diagonal.. + */ + +void +spMNA_Preorder( spMatrix eMatrix ) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register int J, Size; +ElementPtr pTwin1=NULL, pTwin2=NULL; +int Twins, StartAt = 1; +BOOLEAN Swapped, AnotherPassNeeded; + +/* Begin `spMNA_Preorder'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + + if (Matrix->RowsLinked) return; + Size = Matrix->Size; + Matrix->Reordered = YES; + + do + { AnotherPassNeeded = Swapped = NO; + +/* Search for zero diagonals with lone twins. */ + for (J = StartAt; J <= Size; J++) + { if (Matrix->Diag[J] == NULL) + { Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 ); + if (Twins == 1) + { /* Lone twins found, swap rows. */ + SwapCols( Matrix, pTwin1, pTwin2 ); + Swapped = YES; + } + else if ((Twins > 1) AND NOT AnotherPassNeeded) + { AnotherPassNeeded = YES; + StartAt = J; + } + } + } + +/* All lone twins are gone, look for zero diagonals with multiple twins. */ + if (AnotherPassNeeded) + { for (J = StartAt; NOT Swapped AND (J <= Size); J++) + { if (Matrix->Diag[J] == NULL) + { Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 ); + SwapCols( Matrix, pTwin1, pTwin2 ); + Swapped = YES; + } + } + } + } while (AnotherPassNeeded); + return; +} + + + + +/* + * COUNT TWINS + * + * This function counts the number of symmetric twins associated with + * a zero diagonal and returns one set of twins if any exist. The + * count is terminated early at two. + */ + +static int +CountTwins( + MatrixPtr Matrix, + int Col, + ElementPtr *ppTwin1, + ElementPtr *ppTwin2 +) +{ +int Row, Twins = 0; +ElementPtr pTwin1, pTwin2; + +/* Begin `CountTwins'. */ + + pTwin1 = Matrix->FirstInCol[Col]; + while (pTwin1 != NULL) + { if (ABS(pTwin1->Real) == 1.0) + { Row = pTwin1->Row; + pTwin2 = Matrix->FirstInCol[Row]; + while ((pTwin2 != NULL) AND (pTwin2->Row != Col)) + pTwin2 = pTwin2->NextInCol; + if ((pTwin2 != NULL) AND (ABS(pTwin2->Real) == 1.0)) + { /* Found symmetric twins. */ + if (++Twins >= 2) return Twins; + (*ppTwin1 = pTwin1)->Col = Col; + (*ppTwin2 = pTwin2)->Col = Row; + } + } + pTwin1 = pTwin1->NextInCol; + } + return Twins; +} + + + + +/* + * SWAP COLUMNS + * + * This function swaps two columns and is applicable before the rows are + * linked. + */ + +static void +SwapCols( + MatrixPtr Matrix, + ElementPtr pTwin1, + ElementPtr pTwin2 +) +{ +int Col1 = pTwin1->Col, Col2 = pTwin2->Col; + +/* Begin `SwapCols'. */ + + SWAP (ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]); + SWAP (int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]); +#if TRANSLATE + Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col2]]=Col2; + Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col1]]=Col1; +#endif + + Matrix->Diag[Col1] = pTwin2; + Matrix->Diag[Col2] = pTwin1; + Matrix->NumberOfInterchangesIsOdd = NOT Matrix->NumberOfInterchangesIsOdd; + return; +} +#endif /* MODIFIED_NODAL */ + + + + + + + + + +#if SCALING +/*! + * This function scales the matrix to enhance the possibility of + * finding a good pivoting order. Note that scaling enhances accuracy + * of the solution only if it affects the pivoting order, so it makes + * no sense to scale the matrix before spFactor(). If scaling is + * desired it should be done before spOrderAndFactor(). There + * are several things to take into account when choosing the scale + * factors. First, the scale factors are directly multiplied against + * the elements in the matrix. To prevent roundoff, each scale factor + * should be equal to an integer power of the number base of the + * machine. Since most machines operate in base two, scale factors + * should be a power of two. Second, the matrix should be scaled such + * that the matrix of element uncertainties is equilibrated. Third, + * this function multiplies the scale factors by the elements, so if + * one row tends to have uncertainties 1000 times smaller than the + * other rows, then its scale factor should be 1024, not 1/1024. + * Fourth, to save time, this function does not scale rows or columns + * if their scale factors are equal to one. Thus, the scale factors + * should be normalized to the most common scale factor. Rows and + * columns should be normalized separately. For example, if the size + * of the matrix is 100 and 10 rows tend to have uncertainties near + * 1e-6 and the remaining 90 have uncertainties near 1e-12, then the + * scale factor for the 10 should be 1/1,048,576 and the scale factors + * for the remaining 90 should be 1. Fifth, since this routine + * directly operates on the matrix, it is necessary to apply the scale + * factors to the RHS and Solution vectors. It may be easier to + * simply use spOrderAndFactor() on a scaled matrix to choose the + * pivoting order, and then throw away the matrix. Subsequent + * factorizations, performed with spFactor(), will not need to have + * the RHS and Solution vectors descaled. Lastly, this function + * should not be executed before the function spMNA_Preorder(). + * + * \param eMatrix + * Pointer to the matrix to be scaled. + * \param SolutionScaleFactors + * The array of Solution scale factors. These factors scale the columns. + * All scale factors are real valued. + * \param RHS_ScaleFactors + * The array of RHS scale factors. These factors scale the rows. + * All scale factors are real valued. + */ +/* >>> Local variables: + * lSize (int) + * Local version of the size of the matrix. + * pElement (ElementPtr) + * Pointer to an element in the matrix. + * pExtOrder (int *) + * Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to + * compensate for any row or column swaps that have been performed. + * ScaleFactor (RealNumber) + * The scale factor being used on the current row or column. + */ + +void +spScale( + spMatrix eMatrix, + spREAL RHS_ScaleFactors[], + spREAL SolutionScaleFactors[] +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register int I, lSize, *pExtOrder; +RealNumber ScaleFactor; + +/* Begin `spScale'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + if (NOT Matrix->RowsLinked) spcLinkRows( Matrix ); + +#if spCOMPLEX + if (Matrix->Complex) + { ScaleComplexMatrix( Matrix, RHS_ScaleFactors, SolutionScaleFactors ); + return; + } +#endif + +#if REAL + lSize = Matrix->Size; + +/* Correct pointers to arrays for ARRAY_OFFSET */ +#if NOT ARRAY_OFFSET + --RHS_ScaleFactors; + --SolutionScaleFactors; +#endif + +/* Scale Rows */ + pExtOrder = &Matrix->IntToExtRowMap[1]; + for (I = 1; I <= lSize; I++) + { if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0) + { pElement = Matrix->FirstInRow[I]; + + while (pElement != NULL) + { pElement->Real *= ScaleFactor; + pElement = pElement->NextInRow; + } + } + } + +/* Scale Columns */ + pExtOrder = &Matrix->IntToExtColMap[1]; + for (I = 1; I <= lSize; I++) + { if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0) + { pElement = Matrix->FirstInCol[I]; + + while (pElement != NULL) + { pElement->Real *= ScaleFactor; + pElement = pElement->NextInCol; + } + } + } + return; + +#endif /* REAL */ +} +#endif /* SCALING */ + + + + + + + + + +#if spCOMPLEX AND SCALING +/* + * SCALE COMPLEX MATRIX + * + * This function scales the matrix to enhance the possibility of + * finding a good pivoting order. Note that scaling enhances accuracy + * of the solution only if it affects the pivoting order, so it makes + * no sense to scale the matrix before spFactor(). If scaling is + * desired it should be done before spOrderAndFactor(). There + * are several things to take into account when choosing the scale + * factors. First, the scale factors are directly multiplied against + * the elements in the matrix. To prevent roundoff, each scale factor + * should be equal to an integer power of the number base of the + * machine. Since most machines operate in base two, scale factors + * should be a power of two. Second, the matrix should be scaled such + * that the matrix of element uncertainties is equilibrated. Third, + * this function multiplies the scale factors by the elements, so if + * one row tends to have uncertainties 1000 times smaller than the + * other rows, then its scale factor should be 1024, not 1/1024. + * Fourth, to save time, this function does not scale rows or columns + * if their scale factors are equal to one. Thus, the scale factors + * should be normalized to the most common scale factor. Rows and + * columns should be normalized separately. For example, if the size + * of the matrix is 100 and 10 rows tend to have uncertainties near + * 1e-6 and the remaining 90 have uncertainties near 1e-12, then the + * scale factor for the 10 should be 1/1,048,576 and the scale factors + * for the remaining 90 should be 1. Fifth, since this routine + * directly operates on the matrix, it is necessary to apply the scale + * factors to the RHS and Solution vectors. It may be easier to + * simply use spOrderAndFactor() on a scaled matrix to choose the + * pivoting order, and then throw away the matrix. Subsequent + * factorizations, performed with spFactor(), will not need to have + * the RHS and Solution vectors descaled. Lastly, this function + * should not be executed before the function spMNA_Preorder. + * + * >>> Arguments: + * Matrix <input> (char *) + * Pointer to the matrix to be scaled. + * SolutionScaleFactors <input> (RealVector) + * The array of Solution scale factors. These factors scale the columns. + * All scale factors are real valued. + * RHS_ScaleFactors <input> (RealVector) + * The array of RHS scale factors. These factors scale the rows. + * All scale factors are real valued. + * + * >>> Local variables: + * lSize (int) + * Local version of the size of the matrix. + * pElement (ElementPtr) + * Pointer to an element in the matrix. + * pExtOrder (int *) + * Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to + * compensate for any row or column swaps that have been performed. + * ScaleFactor (RealNumber) + * The scale factor being used on the current row or column. + */ + +static void +ScaleComplexMatrix( + MatrixPtr Matrix, + register RealVector RHS_ScaleFactors, + register RealVector SolutionScaleFactors +) +{ +register ElementPtr pElement; +register int I, lSize, *pExtOrder; +RealNumber ScaleFactor; + +/* Begin `ScaleComplexMatrix'. */ + lSize = Matrix->Size; + +/* Correct pointers to arrays for ARRAY_OFFSET */ +#if NOT ARRAY_OFFSET + --RHS_ScaleFactors; + --SolutionScaleFactors; +#endif + +/* Scale Rows */ + pExtOrder = &Matrix->IntToExtRowMap[1]; + for (I = 1; I <= lSize; I++) + { if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0) + { pElement = Matrix->FirstInRow[I]; + + while (pElement != NULL) + { pElement->Real *= ScaleFactor; + pElement->Imag *= ScaleFactor; + pElement = pElement->NextInRow; + } + } + } + +/* Scale Columns */ + pExtOrder = &Matrix->IntToExtColMap[1]; + for (I = 1; I <= lSize; I++) + { if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0) + { pElement = Matrix->FirstInCol[I]; + + while (pElement != NULL) + { pElement->Real *= ScaleFactor; + pElement->Imag *= ScaleFactor; + pElement = pElement->NextInCol; + } + } + } + return; +} +#endif /* SCALING AND spCOMPLEX */ + + + + + + + + +#if MULTIPLICATION +/*! + * Multiplies matrix by solution vector to find source vector. + * Assumes matrix has not been factored. This routine can be used + * as a test to see if solutions are correct. It should not be used + * before spMNA_Preorder(). + * + * \param eMatrix + * Pointer to the matrix. + * \param RHS + * RHS is the right hand side. This is what is being solved for. + * \param Solution + * Solution is the vector being multiplied by the matrix. + * \param iRHS + * iRHS is the imaginary portion of the right hand side. This is + * what is being solved for. This is only necessary if the matrix is + * complex and \a spSEPARATED_COMPLEX_VECTORS is true. + * \param iSolution + * iSolution is the imaginary portion of the vector being multiplied + * by the matrix. This is only necessary if the matrix is + * complex and \a spSEPARATED_COMPLEX_VECTORS is true. + */ + +void +spMultiply( + spMatrix eMatrix, + spREAL RHS[], + spREAL Solution[] +#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS + , spREAL iRHS[] + , spREAL iSolution[] +#endif +) +{ +register ElementPtr pElement; +register RealVector Vector; +register RealNumber Sum; +register int I, *pExtOrder; +MatrixPtr Matrix = (MatrixPtr)eMatrix; + +/* Begin `spMultiply'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + if (NOT Matrix->RowsLinked) + spcLinkRows(Matrix); + if (NOT Matrix->InternalVectorsAllocated) + spcCreateInternalVectors( Matrix ); + +#if spCOMPLEX + if (Matrix->Complex) + { ComplexMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS ); + return; + } +#endif + +#if REAL +#if NOT ARRAY_OFFSET +/* Correct array pointers for ARRAY_OFFSET. */ + --RHS; + --Solution; +#endif + +/* Initialize Intermediate vector with reordered Solution vector. */ + Vector = Matrix->Intermediate; + pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; + for (I = Matrix->Size; I > 0; I--) + Vector[I] = Solution[*(pExtOrder--)]; + + pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; + for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInRow[I]; + Sum = 0.0; + + while (pElement != NULL) + { Sum += pElement->Real * Vector[pElement->Col]; + pElement = pElement->NextInRow; + } + RHS[*pExtOrder--] = Sum; + } + return; +#endif /* REAL */ +} +#endif /* MULTIPLICATION */ + + + + + + + +#if spCOMPLEX AND MULTIPLICATION +/* + * COMPLEX MATRIX MULTIPLICATION + * + * Multiplies matrix by solution vector to find source vector. + * Assumes matrix has not been factored. This routine can be used + * as a test to see if solutions are correct. + * + * >>> Arguments: + * Matrix <input> (char *) + * Pointer to the matrix. + * RHS <output> (RealVector) + * RHS is the right hand side. This is what is being solved for. + * This is only the real portion of the right-hand side if the matrix + * is complex and spSEPARATED_COMPLEX_VECTORS is set true. + * Solution <input> (RealVector) + * Solution is the vector being multiplied by the matrix. This is only + * the real portion if the matrix is complex and + * spSEPARATED_COMPLEX_VECTORS is set true. + * iRHS <output> (RealVector) + * iRHS is the imaginary portion of the right hand side. This is + * what is being solved for. This is only necessary if the matrix is + * complex and spSEPARATED_COMPLEX_VECTORS is true. + * iSolution <input> (RealVector) + * iSolution is the imaginary portion of the vector being multiplied + * by the matrix. This is only necessary if the matrix is + * complex and spSEPARATED_COMPLEX_VECTORS is true. + */ + +static void +ComplexMatrixMultiply( + MatrixPtr Matrix, + RealVector RHS, + RealVector Solution +#if spSEPARATED_COMPLEX_VECTORS + , RealVector iRHS + , RealVector iSolution +#endif +) +{ +register ElementPtr pElement; +register ComplexVector Vector; +ComplexNumber Sum; +register int I, *pExtOrder; + +/* Begin `ComplexMatrixMultiply'. */ + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET +#if spSEPARATED_COMPLEX_VECTORS + --RHS; --iRHS; + --Solution; --iSolution; +#else + RHS -= 2; Solution -= 2; +#endif +#endif + +/* Initialize Intermediate vector with reordered Solution vector. */ + Vector = (ComplexVector)Matrix->Intermediate; + pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; + +#if spSEPARATED_COMPLEX_VECTORS + for (I = Matrix->Size; I > 0; I--) + { Vector[I].Real = Solution[*pExtOrder]; + Vector[I].Imag = iSolution[*(pExtOrder--)]; + } +#else + for (I = Matrix->Size; I > 0; I--) + Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)]; +#endif + + pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; + for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInRow[I]; + Sum.Real = Sum.Imag = 0.0; + + while (pElement != NULL) + { /* Cmplx expression : Sum += Element * Vector[Col] */ + CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Col] ); + pElement = pElement->NextInRow; + } + +#if spSEPARATED_COMPLEX_VECTORS + RHS[*pExtOrder] = Sum.Real; + iRHS[*pExtOrder--] = Sum.Imag; +#else + ((ComplexVector)RHS)[*pExtOrder--] = Sum; +#endif + } + return; +} +#endif /* spCOMPLEX AND MULTIPLICATION */ + + + + + + + + +#if MULTIPLICATION AND TRANSPOSE +/*! + * Multiplies transposed matrix by solution vector to find source vector. + * Assumes matrix has not been factored. This routine can be used + * as a test to see if solutions are correct. It should not be used + * before spMNA_Preorder(). + * + * \param eMatrix + * Pointer to the matrix. + * \param RHS + * RHS is the right hand side. This is what is being solved for. + * \param Solution + * Solution is the vector being multiplied by the matrix. + * \param iRHS + * iRHS is the imaginary portion of the right hand side. This is + * what is being solved for. This is only necessary if the matrix is + * complex and \a spSEPARATED_COMPLEX_VECTORS is true. + * \param iSolution + * iSolution is the imaginary portion of the vector being multiplied + * by the matrix. This is only necessary if the matrix is + * complex and \a spSEPARATED_COMPLEX_VECTORS is true. + */ + +void +spMultTransposed( + spMatrix eMatrix, + spREAL RHS[], + spREAL Solution[] +#if spCOMPLEX AND spSEPARATED_COMPLEX_VECTORS + , spREAL iRHS[] + , spREAL iSolution[] +#endif +) +{ +register ElementPtr pElement; +register RealVector Vector; +register RealNumber Sum; +register int I, *pExtOrder; +MatrixPtr Matrix = (MatrixPtr)eMatrix; + +/* Begin `spMultTransposed'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + if (NOT Matrix->InternalVectorsAllocated) + spcCreateInternalVectors( Matrix ); + +#if spCOMPLEX + if (Matrix->Complex) + { ComplexTransposedMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS ); + return; + } +#endif + +#if REAL +#if NOT ARRAY_OFFSET +/* Correct array pointers for ARRAY_OFFSET. */ + --RHS; + --Solution; +#endif + +/* Initialize Intermediate vector with reordered Solution vector. */ + Vector = Matrix->Intermediate; + pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; + for (I = Matrix->Size; I > 0; I--) + Vector[I] = Solution[*(pExtOrder--)]; + + pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; + for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInCol[I]; + Sum = 0.0; + + while (pElement != NULL) + { Sum += pElement->Real * Vector[pElement->Row]; + pElement = pElement->NextInCol; + } + RHS[*pExtOrder--] = Sum; + } + return; +#endif /* REAL */ +} +#endif /* MULTIPLICATION AND TRANSPOSE */ + + + + + + + +#if spCOMPLEX AND MULTIPLICATION AND TRANSPOSE +/* + * COMPLEX TRANSPOSED MATRIX MULTIPLICATION + * + * Multiplies transposed matrix by solution vector to find source vector. + * Assumes matrix has not been factored. This routine can be used + * as a test to see if solutions are correct. + * + * >>> Arguments: + * Matrix <input> (char *) + * Pointer to the matrix. + * RHS <output> (RealVector) + * RHS is the right hand side. This is what is being solved for. + * This is only the real portion of the right-hand side if the matrix + * is complex and spSEPARATED_COMPLEX_VECTORS is set true. + * Solution <input> (RealVector) + * Solution is the vector being multiplied by the matrix. This is only + * the real portion if the matrix is complex and + * spSEPARATED_COMPLEX_VECTORS is set true. + * iRHS <output> (RealVector) + * iRHS is the imaginary portion of the right hand side. This is + * what is being solved for. This is only necessary if the matrix is + * complex and spSEPARATED_COMPLEX_VECTORS is true. + * iSolution <input> (RealVector) + * iSolution is the imaginary portion of the vector being multiplied + * by the matrix. This is only necessary if the matrix is + * complex and spSEPARATED_COMPLEX_VECTORS is true. + * + * >>> Obscure Macros + * IMAG_VECTORS + * Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and + * spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears + * without a trace. + */ + +static void +ComplexTransposedMatrixMultiply( + MatrixPtr Matrix, + RealVector RHS, + RealVector Solution +#if spSEPARATED_COMPLEX_VECTORS + , RealVector iRHS + , RealVector iSolution +#endif +) +{ +register ElementPtr pElement; +register ComplexVector Vector; +ComplexNumber Sum; +register int I, *pExtOrder; + +/* Begin `ComplexTransposedMatrixMultiply'. */ + +/* Correct array pointers for ARRAY_OFFSET. */ +#if NOT ARRAY_OFFSET +#if spSEPARATED_COMPLEX_VECTORS + --RHS; --iRHS; + --Solution; --iSolution; +#else + RHS -= 2; Solution -= 2; +#endif +#endif + +/* Initialize Intermediate vector with reordered Solution vector. */ + Vector = (ComplexVector)Matrix->Intermediate; + pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size]; + +#if spSEPARATED_COMPLEX_VECTORS + for (I = Matrix->Size; I > 0; I--) + { Vector[I].Real = Solution[*pExtOrder]; + Vector[I].Imag = iSolution[*(pExtOrder--)]; + } +#else + for (I = Matrix->Size; I > 0; I--) + Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)]; +#endif + + pExtOrder = &Matrix->IntToExtColMap[Matrix->Size]; + for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInCol[I]; + Sum.Real = Sum.Imag = 0.0; + + while (pElement != NULL) + { /* Cmplx expression : Sum += Element * Vector[Row] */ + CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Row] ); + pElement = pElement->NextInCol; + } + +#if spSEPARATED_COMPLEX_VECTORS + RHS[*pExtOrder] = Sum.Real; + iRHS[*pExtOrder--] = Sum.Imag; +#else + ((ComplexVector)RHS)[*pExtOrder--] = Sum; +#endif + } + return; +} +#endif /* spCOMPLEX AND MULTIPLICATION AND TRANSPOSE */ + + + + + + + + +#if DETERMINANT +/*! + * This routine in capable of calculating the determinant of the + * matrix once the LU factorization has been performed. Hence, only + * use this routine after spFactor() and before spClear(). + * The determinant equals the product of all the diagonal elements of + * the lower triangular matrix L, except that this product may need + * negating. Whether the product or the negative product equals the + * determinant is determined by the number of row and column + * interchanges performed. Note that the determinants of matrices can + * be very large or very small. On large matrices, the determinant + * can be far larger or smaller than can be represented by a floating + * point number. For this reason the determinant is scaled to a + * reasonable value and the logarithm of the scale factor is returned. + * + * \param eMatrix + * A pointer to the matrix for which the determinant is desired. + * \param pExponent + * The logarithm base 10 of the scale factor for the determinant. To find + * the actual determinant, Exponent should be added to the exponent of + * Determinant. + * \param pDeterminant + * The real portion of the determinant. This number is scaled to be + * greater than or equal to 1.0 and less than 10.0. + * \param piDeterminant + * The imaginary portion of the determinant. When the matrix is real + * this pointer need not be supplied, nothing will be returned. This + * number is scaled to be greater than or equal to 1.0 and less than 10.0. + */ +/* >>> Local variables: + * Norm (RealNumber) + * L-infinity norm of a complex number. + * Size (int) + * Local storage for Matrix->Size. Placed in a register for speed. + * Temp (RealNumber) + * Temporary storage for real portion of determinant. + */ + +void +spDeterminant( + spMatrix eMatrix, + int *pExponent, + spREAL *pDeterminant +#if spCOMPLEX + , spREAL *piDeterminant +#endif +) +{ +register MatrixPtr Matrix = (MatrixPtr)eMatrix; +register int I, Size; +RealNumber Norm, nr, ni; +ComplexNumber Pivot, cDeterminant; + +#define NORM(a) (nr = ABS((a).Real), ni = ABS((a).Imag), MAX (nr,ni)) + +/* Begin `spDeterminant'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_FACTORED( Matrix ); + *pExponent = 0; + + if (Matrix->Error == spSINGULAR) + { *pDeterminant = 0.0; +#if spCOMPLEX + if (Matrix->Complex) *piDeterminant = 0.0; +#endif + return; + } + + Size = Matrix->Size; + I = 0; + +#if spCOMPLEX + if (Matrix->Complex) /* Complex Case. */ + { cDeterminant.Real = 1.0; + cDeterminant.Imag = 0.0; + + while (++I <= Size) + { CMPLX_RECIPROCAL( Pivot, *Matrix->Diag[I] ); + CMPLX_MULT_ASSIGN( cDeterminant, Pivot ); + +/* Scale Determinant. */ + Norm = NORM( cDeterminant ); + if (Norm != 0.0) + { while (Norm >= 1.0e12) + { cDeterminant.Real *= 1.0e-12; + cDeterminant.Imag *= 1.0e-12; + *pExponent += 12; + Norm = NORM( cDeterminant ); + } + while (Norm < 1.0e-12) + { cDeterminant.Real *= 1.0e12; + cDeterminant.Imag *= 1.0e12; + *pExponent -= 12; + Norm = NORM( cDeterminant ); + } + } + } + +/* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */ + Norm = NORM( cDeterminant ); + if (Norm != 0.0) + { while (Norm >= 10.0) + { cDeterminant.Real *= 0.1; + cDeterminant.Imag *= 0.1; + (*pExponent)++; + Norm = NORM( cDeterminant ); + } + while (Norm < 1.0) + { cDeterminant.Real *= 10.0; + cDeterminant.Imag *= 10.0; + (*pExponent)--; + Norm = NORM( cDeterminant ); + } + } + if (Matrix->NumberOfInterchangesIsOdd) + CMPLX_NEGATE( cDeterminant ); + + *pDeterminant = cDeterminant.Real; + *piDeterminant = cDeterminant.Imag; + } +#endif /* spCOMPLEX */ +#if REAL AND spCOMPLEX + else +#endif +#if REAL + { /* Real Case. */ + *pDeterminant = 1.0; + + while (++I <= Size) + { *pDeterminant /= Matrix->Diag[I]->Real; + +/* Scale Determinant. */ + if (*pDeterminant != 0.0) + { while (ABS(*pDeterminant) >= 1.0e12) + { *pDeterminant *= 1.0e-12; + *pExponent += 12; + } + while (ABS(*pDeterminant) < 1.0e-12) + { *pDeterminant *= 1.0e12; + *pExponent -= 12; + } + } + } + +/* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */ + if (*pDeterminant != 0.0) + { while (ABS(*pDeterminant) >= 10.0) + { *pDeterminant *= 0.1; + (*pExponent)++; + } + while (ABS(*pDeterminant) < 1.0) + { *pDeterminant *= 10.0; + (*pExponent)--; + } + } + if (Matrix->NumberOfInterchangesIsOdd) + *pDeterminant = -*pDeterminant; + } +#endif /* REAL */ +} +#endif /* DETERMINANT */ + + + + + + + + +#if STRIP + +/*! + * Strips the matrix of all fill-ins. + * + * \param eMatrix + * Pointer to the matrix to be stripped. + */ +/* >>> Local variables: + * pElement (ElementPtr) + * Pointer that is used to step through the matrix. + * ppElement (ElementPtr *) + * Pointer to the location of an ElementPtr. This location will be + * updated if a fill-in is stripped from the matrix. + * pFillin (ElementPtr) + * Pointer used to step through the lists of fill-ins while marking them. + * pLastFillin (ElementPtr) + * A pointer to the last fill-in in the list. Used to terminate a loop. + * pListNode (struct FillinListNodeStruct *) + * A pointer to a node in the FillinList linked-list. + */ + +void +spStripFills( spMatrix eMatrix ) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +struct FillinListNodeStruct *pListNode; + +/* Begin `spStripFills'. */ + ASSERT_IS_SPARSE( Matrix ); + if (Matrix->Fillins == 0) return; + Matrix->NeedsOrdering = YES; + Matrix->Elements -= Matrix->Fillins; + Matrix->Fillins = 0; + +/* Mark the fill-ins. */ + { register ElementPtr pFillin, pLastFillin; + + pListNode = Matrix->LastFillinListNode = Matrix->FirstFillinListNode; + Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList; + Matrix->NextAvailFillin = pListNode->pFillinList; + + while (pListNode != NULL) + { pFillin = pListNode->pFillinList; + pLastFillin = &(pFillin[ pListNode->NumberOfFillinsInList - 1 ]); + while (pFillin <= pLastFillin) + (pFillin++)->Row = 0; + pListNode = pListNode->Next; + } + } + +/* Unlink fill-ins by searching for elements marked with Row = 0. */ + { register ElementPtr pElement, *ppElement; + register int I, Size = Matrix->Size; + +/* Unlink fill-ins in all columns. */ + for (I = 1; I <= Size; I++) + { ppElement = &(Matrix->FirstInCol[I]); + while ((pElement = *ppElement) != NULL) + { if (pElement->Row == 0) + { *ppElement = pElement->NextInCol; /* Unlink fill-in. */ + if (Matrix->Diag[pElement->Col] == pElement) + Matrix->Diag[pElement->Col] = NULL; + } + else + ppElement = &pElement->NextInCol; /* Skip element.. */ + } + } + +/* Unlink fill-ins in all rows. */ + for (I = 1; I <= Size; I++) + { ppElement = &(Matrix->FirstInRow[I]); + while ((pElement = *ppElement) != NULL) + { if (pElement->Row == 0) + *ppElement = pElement->NextInRow; /* Unlink fill-in. */ + else + ppElement = &pElement->NextInRow; /* Skip element.. */ + } + } + } + return; +} +#endif + + + + + + + +#if TRANSLATE AND DELETE +/*! + * Deletes a row and a column from a matrix. + * + * Sparse will abort if an attempt is made to delete a row or column that + * doesn't exist. + * + * \param eMatrix + * Pointer to the matrix in which the row and column are to be deleted. + * \param Row + * Row to be deleted. + * \param Col + * Column to be deleted. + */ +/* >>> Local variables: + * ExtCol (int) + * The external column that is being deleted. + * ExtRow (int) + * The external row that is being deleted. + * pElement (ElementPtr) + * Pointer to an element in the matrix. Used when scanning rows and + * columns in order to eliminate elements from the last row or column. + * ppElement (ElementPtr *) + * Pointer to the location of an ElementPtr. This location will be + * filled with a NULL pointer if it is the new last element in its row + * or column. + * pElement (ElementPtr) + * Pointer to an element in the last row or column of the matrix. + * Size (int) + * The local version Matrix->Size, the size of the matrix. + */ + +void +spDeleteRowAndCol( + spMatrix eMatrix, + int Row, + int Col +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement, *ppElement, pLastElement; +int Size, ExtRow, ExtCol; + +/* Begin `spDeleteRowAndCol'. */ + ASSERT_IS_SPARSE( Matrix ); + vASSERT( (Row > 0) AND (Col > 0), "Nonpositive row or column number" ); + vASSERT( (Row <= Matrix->ExtSize) AND (Col <= Matrix->ExtSize), + "Row or column number too large" ); + + Size = Matrix->Size; + ExtRow = Row; + ExtCol = Col; + if (NOT Matrix->RowsLinked) spcLinkRows( Matrix ); + + Row = Matrix->ExtToIntRowMap[Row]; + Col = Matrix->ExtToIntColMap[Col]; + ASSERT( Row > 0 AND Col > 0 ); + +/* Move Row so that it is the last row in the matrix. */ + if (Row != Size) spcRowExchange( Matrix, Row, Size ); + +/* Move Col so that it is the last column in the matrix. */ + if (Col != Size) spcColExchange( Matrix, Col, Size ); + +/* Correct Diag pointers. */ + if (Row == Col) + SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Size] ) + else + { Matrix->Diag[Row] = spcFindDiag( Matrix, Row ); + Matrix->Diag[Col] = spcFindDiag( Matrix, Col ); + } + +/* + * Delete last row and column of the matrix. + */ +/* Break the column links to every element in the last row. */ + pLastElement = Matrix->FirstInRow[ Size ]; + while (pLastElement != NULL) + { ppElement = &(Matrix->FirstInCol[ pLastElement->Col ]); + while ((pElement = *ppElement) != NULL) + { if (pElement == pLastElement) + *ppElement = NULL; /* Unlink last element in column. */ + else + ppElement = &pElement->NextInCol; /* Skip element. */ + } + pLastElement = pLastElement->NextInRow; + } + +/* Break the row links to every element in the last column. */ + pLastElement = Matrix->FirstInCol[ Size ]; + while (pLastElement != NULL) + { ppElement = &(Matrix->FirstInRow[ pLastElement->Row ]); + while ((pElement = *ppElement) != NULL) + { if (pElement == pLastElement) + *ppElement = NULL; /* Unlink last element in row. */ + else + ppElement = &pElement->NextInRow; /* Skip element. */ + } + pLastElement = pLastElement->NextInCol; + } + +/* Clean up some details. */ + Matrix->Size = Size - 1; + Matrix->Diag[Size] = NULL; + Matrix->FirstInRow[Size] = NULL; + Matrix->FirstInCol[Size] = NULL; + Matrix->CurrentSize--; + Matrix->ExtToIntRowMap[ExtRow] = -1; + Matrix->ExtToIntColMap[ExtCol] = -1; + Matrix->NeedsOrdering = YES; + + return; +} +#endif + + + + + + + + +#if PSEUDOCONDITION +/*! + * Computes the magnitude of the ratio of the largest to the smallest + * pivots. This quantity is an indicator of ill-conditioning in the + * matrix. If this ratio is large, and if the matrix is scaled such + * that uncertainties in the RHS and the matrix entries are + * equilibrated, then the matrix is ill-conditioned. However, a small + * ratio does not necessarily imply that the matrix is + * well-conditioned. This routine must only be used after a matrix has + * been factored by spOrderAndFactor() or spFactor() and before it is + * cleared by spClear() or spInitialize(). The pseudocondition is + * faster to compute than the condition number calculated by + * spCondition(), but is not as informative. + * + * \return + * The magnitude of the ratio of the largest to smallest pivot used during + * previous factorization. If the matrix was singular, zero is returned. + * + * \param eMatrix + * Pointer to the matrix. + */ + +spREAL +spPseudoCondition( spMatrix eMatrix ) +{ + MatrixPtr Matrix = (MatrixPtr)eMatrix; + register int I; + register ArrayOfElementPtrs Diag; + RealNumber MaxPivot, MinPivot, Mag; + + /* Begin `spPseudoCondition'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_FACTORED( Matrix ); + if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG) + return 0.0; + + Diag = Matrix->Diag; + MaxPivot = MinPivot = ELEMENT_MAG( Diag[1] ); + for (I = 2; I <= Matrix->Size; I++) + { Mag = ELEMENT_MAG( Diag[I] ); + if (Mag > MaxPivot) + MaxPivot = Mag; + else if (Mag < MinPivot) + MinPivot = Mag; + } + ASSERT( MaxPivot > 0.0 ); + return MaxPivot / MinPivot; +} +#endif + + + + + + + + +#if CONDITION +/*! + * Computes an estimate of the condition number using a variation on + * the LINPACK condition number estimation algorithm. This quantity is + * an indicator of ill-conditioning in the matrix. To avoid problems + * with overflow, the reciprocal of the condition number is returned. + * If this number is small, and if the matrix is scaled such that + * uncertainties in the RHS and the matrix entries are equilibrated, + * then the matrix is ill-conditioned. If the this number is near + * one, the matrix is well conditioned. This routine must only be + * used after a matrix has been factored by spOrderAndFactor() or + * spFactor() and before it is cleared by spClear() or spInitialize(). + * + * Unlike the LINPACK condition number estimator, this routines + * returns the L infinity condition number. This is an artifact of + * Sparse placing ones on the diagonal of the upper triangular matrix + * rather than the lower. This difference should be of no importance. + * + * \b References: + * + * A.K. Cline, C.B. Moler, G.W. Stewart, J.H. Wilkinson. An estimate + * for the condition number of a matrix. SIAM Journal on Numerical + * Analysis. Vol. 16, No. 2, pages 368-375, April 1979. + * + * J.J. Dongarra, C.B. Moler, J.R. Bunch, G.W. Stewart. LINPACK + * User's Guide. SIAM, 1979. + * + * Roger G. Grimes, John G. Lewis. Condition number estimation for + * sparse matrices. SIAM Journal on Scientific and Statistical + * Computing. Vol. 2, No. 4, pages 384-388, December 1981. + * + * Dianne Prost O'Leary. Estimating matrix condition numbers. SIAM + * Journal on Scientific and Statistical Computing. Vol. 1, No. 2, + * pages 205-209, June 1980. + * + * \return + * The reciprocal of the condition number. If the matrix was singular, + * zero is returned. + * + * \param eMatrix + * Pointer to the matrix. + * \param NormOfMatrix + * The L-infinity norm of the unfactored matrix as computed by + * spNorm(). + * \param pError + * Used to return error code. Possible errors include \a spSINGULAR + * or \a spNO_MEMORY. + */ + +spREAL +spCondition( + spMatrix eMatrix, + spREAL NormOfMatrix, + 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; +#define SLACK 1e4 + +/* Begin `spCondition'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_FACTORED( Matrix ); + *pError = Matrix->Error; + if (Matrix->Error >= spFATAL) return 0.0; + if (NormOfMatrix == 0.0) + { *pError = spSINGULAR; + return 0.0; + } + +#if spCOMPLEX + if (Matrix->Complex) + return ComplexCondition( Matrix, NormOfMatrix, pError ); +#endif + +#if REAL + Size = Matrix->Size; + T = Matrix->Intermediate; +#if spCOMPLEX + Tm = Matrix->Intermediate + Size; +#else + Tm = ALLOC( RealNumber, Size+1 ); + if (Tm == NULL) + { *pError = spNO_MEMORY; + return 0.0; + } +#endif + for (I = Size; I > 0; I--) T[I] = 0.0; + +/* + * Part 1. Ay = e. + * Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign + * chosen to maximize the size of w in Lw = e. Since the terms in w can + * get very large, scaling is used to avoid overflow. + */ + +/* Forward elimination. Solves Lw = e while choosing e. */ + E = 1.0; + for (I = 1; I <= Size; I++) + { pPivot = Matrix->Diag[I]; + if (T[I] < 0.0) Em = -E; else Em = E; + Wm = (Em + T[I]) * pPivot->Real; + if (ABS(Wm) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(Wm) ); + for (K = Size; K > 0; K--) T[K] *= ScaleFactor; + E *= ScaleFactor; + Em *= ScaleFactor; + Wm = (Em + T[I]) * pPivot->Real; + } + Wp = (T[I] - Em) * pPivot->Real; + ASp = ABS(T[I] - Em); + ASm = ABS(Em + T[I]); + +/* Update T for both values of W, minus value is placed in Tm. */ + pElement = pPivot->NextInCol; + while (pElement != NULL) + { Row = pElement->Row; + Tm[Row] = T[Row] - (Wm * pElement->Real); + T[Row] -= (Wp * pElement->Real); + ASp += ABS(T[Row]); + ASm += ABS(Tm[Row]); + pElement = pElement->NextInCol; + } + +/* If minus value causes more growth, overwrite T with its values. */ + if (ASm > ASp) + { T[I] = Wm; + pElement = pPivot->NextInCol; + while (pElement != NULL) + { T[pElement->Row] = Tm[pElement->Row]; + pElement = pElement->NextInCol; + } + } + else T[I] = Wp; + } + +/* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK.. */ + for (ASw = 0.0, I = Size; I > 0; I--) ASw += ABS(T[I]); + ScaleFactor = 1.0 / (SLACK * ASw); + if (ScaleFactor < 0.5) + { for (I = Size; I > 0; I--) T[I] *= ScaleFactor; + E *= ScaleFactor; + } + +/* Backward Substitution. Solves Uy = w.*/ + for (I = Size; I >= 1; I--) + { pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { T[I] -= pElement->Real * T[pElement->Col]; + pElement = pElement->NextInRow; + } + if (ABS(T[I]) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) ); + for (K = Size; K > 0; K--) T[K] *= ScaleFactor; + E *= ScaleFactor; + } + } + +/* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK.. */ + for (ASy = 0.0, I = Size; I > 0; I--) ASy += ABS(T[I]); + ScaleFactor = 1.0 / (SLACK * ASy); + if (ScaleFactor < 0.5) + { for (I = Size; I > 0; I--) T[I] *= ScaleFactor; + ASy = 1.0 / SLACK; + E *= ScaleFactor; + } + +/* Compute infinity-norm of T for O'Leary's estimate. */ + for (MaxY = 0.0, I = Size; I > 0; I--) + if (MaxY < ABS(T[I])) MaxY = ABS(T[I]); + +/* + * Part 2. A* z = y where the * represents the transpose. + * Recall that A = LU implies A* = U* L*. + */ + +/* Forward elimination, U* v = y. */ + for (I = 1; I <= Size; I++) + { pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { T[pElement->Col] -= T[I] * pElement->Real; + pElement = pElement->NextInRow; + } + if (ABS(T[I]) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) ); + for (K = Size; K > 0; K--) T[K] *= ScaleFactor; + ASy *= ScaleFactor; + } + } + +/* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK.. */ + for (ASv = 0.0, I = Size; I > 0; I--) ASv += ABS(T[I]); + ScaleFactor = 1.0 / (SLACK * ASv); + if (ScaleFactor < 0.5) + { for (I = Size; I > 0; I--) T[I] *= ScaleFactor; + ASy *= ScaleFactor; + } + +/* Backward Substitution, L* z = v. */ + for (I = Size; I >= 1; I--) + { pPivot = Matrix->Diag[I]; + pElement = pPivot->NextInCol; + while (pElement != NULL) + { T[I] -= pElement->Real * T[pElement->Row]; + pElement = pElement->NextInCol; + } + T[I] *= pPivot->Real; + if (ABS(T[I]) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) ); + for (K = Size; K > 0; K--) T[K] *= ScaleFactor; + ASy *= ScaleFactor; + } + } + +/* Compute 1-norm of T, which now contains z. */ + for (ASz = 0.0, I = Size; I > 0; I--) ASz += ABS(T[I]); + +#if NOT spCOMPLEX + FREE( Tm ); +#endif + + Linpack = ASy / ASz; + OLeary = E / MaxY; + InvNormOfInverse = MIN( Linpack, OLeary ); + return InvNormOfInverse / NormOfMatrix; +#endif /* REAL */ +} + + + + + +#if spCOMPLEX +/* + * ESTIMATE CONDITION NUMBER + * + * Complex version of spCondition(). + * + * >>> Returns: + * The reciprocal of the condition number. + * + * >>> Arguments: + * Matrix <input> (MatrixPtr) + * Pointer to the matrix. + * NormOfMatrix <input> (RealNumber) + * The L-infinity norm of the unfactored matrix as computed by + * spNorm(). + * pError <output> (int *) + * Used to return error code. + * + * >>> Possible errors: + * spNO_MEMORY + */ + +static RealNumber +ComplexCondition( + MatrixPtr Matrix, + RealNumber NormOfMatrix, + 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; + +/* Begin `ComplexCondition'. */ + + Size = Matrix->Size; + T = (ComplexVector)Matrix->Intermediate; + Tm = ALLOC( ComplexNumber, Size+1 ); + if (Tm == NULL) + { *pError = spNO_MEMORY; + return 0.0; + } + for (I = Size; I > 0; I--) T[I].Real = T[I].Imag = 0.0; + +/* + * Part 1. Ay = e. + * Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign + * chosen to maximize the size of w in Lw = e. Since the terms in w can + * get very large, scaling is used to avoid overflow. + */ + +/* Forward elimination. Solves Lw = e while choosing e. */ + E = 1.0; + for (I = 1; I <= Size; I++) + { pPivot = Matrix->Diag[I]; + if (T[I].Real < 0.0) Em = -E; else Em = E; + Wm = T[I]; + Wm.Real += Em; + ASm = CMPLX_1_NORM( Wm ); + CMPLX_MULT_ASSIGN( Wm, *pPivot ); + if (CMPLX_1_NORM(Wm) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(Wm) ); + for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); + E *= ScaleFactor; + Em *= ScaleFactor; + ASm *= ScaleFactor; + SCLR_MULT_ASSIGN( Wm, ScaleFactor ); + } + Wp = T[I]; + Wp.Real -= Em; + ASp = CMPLX_1_NORM( Wp ); + CMPLX_MULT_ASSIGN( Wp, *pPivot ); + +/* Update T for both values of W, minus value is placed in Tm. */ + pElement = pPivot->NextInCol; + while (pElement != NULL) + { Row = pElement->Row; + /* Cmplx expr: Tm[Row] = T[Row] - (Wp * *pElement). */ + CMPLX_MULT_SUBT( Tm[Row], Wm, *pElement, T[Row] ); + /* Cmplx expr: T[Row] -= Wp * *pElement. */ + CMPLX_MULT_SUBT_ASSIGN( T[Row], Wm, *pElement ); + ASp += CMPLX_1_NORM(T[Row]); + ASm += CMPLX_1_NORM(Tm[Row]); + pElement = pElement->NextInCol; + } + +/* If minus value causes more growth, overwrite T with its values. */ + if (ASm > ASp) + { T[I] = Wm; + pElement = pPivot->NextInCol; + while (pElement != NULL) + { T[pElement->Row] = Tm[pElement->Row]; + pElement = pElement->NextInCol; + } + } + else T[I] = Wp; + } + +/* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK.. */ + for (ASw = 0.0, I = Size; I > 0; I--) ASw += CMPLX_1_NORM(T[I]); + ScaleFactor = 1.0 / (SLACK * ASw); + if (ScaleFactor < 0.5) + { for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor ); + E *= ScaleFactor; + } + +/* Backward Substitution. Solves Uy = w.*/ + for (I = Size; I >= 1; I--) + { pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { /* Cmplx expr: T[I] -= T[pElement->Col] * *pElement. */ + CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Col], *pElement ); + pElement = pElement->NextInRow; + } + if (CMPLX_1_NORM(T[I]) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) ); + for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); + E *= ScaleFactor; + } + } + +/* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK.. */ + for (ASy = 0.0, I = Size; I > 0; I--) ASy += CMPLX_1_NORM(T[I]); + ScaleFactor = 1.0 / (SLACK * ASy); + if (ScaleFactor < 0.5) + { for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor ); + ASy = 1.0 / SLACK; + E *= ScaleFactor; + } + +/* Compute infinity-norm of T for O'Leary's estimate. */ + for (MaxY = 0.0, I = Size; I > 0; I--) + if (MaxY < CMPLX_1_NORM(T[I])) MaxY = CMPLX_1_NORM(T[I]); + +/* + * Part 2. A* z = y where the * represents the transpose. + * Recall that A = LU implies A* = U* L*. + */ + +/* Forward elimination, U* v = y. */ + for (I = 1; I <= Size; I++) + { pElement = Matrix->Diag[I]->NextInRow; + while (pElement != NULL) + { /* Cmplx expr: T[pElement->Col] -= T[I] * *pElement. */ + CMPLX_MULT_SUBT_ASSIGN( T[pElement->Col], T[I], *pElement ); + pElement = pElement->NextInRow; + } + if (CMPLX_1_NORM(T[I]) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) ); + for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); + ASy *= ScaleFactor; + } + } + +/* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK.. */ + for (ASv = 0.0, I = Size; I > 0; I--) ASv += CMPLX_1_NORM(T[I]); + ScaleFactor = 1.0 / (SLACK * ASv); + if (ScaleFactor < 0.5) + { for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor ); + ASy *= ScaleFactor; + } + +/* Backward Substitution, L* z = v. */ + for (I = Size; I >= 1; I--) + { pPivot = Matrix->Diag[I]; + pElement = pPivot->NextInCol; + while (pElement != NULL) + { /* Cmplx expr: T[I] -= T[pElement->Row] * *pElement. */ + CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Row], *pElement ); + pElement = pElement->NextInCol; + } + CMPLX_MULT_ASSIGN( T[I], *pPivot ); + if (CMPLX_1_NORM(T[I]) > SLACK) + { ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) ); + for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor ); + ASy *= ScaleFactor; + } + } + +/* Compute 1-norm of T, which now contains z. */ + for (ASz = 0.0, I = Size; I > 0; I--) ASz += CMPLX_1_NORM(T[I]); + + FREE( Tm ); + + Linpack = ASy / ASz; + OLeary = E / MaxY; + InvNormOfInverse = MIN( Linpack, OLeary ); + return InvNormOfInverse / NormOfMatrix; +} +#endif /* spCOMPLEX */ + + + + + +/*! + * Computes the L-infinity norm of an unfactored matrix. It is a fatal + * error to pass this routine a factored matrix. + * + * \return + * The largest absolute row sum of matrix. + * + * \param eMatrix + * Pointer to the matrix. + */ + +spREAL +spNorm( spMatrix eMatrix ) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register int I; +RealNumber Max = 0.0, AbsRowSum; + +/* Begin `spNorm'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_NOT_FACTORED( Matrix ); + if (NOT Matrix->RowsLinked) spcLinkRows( Matrix ); + +/* Compute row sums. */ +#if REAL + if (NOT Matrix->Complex) + { for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInRow[I]; + AbsRowSum = 0.0; + while (pElement != NULL) + { AbsRowSum += ABS( pElement->Real ); + pElement = pElement->NextInRow; + } + if (Max < AbsRowSum) Max = AbsRowSum; + } + } +#endif +#if spCOMPLEX + if (Matrix->Complex) + { for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInRow[I]; + AbsRowSum = 0.0; + while (pElement != NULL) + { AbsRowSum += CMPLX_1_NORM( *pElement ); + pElement = pElement->NextInRow; + } + if (Max < AbsRowSum) Max = AbsRowSum; + } + } +#endif + return Max; +} +#endif /* CONDITION */ + + + + + + +#if STABILITY +/*! + * This routine, along with spRoundoff(), are used to gauge the stability of a + * factorization. If the factorization is determined to be too unstable, + * then the matrix should be reordered. The routines compute quantities + * that are needed in the computation of a bound on the error attributed + * to any one element in the matrix during the factorization. In other + * words, there is a matrix \f$ E = [e_{ij}] \f$ of error terms such that + * \f$ A+E = LU \f$. This routine finds a bound on \f$ |e_{ij}| \f$. + * Erisman & Reid [1] showed that \f$ |e_{ij}| < 3.01 u \rho m_{ij} \f$, + * where \f$ u \f$ is the machine rounding unit, + * \f$ \rho = \max a_{ij} \f$ where the max is taken over every row \f$ i \f$, + * column \f$ j \f$, and step \f$ k \f$, and \f$ m_{ij} \f$ is the number + * of multiplications required in the computation of \f$ l_{ij} \f$ if + * \f$ i > j \f$ or \f$ u_{ij} \f$ otherwise. Barlow [2] showed that + * \f$ \rho < \max_i || l_i ||_p \max_j || u_j ||_q \f$ where + * \f$ 1/p + 1/q = 1 \f$. + * + * spLargestElement() finds the magnitude on the largest element in the + * matrix. If the matrix has not yet been factored, the largest + * element is found by direct search. If the matrix is factored, a + * bound on the largest element in any of the reduced submatrices is + * computed using Barlow with \f$ p = \infty \f$ and \f$ q = 1 \f$. + * The ratio of these + * two numbers is the growth, which can be used to determine if the + * pivoting order is adequate. A large growth implies that + * considerable error has been made in the factorization and that it + * is probably a good idea to reorder the matrix. If a large growth + * in encountered after using spFactor(), reconstruct the matrix and + * refactor using spOrderAndFactor(). If a large growth is + * encountered after using spOrderAndFactor(), refactor using + * spOrderAndFactor() with the pivot threshold increased, say to 0.1. + * + * Using only the size of the matrix as an upper bound on \f$ m_{ij} \f$ and + * Barlow's bound, the user can estimate the size of the matrix error + * terms \f$ e_{ij} \f$ using the bound of Erisman and Reid. spRoundoff() + * computes a tighter bound (with more work) based on work by Gear + * [3], \f$ |e_{ij}| < 1.01 u \rho (t c^3 + (1 + t)c^2) \f$ where + * \f$ t \f$ is the threshold and \f$ c \f$ is the maximum number of + * off-diagonal elements in any row of \f$ L \f$. The expensive part + * of computing this bound is determining the maximum number of + * off-diagonals in \f$ L \f$, which changes + * only when the order of the matrix changes. This number is computed + * and saved, and only recomputed if the matrix is reordered. + * + * [1] A. M. Erisman, J. K. Reid. Monitoring the stability of the + * triangular factorization of a sparse matrix. Numerische + * Mathematik. Vol. 22, No. 3, 1974, pp 183-186. + * + * [2] J. L. Barlow. A note on monitoring the stability of triangular + * decomposition of sparse matrices. "SIAM Journal of Scientific + * and Statistical Computing." Vol. 7, No. 1, January 1986, pp 166-168. + * + * [3] I. S. Duff, A. M. Erisman, J. K. Reid. "Direct Methods for Sparse + * Matrices." Oxford 1986. pp 99. + * + * \return + * If matrix is not factored, returns the magnitude of the largest element in + * the matrix. If the matrix is factored, a bound on the magnitude of the + * largest element in any of the reduced submatrices is returned. + * + * \param eMatrix + * Pointer to the matrix. + */ + +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; + +/* Begin `spLargestElement'. */ + ASSERT_IS_SPARSE( Matrix ); + +#if REAL + if (Matrix->Factored AND NOT Matrix->Complex) + { if (Matrix->Error == spSINGULAR) return 0.0; + +/* Find the bound on the size of the largest element over all factorization. */ + for (I = 1; I <= Matrix->Size; I++) + { pDiag = Matrix->Diag[I]; + +/* Lower triangular matrix. */ + Pivot = 1.0 / pDiag->Real; + Mag = ABS( Pivot ); + if (Mag > MaxRow) MaxRow = Mag; + pElement = Matrix->FirstInRow[I]; + while (pElement != pDiag) + { Mag = ABS( pElement->Real ); + if (Mag > MaxRow) MaxRow = Mag; + pElement = pElement->NextInRow; + } + +/* Upper triangular matrix. */ + pElement = Matrix->FirstInCol[I]; + AbsColSum = 1.0; /* Diagonal of U is unity. */ + while (pElement != pDiag) + { AbsColSum += ABS( pElement->Real ); + pElement = pElement->NextInCol; + } + if (AbsColSum > MaxCol) MaxCol = AbsColSum; + } + } + else if (NOT Matrix->Complex) + { for (I = 1; I <= Matrix->Size; I++) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { Mag = ABS( pElement->Real ); + if (Mag > Max) Max = Mag; + pElement = pElement->NextInCol; + } + } + return Max; + } +#endif +#if spCOMPLEX + if (Matrix->Factored AND Matrix->Complex) + { if (Matrix->Error == spSINGULAR) return 0.0; + +/* Find the bound on the size of the largest element over all factorization. */ + for (I = 1; I <= Matrix->Size; I++) + { pDiag = Matrix->Diag[I]; + +/* Lower triangular matrix. */ + CMPLX_RECIPROCAL( cPivot, *pDiag ); + Mag = CMPLX_INF_NORM( cPivot ); + if (Mag > MaxRow) MaxRow = Mag; + pElement = Matrix->FirstInRow[I]; + while (pElement != pDiag) + { Mag = CMPLX_INF_NORM( *pElement ); + if (Mag > MaxRow) MaxRow = Mag; + pElement = pElement->NextInRow; + } + +/* Upper triangular matrix. */ + pElement = Matrix->FirstInCol[I]; + AbsColSum = 1.0; /* Diagonal of U is unity. */ + while (pElement != pDiag) + { AbsColSum += CMPLX_INF_NORM( *pElement ); + pElement = pElement->NextInCol; + } + if (AbsColSum > MaxCol) MaxCol = AbsColSum; + } + } + else if (Matrix->Complex) + { for (I = 1; I <= Matrix->Size; I++) + { pElement = Matrix->FirstInCol[I]; + while (pElement != NULL) + { Mag = CMPLX_INF_NORM( *pElement ); + if (Mag > Max) Max = Mag; + pElement = pElement->NextInCol; + } + } + return Max; + } +#endif + return MaxRow * MaxCol; +} + + + + +/*! + * This routine, along with spLargestElement(), are used to gauge the + * stability of a factorization. See description of spLargestElement() + * for more information. + * + * \return + * Returns a bound on the magnitude of the largest element in + * \f$ E = A - LU \f$. + * + * \param eMatrix + * Pointer to the matrix. + * \param Rho + * The bound on the magnitude of the largest element in any of the + * reduced submatrices. This is the number computed by the function + * spLargestElement() when given a factored matrix. If this number is + * negative, the bound will be computed automatically. + */ + +spREAL +spRoundoff( + spMatrix eMatrix, + spREAL Rho +) +{ +MatrixPtr Matrix = (MatrixPtr)eMatrix; +register ElementPtr pElement; +register int Count, I, MaxCount = 0; +RealNumber Reid, Gear; + +/* Begin `spRoundoff'. */ + ASSERT_IS_SPARSE( Matrix ); + ASSERT_NO_ERRORS( Matrix ); + ASSERT_IS_FACTORED( Matrix ); + +/* Compute Barlow's bound if it is not given. */ + if (Rho < 0.0) Rho = spLargestElement( eMatrix ); + +/* Find the maximum number of off-diagonals in L if not previously computed. */ + if (Matrix->MaxRowCountInLowerTri < 0) + { for (I = Matrix->Size; I > 0; I--) + { pElement = Matrix->FirstInRow[I]; + Count = 0; + while (pElement->Col < I) + { Count++; + pElement = pElement->NextInRow; + } + if (Count > MaxCount) MaxCount = Count; + } + Matrix->MaxRowCountInLowerTri = MaxCount; + } + else MaxCount = Matrix->MaxRowCountInLowerTri; + +/* Compute error bound. */ + Gear = 1.01*((MaxCount + 1) * Matrix->RelThreshold + 1.0) * SQR(MaxCount); + Reid = 3.01 * Matrix->Size; + + if (Gear < Reid) + return (MACHINE_RESOLUTION * Rho * Gear); + else + return (MACHINE_RESOLUTION * Rho * Reid); +} +#endif + + + + + + + +#if DOCUMENTATION +/*! + * This routine prints a short message describing the error error state + * of sparse. No message is produced if there is no error. + * The error state is cleared. + * + * \param eMatrix + * Matrix for which the error message is to be printed. + * \param Stream + * Stream to which the error message is to be printed. + * \param Originator + * Name of originator of error message. If NULL, `sparse' is used. + * If zero-length string, no originator is printed. + */ + +void +spErrorMessage( + spMatrix eMatrix, + FILE *Stream, + char *Originator +) +{ +int Row, Col, Error; + +/* Begin `spErrorMessage'. */ + if (eMatrix == NULL) + Error = spNO_MEMORY; + else + { ASSERT_IS_SPARSE( (MatrixPtr)eMatrix ); + Error = ((MatrixPtr)eMatrix)->Error; + } + + if (Error == spOKAY) return; + if (Originator == NULL) Originator = "sparse"; + if (Stream == NULL) Stream = stderr; + if (Originator[0] != '\0') fprintf( Stream, "%s: ", Originator ); + if (Error >= spFATAL) + fprintf( Stream, "fatal error: "); + else + fprintf( Stream, "warning: "); +/* + * Print particular error message. + * Do not use switch statement because error codes may not be unique. + */ + if (Error == spPANIC) + fprintf( Stream, "Sparse called improperly.\n"); + else if (Error == spNO_MEMORY) + fprintf( Stream, "insufficient memory available.\n"); + else if (Error == spMANGLED) + fprintf( Stream, "matrix is mangled.\n"); + else if (Error == spSINGULAR) + { spWhereSingular( eMatrix, &Row, &Col ); + fprintf( Stream, "singular matrix detected at row %d and column %d.\n", + Row, Col); + } + else if (Error == spZERO_DIAG) + { spWhereSingular( eMatrix, &Row, &Col ); + fprintf( Stream, "zero diagonal detected at row %d and column %d.\n", + Row, Col); + } + else if (Error == spSMALL_PIVOT) + { fprintf( Stream, + "unable to find a pivot that is larger than absolute threshold.\n"); + } + else ABORT(); + + ((MatrixPtr)eMatrix)->Error = spOKAY; + return; +} +#endif /* DOCUMENTATION */ Added: trunk/math/m.h =================================================================== --- trunk/math/m.h (rev 0) +++ trunk/math/m.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,30 @@ +/* Public domain */ + +#ifndef _AGAR_MATH_M_H_ +#define _AGAR_MATH_M_H_ + +#include <agar/math/m_begin.h> + +#define spREAL M_Real + +#include <agar/math/m_math.h> +#include <agar/math/m_int_vector.h> +#include <agar/math/m_complex.h> +#include <agar/math/m_vector.h> +#include <agar/math/m_matrix.h> +#include <agar/math/m_quaternion.h> +#include <agar/math/m_coordinates.h> +#include <agar/math/m_color.h> +#include <agar/math/m_geometry.h> +#include <agar/math/m_gaussj.h> +#include <agar/math/m_lu.h> + +#ifdef _M_INTERNAL +# undef _ +# undef N_ +# define _(s) (s) +# define N_(s) (s) +#endif + +#include <agar/math/m_close.h> +#endif /* _AGAR_MATH_M_H_ */ Added: trunk/math/m_begin.h =================================================================== --- trunk/math/m_begin.h (rev 0) +++ trunk/math/m_begin.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,47 @@ +/* Public domain */ +/* + * Definitions internal to our headers + */ + +#ifdef SINGLE_PRECISION +# define _M_UNDEFINED_SINGLE_PRECISION +# undef SINGLE_PRECISION +#endif +#ifdef DOUBLE_PRECISION +# define _M_UNDEFINED_DOUBLE_PRECISION +# undef DOUBLE_PRECISION +#endif +#ifdef QUAD_PRECISION +# define _M_UNDEFINED_QUAD_PRECISION +# undef QUAD_PRECISION +#endif +#include <agar/config/single_precision.h> +#include <agar/config/quad_precision.h> +#include <agar/config/double_precision.h> + +#undef _MK_HAVE_UNSIGNED_TYPEDEFS +#include <agar/config/_mk_have_unsigned_typedefs.h> +#ifndef _MK_HAVE_UNSIGNED_TYPEDEFS +# define _MK_HAVE_UNSIGNED_TYPEDEFS +# define Uint unsigned int +# define Uchar unsigned char +# define Ulong unsigned long +#endif +#if !defined(__BEGIN_DECLS) || !defined(__END_DECLS) +# define _M_DEFINED_CDECLS +# if defined(__cplusplus) +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +# else +# define __BEGIN_DECLS +# define __END_DECLS +# endif +#endif + +/* Definitions internal to the math library */ +#ifdef _M_INTERNAL +# undef MAX +# define MAX(h,i) ((h) > (i) ? (h) : (i)) +# undef MIN +# define MIN(l,o) ((l) < (o) ? (l) : (o)) +#endif /* _M_INTERNAL */ Added: trunk/math/m_bitstring.h =================================================================== --- trunk/math/m_bitstring.h (rev 0) +++ trunk/math/m_bitstring.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Paul Vixie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 + */ + +#ifndef _AGAR_MATH_M_BITSTRING_H_ +#define _AGAR_MATH_M_BITSTRING_H_ + +/* modified for SV/AT and bitstring bugfix by M.R.Murphy, 11oct91 + * bitstr_size changed gratuitously, but shorter + * bit_alloc spelling error fixed + * the following were efficient, but didn't work, they've been made to + * work, but are no longer as efficient :-) + * bit_nclear, bit_nset, bit_ffc, bit_ffs + */ +typedef unsigned char bitstr_t; + +/* internal macros */ + /* byte of the bitstring bit is in */ +#define _bit_byte(bit) \ + ((bit) >> 3) + + /* mask for the bit within its byte */ +#define _bit_mask(bit) \ + (1 << ((bit)&0x7)) + +/* external macros */ + /* bytes in a bitstring of nbits bits */ +#define bitstr_size(nbits) \ + (((nbits) + 7) >> 3) + + /* allocate a bitstring */ +#define bit_alloc(nbits) \ + (bitstr_t *)calloc((size_t)bitstr_size(nbits), sizeof(bitstr_t)) + + /* allocate a bitstring on the stack */ +#define bit_decl(name, nbits) \ + ((name)[bitstr_size(nbits)]) + + /* is bit N of bitstring name set? */ +#define bit_test(name, bit) \ + ((name)[_bit_byte(bit)] & _bit_mask(bit)) + + /* set bit N of bitstring name */ +#define bit_set(name, bit) \ + ((name)[_bit_byte(bit)] |= _bit_mask(bit)) + + /* clear bit N of bitstring name */ +#define bit_clear(name, bit) \ + ((name)[_bit_byte(bit)] &= ~_bit_mask(bit)) + + /* clear bits start ... stop in bitstring */ +#define bit_nclear(name, start, stop) do { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + while (_start <= _stop) { \ + bit_clear(_name, _start); \ + _start++; \ + } \ +} while(0) + + /* set bits start ... stop in bitstring */ +#define bit_nset(name, start, stop) do { \ + register bitstr_t *_name = name; \ + register int _start = start, _stop = stop; \ + while (_start <= _stop) { \ + bit_set(_name, _start); \ + _start++; \ + } \ +} while(0) + + /* find first bit clear in name */ +#define bit_ffc(name, nbits, value) do { \ + register bitstr_t *_name = name; \ + register int _bit, _nbits = nbits, _value = -1; \ + for (_bit = 0; _bit < _nbits; ++_bit) \ + if (!bit_test(_name, _bit)) { \ + _value = _bit; \ + break; \ + } \ + *(value) = _value; \ +} while(0) + + /* find first bit set in name */ +#define bit_ffs(name, nbits, value) do { \ + register bitstr_t *_name = name; \ + register int _bit, _nbits = nbits, _value = -1; \ + for (_bit = 0; _bit < _nbits; ++_bit) \ + if (bit_test(_name, _bit)) { \ + _value = _bit; \ + break; \ + } \ + *(value) = _value; \ +} while(0) + +#endif /* !_AGAR_MATH_M_BITSTRING_H_ */ Added: trunk/math/m_circle.c =================================================================== --- trunk/math/m_circle.c (rev 0) +++ trunk/math/m_circle.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2008 Hypertriton, Inc. <http://hypertriton.com/> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +/* + * Routines related to circles. + */ + +#include <core/core.h> +#include "m.h" + +M_Circle2 +M_CircleRead2(AG_DataSource *ds) +{ + M_Circle2 C; + + C.p = M_ReadVector2(ds); + C.r = M_ReadReal(ds); + return (C); +} +M_Circle3 +M_CircleRead3(AG_DataSource *ds) +{ + M_Circle3 C; + + C.p = M_ReadVector3(ds); + C.r = M_ReadReal(ds); + return (C); +} + +void +M_CircleWrite2(AG_DataSource *ds, M_Circle2 *C) +{ + M_WriteVector2(ds, &C->p); + M_WriteReal(ds, C->r); +} +void +M_CircleWrite3(AG_DataSource *ds, M_Circle3 *C) +{ + M_WriteVector3(ds, &C->p); + M_WriteReal(ds, C->r); +} + +/* Create a line from a point, direction vector and length. */ +M_Circle2 +M_CircleFromPt(M_Vector2 p, M_Real r) +{ + M_Circle2 C; + + C.p = p; + C.r = r; + return (C); +} + +/* Compute minimal distance from a circle to a point p. */ +M_Real +M_CirclePointDistance2(M_Circle2 C, M_Vector2 p) +{ + M_Vector2 vR = M_VecSub2(p, C.p); + M_Real theta = Atan2(vR.y, vR.x); + + return M_VecDistance2(p, M_VECTOR2(C.p.x + C.r*Cos(theta), + C.p.y + C.r*Sin(theta))); +} + +/* Compute the intersection of two circles. */ +M_GeomSet2 +M_IntersectCircleCircle2(M_Circle2 C1, M_Circle2 C2) +{ + M_GeomSet2 Sint = M_GEOM_SET_EMPTY; + M_Real d12 = M_VecDistance2(C1.p, C2.p); + M_Real a, h, b; + M_Vector2 p; + M_Geom2 G1, G2; + + if (Fabs(C1.p.x - C2.p.x) <= M_MACHEP && + Fabs(C1.p.x - C2.p.x) <= M_MACHEP && + Fabs(C1.r - C2.r) <= M_MACHEP) { + G1.type = M_CIRCLE; + G1.g_circle = C1; + M_GeomSetAdd2(&Sint, &G1); + return (Sint); + } + + if (d12 > (C1.r + C2.r) || + d12 < Fabs(C1.r - C2.r)) { + return (Sint); + } + + a = (C1.r*C1.r - C2.r*C2.r + d12*d12) / (2.0*d12); + h = Sqrt(C1.r*C1.r - a*a); + p = M_VecLERP2(C1.p, C2.p, a/d12); + b = h/d12; + + G1.type = M_POINT; + G1.g_point.x = p.x - b*(C2.p.y - C1.p.y); + G1.g_point.y = p.y + b*(C2.p.x - C1.p.x); + G2.type = M_POINT; + G2.g_point.x = p.x + b*(C2.p.y - C1.p.y); + G2.g_point.y = p.y - b*(C2.p.x - C1.p.x); + + M_GeomSetAdd2(&Sint, &G1); + if (M_VecDistance2(G1.g_point, G2.g_point) > M_MACHEP) { + M_GeomSetAdd2(&Sint, &G2); + } + return (Sint); +} + +/* Compute the intersection of a circle and a line. */ +M_GeomSet2 +M_IntersectCircleLine2(M_Circle2 C, M_Line2 L) +{ + M_GeomSet2 Sint = M_GEOM_SET_EMPTY; + M_Vector2 p1 = M_LineFirstPt2(L); + M_Vector2 p2 = M_LineSecondPt2(L); + M_Vector2 p3 = C.p; + M_Real a, b, c, det; + M_Geom2 G; + + a = (p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y)*(p2.y - p1.y); + b = 2.0*( (p2.x - p1.x)*(p1.x - p3.x) + (p2.y - p1.y)*(p1.y - p3.y) ); + c = p3.x*p3.x + p3.y*p3.y + p1.x*p1.x + p1.y*p1.y - + 2.0*(p3.x*p1.x + p3.y*p1.y) - C.r*C.r; + det = b*b - 4.0*a*c; + + if (det < 0.0) { + return (Sint); + } else if (det == 0.0) { + /* TODO Tangent! */ + return (Sint); + } else { + M_Real e = Sqrt(det); + M_Real u1 = (-b + e) / (2.0*a); + M_Real u2 = (-b - e) / (2.0*a); + + if ((u1 < 0.0 || u1 > 1.0) && + (u2 < 0.0 || u2 > 1.0)) { + if ((u1 < 0.0 && u2 < 0.0) || + (u1 > 1.0 && u2 > 1.0)) { + return (Sint); + } else { + if (u1 >= 0.0 && u1 <= 1.0) { + G.type = M_POINT; + G.g_point = M_VecLERP2(p1,p2,u1); + M_GeomSetAdd2(&Sint, &G); + } + if (u2 >= 0.0 && u2 <= 1.0) { + G.type = M_POINT; + G.g_point = M_VecLERP2(p1,p2,u2); + M_GeomSetAdd2(&Sint, &G); + } + } + } else { + if (u1 >= 0.0 && u1 <= 1.0) { + G.type = M_POINT; + G.g_point = M_VecLERP2(p1,p2,u1); + M_GeomSetAdd2(&Sint, &G); + } + if (u2 >= 0.0 && u2 <= 1.0) { + G.type = M_POINT; + G.g_point = M_VecLERP2(p1,p2,u2); + M_GeomSetAdd2(&Sint, &G); + } + } + } + return (Sint); +} Added: trunk/math/m_circle.h =================================================================== --- trunk/math/m_circle.h (rev 0) +++ trunk/math/m_circle.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,13 @@ +/* Public domain */ + +__BEGIN_DECLS +M_Circle2 M_CircleRead2(AG_DataSource *); +M_Circle3 M_CircleRead3(AG_DataSource *); +void M_CircleWrite2(AG_DataSource *, M_Circle2 *); +void M_CircleWrite3(AG_DataSource *, M_Circle3 *); + +M_Circle2 M_CircleFromPt(M_Vector2, M_Real); +M_Real M_CirclePointDistance2(M_Circle2, M_Vector2); +M_GeomSet2 M_IntersectCircleCircle2(M_Circle2, M_Circle2); +M_GeomSet2 M_IntersectCircleLine2(M_Circle2, M_Line2); +__END_DECLS Added: trunk/math/m_close.h =================================================================== --- trunk/math/m_close.h (rev 0) +++ trunk/math/m_close.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,30 @@ +/* Public domain */ +/* + * Clean up internal header definitions. + */ +#ifndef _M_INTERNAL +# ifdef _MK_HAVE_UNSIGNED_TYPEDEFS +# undef _MK_HAVE_UNSIGNED_TYPEDEFS +# undef Uint +# undef Uchar +# undef Ulong +# endif +# ifdef _M_DEFINED_CDECLS +# undef _M_DEFINED_CDECLS +# undef __BEGIN_DECLS +# undef __END_DECLS +# endif + +# undef SINGLE_PRECISION +# undef DOUBLE_PRECISION +# undef QUAD_PRECISION +# ifdef _M_UNDEFINED_SINGLE_PRECISION +# define SINGLE_PRECISION +# endif +# ifdef _M_UNDEFINED_DOUBLE_PRECISION +# define DOUBLE_PRECISION +# endif +# ifdef _M_UNDEFINED_QUAD_PRECISION +# define QUAD_PRECISION +# endif +#endif /* !_M_INTERNAL */ Added: trunk/math/m_color.c =================================================================== --- trunk/math/m_color.c (rev 0) +++ trunk/math/m_color.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2008 Hypertriton, Inc. <http://hypertriton.com/> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +/* + * Color-related operations and conversions between different color spaces. + */ + +#include <core/core.h> +#include "m.h" + +M_Color +M_ColorHSVA(M_Real h, M_Real s, M_Real v, M_Real a) +{ + M_Color C; + M_Real var[3], hv; + int iv; + + if (M_MACHZERO(s)) { /* Gray */ + C.r = v; + C.g = v; + C.b = v; + C.a = a; + return (C); + } + + hv = h/60.0; + iv = (int)Floor(hv); + var[0] = v*(1.0F - s); + var[1] = v*(1.0F - s*(hv-iv)); + var[2] = v*(1.0F - s*(1.0-hv-iv)); + + switch (iv) { + case 0: C.r = v; C.g = var[2]; C.b = var[0]; break; + case 1: C.r = var[1]; C.g = v; C.b = var[0]; break; + case 2: C.r = var[0]; C.g = v; C.b = var[2]; break; + case 3: C.r = var[0]; C.g = var[1]; C.b = v; break; + case 4: C.r = var[2]; C.g = var[0]; C.b = v; break; + default: C.r = v; C.g = var[0]; C.b = var[1]; break; + } + return (C); +} + +M_Color +M_ReadColor(AG_DataSource *buf) +{ + M_Color C; + + AG_ReadUint8(buf); /* Expn: type */ + C.r = M_ReadReal(buf); + C.g = M_ReadReal(buf); + C.b = M_ReadReal(buf); + C.a = M_ReadReal(buf); + return (C); +} + +void +M_WriteColor(AG_DataSource *buf, const M_Color *C) +{ + AG_WriteUint8(buf, 0); /* Expn: type */ + M_WriteReal(buf, C->r); + M_WriteReal(buf, C->g); + M_WriteReal(buf, C->b); + M_WriteReal(buf, C->a); +} + +void +M_ColorTo4fv(const M_Color *C, float *v) +{ +#ifdef SINGLE_PRECISION + memcpy(v, C, 4*sizeof(float)); +#else + v[0] = (float)C->r; + v[1] = (float)C->g; + v[2] = (float)C->b; + v[3] = (float)C->a; +#endif +} + +void +M_ColorTo4dv(const M_Color *C, double *v) +{ +#ifdef DOUBLE_PRECISION + memcpy(v, C, 4*sizeof(double)); +#else + /* TODO SIMD */ + v[0] = (double)C->r; + v[1] = (double)C->g; + v[2] = (double)C->b; + v[3] = (double)C->a; +#endif +} Added: trunk/math/m_color.h =================================================================== --- trunk/math/m_color.h (rev 0) +++ trunk/math/m_color.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,39 @@ +/* Public domain */ + +__BEGIN_DECLS +static __inline__ M_Color +M_ColorRGB(M_Real r, M_Real g, M_Real b) +{ + M_Color c; + + c.r = r; + c.g = g; + c.b = b; + c.a = 1.0; + return (c); +} + +static __inline__ M_Color +M_ColorRGBA(M_Real r, M_Real g, M_Real b, M_Real a) +{ + M_Color c; + + c.r = r; + c.g = g; + c.b = b; + c.a = a; + return (c); +} + +void M_WriteColor(AG_DataSource *, const M_Color *); +M_Color M_ReadColor(AG_DataSource *); +M_Color M_ColorRGB(M_Real, M_Real, M_Real); +M_Color M_ColorRGBA(M_Real, M_Real, M_Real, M_Real); +M_Color M_ColorHSVA(M_Real, M_Real, M_Real, M_Real); +void M_ColorTo4fv(const M_Color *, float *); +void M_ColorTo4dv(const M_Color *, double *); +#define M_ColorHSV(h,s,v) M_ColorHSVA((h),(s),(v),1.0) +#define M_ColorBlack() M_ColorRGB(0.0, 0.0, 0.0) +#define M_ColorWhite() M_ColorRGB(1.0, 1.0, 1.0) +#define M_ColorGray(x) M_ColorRGB((x),(x),(x)) +__END_DECLS Added: trunk/math/m_complex.c =================================================================== --- trunk/math/m_complex.c (rev 0) +++ trunk/math/m_complex.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,458 @@ +/* Public domain */ +/* + * Complex number arithmetic. + */ + +#include <core/core.h> +#include "m.h" + +static M_Real Ctans(M_Complex); + +/* + * Compute the complex absolute value (sqrt(r^2 + i^2)). If the magnitude + * of either the real or imaginary parts are outside half of the precision + * range, both parts are rescaled prior to squaring. + */ +M_Real +M_ComplexAbs(M_Complex z) +{ + M_Real x, y, b, rPart, iPart; + int ex, ey, e; + + if (z.r == M_INFINITY || z.i == M_INFINITY || + z.r == -M_INFINITY || z.i == -M_INFINITY) { + return (M_INFINITY); + } + if ((rPart = Fabs(z.r)) == 0.0) { return (iPart); } + if ((iPart = Fabs(z.i)) == 0.0) { return (rPart); } + x = Frexp(rPart, &ex); + y = Frexp(iPart, &ey); + e = ex - ey; + if (e > M_PRECISION_2) { return (rPart); } + if (e < -M_PRECISION_2) { return (iPart); } + e = (ex + ey)/2; + x = Ldexp(rPart, -e); + y = Ldexp(iPart, -e); + b = Sqrt(x*x + y*y); + y = Frexp(b, &ey); + ey = e + ey; + if (ey < -M_EXPMIN) { return (0.0); } + if (ey > M_EXPMAX) { return (M_INFINITY); } + b = Ldexp(b, e); + return (b); +} + +/* Divide two complex numbers. */ +M_Complex +M_ComplexDiv(M_Complex a, M_Complex b) +{ + M_Real y, p, q, w; + M_Complex z; + + y = a.r*a.r + a.i*a.i; + p = b.r*a.r + b.i*a.i; + q = b.i*a.r - b.r*a.i; + if (y < 1.0) { + w = M_INFINITY*y; + if (Fabs(p) > w || Fabs(q) > w || y == 0.0) { + z.r = M_INFINITY; + z.i = M_INFINITY; + return (z); + } + } + z.r = p/y; + z.i = q/y; + return (z); +} + +/* Compute the complex square root of z. */ +M_Complex +M_ComplexSqrt(M_Complex z) +{ + M_Complex w; + M_Real x = z.r; + M_Real y = z.i; + M_Real r, t, scale; + + if (y == 0.0) { + if (x == 0.0) { + w.r = 0.0; + w.i = y; + } else { + r = Fabs(x); + r = Sqrt(r); + if (x < 0.0) { + w.r = 0.0; + w.i = r; + } else { + w.r = r; + w.i = y; + } + } + return (w); + } + if (x == 0.0) { + r = Fabs(y); + r = Sqrt(r/2.0); + if (y > 0) { + w.r = r; + w.i = r; + } else { + w.r = r; + w.i = -r; + } + return (w); + } + if (Fabs(x) > 4.0 || Fabs(y) > 4.0) { /* Rescale */ + x /= 4.0; + y /= 4.0; + scale = 2.0; + } else { + x *= 1.8014398509481984e16; /* 2^54 */ + y *= 1.8014398509481984e16; /* 2^54 */ + scale = 7.450580596923828125e-9; /* 2^-27 */ + } + w.r = x; + w.i = y; + r = M_ComplexAbs(w); + if (x > 0) { + t = Sqrt(0.5*r + 0.5*x); + r = scale*Fabs((0.5*y)/t); + t *= scale; + } else { + r = Sqrt(0.5*r - 0.5*x); + t = scale*Fabs((0.5*y)/r); + r *= scale; + } + if (y < 0) { + w.r = t; + w.i = -r; + } else { + w.r = t; + w.i = r; + } + return (w); + +} + +/* Compute the complex natural logarithm of z. */ +M_Complex +M_ComplexLog(M_Complex z) +{ + M_Complex w; + M_Real p, rr; + + rr = M_ComplexAbs(z); + p = Log(rr); + rr = Atan2(z.i, z.r); + w.r = p; + w.i = rr; + return (w); +} + +/* Compute the exponential of z. */ +M_Complex +M_ComplexExp(M_Complex z) +{ + M_Complex w; + M_Real r; + + r = Exp(z.r); + w.r = r*Cos(z.i); + w.i = r*Sin(z.i); + return (w); +} + +/* Compute a to the complex power z. */ +M_Complex +M_ComplexPow(M_Complex a, M_Complex z) +{ + M_Complex w; + M_Real aAbs, aArg; + M_Real r, theta; + + if ((aAbs = M_ComplexAbs(a)) == 0.0) { + w.r = 0.0; + w.i = 0.0; + return (w); + } + aArg = M_ComplexArg(a); + r = Pow(aAbs, z.r); + theta = z.r*aArg; + if (z.i != 0.0) { + r = r*Exp(-z.i*aArg); + theta += z.i*Log(aAbs); + } + w.r = r*Cos(theta); + w.i = r*Sin(theta); + return (w); +} + +/* Compute the complex sine of z. */ +M_Complex +M_ComplexSin(M_Complex z) +{ + M_Complex w; + M_Real ch, sh; + + if (Fabs(z.i) <= 0.5) { + ch = Cosh(z.i); + sh = Sinh(z.i); + } else { + M_Real e, ei; + e = Exp(z.i); + ei = 0.5/e; + e = 0.5*e; + ch = e+ei; + sh = e-ei; + } + w.r = Sin(z.r)*ch; + w.i = Cos(z.r)*sh; + return (w); +} + +/* Compute the complex cosine of z. */ +M_Complex +M_ComplexCos(M_Complex z) +{ + M_Complex w; + M_Real ch, sh; + + if (Fabs(z.i) <= 0.5) { + ch = Cosh(z.i); + sh = Sinh(z.i); + } else { + M_Real e, ei; + e = Exp(z.i); + ei = 0.5/e; + e = 0.5*e; + ch = e+ei; + sh = e-ei; + } + w.r = Cos(z.r)*ch; + w.i = -Sin(z.r)*sh; + return (w); +} + +/* Compute the complex tangent of z. */ +M_Complex +M_ComplexTan(M_Complex z) +{ + M_Complex w; + M_Real d; + + d = Cos(2.0*z.r) + Cosh(2.0*z.i); + if (Fabs(d) < 0.25) { + d = Ctans(z); + } + if (d == 0.0) { + w.r = M_INFINITY; + w.i = M_INFINITY; + return (w); + } + w.r = Sin(2.0*z.r) / d; + w.i = Sinh(2.0*z.i) / d; + return (w); +} + +/* Compute the complex cotangent of z. */ +M_Complex +M_ComplexCot(M_Complex z) +{ + M_Complex w; + M_Real d; + + d = Cosh(2.0*z.i) - Cos(2.0*z.r); + if (Fabs(d) < 0.25) { + d = Ctans(z); + } + if (d == 0.0) { + w.r = M_INFINITY; + w.i = M_INFINITY; + return (w); + } + w.r = Sin(2.0*z.r) / d; + w.i = -Sinh(2.0*z.i) / d; + return (w); +} + +/* Subtract nearest integer multiple of pi. */ +static M_Real +SubNearestIntMultOfPi(M_Real x) +{ + const M_Real dp1 = 3.14159265160560607910E0; + const M_Real dp2 = 1.98418714791870343106E-9; + const M_Real dp3 = 1.14423774522196636802E-17; + M_Real t; + long i; + + t = x/M_PI; + if (t >= 0.0) { + t += 0.5; + } else { + t -= 0.5; + } + i = t; + t = i; + t = ((x - t*dp1) - t*dp2) - t*dp3; + return (t); +} + +/* Taylor series expansion for cosh(2y) - cos(2x) */ +static M_Real +Ctans(M_Complex z) +{ + M_Real x2 = 1.0, y2 = 1.0, f = 1.0, rn = 0.0, d = 0.0; + M_Real x, y, t; + + x = Fabs(2.0*z.r); + y = Fabs(2.0*z.i); + x = SubNearestIntMultOfPi(x); + x = x*x; + y = y*y; + do { + rn += 1.0; f *= rn; + rn += 1.0; f *= rn; + x2 *= x; y2 *= y; + t = y2+x2; t /= f; d += t; + rn += 1.0; f *= rn; + rn += 1.0; f *= rn; + x2 *= x; y2 *= y; + t = y2-x2; t /= f; d += t; + } while (Fabs(t/d) > M_MACHEP); + return (d); +} + +/* Compute the complex arc sine of z. */ +M_Complex +M_ComplexAsin(M_Complex z) +{ + M_Complex ca, ct, zz, z2; + M_Real x = z.r, y = z.i; + + if (y == 0.0) { + M_Complex w; + w.r = (Fabs(x) > 1.0) ? M_PI_2 : Asin(x); + w.i = 0.0; + return (w); + } + ca.r = x; + ca.i = y; + ct = M_ComplexMult(ca, M_ComplexI()); + zz.r = (x-y)*(x+y); + zz.i = (2.0*x*y); + zz.r = 1.0-zz.r; + zz.i = -zz.i; + z2 = M_ComplexSqrt(zz); + zz = M_ComplexAdd(ct, z2); + zz = M_ComplexLog(zz); + return M_ComplexMult(zz, M_ComplexMinusI()); +} + +/* Compute the complex arc cosine of z. */ +M_Complex +M_ComplexAcos(M_Complex z) +{ + M_Complex w; + + w = M_ComplexAsin(z); + w.r = M_PI_2 - w.r; + w.i = -w.i; + return (w); +} + +/* Compute the complex arc tangent of z. */ +M_Complex +M_ComplexAtan(M_Complex z) +{ + M_Complex w; + M_Real a, t, x, x2, y; + + x = z.r; + y = z.i; + + if ((x == 0.0) && (y > 1.0)) { + goto oflow; + } + x2 = x*x; + a = 1.0 - x2 - y*y; + if (a == 0.0) { goto oflow; } + t = Atan2(2.0*x,a)/2.0; + + w.r = SubNearestIntMultOfPi(t); + w.i = 0.0; + + t = y-1.0; + a = (x2 + t*t); + if (a == 0.0) { goto oflow; } + t = y+1.0; + a = (x2 + t*t)/a; + w.i += Log(a)/4.0; + return (w); +oflow: + w.r = M_INFINITY; + w.i = M_INFINITY; + return (w); +} + +/* Compute the complex hyperbolic sine of z. */ +M_Complex +M_ComplexSinh(M_Complex z) +{ + M_Complex w; + + w.r = Sinh(z.r) * Cos(z.i); + w.i = Cosh(z.r) * Sin(z.i); + return (w); +} + +/* Compute the complex hyperbolic arc sine of z. */ +M_Complex +M_ComplexAsinh(M_Complex z) +{ + M_Complex zi; + + zi = M_ComplexMult(z, M_ComplexI()); + return M_ComplexMult(M_ComplexAsin(zi), M_ComplexMinusI()); +} + +/* Compute the complex hyperbolic cosine of z. */ +M_Complex +M_ComplexCosh(M_Complex z) +{ + M_Complex w; + + w.r = Cosh(z.r)*Cos(z.i); + w.i = Sinh(z.r)*Sin(z.i); + return (w); +} + +/* Compute the complex hyperbolic arc cosine of z. */ +M_Complex +M_ComplexAcosh(M_Complex z) +{ + return M_ComplexMult(M_ComplexI(), M_ComplexAcos(z)); +} + +/* Compute the complex hyperbolic tangent of z. */ +M_Complex +M_ComplexTanh(M_Complex z) +{ + M_Complex w; + M_Real d; + + d = Cosh(2.0*z.r) + Cos(2.0*z.i); + w.r = Sinh(2.0*z.r)/d; + w.i = Sin (2.0*z.i)/d; + return (w); +} + +/* Compute the complex hyperbolic arc tangent of z. */ +M_Complex +M_ComplexAtanh(M_Complex z) +{ + M_Complex zi; + + zi = M_ComplexMult(z, M_ComplexI()); + return M_ComplexMult(zi, M_ComplexMinusI()); +} Added: trunk/math/m_complex.h =================================================================== --- trunk/math/m_complex.h (rev 0) +++ trunk/math/m_complex.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,135 @@ +/* Public domain */ +/* + * Complex number arithmetic. + */ + +__BEGIN_DECLS +static __inline__ M_Complex +M_ComplexGet(M_Real r, M_Real i) +{ + M_Complex z; + + z.r = r; + z.i = i; + return (z); +} + +static __inline__ M_Complex +M_ComplexAddIdentity(void) +{ + M_Complex z; + + z.r = 0.0; + z.i = 0.0; + return (z); +} + +static __inline__ M_Complex +M_ComplexMultIdentity(void) +{ + M_Complex z; + + z.r = 1.0; + z.i = 0.0; + return (z); +} + +static __inline__ M_Complex +M_ComplexAddInverse(M_Complex z) +{ + M_Complex w; + + w.r = -z.r; + w.i = -z.i; + return (z); +} + +static __inline__ M_Complex +M_ComplexMultInverse(M_Complex z) +{ + M_Complex w; + M_Real divisor; + + divisor = z.r*z.r + z.i*z.i; + w.r = z.r/divisor; + w.i = -z.i/divisor; + return (w); +} + +static __inline__ M_Real +M_ComplexModulus(M_Complex z) +{ + return M_Sqrt(z.r*z.r + z.i*z.i); +} + +static __inline__ M_Real +M_ComplexArg(M_Complex z) +{ + return M_Atan2(z.i, z.r); +} + +static __inline__ void +M_ComplexPolar(M_Complex z, M_Real *r, M_Real *theta) +{ + *r = M_ComplexModulus(z); + *theta = M_ComplexArg(z); +} + +static __inline__ M_Complex +M_ComplexAdd(M_Complex a, M_Complex b) +{ + M_Complex w; + + w.r = a.r + b.r; + w.i = a.i + b.i; + return (w); +} + +static __inline__ M_Complex +M_ComplexSub(M_Complex a, M_Complex b) +{ + M_Complex w; + + w.r = a.r - b.r; + w.i = a.i - b.i; + return (w); +} + +static __inline__ M_Complex +M_ComplexMult(M_Complex a, M_Complex b) +{ + M_Complex rv; + + rv.r = a.r*b.r - a.i*b.i; + rv.i = a.i*b.r + a.r*b.i; + return (rv); +} +__END_DECLS + +__BEGIN_DECLS +#define M_ComplexI() M_ComplexGet(0.0, 1.0) +#define M_ComplexMinusI() M_ComplexGet(0.0, -1.0) +#define M_ComplexReal(z) ((z).r) +#define M_ComplexImag(z) ((z).i) +M_Real M_ComplexAbs(M_Complex); +M_Complex M_ComplexDiv(M_Complex, M_Complex); +M_Complex M_ComplexSqrt(M_Complex); +M_Complex M_ComplexLog(M_Complex); +M_Complex M_ComplexExp(M_Complex); +M_Complex M_ComplexPow(M_Complex, M_Complex); + +M_Complex M_ComplexSin(M_Complex); +M_Complex M_ComplexCos(M_Complex); +M_Complex M_ComplexTan(M_Complex); +M_Complex M_ComplexCot(M_Complex); +M_Complex M_ComplexAsin(M_Complex); +M_Complex M_ComplexAcos(M_Complex); +M_Complex M_ComplexAtan(M_Complex); +M_Complex M_ComplexSinh(M_Complex); +M_Complex M_ComplexAsinh(M_Complex); +M_Complex M_ComplexCosh(M_Complex); +M_Complex M_ComplexAcosh(M_Complex); +M_Complex M_ComplexTanh(M_Complex); +M_Complex M_ComplexAtanh(M_Complex); +M_Complex M_ComplexPow(M_Complex, M_Complex); +__END_DECLS Added: trunk/math/m_convexhull.c =================================================================== --- trunk/math/m_convexhull.c (rev 0) +++ trunk/math/m_convexhull.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008 Hypertriton, Inc. <http://hypertriton.com/> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +/* + * Compute the convex hull of a set of points. + */ + +#include <core/core.h> +#include "m.h" + +M_PointSet2 +M_ConvexHull2(M_PointSet2 *P) +{ + M_PointSet2 *CH; + +} Added: trunk/math/m_coordinates.c =================================================================== --- trunk/math/m_coordinates.c (rev 0) +++ trunk/math/m_coordinates.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008 Hypertriton, Inc. <http://hypertriton.com/> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +/* + * Conversion between different coordinate systems. + */ + +#include <core/core.h> +#include "m.h" + +M_Rectangular +M_RectangularFromSpherical(M_Spherical s) +{ + M_Rectangular r; + + r.x = s.r*Cos(s.phi)*Cos(s.theta); + r.y = s.r*Cos(s.phi)*Sin(s.theta); + r.z = s.r*Sin(s.phi); + return (r); +} + +M_Rectangular +M_RectangularFromCylindrical(M_Cylindrical c) +{ + M_Rectangular r; + + r.x = c.rho*Cos(c.phi); + r.y = c.rho*Sin(c.phi); + r.z = c.z; + return (r); +} + +M_Spherical +M_SphericalFromRectangular(M_Rectangular r) +{ + M_Spherical s; + M_Real xy2 = r.x*r.x + r.y*r.y; + + s.theta = Atan2(r.y, r.x); + s.phi = Atan2(Sqrt(xy2), r.z); + s.r = Sqrt(xy2 + r.z*r.z); + return (s); +} + +M_Spherical +M_SphericalFromCylindrical(M_Cylindrical c) +{ + M_Spherical s; + + s.theta = Atan2(c.rho, c.z); + s.phi = c.phi; + s.r = Sqrt(c.rho*c.rho + c.z*c.z); + return (s); +} + +M_Cylindrical +M_CylindricalFromRectangular(M_Rectangular r) +{ + M_Cylindrical c; + + c.rho = Sqrt(r.x*r.x + r.y*r.y); + c.phi = Atan2(r.y, r.x); + c.z = r.z; + return (c); +} + +M_Cylindrical +M_CylindricalFromSpherical(M_Spherical s) +{ + M_Cylindrical c; + + c.rho = s.r*Sin(s.theta); + c.phi = s.phi; + c.z = s.r*Cos(s.theta); + return (c); +} Added: trunk/math/m_coordinates.h =================================================================== --- trunk/math/m_coordinates.h (rev 0) +++ trunk/math/m_coordinates.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,10 @@ +/* Public domain */ + +__BEGIN_DECLS +M_Rectangular M_RectangularFromSpherical(M_Spherical); +M_Rectangular M_RectangularFromCylindrical(M_Cylindrical); +M_Spherical M_SphericalFromRectangular(M_Rectangular); +M_Spherical M_SphericalFromCylindrical(M_Cylindrical); +M_Cylindrical M_CylindricalFromRectangular(M_Rectangular); +M_Cylindrical M_CylindricalFromSpherical(M_Spherical); +__END_DECLS Added: trunk/math/m_gaussj.c =================================================================== --- trunk/math/m_gaussj.c (rev 0) +++ trunk/math/m_gaussj.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,119 @@ +/* Public domain */ + +#include <core/core.h> +#include "m.h" + +#undef SWAP +#define SWAP(a,b) { tmp=(a); (a)=(b); (b)=tmp; } + +/* + * Apply Gauss-Jordan elimination to a matrix A and a right-hand side b. + * The original contents of A are destroyed, as it is replaced by the matrix + * inverse. The solution vectors are returned in b. + */ +int +M_InvertGaussJordanv_FPU(void *pA, void *pb) +{ + M_MatrixFPU *A=pA, *b=pb; + M_IntVector *iCol, *iRow, *iPivot; + int col = 0, row = 0; + int i, j, k, l, m; + M_Real big, dum, pivinv, tmp; + + M_ASSERT_SQUARE_MATRIX(A, -1); + iRow = M_IntVectorNew(MCOLS(A)); + iCol = M_IntVectorNew(MCOLS(A)); + iPivot = M_IntVectorNew(MCOLS(A)); + M_IntVectorSet(iPivot, 0); + + for (i = 0; i < MCOLS(A); i++) { + big = 0.0; + + /* Search for the pivot element of this column. */ + for (j = 0; j < MCOLS(A); j++) { + if (iPivot->v[j] != 1) { + for (k = 0; k < MCOLS(A); k++) { + if (iPivot->v[k] == 0) { + if (Fabs(A->v[j][k]) >= big) { + big = Fabs(A->v[j][k]); + row = j; + col = k; + } + } else if (iPivot->v[k] > 1) { + AG_SetError("Singular matrix"); + goto fail; + } + } + } + } + iPivot->v[col]++; + + /* Move the pivot to the diagonal and record the interchange. */ + if (row != col) { + for (l = 0; l < MCOLS(A); l++) + SWAP(A->v[row][l], A->v[col][l]); + for (l = 0; l < MCOLS(b); l++) + SWAP(b->v[row][l], b->v[col][l]); + } + iRow->v[i] = row; + iCol->v[i] = col; + + if (Fabs(A->v[col][col]) < M_MACHEP) { + AG_SetError("Matrix singular to machine precision"); + goto fail; + } + pivinv = 1.0/A->v[col][col]; + A->v[col][col] = 1.0; + + for (l = 0; l < MCOLS(A); l++) { A->v[col][l] *= pivinv; } + for (l = 0; l < MCOLS(b); l++) { b->v[col][l] *= pivinv; } + + /* Reduce the rows except for the pivot one. */ + for (m = 0; m < MCOLS(A); m++) { + if (m == col) { + continue; + } + dum = A->v[m][col]; + A->v[m][col] = 0.0; + + for (l = 0; l < MCOLS(A); l++) + A->v[m][l] -= A->v[col][l]*dum; + for (l = 0; l < MCOLS(b); l++) + b->v[m][l] -= b->v[col][l]*dum; + } + } + + for (l = MCOLS(A)-1; l >= 0; l--) { + if (iRow->v[l] != iCol->v[l]) { + for (k = 0; k < MCOLS(A); k++) + SWAP(A->v[k][iRow->v[l]], + A->v[k][iCol->v[l]]); + } + } + + M_IntVectorFree(iRow); + M_IntVectorFree(iCol); + M_IntVectorFree(iPivot); + return (0); +fail: + M_IntVectorFree(iRow); + M_IntVectorFree(iCol); + M_IntVectorFree(iPivot); + return (-1); +} + +void * +M_InvertGaussJordan_FPU(const void *pA, void *pb) +{ + const M_MatrixFPU *A=pA; + M_MatrixFPU *b=pb, *Ainv; + + if ((Ainv = M_Dup(A)) == NULL) { + return (NULL); + } + if (M_InvertGaussJordanv(Ainv, b) == -1) { + M_Free(Ainv); + return (NULL); + } + return (Ainv); +} Added: trunk/math/m_gaussj.h =================================================================== --- trunk/math/m_gaussj.h (rev 0) +++ trunk/math/m_gaussj.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,6 @@ +/* Public domain */ + +__BEGIN_DECLS +int M_InvertGaussJordanv_FPU(void *, void *); +void *M_InvertGaussJordan_FPU(const void *, void *); +__END_DECLS Added: trunk/math/m_geometry.h =================================================================== --- trunk/math/m_geometry.h (rev 0) +++ trunk/math/m_geometry.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,236 @@ +/* Public domain */ + +/* + * Line segment or ray in R2/R3. + */ +typedef struct m_line2 { + M_Vector2 p; /* Origin point */ + M_Vector2 d; /* Direction vector (unit-length) */ + M_Real t; /* Length of line (or Inf for a halfline) */ +} M_Line2; +typedef struct m_line3 { + M_Vector3 p; + M_Vector3 d; + M_Real t; +} M_Line3; + +#define M_LINE2_INITIALIZER(px,py,nx,ny,t) { {px,py}, {nx,ny}, t } +#define M_LINE3_INITIALIZER(px,py,pz,nx,ny,nz,t) { {px,py,pz}, {nx,ny,nz}, t } +#define MLINE2(v) ((M_Line2 *)(v)) +#define MLINE3(v) ((M_Line3 *)(v)) + +/* + * Circle in R2/R3. + */ +typedef struct m_circle2 { + M_Vector2 p; /* Origin point */ + M_Real r; /* Radius */ +} M_Circle2; +typedef struct m_circle3 { + M_Vector3 p; + M_Real r; +} M_Circle3; + +#define M_CIRCLE2_INITIALIZER(px,py,r) { { px,py }, r } +#define M_CIRCLE3_INITIALIZER(px,py,pz,r) { { px,py,pz }, r } + +/* + * Sphere in R3 + */ +typedef struct m_sphere3 { + M_Vector3 p; /* Origin point */ + M_Real r; /* Radius */ +} M_Sphere3; + +#define M_SPHERE_INITIALIZER(px,py,pz,r) { { px,py,pz }, r } + +/* + * Plane in R3 or hyperplane in Rn + */ +typedef struct m_plane3 { + M_Real a, b, c, d; /* Coefficients of plane equation */ +} M_Plane3; +typedef struct m_plane { + M_Real *c; /* Coefficients of plane equation */ + Uint n; +} M_Plane; + +#define M_PLANE3_INITIALIZER(a,b,c,d) { a,b,c,d } +#define M_PLANE_INITIALIZER { NULL, 0 } +#define MPLANE3(v) ((M_Plane3 *)(v)) +#define MPLANE(v) ((M_Plane *)(v)) + +/* + * Triangle in R2/R3 + */ +typedef struct m_triangle2 { + M_Line2 a, b, c; +} M_Triangle2; +typedef struct m_triangle3 { + M_Line3 a, b, c; +} M_Triangle3; + +#define M_TRIANGLE2_INITIALIZER(ax,ay,bx,by,cx,cy) \ + { M_LineFromPts2(ax,ay), M_LineFromPts2(bx,by), \ + M_LineFromPts2(cx,cy) } +#define M_TRIANGLE3_INITIALIZER(ax,ay,az,bx,by,bz,cx,cy,cz) \ + { M_LineFromPts3(ax,ay,az), M_LineFromPts3(bx,by,bz), \ + M_LineFromPts3(cx,cy,cz) } + +/* + * Rectangle in R2/R3 + */ +typedef struct m_rectangle2 { + M_Line2 a, b, c, d; +} M_Rectangle2; +typedef struct m_rectangle3 { + M_Line3 a, b, c, d; +} M_Rectangle3; + +#define M_RECTANGLE2_INITIALIZER(ax,ay,bx,by,cx,cy,dx,dy) \ + { M_LineFromPts2(ax,ay), M_LineFromPts2(bx,by), \ + M_LineFromPts2(cx,cy), M_LineFromPts2(dx,dy) } +#define M_RECTANGLE_INITIALIZER(ax,ay,az,bx,by,bz,cx,cy,cz,dx,dy,dz) \ + { M_LineFromPts3(ax,ay,az), M_LineFromPts3(bx,by,bz), \ + M_LineFromPts3(cx,cy,cz), M_LineFromPts3(dx,dy,dz) } + +/* + * Simple polygon in R2/R3 (no self-intersections, no holes) + */ +typedef struct m_polygon2 { + M_Line2 *s; /* Sides (must be closed) */ + Uint n; /* Number of sides */ +} M_Polygon2; +typedef struct m_polygon3 { + M_Line3 *s; + Uint n; +} M_Polygon3; + +#define M_POLYGON2_INITIALIZER { NULL, 0 } +#define M_POLYGON3_INITIALIZER { NULL, 0 } + +/* + * Generic geometrical structure in R2 and R3 + */ +typedef enum m_geom_type { + M_NONE, + M_POINT, + M_LINE, + M_CIRCLE, + M_SPHERE, + M_PLANE, + M_POLYGON, + M_TRIANGLE, + M_RECTANGLE +} M_GeomType; + +typedef struct m_geom2 { + M_GeomType type; + union { + M_Vector2 point; + M_Line2 line; + M_Circle2 circle; + M_Polygon2 polygon; + M_Triangle2 triangle; + M_Rectangle2 rectangle; + } g; +} M_Geom2; + +typedef struct m_geom3 { + M_GeomType type; + union { + M_Vector3 point; + M_Line3 line; + M_Circle3 circle; + M_Sphere3 sphere; + M_Plane3 plane; + M_Polygon3 polygon; + M_Triangle3 triangle; + M_Rectangle3 rectangle; + } g; +} M_Geom3; + +#ifdef _M_INTERNAL +#define g_point g.point +#define g_line g.line +#define g_circle g.circle +#define g_sphere g.sphere +#define g_plane g.plane +#define g_polygon g.polygon +#define g_triangle g.triangle +#define g_rectangle g.rectangle +#endif + +/* Sets of any geometric structures in R2 and R3. */ +typedef struct m_geom_set2 { + M_Geom2 *g; + Uint n; +} M_GeomSet2; +typedef struct m_geom_set3 { + M_Geom3 *g; + Uint n; +} M_GeomSet3; +#define M_GEOM_SET_EMPTY { NULL, 0 } + +/* Sets of points in R2 and R3. */ +typedef struct m_point_set2 { + M_Vector2 *p; + Uint n; +} M_PointSet2; +typedef struct m_point_set3 { + M_Vector3 *p; + Uint n; +} M_PointSet3; +#define M_POINT_SET_EMPTY { NULL, 0 } + +#include <agar/math/m_line.h> +#include <agar/math/m_circle.h> +#include <agar/math/m_plane.h> +#include <agar/math/m_triangle.h> +#include <agar/math/m_rectangle.h> +#include <agar/math/m_polygon.h> +#include <agar/math/m_point_set.h> + +__BEGIN_DECLS + +/* + * Basic routines for manipulating general sets. + */ +static __inline__ void +M_GeomSetAdd2(M_GeomSet2 *S, const M_Geom2 *g) +{ + S->g = AG_Realloc(S->g, (S->n+1)*sizeof(M_Geom2)); + S->g[S->n++] = *g; +} +static __inline__ void +M_GeomSetAdd3(M_GeomSet3 *S, const M_Geom3 *g) +{ + S->g = AG_Realloc(S->g, (S->n+1)*sizeof(M_Geom3)); + S->g[S->n++] = *g; +} + +static __inline__ void +M_GeomSetFree2(M_GeomSet2 *S) +{ + Uint i; + for (i = 0; i < S->n; i++) { + if (S->g[i].type == M_POLYGON) + M_PolygonFree2(&S->g[i].g.polygon); + } + AG_Free(S->g); + S->g = NULL; + S->n = 0; +} +static __inline__ void +M_GeomSetFree3(M_GeomSet3 *S) +{ + Uint i; + for (i = 0; i < S->n; i++) { + if (S->g[i].type == M_POLYGON) + M_PolygonFree3(&S->g[i].g.polygon); + } + AG_Free(S->g); + S->g = NULL; + S->n = 0; +} +__END_DECLS Added: trunk/math/m_gui.c =================================================================== --- trunk/math/m_gui.c (rev 0) +++ trunk/math/m_gui.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2006-2008 Hypertriton, Inc. <http://hypertriton.com/> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +/* + * Utility GUI routines for types defined by the math library. + */ + +#include <core/core.h> +#include <gui/widget.h> +#include <gui/box.h> +#include <gui/fspinbutton.h> +#include <gui/spinbutton.h> + +#include "m.h" +#include "m_gui.h" + +AG_FSpinbutton * +M_SpinReal(void *parent, const char *label, M_Real *pv) +{ + AG_FSpinbutton *fsb; + + fsb = AG_FSpinbuttonNew(parent, 0, NULL, label); + AG_WidgetBind(fsb, "value", M_WIDGET_REAL, pv); + AG_FSpinbuttonSetIncrement(fsb, 0.05); + return (fsb); +} + +AG_FSpinbutton * +M_SpinRealInc(void *parent, const char *label, M_Real *pv, M_Real inc) +{ + AG_FSpinbutton *fsb; + + fsb = M_SpinReal(parent, label, pv); + AG_FSpinbuttonSetIncrement(fsb, inc); + return (fsb); +} + +AG_FSpinbutton * +M_SpinFloat(void *parent, const char *label, float *pv) +{ + AG_FSpinbutton *fsb; + + fsb = AG_FSpinbuttonNew(parent, 0, NULL, label); + AG_WidgetBind(fsb, "value", AG_WIDGET_FLOAT, pv); + return (fsb); +} + +AG_FSpinbutton * +M_SpinDouble(void *parent, const char *label, double *pv) +{ + AG_FSpinbutton *fsb; + + fsb = AG_FSpinbuttonNew(parent, 0, NULL, label); + AG_WidgetBind(fsb, "value", AG_WIDGET_DOUBLE, pv); + return (fsb); +} + +AG_Spinbutton * +M_SpinInt(void *parent, const char *label, int *pv) +{ + AG_Spinbutton *sb; + + sb = AG_SpinbuttonNew(parent, 0, label); + AG_WidgetBind(sb, "value", AG_WIDGET_INT, pv); + return (sb); +} + +void * +M_EditVector3(void *parent, const char *label, M_Vector3 *pv) +{ + AG_Box *box; + AG_FSpinbutton *fsb; + + box = AG_BoxNew(parent, AG_BOX_HORIZ, AG_BOX_HOMOGENOUS|AG_BOX_HFILL); + AG_LabelNewStatic(box, 0, label); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &pv->x); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &pv->y); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &pv->z); + + return (box); +} + +void * +M_EditVector4(void *parent, const char *label, M_Vector4 *pv) +{ + AG_Box *box; + AG_FSpinbutton *fsb; + + box = AG_BoxNew(parent, AG_BOX_HORIZ, AG_BOX_HOMOGENOUS|AG_BOX_HFILL); + AG_LabelNewStatic(box, 0, label); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &pv->x); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &pv->y); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &pv->z); + + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.01); + M_WidgetBindReal(fsb, "value", &pv->w); + return (box); +} + +void * +M_EditMatrix44(void *parent, const char *label, M_Matrix44 *T) +{ + AG_Box *hbox, *vbox; + AG_FSpinbutton *fsb; + int i, j; + + vbox = AG_BoxNew(parent, AG_BOX_VERT, AG_BOX_EXPAND); + if (label != NULL) { + AG_LabelNewStatic(vbox, 0, label); + } + for (j = 0; j < 4; j++) { + hbox = AG_BoxNew(vbox, AG_BOX_HORIZ, + AG_BOX_HOMOGENOUS|AG_BOX_HFILL); + for (i = 0; i < 4; i++) { + fsb = AG_FSpinbuttonNew(hbox, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.1); + M_WidgetBindReal(fsb, "value", &T->m[i][j]); + } + } + return (vbox); +} + +void * +M_EditTranslate3(void *parent, const char *label, M_Matrix44 *T) +{ + AG_Box *box; + AG_FSpinbutton *fsb; + int i; + + box = AG_BoxNew(parent, AG_BOX_VERT, AG_BOX_HFILL); + if (label != NULL) { + AG_LabelNewStatic(box, 0, label); + } + for (i = 0; i < 3; i++) { + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &T->m[i][3]); + } + return (box); +} + +void * +M_EditTranslate4(void *parent, const char *label, M_Matrix44 *T) +{ + AG_Box *box; + AG_FSpinbutton *fsb; + int i; + + box = AG_BoxNew(parent, AG_BOX_VERT, AG_BOX_HFILL); + if (label != NULL) { + AG_LabelNewStatic(box, 0, label); + } + for (i = 0; i < 4; i++) { + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.5); + M_WidgetBindReal(fsb, "value", &T->m[i][3]); + } + return (box); +} + +void * +M_EditScale3(void *parent, const char *label, M_Matrix44 *T) +{ + AG_Box *box; + AG_FSpinbutton *fsb; + int i; + + box = AG_BoxNew(parent, AG_BOX_VERT, AG_BOX_HFILL); + if (label != NULL) { + AG_LabelNewStatic(box, 0, label); + } + for (i = 0; i < 3; i++) { + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.05); + M_WidgetBindReal(fsb, "value", &T->m[i][i]); + } + return (box); +} + +void * +M_EditScale4(void *parent, const char *label, M_Matrix44 *T) +{ + AG_Box *box; + AG_FSpinbutton *fsb; + int i; + + box = AG_BoxNew(parent, AG_BOX_VERT, AG_BOX_HFILL); + if (label != NULL) { + AG_LabelNewStatic(box, 0, label); + } + for (i = 0; i < 4; i++) { + fsb = AG_FSpinbuttonNew(box, 0, NULL, NULL); + AG_FSpinbuttonSetIncrement(fsb, 0.05); + M_WidgetBindReal(fsb, "value", &T->m[i][i]); + } + return (box); +} Added: trunk/math/m_gui.h =================================================================== --- trunk/math/m_gui.h (rev 0) +++ trunk/math/m_gui.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,94 @@ +/* Public domain */ + +#ifndef _AGAR_MATH_M_GUI_H_ +#define _AGAR_MATH_M_GUI_H_ +#include <agar/math/m_begin.h> + +#if defined(QUAD_PRECISION) + +# define M_WIDGET_REAL AG_WIDGET_LONG_DOUBLE +# define M_WidgetBindReal(w,n,p) AG_WidgetBind((w),(n),AG_WIDGET_LONG_DOUBLE,\ + (p)) +# define M_WidgetReal(w,n) AG_WidgetLongDouble((w),(n)) +# define M_NumericalNewReal AG_NumericalNewLongDbl +# define M_NumericalNewRealR AG_NumericalNewLongDblR +# define M_NumericalGetReal AG_NumericalGetLongDbl +# define M_NumericalNewRealPNZ(p,f,u,l,v) \ + AG_NumericalNewLongDblR((p),(f),(u),(l),(v),M_TINYVAL,M_INFINITY) +# define M_NumericalNewRealPNZF(p,f,u,l,v) \ + AG_NumericalNewLongDblR((p),(f),(u),(l),(v),M_TINYVAL,M_HUGEVAL) +# define M_NumericalNewRealP(p,f,u,l,v) \ + AG_NumericalNewLongDblR((p),(f),(u),(l),(v),0.0,M_INFINITY) +# define M_NumericalNewRealPF(p,f,u,l,v) \ + AG_NumericalNewLongDblR((p),(f),(u),(l),(v),0.0,M_HUGEVAL) + +#elif defined(DOUBLE_PRECISION) + +# define M_WIDGET_REAL AG_WIDGET_DOUBLE +# define M_WidgetBindReal(w,n,p) AG_WidgetBind((w),(n),AG_WIDGET_DOUBLE,(p)) +# define M_WidgetReal(w,n) AG_WidgetDouble((w),(n)) +# define M_NumericalNewReal AG_NumericalNewDbl +# define M_NumericalNewRealR AG_NumericalNewDblR +# define M_NumericalGetReal AG_NumericalGetDbl +# define M_NumericalNewRealPNZ(p,f,u,l,v) \ + AG_NumericalNewDblR((p),(f),(u),(l),(v),M_TINYVAL,M_INFINITY) +# define M_NumericalNewRealPNZF(p,f,u,l,v) \ + AG_NumericalNewDblR((p),(f),(u),(l),(v),M_TINYVAL,M_HUGEVAL) +# define M_NumericalNewRealP(p,f,u,l,v) \ + AG_NumericalNewDblR((p),(f),(u),(l),(v),0.0,M_INFINITY) +# define M_NumericalNewRealPF(p,f,u,l,v) \ + AG_NumericalNewDblR((p),(f),(u),(l),(v),0.0,M_HUGEVAL) + +#elif defined(SINGLE_PRECISION) + +# define M_WIDGET_REAL AG_WIDGET_FLOAT +# define M_WidgetBindReal(w,n,p) AG_WidgetBind((w),(n),AG_WIDGET_FLOAT,(p)) +# define M_WidgetReal(w,n) AG_WidgetFloat((w),(n)) +# define M_NumericalNewReal AG_NumericalNewFlt +# define M_NumericalNewRealR AG_NumericalNewFltR +# define M_NumericalGetReal AG_NumericalGetFlt +# define M_NumericalNewRealPNZ(p,f,u,l,v) \ + AG_NumericalNewFltR((p),(f),(u),(l),(v),M_TINYVAL,M_INFINITY) +# define M_NumericalNewRealPNZF(p,f,u,l,v) \ + AG_NumericalNewFltR((p),(f),(u),(l),(v),M_TINYVAL,M_HUGEVAL) +# define M_NumericalNewRealP(p,f,u,l,v) \ + AG_NumericalNewFltR((p),(f),(u),(l),(v),0.0,M_INFINITY) +# define M_NumericalNewRealPF(p,f,u,l,v) \ + AG_NumericalNewFltR((p),(f),(u),(l),(v),0.0,M_HUGEVAL) + +#else +# error "Precision is not defined" +#endif + +#define M_WIDGET_TIME AG_WIDGET_SINT32 +#define M_WidgetBindTime(w,n,p) AG_WidgetBind((w),(n),M_WIDGET_TIME,(p)) +#define M_WidgetTime(w,n) AG_WidgetSint32((w),(n)) +#define M_NumericalNewTime AG_NumericalNewSint32 +#define M_NumericalNewTimeR AG_NumericalNewSint32R +#define M_NumericalNewTimeP(p,f,u,l,v) \ + AG_NumericalNewSint32R((p),(f),(u),(l),(v),0,0x7fffffff-1) +#define M_NumericalNewTimePNZ(p,f,u,l,v) \ + AG_NumericalNewSint32R((p),(f),(u),(l),(v),1,0x7fffffff-1) +#define M_NumericalGetTime AG_NumericalGetSint32 + +__BEGIN_DECLS +AG_FSpinbutton *M_SpinReal(void *, const char *, M_Real *); +AG_FSpinbutton *M_SpinRealInc(void *, const char *, M_Real *, M_Real); +AG_FSpinbutton *M_SpinFloat(void *, const char *, float *); +AG_FSpinbutton *M_SpinDouble(void *, const char *, double *); +AG_Spinbutton *M_SpinInt(void *, const char *, int *); + +void *M_EditVector3(void *, const char *, M_Vector3 *); +void *M_EditVector4(void *, const char *, M_Vector4 *); +void *M_EditMatrix44(void *, const char *, M_Matrix44 *); +void *M_EditTranslate3(void *, const char *, M_Matrix44 *); +void *M_EditTranslate4(void *, const char *, M_Matrix44 *); +void *M_EditScale3(void *, const char *, M_Matrix44 *); +void *M_EditScale4(void *, const char *, M_Matrix44 *); +__END_DECLS + +#include <agar/math/m_plotter.h> +#include <agar/math/m_matview.h> +#include <agar/math/m_close.h> + +#endif /* _AGAR_MATH_M_GUI_H_ */ Added: trunk/math/m_heapsort.c =================================================================== --- trunk/math/m_heapsort.c (rev 0) +++ trunk/math/m_heapsort.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ronnie Kon at Mindcraft Inc., Kevin Lew and Elmer Yglesias. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +/* + * Heapsort -- Knuth, Vol. 3, page 145. Runs in O (N lg N), both average + * and worst. While heapsort is faster than the worst case of quicksort, + * the BSD quicksort does median selection so that the chance of finding + * a data set that will trigger the worst case is nonexistent. Heapsort's + * only advantage over quicksort is that it requires little additional memory. + */ + +#include <core/core.h> + +#include "m.h" + +/* + * Swap two areas of size number of bytes. Although qsort(3) permits random + * blocks of memory to be sorted, sorting pointers is almost certainly the + * common case (and, were it not, could easily be made so). Regardless, it + * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer + * arithmetic gets lost in the time required for comparison function calls. + */ +#define SWAP(a, b, count, size, tmp) { \ + count = size; \ + do { \ + tmp = *a; \ + *a++ = *b; \ + *b++ = tmp; \ + } while (--count); \ +} + +/* Copy one block of size size to another. */ +#define COPY(a, b, count, size, tmp1, tmp2) { \ + count = size; \ + tmp1 = a; \ + tmp2 = b; \ + do { \ + *tmp1++ = *tmp2++; \ + } while (--count); \ +} + +/* + * Build the list into a heap, where a heap is defined such that for + * the records K1 ... KN, Kj/2 >= Kj for 1 <= j/2 <= j <= N. + * + * There are two cases. If j == nmemb, select largest of Ki and Kj. If + * j < nmemb, select largest of Ki, Kj and Kj+1. + */ +#define CREATE(initval, nmemb, par_i, child_i, par, child, size, count, tmp) { \ + for (par_i = initval; (child_i = par_i * 2) <= nmemb; \ + par_i = child_i) { \ + child = base + child_i * size; \ + if (child_i < nmemb && compar(child, child + size) < 0) { \ + child += size; \ + ++child_i; \ + } \ + par = base + par_i * size; \ + if (compar(child, par) <= 0) \ + break; \ + SWAP(par, child, count, size, tmp); \ + } \ +} + +/* + * Select the top of the heap and 'heapify'. Since by far the most expensive + * action is the call to the compar function, a considerable optimization + * in the average case can be achieved due to the fact that k, the displaced + * element, is usually quite small, so it would be preferable to first + * heapify, always maintaining the invariant that the larger child is copied + * over its parent's record. + * + * Then, starting from the *bottom* of the heap, finding k's correct place, + * again maintaining the invariant. As a result of the invariant no element + * is 'lost' when k is assigned its correct place in the heap. + * + * The time savings from this optimization are on the order of 15-20% for the + * average case. See Knuth, Vol. 3, page 158, problem 18. + * + * XXX Don't break the #define SELECT line, below. Reiser cpp gets upset. + */ +#define SELECT(par_i, child_i, nmemb, par, child, size, k, count, tmp1, tmp2) { \ + for (par_i = 1; (child_i = par_i * 2) <= nmemb; par_i = child_i) { \ + child = base + child_i * size; \ + if (child_i < nmemb && compar(child, child + size) < 0) { \ + child += size; \ + ++child_i; \ + } \ + par = base + par_i * size; \ + COPY(par, child, count, size, tmp1, tmp2); \ + } \ + for (;;) { \ + child_i = par_i; \ + par_i = child_i / 2; \ + child = base + child_i * size; \ + par = base + par_i * size; \ + if (child_i == 1 || compar(k, par) < 0) { \ + COPY(child, k, count, size, tmp1, tmp2); \ + break; \ + } \ + COPY(child, par, count, size, tmp1, tmp2); \ + } \ +} + +int +M_HeapSort(void *vbase, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)) +{ + int cnt, i, j, l; + char tmp, *tmp1, *tmp2; + char *base, *k, *p, *t; + + if (nmemb <= 1) + return (0); + + if (!size) { + AG_SetError("Size==0"); + return (-1); + } + + if ((k = malloc(size)) == NULL) { + AG_SetError("Out of memory"); + return (-1); + } + + /* + * Items are numbered from 1 to nmemb, so offset from size bytes + * below the starting address. + */ + base = (char *)vbase - size; + + for (l = nmemb / 2 + 1; --l;) + CREATE(l, nmemb, i, j, t, p, size, cnt, tmp); + + /* + * For each element of the heap, save the largest element into its + * final slot, save the displaced element (k), then recreate the + * heap. + */ + while (nmemb > 1) { + COPY(k, base + nmemb * size, cnt, size, tmp1, tmp2); + COPY(base + nmemb * size, base + size, cnt, size, tmp1, tmp2); + --nmemb; + SELECT(i, j, nmemb, t, p, size, k, cnt, tmp1, tmp2); + } + free(k); + return (0); +} Added: trunk/math/m_int_vector.c =================================================================== --- trunk/math/m_int_vector.c (rev 0) +++ trunk/math/m_int_vector.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2004-2008 Hypertriton, Inc. <http://hypertriton.com/> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +#include <core/core.h> +#include "m.h" + +#define ASSERT_LENGTH(A, B) \ + if ((A)->n != (B)->n) \ + AG_FatalError("Incompatible vectors") + +M_IntVector * +M_IntVectorNew(Uint n) +{ + M_IntVector *veci; + + veci = Malloc(sizeof(M_IntVector)); + veci->v = Malloc(n*sizeof(int)); + veci->n = n; + return (veci); +} + +void +M_IntVectorSet(M_IntVector *v, int val) +{ + Uint i; + + for (i = 0; i < v->n; i++) + v->v[i] = val; +} + +void +M_IntVectorCopy(const M_IntVector *v1, M_IntVector *v2) +{ + Uint i; + + ASSERT_LENGTH(v1, v2); + for (i = 0; i < v1->n; i++) + v2->v[i] = v1->v[i]; +} + +void +M_IntVectorFree(M_IntVector *v) +{ + Free(v->v); + Free(v); +} + +void +M_IntVectorAddv(M_IntVector *v2, const M_IntVector *v1) +{ + Uint i; + + ASSERT_LENGTH(v1, v2); + for (i = 0; i < v1->n; i++) + v2->v[i] = v1->v[i]+v2->v[i]; +} + +void +M_IntVectorSubv(M_IntVector *v2, const M_IntVector *v1) +{ + Uint i; + + ASSERT_LENGTH(v1, v2); + for (i = 0; i < v1->n; i++) + v2->v[i] = v1->v[i]-v2->v[i]; +} + +void +M_IntVectorScalev(M_IntVector *v, M_Real c) +{ + Uint i; + + for (i = 0; i < v->n; i++) + v->v[i] *= c; +} + +void +M_IntVectorResize(M_IntVector *v, Uint n) +{ + v->v = Realloc(v->v, n*sizeof(int)); + v->n = n; +} + +void +M_IntVectorPrint(const M_IntVector *v) +{ + Uint i; + + fputs(" ----\n", stdout); + for (i = 0; i < v->n; i++) { + printf("| %4d: %d\n", i, v->v[i]); + } + fputs(" ----\n", stdout); +} Added: trunk/math/m_int_vector.h =================================================================== --- trunk/math/m_int_vector.h (rev 0) +++ trunk/math/m_int_vector.h 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,18 @@ +/* Public domain */ + +typedef struct m_int_vector { + Uint n; + int *v; +} M_IntVector; + +__BEGIN_DECLS +M_IntVector *M_IntVectorNew(Uint); +void M_IntVectorSet(M_IntVector *, int); +void M_IntVectorCopy(const M_IntVector *, M_IntVector *); +void M_IntVectorFree(M_IntVector *); +void M_IntVectorAddv(M_IntVector *, const M_IntVector *); +void M_IntVectorSubv(M_IntVector *, const M_IntVector *); +void M_IntVectorScalev(M_IntVector *, M_Real); +void M_IntVectorResize(M_IntVector *, Uint); +void M_IntVectorPrint(const M_IntVector *); +__END_DECLS Added: trunk/math/m_line.c =================================================================== --- trunk/math/m_line.c (rev 0) +++ trunk/math/m_line.c 2008-09-01 16:13:21 UTC (rev 7562) @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2007-2008 Hypertriton, Inc. <http://hypertriton.com/> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 AUTHOR 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. + */ + +/* + * Routines related to lines, line segments and rays. + */ + +#include <core/core.h> +#include "m.h" + +M_Line2 +M_LineRead2(AG_DataSource *ds) +{ + M_Line2 L; + + L.p = M_ReadVector2(ds); + L.d = M_ReadVector2(ds); + L.t = M_ReadReal(ds); + return (L); +} + +M_Line3 +M_LineRead3(AG_DataSource *ds) +{ + M_Line3 L; + + L.p = M_ReadVector3(ds); + L.d = M_ReadVector3(ds); + L.t = M_ReadReal(ds); + return (L); +} + +void +M_LineWrite2(AG_DataSource *ds, M_Line2 *L) +{ + M_WriteVector2(ds, &L->p); + M_WriteVector2(ds, &L->d); + M_WriteReal(ds, L->t); +} + +void +M_LineWrite3(AG_DataSource *ds, M_Line3 *L) +{ + M_WriteVector3(ds, &L->p); + M_WriteVector3(ds, &L->d); + M_WriteReal(ds, L->t); +} + +/* Create a line from a point, direction vector and length. */ +M_Line2 +M_LineFromPtDir2(M_Vector2 p, M_Vector2 d, M_Real len) +{ + M_Line2 L; + + L.p = p; + L.d = d; + L.t = len; + return (L); +} + +/* Create a line from a point, direction vector and length. */ +M_Line3 +M_LineFromPtDir3(M_Vector3 p, M_Vector3 d, M_Real len) +{ + M_Line3 L; + + L.p = p; + L.d = d; + L.t = len; + return (L); +} + +/* Create a line from two points in R2. */ +M_Line2 +M_LineFromPts2(M_Vector2 p1, M_Vector2 p2) +{ + M_Line2 L; + + L.p = p1; + L.d.x = p2.x - p1.x; + L.d.y = p2.y - p1.y; + L.t = M_VecLen2p(&L.d); + L.d.x /= L.t; + L.d.y /= L.t; + return (L); +} + +/* Create a line from two points in R3. */ +M_Line3 +M_LineFromPts3(M_Vector3 p1, M_Vector3 p2) +{ + M_Line3 L; + + L.p = p1; + L.d = M_VecSub3p(&p2, &p1); + L.t = M_VecLen3p(&L.d); + M_VecScale3v(&L.d, 1.0/L.t); + return (L); +} + +/* + * Compute a new line parallel to the given line, with perpendicular + * endpoints. + */ +M_Line2 +M_LineParallel2(M_Line2 L, M_Real dist) +{ + M_Vector2 p1, p2, pd; + + M_LineToPts2(L, &p1, &p2); + pd.x = L.d.y; + pd.y = -L.d.x; + M_VecScale2v(&pd, dist); + M_VecAdd2v(&p1, &pd); + M_VecAdd2v(&p2, &pd); + return M_LineFromPts2(p1, p2); +} + +/* + * Compute a new line parallel to the given line, with perpendicular + * endpoints. XXX this is a circle of solutions in R3 + */ +M_Line3 +M_LineParallel3(M_Line3 L, M_Real dist) +{ + M_Vector3 p1, p2, pd; + + M_LineToPts3(L, &p1, &p2); + pd.x = L.d.y; + pd.y = -L.d.x; + M_VecScale3v(&pd, dist); + M_VecAdd3v(&p1, &pd); + M_VecAdd3v(&p2, &pd); + return M_LineFromPts3(p1, p2); +} + +/* Compute a line in R2 from the projection on the X-Y plane of a line in R3. */ +M_Line2 +M_LineProject2(M_Line3 L3) +{ + M_Line2 L2; + + L2.p = M_Vector3to2(L3.p); + L2.d = M_Vector3to2(L3.d); + L2.t = M_VecLen2p(&L2.d); + L2.d.x /= L2.t; + L2.d.y /= L2.t; + return (L2); +} + +/* Project a line in R2 onto the X-Y plane of R3. */ +M_Line3 +M_LineProject3(M_Line2 L2) +{ + M_Line3 L3; + + L3.p = M_Vector2to3(L2.p); + L3.d = M_Vector2to3(L2.d); + L3.t = M_VecLen3p(&L3.d); + L3.d.x /= L3.t; + L3.d.y /= L3.t; + L3.d.z /= L3.t; + return (L3); +} + +/* Compute minimal distance from a line segment L to a point p. */ +M_Real +M_LinePointDistance2(M_Line2 L, M_Vector2 p3) +{ + M_Vector2 p1, p2, s; + M_Real u; + + /* [p3 - p1 - u(p2-p1)] dot (p2-p1) = 0 */ + M_LineToPts2(L, &p1, &p2); + u = ((p3.x - p1.x)*(p2.x - p1.x) + + (p3.y - p1.y)*(p2.y - p1.y)) / (L.t*L.t); + + /* s = p1 + u(p2-p1) */ + s = M_VecAdd2(p1, M_VecScale2(M_VecSub2(p2,p1), u)); + return M_VecDistance2p(&p3, &s); +} + +/* Compute minimal distance from a line segment L to a point p. */ +M_Real +M_LinePointDistance3(M_Line3 L, M_Vector3 p3) +{ + M_Vector3 p1, p2, s; + M_Real u; + + /* [p3 - p1 - u(p2-p1)] dot (p2-p1) = 0 */ + M_LineToPts3(L, &p1, &p2); + u = ((p3.x - p1.x)*(p2.x - p1.x) + + (p3.y - p1.y)*(p2.y - p1.y) + + (p3.z - p1.z)*(p2.z - p1.z)) / (L.t*L.t); + + /* s = p1 + u(p2-p1) */ + s = M_VecAdd3(p1, M_VecScale3(M_VecSub3(p2,p1), u)); + return M_VecDistance3p(&p3, &s); +} + +/* Compute the CCW angle between two lines in R2. */ +M_Real +M_LineLineAngle2(M_Line2 L1, M_Line2 L2) +{ + return (Atan2(L2.d.y - L1.d.y, + L2.d.x - L1.d.x)); +} + +/* Compute the CCW angle between two lines in R3. */ +M_Real +M_LineLineAngle3(M_Line3 L1, M_Line3 L2) +{ + return Acos(M_VecDot3(L1.d, L2.d)); +} + +/* Compute intersection between two line segments in R2. */ +M_GeomSet2 +M_IntersectLineLine2(M_Line2 L1, M_Line2 L2) +{ + M_GeomSet2 Sint = M_GEOM_SET_EMPTY; + M_Vector2 a1 = M_LineFirstPt2(L1); + M_Vector2 a2 = M_LineSecondPt2(L1); + M_Vector2 b1 = M_LineFirstPt2(L2); + M_Vector2 b2 = M_LineSecondPt2(L2); + M_Real a = (b2.x - b1.x)*(a1.y - b1.y) - (b2.y - b1.y)*(a1.x - b1.x); + M_Real b = (a2.x - a1.x)*(a1.y - b1.y) - (a2.y - a1.y)*(a1.x - b1.x); + M_Real c = (b2.y - b1.y)*(a2.x - a1.x) - (b2.x - b1.x)*(a2.y - a1.y); + M_Geom2 G; + + if (c != 0.0) { + M_Real ac = a/c; + M_Real bc = b/c; + + if (ac >= 0.0 && ac <= 1.0 && + bc >= 0.0 && bc <= 1.0) { + G.type = M_POINT; + G.g_point = M_VecAdd2(a |