Index: /tags/pap_tags/pap_branch_050518/psLib/src/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/.cvsignore	(revision 22271)
@@ -0,0 +1,11 @@
+DoxygenLog
+Makefile.in
+.deps
+.libs
+Makefile
+config.h
+*.la
+*.lo
+stamp-h1
+libpslib.la.temp
+config.h.in
Index: /tags/pap_tags/pap_branch_050518/psLib/src/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/Makefile.am	(revision 22271)
@@ -0,0 +1,36 @@
+SUBDIRS = astronomy collections dataManip dataIO image sysUtils
+
+INCLUDES = \
+	-I$(top_srcdir)/src/astronomy \
+	-I$(top_srcdir)/src/collections \
+	-I$(top_srcdir)/src/dataManip \
+	-I$(top_srcdir)/src/dataIO \
+	-I$(top_srcdir)/src/image \
+	-I$(top_srcdir)/src/sysUtils \
+	$(all_includes)
+
+lib_LTLIBRARIES = libpslib.la
+
+libpslib_la_LIBADD = \
+	$(top_builddir)/src/astronomy/libpslibastronomy.la \
+	$(top_builddir)/src/collections/libpslibcollections.la \
+	$(top_builddir)/src/dataManip/libpslibdataManip.la \
+	$(top_builddir)/src/dataIO/libpslibdataIO.la \
+	$(top_builddir)/src/image/libpslibimage.la \
+	$(top_builddir)/src/sysUtils/libpslibsysUtils.la 
+
+libpslib_la_SOURCES = psTest.c 
+libpslib_la_LDFLAGS = -version-info $(PSLIB_LT_VERSION) 
+
+EXTRA_DIST = parseErrorCodes.pl mainpage.dox psErrorCodes.dat psTest.h
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psTest.h \
+	pslib.h \
+	pslib_strict.h
+
+install-exec-hook: libpslib.la
+	sed "s|\(^dependency_libs=.*\)|dependency_libs=\'$(PSLIB_LIBS)\'|" $(libdir)/libpslib.la > libpslib.la.temp
+	$(INSTALL) libpslib.la.temp $(libdir)/libpslib.la
+    
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astro/psCoord.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astro/psCoord.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astro/psCoord.c	(revision 22271)
@@ -0,0 +1,1225 @@
+/** @file  psCoord.c
+*
+*  @brief Contains basic coordinate transformation definitions and operations
+*
+*  This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup CoordinateTransform
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.66 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-11 22:02:15 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include "psType.h"
+#include "psCoord.h"
+#include "psMemory.h"
+#include "psTime.h"
+#include "psConstants.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psAstronomyErrors.h"
+#include "psAstrometry.h"
+#include "psMatrix.h"
+#include <math.h>
+#include <float.h>
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+// Modified Julian Day 01/01/1900 00:00:00
+#define MJD_1900 15021.0
+
+// Days in Julian century
+#define JULIAN_CENTURY 36525.0
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+static void planeFree(psPlane *p)
+{
+    // There are non dynamic allocated items
+}
+
+/*****************************************************************************
+p_psPlaneTransformLinearInvert(transform): : this is a private function which
+simply inverts the supplied psPlaneTransform transform.  It assumes that
+"transform" is linear.
+ 
+This program assumes that the inverse of the following linear equations:
+        X2 = A + (B * X1) + (C * Y1);
+        Y2 = D + (E * X1) + (F * Y1);
+is
+        Y1 = (Y2 - ((E/B) * X2) - D + ((E*A)/B)) / (F - ((C*E)/B));
+        X1 = (Y2 - ((F/C) * X2) - D + ((F*A)/C)) / (E - ((F*B)/C));
+or
+ X1 = (-D + ((F*A)/C)) / (E - ((F*B)/C)) +
+      (X2 * -((F/C) / (E - ((F*B)/C)))) +
+      (Y2 * (1.0 / (E - ((F*B)/C))));
+ Y1 = (-D + ((E*A)/B))/(F - ((C*E)/B)) +
+      (X2 * -((E/B) / (F - ((C*E)/B)))) +
+      (Y2 * (1.0 / (F - ((C*E)/B))));
+ 
+XXX: Since thre is now a general psPlaneTransformInvert() function, we
+should rename this.
+ *****************************************************************************/
+psPlaneTransform *p_psPlaneTransformLinearInvert(psPlaneTransform *transform)
+{
+    PS_PTR_CHECK_NULL(transform, 0);
+    PS_PTR_CHECK_NULL(transform->x, 0);
+    PS_PTR_CHECK_NULL(transform->y, 0);
+
+    psF64 A = 0.0;
+    psF64 B = 0.0;
+    psF64 C = 0.0;
+    psF64 D = 0.0;
+    psF64 E = 0.0;
+    psF64 F = 0.0;
+
+    A = transform->x->coeff[0][0];
+    if (transform->x->nX >= 2) {
+        B = transform->x->coeff[1][0];
+    }
+    if (transform->x->nY >= 2) {
+        C = transform->x->coeff[0][1];
+    }
+    D = transform->y->coeff[0][0];
+    if (transform->y->nX >= 2) {
+        E = transform->y->coeff[1][0];
+    }
+    if (transform->y->nY >= 2) {
+        F = transform->y->coeff[0][1];
+    }
+
+    psPlaneTransform *out = psPlaneTransformAlloc(2, 2);
+
+    out->x->coeff[0][0] = (-D + ((F*A)/C)) / (E - ((F*B)/C));
+    out->x->coeff[1][0] = -(F/C) / (E - ((F*B)/C));
+    out->x->coeff[0][1] =  1.0 / (E - ((F*B)/C));
+    out->y->coeff[0][0] = (-D + ((E*A)/B)) / (F - ((C*E)/B));
+    out->y->coeff[1][0] = -(E/B) / (F - ((C*E)/B));
+    out->y->coeff[0][1] =  1.0 / (F - ((C*E)/B));
+
+    return(out);
+}
+
+/*****************************************************************************
+p_psIsProjectionLinear(): this is a private function which simply determines
+if the supplied psPlaneTransform transform is linear: if any of the
+cooefficients of order 2 are higher are non-zero, then it is not linear.
+ *****************************************************************************/
+psS32 p_psIsProjectionLinear(psPlaneTransform *transform)
+{
+    PS_PTR_CHECK_NULL(transform, 0);
+    PS_PTR_CHECK_NULL(transform->x, 0);
+    PS_PTR_CHECK_NULL(transform->y, 0);
+
+    for (psS32 i=0;i<(transform->x->nX);i++) {
+        for (psS32 j=0;j<(transform->x->nY);j++) {
+            if (transform->x->coeff[i][j] != 0.0) {
+                if (!(((i == 0) && (j == 0)) ||
+                        ((i == 0) && (j == 1)) ||
+                        ((i == 1) && (j == 0)))) {
+                    return(0);
+                }
+            }
+        }
+    }
+
+    for (psS32 i=0;i<(transform->y->nX);i++) {
+        for (psS32 j=0;j<(transform->y->nY);j++) {
+            if (transform->y->coeff[i][j] != 0.0) {
+                if (!(((i == 0) && (j == 0)) ||
+                        ((i == 0) && (j == 1)) ||
+                        ((i == 1) && (j == 0)))) {
+                    return(0);
+                }
+            }
+        }
+    }
+
+    return(1);
+}
+
+// XXX: Must test psPlaneAlloc() and planeFree().
+// XXX: Must rewrite code and tests to use these functions.
+psPlane* psPlaneAlloc(void)
+{
+    psPlane *p = psAlloc(sizeof(psPlane));
+
+    psMemSetDeallocator(p, (psFreeFcn) planeFree);
+    return(p);
+}
+
+
+static void sphereFree(psSphere *s)
+{
+    // There are non dynamic allocated items
+}
+
+psSphere* psSphereAlloc(void)
+{
+    psSphere *s = psAlloc(sizeof(psSphere));
+
+    psMemSetDeallocator(s, (psFreeFcn) sphereFree);
+    return(s);
+}
+
+static void planeTransformFree(psPlaneTransform *pt)
+{
+    psFree(pt->x);
+    psFree(pt->y);
+}
+
+psPlaneTransform* psPlaneTransformAlloc(psS32 n1, psS32 n2)
+{
+    PS_INT_CHECK_NON_NEGATIVE(n1, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n2, NULL);
+
+    psPlaneTransform *pt = psAlloc(sizeof(psPlaneTransform));
+    pt->x = psDPolynomial2DAlloc(n1, n2, PS_POLYNOMIAL_ORD);
+    pt->y = psDPolynomial2DAlloc(n1, n2, PS_POLYNOMIAL_ORD);
+
+    psMemSetDeallocator(pt, (psFreeFcn) planeTransformFree);
+    return(pt);
+}
+
+psPlane* psPlaneTransformApply(psPlane* out,
+                               const psPlaneTransform* transform,
+                               const psPlane* coords)
+{
+    PS_PTR_CHECK_NULL(transform, NULL);
+    PS_PTR_CHECK_NULL(transform->x, NULL);
+    PS_PTR_CHECK_NULL(transform->y, NULL);
+    PS_PTR_CHECK_NULL(coords, NULL);
+
+    if (out == NULL) {
+        out = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+    out->x = psDPolynomial2DEval(
+                 transform->x,
+                 coords->x,
+                 coords->y
+             );
+    out->y = psDPolynomial2DEval(
+                 transform->y,
+                 coords->x,
+                 coords->y
+             );
+    return (out);
+}
+
+static void planeDistortFree(psPlaneDistort *pt)
+{
+    psFree(pt->x);
+    psFree(pt->y);
+}
+
+psPlaneDistort* psPlaneDistortAlloc(psS32 n1, psS32 n2, psS32 n3, psS32 n4)
+{
+    PS_INT_CHECK_NON_NEGATIVE(n1, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n2, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n3, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n4, NULL);
+
+    psPlaneDistort *pt = psAlloc(sizeof(psPlaneDistort));
+    pt->x = psDPolynomial4DAlloc(n1, n2, n3, n4, PS_POLYNOMIAL_ORD);
+    pt->y = psDPolynomial4DAlloc(n1, n2, n3, n4, PS_POLYNOMIAL_ORD);
+
+    psMemSetDeallocator(pt, (psFreeFcn) planeDistortFree);
+    return(pt);
+}
+
+/******************************************************************************
+This transformation takes into account parameters beyond an objects spatial
+coordinates: term3 and term4 (magnitude and color).
+ *****************************************************************************/
+psPlane* psPlaneDistortApply(psPlane* out,
+                             const psPlaneDistort* transform,
+                             const psPlane* coords,
+                             float color,
+                             float magnitude)
+{
+    PS_PTR_CHECK_NULL(transform, NULL);
+    PS_PTR_CHECK_NULL(transform->x, NULL);
+    PS_PTR_CHECK_NULL(transform->y, NULL);
+    PS_PTR_CHECK_NULL(coords, NULL);
+
+    if (out == NULL) {
+        out = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+    out->x = psDPolynomial4DEval(
+                 transform->x,
+                 coords->x,
+                 coords->y,
+                 color,
+                 magnitude
+             );
+    out->y = psDPolynomial4DEval(
+                 transform->y,
+                 coords->x,
+                 coords->y,
+                 color,
+                 magnitude
+             );
+    return (out);
+}
+
+/******************************************************************************
+alpha is LONGITUDE
+delta is LATITUDE
+ 
+    alphaP: Take the target pole in the source system; calculate its LONGITUDE
+     in the target system.  That longitude is alphaP.
+    DeltaP: Take the target pole in the source system; calculate its LATITUDE
+     in the target system.  That longitude is deltaP.
+    phiP:   This is the LONGITUDE of the ascending node in the target system.
+ *****************************************************************************/
+psSphereTransform* psSphereTransformAlloc(psF64 alphaP,
+        psF64 deltaP,
+        psF64 phiP)
+{
+    psSphereTransform* tmp = (psSphereTransform* ) psAlloc(sizeof(psSphereTransform));
+
+    tmp->cosDeltaP = cos(deltaP);
+    tmp->sinDeltaP = sin(deltaP);
+    tmp->alphaP = alphaP;
+    tmp->phiP = phiP;
+
+    return (tmp);
+}
+
+/******************************************************************************
+XXX: Private Function.
+ 
+piNormalize(): take an input angle in radians and convert it to the range 0:2*PI.
+ *****************************************************************************/
+psF32 piNormalize(psF32 angle)
+{
+    while (angle < FLT_EPSILON) {
+        angle+=M_PI*2;
+    }
+
+    while (angle >= (M_PI*2)) {
+        angle-=M_PI*2;
+    }
+    return(angle);
+}
+
+/******************************************************************************
+XXX: We convert Right Ascension angles to the range 0:PI.  Is that acceptable?
+XXX: Should we do something for Declination as well?
+ *****************************************************************************/
+psSphere* psSphereTransformApply(psSphere* out,
+                                 const psSphereTransform* transform,
+                                 const psSphere* coord)
+{
+    PS_PTR_CHECK_NULL(transform, NULL);
+    PS_PTR_CHECK_NULL(coord, NULL);
+
+    if (out == NULL) {
+        out = (psSphere* ) psAlloc(sizeof(psSphere));
+    }
+
+    psF64 alpha = coord->r;
+    psF64 delta = coord->d;
+    psF64 alphaMinusAlphaP = alpha - transform->alphaP;
+
+    psF64 eq55 = (sin(delta) * transform->cosDeltaP) -
+                 (cos(delta) * transform->sinDeltaP * sin(alphaMinusAlphaP));
+    psF64 eq56 = (cos(delta) * transform->cosDeltaP * sin(alphaMinusAlphaP)) +
+                 (sin(delta) * transform->sinDeltaP);
+    psF64 eq57 = cos(delta) * cos(alphaMinusAlphaP);
+
+    psF64 theta = asin(eq55);
+    psF64 phi = atan2(eq56, eq57) + transform->phiP;
+    out->r = piNormalize(phi);
+    out->d = theta;
+
+    return(out);
+}
+
+psSphereTransform* psSphereTransformICRSToEcliptic(psTime *time)
+{
+    psF64 T;
+
+    // Check for null parameter
+    PS_PTR_CHECK_NULL(time, NULL);
+
+    // Convert psTime to MJD
+    psF64 MJD = psTimeToMJD(time);
+
+    // Check the specified MJD is greater than 1900
+    if ( MJD < MJD_1900 ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE,true,PS_ERRORTEXT_psCoord_INVALID_MJD);
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 1900
+    T = ( MJD - MJD_1900 ) / JULIAN_CENTURY;
+
+    psF64 alphaP = 0.0;
+    psF64 deltaP = DEG_TO_RAD(23.0) +
+                   MIN_TO_RAD(27.0) +
+                   SEC_TO_RAD(8.26) -
+                   (SEC_TO_RAD(46.845) * T) -
+                   (SEC_TO_RAD(0.0059) * T * T) +
+                   (SEC_TO_RAD(0.00181) * T * T * T);
+    psF64 phiP = 0.0;
+
+    // Don't neglect the minus sign on deltaP (bug 244):
+    return (psSphereTransformAlloc(alphaP, deltaP, phiP));
+}
+
+
+psSphereTransform* psSphereTransformEclipticToICRS(psTime *time)
+{
+    psF64 T;
+
+    // Check for null parameter
+    PS_PTR_CHECK_NULL(time, NULL);
+
+    // Convert psTime to MJD
+    psF64 MJD = psTimeToMJD(time);
+
+    // Check the specified MJD is greater than 1900
+    if ( MJD < MJD_1900 ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE,true,PS_ERRORTEXT_psCoord_INVALID_MJD);
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 1900
+    T = ( MJD - MJD_1900 ) / JULIAN_CENTURY;
+
+    psF64 alphaP = 0.0;
+    psF64 deltaP = DEG_TO_RAD(23.0) +
+                   MIN_TO_RAD(27.0) +
+                   SEC_TO_RAD(8.26) -
+                   (SEC_TO_RAD(46.845) * T) -
+                   (SEC_TO_RAD(0.0059) * T * T) +
+                   (SEC_TO_RAD(0.00181) * T * T * T);
+    psF64 phiP = 0.0;
+
+    return (psSphereTransformAlloc(alphaP, -deltaP, phiP));
+}
+
+// XXX: This is bug 245: alphaP swaps with phiP from psSphereTransformGalacticToICRS()
+psSphereTransform* psSphereTransformGalacticToICRS(void)
+{
+    psF64 alphaP = DEG_TO_RAD(32.93192);
+    psF64 deltaP = DEG_TO_RAD(-62.87175);
+    psF64 phiP = DEG_TO_RAD(282.85948);
+
+    return (psSphereTransformAlloc(alphaP, deltaP, phiP));
+}
+
+psSphereTransform* psSphereTransformICRSToGalactic(void)
+{
+    psF64 alphaP = DEG_TO_RAD(282.85948);
+    psF64 deltaP = DEG_TO_RAD(62.87175);
+    psF64 phiP = DEG_TO_RAD(32.93192);
+
+    return (psSphereTransformAlloc(alphaP, deltaP, phiP));
+}
+
+void projectionFree(psProjection *p)
+{
+    // There are no dynamically allocated items
+}
+
+psProjection* psProjectionAlloc(
+    psF64 R,
+    psF64 D,
+    psF64 Xs,
+    psF64 Ys,
+    psProjectionType type)
+{
+    psProjection *p = psAlloc(sizeof(psProjection));
+    p->D = D;
+    p->R = R;
+    p->Xs = Xs;
+    p->Ys = Ys;
+    p->type = type;
+
+    psMemSetDeallocator(p, (psFreeFcn) projectionFree);
+    return(p);
+}
+
+psPlane* psProject(const psSphere* coord,
+                   const psProjection* projection)
+{
+    PS_PTR_CHECK_NULL(coord, NULL);
+    PS_PTR_CHECK_NULL(projection, NULL);
+
+    psF64   theta = 0.0;
+    psF64   phi   = 0.0;
+
+    // Allocate return value
+    psPlane* out = psPlaneAlloc();
+
+    // Convert to projection spherical coordinate system
+    theta = asin( sin(coord->d)*sin(projection->D) +
+                  cos(coord->d)*cos(projection->D)*cos(coord->r-projection->R));
+    phi = atan2( -1.0*cos(coord->d)*sin(coord->r-projection->R),
+                 sin(coord->d)*cos(projection->D) - cos(coord->d)*sin(projection->D)*cos(coord->r-projection->R) );
+
+    // Perform the specified projection
+    // Gnomonic projection
+    if (projection->type == PS_PROJ_TAN) {
+        out->x = (cos(theta)*sin(phi))/sin(theta);
+        out->y = (-1.0*cos(theta)*cos(phi))/sin(theta);
+        // Othrographic projection
+    } else if (projection->type == PS_PROJ_SIN) {
+        out->x = cos(theta)*sin(phi);
+        out->y = -1.0*cos(theta)*cos(phi);
+        // Hammer-Aitoff projection
+    } else if ( projection->type == PS_PROJ_AIT) {
+        psF64 zeta = 1.0/sqrt(0.5*(1.0+cos(theta)*cos(phi/2.0)));
+        out->x = 2.0*zeta*cos(theta)*sin(phi/2.0);
+        out->y = zeta*sin(theta);
+        // Parabolic projection
+    } else if ( projection->type == PS_PROJ_PAR) {
+        out->x = phi*(2.0*cos(2.0*theta/3.0) - 1.0);
+        out->y = M_PI*sin(theta/3.0);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psCoord_PROJECTION_TYPE_UNKNOWN,
+                projection->type);
+        psFree(out);
+        return NULL;
+    }
+
+    // Apply plate scales
+    out->x *= projection->Xs;
+    out->y *= projection->Ys;
+
+    // Return output
+    return out;
+}
+
+psSphere* psDeproject(const psPlane* coord,
+                      const psProjection* projection)
+{
+    PS_PTR_CHECK_NULL(coord, NULL);
+    PS_PTR_CHECK_NULL(projection, NULL);
+
+    psF64  theta = 0.0;
+    psF64  phi   = 0.0;
+
+    // Allocate return sphere structure
+    psSphere* out = psSphereAlloc();
+
+    // Remove plate scales
+    psF64  x = coord->x/projection->Xs;
+    psF64  y = coord->y/projection->Ys;
+
+    // Perform inverse projection
+    // Gnonomic deprojection
+    if ( projection->type == PS_PROJ_TAN) {
+        phi = atan(-1.0*x/y);
+        theta = atan(1.0/sqrt(x*x+y*y));
+        // Orhtographic deprojection
+    } else if ( projection->type == PS_PROJ_SIN) {
+        phi = atan((-1.0*x)/y);
+        theta = atan( sqrt(1.0-(x*x+y*y)) / sqrt(x*x+y*y));
+        // Hammer-Aitoff deprojection
+    } else if ( projection->type == PS_PROJ_AIT) {
+        psF64 z = sqrt(1.0 - ((x/4.0)*(x/4.0)) - ((y/2.0)*(y/2.0)));
+        phi = 2.0*atan((z*x) / (2.0*(2.0*z*z-1.0)) );
+        theta = asin(y*z);
+        // Parabolic deprojection
+    } else if ( projection->type == PS_PROJ_PAR) {
+        psF64 rho = y/M_PI;
+        phi = x/(1.0 - 4.0*rho*rho);
+        theta = 3.0*asin(rho);
+        // Invalid deprojection type
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psCoord_PROJECTION_TYPE_UNKNOWN,
+                projection->type);
+        psFree(out);
+        return NULL;
+    }
+
+    // Convert from projection spherical coordinates
+    out->d = asin( sin(theta)*sin(projection->D) +
+                   cos(theta)*cos(projection->D)*cos(phi) );
+    out->r = projection->R + atan2( -1.0*cos(theta)*sin(phi),
+                                    sin(theta)*cos(projection->D) -
+                                    cos(theta)*sin(projection->D)*cos(phi) );
+
+    // Return sphere coordinate
+    return out;
+}
+
+/******************************************************************************
+The basic idea is to project both positions onto the linear plane, with
+position1 at the center, then calculate the linear offset between those
+projections.
+ 
+XXX: Do I need to check for unacceptable transformation parameters?  Maybe,
+     if the points are on the North/South Pole, etc?
+ 
+XXX: Do I need to somehow scale this projection?
+ 
+XXX: Does PS_LINEAR mode make sense?  The result must be returned in psSphere
+     regardless of the mode.
+ 
+XXX: How to compound errors?
+ *****************************************************************************/
+psSphere* psSphereGetOffset(const psSphere* position1,
+                            const psSphere* position2,
+                            psSphereOffsetMode mode,
+                            psSphereOffsetUnit unit)
+{
+    PS_PTR_CHECK_NULL(position1, NULL);
+    PS_PTR_CHECK_NULL(position2, NULL);
+
+    // Check positions near 90 degree and issue warnings if necessary
+    if (position1->d >= DEG_TO_RAD(90.0)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psDeproject(): position1->d is larger than 90 degrees.  Returning NULL.");
+        return NULL;
+    }
+    if (position2->d >= DEG_TO_RAD(90.0)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psDeproject(): position2->d is larger than 90 degrees.  Returning NULL.");
+        return NULL;
+    }
+
+    // Allocate return structure
+    psSphere* tmp = psSphereAlloc();
+
+    // Mode is LINEAR - Use first position as projection center and project second point
+    // onto tangent plane, set point projected into psSphere structure x->r y->d
+    if (mode == PS_LINEAR) {
+        psProjection* proj = psProjectionAlloc(position1->r,
+                                               position1->d,
+                                               1.0,
+                                               1.0,
+                                               PS_PROJ_TAN);
+
+        // Perform projection onto tangent plane
+        psPlane* lin = psProject(position2, proj);
+
+        // Set return values
+        tmp->r = lin->x;
+        tmp->d = lin->y;
+
+        // Free data structures allocated
+        psFree(proj);
+        psFree(lin);
+
+        // Mode is SPHERICAL - Get difference between positiion 1 and position 2 and convert
+        // offset value from radians to desired units and return
+    } else if (mode == PS_SPHERICAL) {
+        tmp->r = position2->r - position1->r;
+        tmp->d = position2->d - position1->d;
+
+        // Wrap these to an acceptable range.  This assumes that all
+        // angles are in radians.
+        tmp->r = fmod(tmp->r, 2*M_PI);
+        tmp->d = fmod(tmp->d, 2*M_PI);
+        tmp->rErr = 0.0;
+        tmp->dErr = 0.0;
+
+        // Convert to desired units
+        if (unit == PS_ARCSEC) {
+            tmp->r = RAD_TO_SEC(tmp->r);
+            tmp->d = RAD_TO_SEC(tmp->d);
+        } else if (unit == PS_ARCMIN) {
+            tmp->r = RAD_TO_MIN(tmp->r);
+            tmp->d = RAD_TO_MIN(tmp->d);
+        } else if (unit == PS_DEGREE) {
+            tmp->r = RAD_TO_DEG(tmp->r);
+            tmp->d = RAD_TO_DEG(tmp->d);
+        } else if (unit == PS_RADIAN) {}
+        else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psCoord_UNITS_UNKNOWN,
+                    unit);
+            psFree(tmp);
+            return NULL;
+        }
+        // Invalid mode
+    } else {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psCoord_OFFSET_MODE_UNKNOWN,
+                mode);
+        psFree(tmp);
+        return NULL;
+    }
+
+    // Return value
+    return tmp;
+}
+
+/******************************************************************************
+XXX: Do we need to check for unacceptable transformation parameters?  Maybe,
+     if the points are on the North/South Pole, etc?
+ 
+XXX: Do we need to somehow scale this projection?
+ 
+XXX: I copied the algorithm from the ADD exactly.
+ 
+XXX: Should we compound errors?
+ *****************************************************************************/
+
+psSphere* psSphereSetOffset(const psSphere* position,
+                            const psSphere* offset,
+                            psSphereOffsetMode mode,
+                            psSphereOffsetUnit unit)
+{
+    PS_PTR_CHECK_NULL(position, NULL);
+    PS_PTR_CHECK_NULL(offset, NULL);
+
+    psSphere* tmp;
+    psF64 tmpR = 0.0;
+    psF64 tmpD = 0.0;
+
+    // If mode is linear then set position to projection center
+    // and offset to linear coordinate then deproject to obtain
+    // new sphere coordinate
+    if (mode == PS_LINEAR) {
+
+        // Allocate plane coordinate and set coordinate
+        psPlane*  lin = psPlaneAlloc();
+        lin->x = offset->r;
+        lin->y = offset->d;
+
+        // Allocate and set projection structure
+        psProjection* proj = psProjectionAlloc(position->r,
+                                               position->d,
+                                               1.0,
+                                               1.0,
+                                               PS_PROJ_TAN);
+
+        // Project tangent plane coord to spherical coord
+        tmp = psDeproject(lin, proj);
+
+        // Free data structures used
+        psFree(proj);
+        psFree(lin);
+
+        // If mode is spherical then convert offset to radians, add the offset
+        // to the position and wrap to 0 to 2pi
+    } else if (mode == PS_SPHERICAL) {
+
+        // Convert offset unit to radians
+        if (unit == PS_ARCSEC) {
+            tmpR = SEC_TO_RAD(offset->r);
+            tmpD = SEC_TO_RAD(offset->d);
+        } else if (unit == PS_ARCMIN) {
+            tmpR = MIN_TO_RAD(offset->r);
+            tmpD = MIN_TO_RAD(offset->d);
+        } else if (unit == PS_DEGREE) {
+            tmpR = DEG_TO_RAD(offset->r);
+            tmpD = DEG_TO_RAD(offset->d);
+        } else if (unit == PS_RADIAN) {
+            tmpR = offset->r;
+            tmpD = offset->d;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psCoord_UNITS_UNKNOWN,
+                    unit);
+            return NULL;
+        }
+
+        // Allocate sphere structure to return
+        tmp = psSphereAlloc();
+
+        // Add offset and wrap to 0 to 2PI if necessary
+        tmp->r = position->r + tmpR;
+        tmp->r = fmod(tmp->r, 2.0*M_PI);
+        tmp->d = position->d + tmpD;
+        tmp->d = fmod(tmp->d, 2.0*M_PI);
+        tmp->rErr = 0.0;
+        tmp->dErr = 0.0;
+
+        // Invalid mode report error
+    } else {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psCoord_OFFSET_MODE_UNKNOWN,
+                mode);
+        return NULL;
+    }
+
+    return tmp;
+}
+
+
+
+/******************************************************************************
+psSpherePrecess(coords, fromTime, toTime):
+ 
+XXX: Use static memory for tmpST.
+ *****************************************************************************/
+psSphere *psSpherePrecess(psSphere *coords,
+                          const psTime *fromTime,
+                          const psTime *toTime)
+{
+    // Check input for NULL pointers
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(fromTime, NULL);
+    PS_PTR_CHECK_NULL(toTime, NULL);
+
+    // Calculate Julian centuries
+    psF64 fromMJD = psTimeToMJD(fromTime);
+    psF64 toMJD = psTimeToMJD(toTime);
+    psF64 T = (toMJD - fromMJD) / JULIAN_CENTURY;
+
+    // Calculate conversion constants
+    psF64 alphaP = DEG_TO_RAD(90.0) - ((DEG_TO_RAD(0.6406161) * T) +
+                                       (DEG_TO_RAD(0.0000839) * T * T) +
+                                       (DEG_TO_RAD(0.000005) * T * T * T));
+
+    psF64 deltaP = (DEG_TO_RAD(0.5567530) * T) -
+                   (DEG_TO_RAD(0.0001185) * T * T) -
+                   (DEG_TO_RAD(0.0000116) * T * T * T);
+
+    psF64 phiP = DEG_TO_RAD(90.0) + ((DEG_TO_RAD(0.6406161) * T) +
+                                     (DEG_TO_RAD(0.0003041) * T * T) +
+                                     (DEG_TO_RAD(0.0000051) * T * T * T));
+
+    // Create transform with proper constants
+    psSphereTransform *tmpST = psSphereTransformAlloc(alphaP, deltaP, phiP);
+
+    // Apply transform to coordinates
+    psSphere *out = psSphereTransformApply(NULL, tmpST, coords);
+
+    psFree(tmpST);
+
+    return(out);
+}
+
+/*****************************************************************************
+multiplyDPoly2D(trans1, trans2): Takes two 2-D polynomials as input and
+multiplies them.  Basically, for each non-zero coeff in the trans1 coeff[][]
+array, you must multiply by all non-zero coeffs in trans2.
+ 
+XXX: Inefficient in that the out polynomial is allocated every time.
+ *****************************************************************************/
+
+psDPolynomial2D *multiplyDPoly2D(psDPolynomial2D *trans1,
+                                 psDPolynomial2D *trans2)
+{
+    //TRACE: printf("multiplyDPoly2D(%d %d: %d %d)\n", trans1->nX, trans1->nY, trans2->nX, trans2->nY);
+    psS32 orderX = (trans1->nX + trans2->nX) - 1;
+    psS32 orderY = (trans1->nY + trans2->nY) - 1;
+
+    psDPolynomial2D *out = psDPolynomial2DAlloc(orderX, orderY, PS_POLYNOMIAL_ORD);
+    //TRACE: printf("Creating poly (%d, %d)\n", orderX, orderY);
+    for (psS32 i = 0 ; i < out->nX; i++) {
+        for (psS32 j = 0 ; j < out->nY; j++) {
+            out->coeff[i][j] = 0.0;
+            out->mask[i][j] = 0;
+        }
+    }
+
+    for (psS32 t1x = 0 ; t1x < trans1->nX ; t1x++) {
+        for (psS32 t1y = 0 ; t1y < trans1->nY ; t1y++) {
+            if (0.0 != trans1->coeff[t1x][t1y]) {
+                for (psS32 t2x = 0 ; t2x < trans2->nX ; t2x++) {
+                    for (psS32 t2y = 0 ; t2y < trans2->nY ; t2y++) {
+                        /* Possible debug-only macro which checks these coords?
+                        if ((t1x+t2x) >= orderX)
+                            printf("BAD 1\n");
+                        if ((t1y+t2y) >= orderY)
+                            printf("BAD 2\n");
+                        */
+                        out->coeff[t1x+t2x][t1y+t2y]+= (trans1->coeff[t1x][t1y] * trans2->coeff[t2x][t2y]);
+                    }
+                }
+            }
+        }
+    }
+    return(out);
+}
+
+
+/*****************************************************************************
+psPlaneTransformCombine(out, trans1, trans2)
+ 
+XXX: Much room for optimization.  Currently, we call the polyMultiply
+routine far too many times.
+ *****************************************************************************/
+psPlaneTransform *psPlaneTransformCombine(psPlaneTransform *out,
+        const psPlaneTransform *trans1,
+        const psPlaneTransform *trans2)
+{
+    PS_PTR_CHECK_NULL(trans1, NULL);
+    PS_PTR_CHECK_NULL(trans2, NULL);
+    //TRACE: printf("psPlaneTransformCombine(%d, %d, %d, %d: %d, %d, %d, %d)\n", trans1->x->nX, trans1->x->nY, trans1->y->nX, trans1->y->nY, trans2->x->nX, trans2->x->nY, trans2->y->nX, trans2->y->nY);
+    //
+    // Determine the size of the new psPlaneTransform.
+    //
+    // PS_MAX(  Number of x terms in T2->x * number of x terms in T1->x,
+    //          Number of y terms in T2->x * number of x terms in T1->y,
+    psS32 orderXnX = PS_MAX((trans2->x->nX * trans1->x->nX),
+                            (trans2->x->nY * trans1->y->nX));
+    psS32 orderXnY = PS_MAX((trans2->x->nX * trans1->x->nY),
+                            (trans2->x->nY * trans1->y->nY));
+
+    psS32 orderYnX = PS_MAX((trans2->y->nX * trans1->x->nX),
+                            (trans2->y->nY * trans1->y->nX));
+    psS32 orderYnY = PS_MAX((trans2->y->nX * trans1->x->nY),
+                            (trans2->y->nY * trans1->y->nY));
+    psS32 orderX = PS_MAX(orderXnX, orderYnX);
+    psS32 orderY = PS_MAX(orderXnY, orderYnY);
+
+    //
+    // Allocate the new psPlaneTransform, if necessary.
+    //
+    psPlaneTransform *myPT = NULL;
+    if (out == NULL) {
+        myPT = psPlaneTransformAlloc(orderX, orderY);
+    } else {
+        if ((out->x->nX == orderX) && (out->x->nY == orderY) &&
+                (out->y->nX == orderX) && (out->y->nY == orderY)) {
+            myPT = out;
+        } else {
+            psFree(out);
+            myPT = psPlaneTransformAlloc(orderX, orderY);
+        }
+    }
+
+    //
+    // Initialize the new psPlaneTransform, if necessary.
+    //
+    for (psS32 i = 0 ; i < orderX ; i++) {
+        for (psS32 j = 0 ; j < orderY ; j++) {
+            myPT->x->coeff[i][j] = 0.0;
+            myPT->x->mask[i][j] = 0;
+            myPT->y->coeff[i][j] = 0.0;
+            myPT->y->mask[i][j] = 0;
+        }
+    }
+
+    //
+    // For each term (a * x^i * y^j) in trans2, we substitute the appropriate
+    // equation from trans1, and raise it to the appropriate power.  This is
+    // done via the multiplyDPoly2D().  The result is a polynomial (currPoly)
+    // and its coefficients are added into the myPT coeff matrix.
+    //
+    // XXX: This is horribly inefficient in that the trans1 polys are repeatedly
+    // multiplied against themselves.  This can easily be improved.
+    //
+
+    for (psS32 t2x = 0 ; t2x < trans2->x->nX ; t2x++) {
+        for (psS32 t2y = 0 ; t2y < trans2->x->nY ; t2y++) {
+            psDPolynomial2D *currPoly = psDPolynomial2DAlloc(1, 1, PS_POLYNOMIAL_ORD);
+
+            currPoly->coeff[0][0] = 1.0;
+            currPoly->mask[0][0] = 0;
+            psDPolynomial2D *newPoly = NULL;
+
+            if (trans2->x->mask[t2x][t2y] == 0) {
+                // Must raise trans1->y to the t2y-power.
+                for (psS32 c = 0 ; c < t2y; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->y);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Must raise trans1->x to the t2x-power.
+                for (psS32 c = 0 ; c < t2x; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->x);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Set the appropriate coeffs in myPT->x
+                for (psS32 i = 0 ; i < currPoly->nX ; i++) {
+                    for (psS32 j = 0 ; j < currPoly->nY ; j++) {
+                        myPT->x->coeff[i][j]+= currPoly->coeff[i][j] * trans2->x->coeff[t2x][t2y];
+                    }
+                }
+            }
+            psFree(currPoly);
+        }
+    }
+
+
+    for (psS32 t2x = 0 ; t2x < trans2->y->nX ; t2x++) {
+        for (psS32 t2y = 0 ; t2y < trans2->y->nY ; t2y++) {
+            psDPolynomial2D *currPoly = psDPolynomial2DAlloc(1, 1, PS_POLYNOMIAL_ORD);
+            currPoly->coeff[0][0] = 1.0;
+            currPoly->mask[0][0] = 0;
+            psDPolynomial2D *newPoly = NULL;
+
+            if (trans2->y->mask[t2x][t2y] == 0) {
+
+                // Must raise trans1->y to the t2y-power.
+                for (psS32 c = 0 ; c < t2y; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->y);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Must raise trans1->x to the t2x-power.
+                for (psS32 c = 0 ; c < t2x; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->x);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Set the appropriate coeffs in myPT->x
+                for (psS32 i = 0 ; i < currPoly->nX ; i++) {
+                    for (psS32 j = 0 ; j < currPoly->nY ; j++) {
+                        myPT->y->coeff[i][j]+= currPoly->coeff[i][j] * trans2->y->coeff[t2x][t2y];
+                    }
+                }
+            }
+            psFree(currPoly);
+        }
+    }
+
+    //TRACE: printf("Exiting combine()\n");
+    return(myPT);
+}
+
+/*****************************************************************************
+psPlaneTransformFit(trans, source, dest, nRejIter, sigmaClip)
+ 
+XXX: What about nRejIter?  Iterations?
+XXX: Use static vectors for internal data.
+ *****************************************************************************/
+bool psPlaneTransformFit(psPlaneTransform *trans,
+                         const psArray *source,
+                         const psArray *dest,
+                         int nRejIter,
+                         float sigmaClip)
+{
+    PS_PTR_CHECK_NULL(trans, NULL);
+    PS_PTR_CHECK_NULL(source, NULL);
+    PS_PTR_CHECK_NULL(dest, NULL);
+
+    psS32 numCoords = PS_MIN(source->n, dest->n);
+    psS32 order = PS_MAX(trans->x->nX, trans->x->nY);
+
+    //
+    // Create fake polynomial to use in evaluation
+    //
+    psDPolynomial2D *fakePoly = psDPolynomial2DAlloc(order, order, PS_POLYNOMIAL_ORD);
+    for (int i = 0; i < order; i++) {
+        for (int j = 0; j < order; j++) {
+            fakePoly->coeff[i][j] = 1.0;
+            fakePoly->mask[i][j] = 1;       // Mask all coefficients; unmask to evaluate
+        }
+    }
+
+    //
+    // Initialize the matrix and vectors
+    //
+    psS32 nCoeff = order * (order + 1) / 2; // Number of polynomial coefficients
+    psImage *matrix = psImageAlloc(nCoeff, nCoeff, PS_TYPE_F64); // Matrix for solution
+    psVector *xVector = psVectorAlloc(nCoeff, PS_TYPE_F64); // Vector for solution in x
+    psVector *yVector = psVectorAlloc(nCoeff, PS_TYPE_F64); // Vector for solution in y
+    for (psS32 i = 0; i < nCoeff; i++) {
+        for (psS32 j = 0; j < nCoeff; j++) {
+            matrix->data.F64[i][j] = 0.0;
+        }
+        xVector->data.F64[i] = 0.0;
+        yVector->data.F64[i] = 0.0;
+    }
+
+    //
+    // Iterate over the grid points
+    //
+    for (psS32 g = 0; g < numCoords; g++) {
+        // Iterate over the polynomial coefficients, accumulating the matrix and vectors
+
+        for (psS32 i = 0, ijIndex = 0; i < order; i++) {
+            for (psS32 j = 0; j < order - i; j++, ijIndex++) {
+                fakePoly->mask[i][j] = 0;
+                psF64 xIn = ((psPlane *) source->data[g])->x;
+                psF64 yIn = ((psPlane *) source->data[g])->y;
+                psF64 xOut = ((psPlane *) dest->data[g])->x;
+                psF64 yOut = ((psPlane *) dest->data[g])->y;
+                psF64 ijPoly = psDPolynomial2DEval(fakePoly, xIn, yIn);
+                fakePoly->mask[i][j] = 1;
+
+                for (psS32 m = 0, mnIndex = 0; m < order; m++) {
+                    for (psS32 n = 0; n < order - m; n++, mnIndex++) {
+                        fakePoly->mask[m][n] = 0;
+                        psF64 mnPoly = psDPolynomial2DEval(fakePoly, xIn, yIn);
+                        fakePoly->mask[m][n] = 1;
+
+                        matrix->data.F64[ijIndex][mnIndex] += ijPoly * mnPoly;
+                    }
+                }
+
+                xVector->data.F64[ijIndex] += ijPoly * xOut;
+                yVector->data.F64[ijIndex] += ijPoly * yOut;
+            }
+        }
+    }
+
+    //
+    // Solution via LU Decomposition
+    //
+    psVector *permutation = psVectorAlloc(nCoeff, PS_TYPE_F64); // Permutation vector for LU Decomposition
+    psImage *luMatrix = psMatrixLUD(NULL, &permutation, matrix); // LU decomposed matrix
+    psVector *xSolution = psMatrixLUSolve(NULL, luMatrix, xVector, permutation); // Solution in x
+    psVector *ySolution = psMatrixLUSolve(NULL, luMatrix, yVector, permutation); // Solution in y
+
+    //
+    // XXX: Should check the output of the matrix routines and return false if bad.
+    //
+
+    //
+    // Stuff coefficients into transformation
+    //
+    for (psS32 i = 0, ijIndex = 0; i < order; i++) {
+        for (psS32 j = 0; j < order - i; j++, ijIndex++) {
+            trans->x->coeff[i][j] = xSolution->data.F64[ijIndex];
+            trans->y->coeff[i][j] = ySolution->data.F64[ijIndex];
+        }
+    }
+
+    psFree(fakePoly);
+    psFree(permutation);
+    psFree(luMatrix);
+    psFree(xSolution);
+    psFree(ySolution);
+    psFree(matrix);
+    psFree(xVector);
+    psFree(yVector);
+
+    return(true);
+}
+
+
+/*****************************************************************************
+psPlaneTransformInvert(out, in, region, nSamples)
+ 
+// XXX: Use static data structures.
+ *****************************************************************************/
+psPlaneTransform *psPlaneTransformInvert(psPlaneTransform *out,
+        const psPlaneTransform *in,
+        psRegion *region,
+        int nSamples)
+{
+    PS_PTR_CHECK_NULL(in, NULL);
+    //
+    // If the transform is linear, then invert it exactly and return.
+    //
+    if (p_psIsProjectionLinear((psPlaneTransform *) in)) {
+        return(p_psPlaneTransformLinearInvert((psPlaneTransform *) in));
+    }
+    PS_PTR_CHECK_NULL(region, NULL);
+    PS_INT_COMPARE(1, nSamples, NULL);
+
+    // Ensure that the input transformation is symmetrical.
+    if ((in->x->nX != in->x->nY) ||
+            (in->y->nX != in->y->nY) ||
+            (in->x->nX != in->y->nX)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Input transformation must have same nX==nY.");
+    }
+    psS32 order = PS_MAX(in->x->nX, in->x->nY);
+
+    psPlaneTransform *myPT = NULL;
+    psPlane *inCoord = psPlaneAlloc();
+    psPlane *outCoord = psPlaneAlloc();
+
+    //
+    // Allocate a new psPlaneTransform if "out" is NULL, or has the wrong size.
+    //
+    if (out == NULL) {
+        myPT = psPlaneTransformAlloc(order, order);
+    } else {
+        if ((out->x->nX == order) && (out->x->nY == order) &&
+                (out->y->nX == order) && (out->y->nY == order)) {
+            myPT = out;
+        } else {
+            psFree(out);
+            myPT = psPlaneTransformAlloc(order, order);
+        }
+    }
+
+    //
+    // Copy the input transform to myPT.
+    //
+    for (psS32 i = 0 ; i < in->x->nX ; i++) {
+        for (psS32 j = 0 ; j < in->x->nY ; j++) {
+            myPT->x->coeff[i][j] = in->x->coeff[i][j];
+        }
+    }
+    for (psS32 i = 0 ; i < in->y->nX ; i++) {
+        for (psS32 j = 0 ; j < in->y->nY ; j++) {
+            myPT->y->coeff[i][j] = in->y->coeff[i][j];
+        }
+    }
+
+    //
+    // Create a grid of xin,yin --> xout,yout
+    //
+    psArray *inData = psArrayAlloc(nSamples * nSamples);
+    psArray *outData = psArrayAlloc(nSamples * nSamples);
+    for (psS32 i = 0 ; i < inData->n; i++) {
+        inData->data[i] = (psPtr *) psPlaneAlloc();
+        outData->data[i] = (psPtr *) psPlaneAlloc();
+    }
+
+    //
+    // Initialize the grid.
+    //
+    psS32 cnt = 0;
+    for (int yint = 0; yint < nSamples; yint++) {
+        inCoord->y = region->y0 + ((psF32) yint) * ((region->y1 - region->y0) / ((psF32) nSamples));
+        for (int xint = 0; xint < nSamples; xint++) {
+            inCoord->x = region->x0 + ((psF32) xint) * ((region->x1 - region->x0) / ((psF32) nSamples));
+            (void)psPlaneTransformApply(outCoord, in, inCoord);
+
+            ((psPlane *) outData->data[cnt])->x = inCoord->x;
+            ((psPlane *) outData->data[cnt])->y = inCoord->y;
+            ((psPlane *) inData->data[cnt])->x = outCoord->x;
+            ((psPlane *) inData->data[cnt])->y = outCoord->y;
+
+            cnt++;
+        }
+    }
+    bool rc = psPlaneTransformFit(myPT, inData, outData, 10, 100.0);
+
+    psFree(inCoord);
+    psFree(outCoord);
+    psFree(inData);
+    psFree(outData);
+
+    if (rc == true) {
+        return(myPT);
+    }
+
+    // XXX: Generate an error message, or warning message.
+    return(NULL);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astro/psCoord.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astro/psCoord.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astro/psCoord.h	(revision 22271)
@@ -0,0 +1,436 @@
+/** @file  psCoord.h
+*
+*  @brief Contains basic coordinate transformation definitions and operations
+*
+*  This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup CoordinateTransform
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.30 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-03-31 23:01:46 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_COORD_H
+#define PS_COORD_H
+
+#include "psType.h"
+#include "psImage.h"
+#include "psArray.h"
+#include "psList.h"
+#include "psFunctions.h"
+#include "psTime.h"
+
+/// @addtogroup CoordinateTransform
+/// @{
+
+/** Euclidiean Coordinate System.
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is linear coordinates which conform to
+ *  Euclidean geometry.
+ *
+ */
+typedef struct
+{
+    double x;                   ///< x position
+    double y;                   ///< y position
+    double xErr;                ///< Error in x position
+    double yErr;                ///< Error in y position
+}
+psPlane;
+
+/** Angular Coordinate System
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is angular coordinates for which additional
+ *  care must often be taken in comparison to a euclidiean coordinate system.
+ *
+ */
+typedef struct psSphere
+{
+    double r;                   ///< RA
+    double d;                   ///< Dec
+    double rErr;                ///< Error in RA
+    double dErr;                ///< Error in Dec
+}
+psSphere;
+
+/** 2D Polynomial Transform
+ *
+ *  A transform between coordinate systems that consists simply of two 2D
+ *  polynomials to transform both components - the output coordinates depend
+ *  only on the input coordinates and no other quantities of objects at those
+ *  coordinates.
+ *
+ */
+typedef struct
+{
+    psDPolynomial2D* x;         ///< 2D polynomial transform of X coordinates
+    psDPolynomial2D* y;         ///< 2D polynomial transform of Y coordinates
+}
+psPlaneTransform;
+
+/** 4D Polynomial Transform
+ *
+ *  A transform between coordinate systems that consists of two 4D polynomials
+ *  in which the output coordinates are also specified to be a function of the
+ *  magnitude and color of the object with the given coordinates. This type of
+ *  coordinate transformation is necessary to represent the (color-dependent)
+ *  optical distortions caused by the atmosphere and camera optics, and the
+ *  possibly effects of charge transfer inefficiency.
+ *
+ *  The lowest two terms are the x and y axis of the target system.  The higher
+ *  two terms may represent magnitude and color terms.
+ */
+typedef struct
+{
+    psDPolynomial4D* x;         ///< 4D polynomial transform of X coordinates
+    psDPolynomial4D* y;         ///< 4D polynomial transform of Y coordinates
+}
+psPlaneDistort;
+
+/** Spherical Transform Definition
+ *
+ *  We need to be able to convert between ICRS, Galactic and Ecliptic
+ *  coordinates, and potentially between arbitrary spherical coordinate
+ *  systems. All of these basic spherical transformations represent rotations
+ *  of the spherical coordinate reference. We specify a general
+ *  transformation function which takes a structure, psSphereTransform,
+ *  defining the transformation between two spherical coordinate systems
+ *
+ */
+typedef struct
+{
+    double alphaP;                    ///< Longitude of the target system pole in the source system
+    double cosDeltaP;                 ///< Cosine of target pole latitude in the source system
+    double sinDeltaP;                 ///< Sine of target pole latitude in the source system
+    double phiP;                      ///< Longitude of the ascending node in the target system
+}
+psSphereTransform;
+
+/** Projection type for projection/deprojection
+ *
+ *  @see psProject, psDeproject
+ *
+ */
+typedef enum {
+    PS_PROJ_TAN,                ///< Tangent projection
+    PS_PROJ_SIN,                ///< Sine projection
+    PS_PROJ_AIT,                ///< Aitoff projection
+    PS_PROJ_PAR,                ///< Par projection
+    //    PS_PROJ_GLS,                ///< GLS projection
+    //    PS_PROJ_CAR,                ///< CAR projection
+    //    PS_PROJ_MER,                ///< MER projection
+    PS_PROJ_NTYPE               ///< Number of types; must be last.
+} psProjectionType;
+
+/** Parameter set for projection/deprojection
+ *
+ *  @see psProject, psDeproject
+ *
+ */
+typedef struct
+{
+    double R;                   ///< Coordinates of projection center
+    double D;                   ///< Coordinates of projection center
+    double Xs;                  ///< plate-scale in X direction
+    double Ys;                  ///< plate-scale in Y direction
+    psProjectionType type;      ///< Projection type
+}
+psProjection;
+
+/** Mode for Offset calculation between two sky positions
+ *
+ *  @see  psSphereGetOffset, psSphereSetOffset
+ *
+ */
+typedef enum {
+    PS_SPHERICAL,               ///< offset corresponds to an angular offset
+    PS_LINEAR                   ///< offset corresponds to a linear offset
+} psSphereOffsetMode;
+
+/** The units of the offset
+ *
+ *  @see  psSphereGetOffset, psSphereSetOffset
+ *
+ */
+typedef enum {
+    PS_ARCSEC,                  ///< Arcseconds
+    PS_ARCMIN,                  ///< Arcminutes
+    PS_DEGREE,                  ///< Degrees
+    PS_RADIAN                   ///< Radians
+} psSphereOffsetUnit;
+
+/** Allocates a psPlane
+ *
+ *  @return psPlane*     resulting plane structure.
+ */
+
+psPlane* psPlaneAlloc(void);
+
+/** Allocates a psSphere
+ *
+ *  @return psSphere*     resulting sphere structure.
+ */
+
+psSphere* psSphereAlloc(void);
+
+
+/** Allocates a psPlaneTransform transform.
+ *
+ *  @return psPlaneTransform*     resulting plane transform
+ */
+
+psPlaneTransform* psPlaneTransformAlloc(
+    psS32 n1,  ///< The order of the x term in the transform.
+    psS32 n2   ///< The order of the y term in the transform.
+);
+
+/** Applies the psPlaneTransform transform to a specified coordinate
+ *
+ *  @return psPlane*     resulting coordinate based on transform
+ */
+psPlane* psPlaneTransformApply(
+    psPlane* out,                      ///< a psPlane to recycle.  If NULL, a new one is generated.
+    const psPlaneTransform* transform, ///< the transform to apply
+    const psPlane* coords              ///< the coordinate to apply the transform above.
+);
+
+/** Allocates a psPlaneDistort transform.
+ *
+ *  @return psPlaneDistort*     resulting plane distort transform
+ */
+
+psPlaneDistort* psPlaneDistortAlloc(
+    psS32 n1,  ///< The order of the w term in the transform.
+    psS32 n2,  ///< The order of the x term in the transform.
+    psS32 n3,  ///< The order of the y term in the transform.
+    psS32 n4   ///< The order of the z term in the transform.
+);
+
+
+/** Applies the psPlaneDistort transform to a specified coordinate
+ *
+ *  @return psPlane*     resulting coordinate based on transform
+ */
+psPlane* psPlaneDistortApply(
+    psPlane* out,                      ///< a psPlane to recycle.  If NULL, a new one is generated.
+    const psPlaneDistort* transform,   ///< the transform to apply
+    const psPlane* coords,             ///< the coordinate to apply the transform above.
+    float term3,                       ///< third term -- maybe magnitude
+    float term4                        ///< forth term -- maybe color
+);
+
+/** Allocator for psSphereTransform
+ *
+ *  @return psSphereTransform*         newly allocated struct
+ */
+
+psSphereTransform* psSphereTransformAlloc(
+    double alphaP,                      ///< north pole latitude
+    double deltaP,                      ///< north pole longitude?
+    double phiP                         ///< defines the longitude in the input system of the equatorial intersection between the two systems (e.g, the first point of Ares).
+);
+
+/** Applies the psSphereTransform transform for a specified coordinate
+ *
+ *  @return psSphere*      resulting coordinate based on transform
+ */
+psSphere* psSphereTransformApply(
+    psSphere* out,                     ///< a psSphere to recycle.  If NULL, a new one is generated.
+    const psSphereTransform* transform,///< the transform to apply
+    const psSphere* coord              ///< the coordinate to apply the transform above.x
+);
+
+/** Creates the appropriate transform for converting from ICRS to Ecliptic
+ *  coordinate systems.
+ *
+ *  @return psSphereTransform*     transform for ICRS->Ecliptic coordinate systems
+ */
+psSphereTransform* psSphereTransformICRSToEcliptic(
+    psTime *time                        ///< the time for which the resulting transform will be valid
+);
+
+/** Creates the appropriate transform for converting from Ecliptic to ICRS
+ *  coordinate systems.
+ *
+ *  @return psSphereTransform*     transform for Ecliptic->ICRS coordinate systems
+ */
+psSphereTransform* psSphereTransformEclipticToICRS(
+    psTime *time                        ///< the time for which the resulting transform will be valid
+);
+
+/** Creates the appropriate transform for converting from ICRS to Galactic
+ *  coordinate systems.
+ *
+ */
+psSphereTransform* psSphereTransformICRSToGalactic(void);
+
+/** Creates the appropriate transform for converting from Galactic to ICRS
+ *  coordinate systems.
+ *
+ */
+psSphereTransform* psSphereTransformGalacticToICRS(void);
+
+/** Allocates memory for a psProjection structure
+ *
+ *  @return psProjection*    psProjection structure
+ */
+psProjection* psProjectionAlloc(
+    psF64 R,                   ///< Right-ascension of projection center.
+    psF64 D,                   ///< Declination of projection center.
+    psF64 Xs,                  ///< Scale in x-dimension
+    psF64 Ys,                  ///< Scale in y-dimension
+    psProjectionType type
+);
+
+/** Projects a spherical coordinate to a linear coordinate system
+ *
+ *  @return psPlane*    projected coordinate
+ */
+psPlane* psProject(
+    const psSphere* coord,             ///< coordinate to project
+    const psProjection* projection     ///< parameters of the projection
+);
+
+/** Reverse projection of a linear coordinate to a spherical coordinate system
+ *
+ *  @return psPlane*    projected coordinate
+ */
+psSphere* psDeproject(
+    const psPlane* coord,              ///< coordinate to project
+    const psProjection* projection     ///< parameters of the projection
+);
+
+/** Determines the offset (RA,Dec) on the sky between two positions.
+ *
+ *  Both an offset mode and an offset unit may be defined. The mode may be
+ *  either PS_SPHERICAL, in which case the specified offset corresponds to an
+ *  offset in angles, or it may be PS_LINEAR, in which case the offset
+ *  corresponds to a linear offset in a local projection. The offset unit may
+ *  be in one of PS_ARCSEC, PS_ARCMIN, PS_DEGREE, and PS_RADIAN, which
+ *  specifies the units of the offset only.
+ *
+ *  @return psSphere*    the offset between position1 and position2
+ */
+psSphere* psSphereGetOffset(
+    const psSphere* position1,
+    const psSphere* position2,
+    psSphereOffsetMode mode,
+    psSphereOffsetUnit unit
+);
+
+/** Applies the given offset to a coordinate.
+ *
+ *  Both an offset mode and an offset unit may be defined. The mode may be
+ *  either PS_SPHERICAL, in which case the specified offset corresponds to an
+ *  offset in angles, or it may be PS_LINEAR, in which case the offset
+ *  corresponds to a linear offset in a local projection. The offset unit may
+ *  be in one of PS_ARCSEC, PS_ARCMIN, PS_DEGREE, and PS_RADIAN, which
+ *  specifies the units of the offset only.
+ *
+ *  @return psSphere*    the given position with the given offset applied.
+ */
+psSphere* psSphereSetOffset(
+    const psSphere* position,
+    const psSphere* offset,
+    psSphereOffsetMode mode,
+    psSphereOffsetUnit unit
+);
+
+/** Generates the complete spherical rotation to account for precession
+ *  between two times.  The equinoxes shall be Julian equinoxes.
+ *
+ *  @return psSphere* the resulting spherical rotation
+ */
+psSphere* psSpherePrecess(
+    psSphere *coords,                  ///< coordinates (modified in-place)
+    const psTime *fromTime,            ///< equinox of coords input
+    const psTime *toTime               ///< equinox of coords output
+);
+
+// XXX: Doxygenate.
+psPlaneTransform *p_psPlaneTransformLinearInvert(
+    psPlaneTransform *transform
+);
+
+// XXX: Doxygenate
+psS32 p_psIsProjectionLinear(
+    psPlaneTransform *transform
+);
+
+/** inverts a given transformation.
+ *
+ *  It may assume that the input transformation is one-to-one, and that the
+ *  inverse transformation may be specified through using polynomials of the
+ *  same type and order as the forward transformation. In the event that the
+ *  input transformation is linear, an exact solution may be calculated;
+ *  otherwise nSamples samples in each axis, covering the region specified by
+ *  region shall be used as a grid to fit the best inverse transformation. The
+ *  function shall return NULL if it was unable to generate the inverse
+ *  transformation; otherwise it shall return the inverse transformation. In
+ *  the event that out is NULL, a new psPlaneTransform shall be allocated and
+ *  returned.
+ *
+ *  @return psPlaneTransform*  the resulting inverted transform
+ */
+psPlaneTransform* psPlaneTransformInvert(
+    psPlaneTransform *out,             ///< a transform to recycle, or NULL if one is to be created.
+    const psPlaneTransform *in,        ///< transform to invert
+    psRegion *region,                  ///< region to fit for non-linear transform inversion
+    int nSamples                       ///< number of samples in each axis for fit
+);
+
+/** Creates a single transformation that has the effect of performing trans1
+ *  followed by trans2.
+ *
+ *  psPlaneTransformCombine takes two transformations (trans1 and trans2) and
+ *  returns a single transformation that has the effect of performing trans1
+ *  followed by trans2. In the event that the input transformation is linear,
+ *  an exact solution may be calculated; otherwise nSamples samples in each
+ *  axis, covering the region specified by region shall be used as a grid to
+ *  fit the best inverse transformation. The function shall return NULL if it
+ *  was unable to generate the transformation; otherwise it shall return the
+ *  transformation.
+ *
+ *  @return psPlaneTransform*    resulting transformation
+ */
+psPlaneTransform* psPlaneTransformCombine(
+    psPlaneTransform *out,             ///< a transform to recycle, or NULL if one is to be created.
+    const psPlaneTransform *trans1,    ///< first transform to combine
+    const psPlaneTransform *trans2     ///< first transform to combine
+);
+
+
+/** takes two arrays containing matched coordinates and returns the
+ *  best-fitting transformation.
+ *
+ *  psPlaneTransformFit takes two arrays containing matched coordinates (i.e.,
+ *  coordinates in the source array correspond to the coordinates in the dest
+ *  array) and returns the best-fitting transformation. The source and dest
+ *  will contain psCoords. In the event that the number of coordinates in each
+ *  is not identical, the function shall generate a warning, and extra
+ *  coordinates in the longer of the two shall be ignored. The trans transform
+ *  may not be NULL, since it specifies the desired order, polynomial type and
+ *  any polynomial terms to mask. nRejIter rejection iterations shall be
+ *  performed, wherein coordinates lying more than sigmaClip standard
+ *  deviations from the fit shall be rejected.
+ *
+ *  @return bool        TRUE if successful, otherwise FALSE.
+ */
+bool psPlaneTransformFit(
+    psPlaneTransform *trans,
+    const psArray *source,
+    const psArray *dest,
+    int nRejIter,
+    float sigmaClip
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astro/psTime.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astro/psTime.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astro/psTime.c	(revision 22271)
@@ -0,0 +1,1178 @@
+/** @file  psTime.c
+ *
+ *  @brief Definitions for time, time utilities, and conversion functions for use with psLib astronomy
+ *  functions.
+ *
+ *  A collection of functions are required by psLib to manipulate time data. These functions primarily consist
+ *  of conversions between specific time formats.  They use the UNIX timeval time system as the
+ *  base upon which International Atomic Time (TAI) and Universal Time Coordinated (UTC) are calculated.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.60 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:15 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "psTime.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psAbort.h"
+#include "psImage.h"
+#include "psCoord.h"
+#include "psString.h"
+#include "psMetadata.h"
+#include "psMetadataIO.h"
+#include "psLookupTable.h"
+#include "psConstants.h"
+#include "psAstronomyErrors.h"
+
+#include "config.h"
+
+#define MAX_STRING_LENGTH 256
+
+/** Sidereal angular conversion from seconds to radians for GMST in seconds (i.e. pi/(180*240)) */
+#define S2R (7.272205216643039903848711535369e-5)
+
+/** Two times pi with double precision accuracy */
+#define TWOPI (2.0*M_PI)
+
+/** Conversion from radians to degrees */
+#define R2DEG = (180.0/M_PI)
+
+                /** Maximum length of time string */
+                #define MAX_TIME_STRING_LENGTH 256
+
+                /** Seconds per minute */
+                #define  SEC_PER_MINUTE 60.0
+
+                /** Seconds per hour */
+                #define  SEC_PER_HOUR (60.0*SEC_PER_MINUTE)
+
+                /** Seconds per day */
+                #define  SEC_PER_DAY (24.0*SEC_PER_HOUR)
+
+                /** Seconds per year */
+                #define  SEC_PER_YEAR (365.0*SEC_PER_DAY)
+
+                /** Microseconds per day */
+                #define NSEC_PER_DAY 86400000000000.0
+
+                /** Time metadata read from config file */
+                static psMetadata *timeMetadata = NULL;
+
+/** Static function prototypes */
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+psF64 searchTables(psF64 index, psU64 column, psLookupStatusType *status, char *metadataTableNames[], psU32 nTables);
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+static char *cleanString(char *inString, int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = PS_PARSE_ERROR_GENERAL;
+    }
+
+    return cleanToken;
+}
+
+// get the psTime.config filename by checking environment variable first, then original installation area.
+char* p_psGetConfigFileName()
+{
+    char* filename = getenv("PS_CONFIG_FILE");
+
+    if (filename == NULL) { // environment variable not found
+        filename = PS_CONFIG_FILE_DEFAULT; // this should come from configure.ac
+    }
+
+    return filename;
+}
+
+
+/** Searches time tables in priority order and performs interpolation if input index value is within a table.
+ * If the index value is out of range, the status is set accordingly. */
+psF64 searchTables(psF64 index, psU64 column, psLookupStatusType *status, char *metadataTableNames[], psU32 nTables)
+{
+    psU32 i = 0;
+    char *tableName = NULL;
+    psF64 result = 0.0;
+    psLookupTable *table = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+
+
+
+    // Check time metadata. Function call reports errors.
+    if(timeMetadata == NULL) {
+        if(!p_psTimeInit(p_psGetConfigFileName()))
+            return 0.0;
+    }
+
+    // Search each table in priority order: ser7, eopc,finals
+    for(i=0; i<nTables; i++) {
+
+        // Get table from metadata
+        tableName = metadataTableNames[i];
+        tableMetadataItem = psMetadataLookup(timeMetadata, tableName);
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                    tableName);
+            return 0.0;
+        }
+        table = (psLookupTable*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(table,0.0);
+
+        // Attempt to interpolate table
+        *status = PS_LOOKUP_SUCCESS;
+        result = psLookupTableInterpolate(table, index, 2, status);
+        if(*status == PS_LOOKUP_SUCCESS) {
+            return result;
+        } else if(*status == PS_LOOKUP_ERROR) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED_NAME,
+                    tableName);
+            return 0.0;
+        }
+    }
+
+    return result;
+}
+
+bool p_psTimeInit(const char *fileName)
+{
+    bool foundTable = false;
+    char *tableDir = NULL;
+    char *tableNames = NULL;
+    char *namesPtr = NULL;
+    char *metadataNamesPtr = NULL;
+    char *tableName = NULL;
+    char *fullTableName = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 numTables = 0;
+    psU32 nFail = 0;
+    psVector *tablesFrom = NULL;
+    psVector *tablesTo = NULL;
+    psMetadataItem *metadataItem = NULL;
+    psLookupTable *table = NULL;
+    psParseErrorType status = PS_PARSE_SUCCESS;
+    char metadataTableNames[4][MAX_STRING_LENGTH] = {"ser7", "eopc",  "finals", "tai"};
+
+    // Read time config file
+    timeMetadata = psMetadataParseConfig(timeMetadata, &nFail, fileName, true);
+    if(timeMetadata == NULL) {
+        return false;
+    } else if(nFail != 0) {
+        return false;
+    }
+
+    // Get number of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.n");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.n");
+        return false;
+    }
+    numTables = (psS32)metadataItem->data.S32;
+
+    // Get lower range of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.from");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.from");
+        return false;
+    }
+    tablesFrom = psVectorCopy(tablesFrom, metadataItem->data.V, PS_TYPE_F64);
+    if(tablesFrom->n != numTables) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_VECTOR, tablesFrom->n, numTables);
+        psFree(tablesFrom);
+        return false;
+    }
+
+    // Get upper range of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.to");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.to");
+        return false;
+    }
+    tablesTo = psVectorCopy(tablesTo, metadataItem->data.V, PS_TYPE_F64);
+    if(tablesTo->n != numTables) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_VECTOR, tablesTo->n, numTables);
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        return false;
+    }
+
+    // Get path to time data files
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.dir");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.dir");
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        return false;
+    }
+    tableDir = psStringCopy(metadataItem->data.V);
+
+    // Table file names
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.files");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.files");
+
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        psFree(tableDir);
+        return false;
+    }
+    tableNames = psStringCopy(metadataItem->data.V);
+
+    // Read time tables
+    namesPtr = tableNames;
+    while((tableName=getToken(&namesPtr, " ", &status)) != NULL) {
+
+        // Form path with table name, adding one to length for last '/' that may not occur in string in cong file
+        fullTableName = (char*)psAlloc(strlen(tableDir)+strlen(tableName)+1+1);
+
+        // Old strings may come back from psAlloc(), so set initial position to EOL
+        fullTableName[0]='\0';
+        strcat(fullTableName, tableDir);
+        strcat(fullTableName, "/");
+        strcat(fullTableName, tableName);
+
+        // Create and read table
+        if(i < numTables) {
+            table = psLookupTableAlloc(fullTableName, tablesFrom->data.F64[i], tablesTo->data.F64[i]);
+            table = psLookupTableRead(table);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_TABLE_COUNT, i+1, numTables);
+        }
+
+        // Place tables into metadata slightly altered names as keys to create consistent naming conventions
+        foundTable = false;
+        for(j=0; j<numTables; j++) {
+            metadataNamesPtr = strstr(tableName, metadataTableNames[j]);
+            if(metadataNamesPtr != NULL) {
+                psMetadataAdd(timeMetadata, PS_LIST_TAIL, strcat(metadataTableNames[j], "Table"),
+                              PS_META_LOOKUPTABLE, NULL, table);
+                foundTable = true;
+            } else if(foundTable==false && j==numTables-1) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_TABLE_COUNT, j, numTables);
+            }
+        }
+
+        psFree(fullTableName);
+        psFree(tableName);
+        psFree(table);
+        i++;
+    }
+
+    if(numTables != i) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_TABLE_COUNT, i, numTables);
+    }
+
+    psFree(tableDir);
+    psFree(tableNames);
+    psFree(tablesFrom);
+    psFree(tablesTo);
+
+    return true;
+}
+
+bool p_psTimeFinalize(void)
+{
+    if(timeMetadata != NULL) {
+        psFree(timeMetadata);
+        timeMetadata = NULL;
+    }
+
+    return true;
+}
+
+psTime* psTimeAlloc(psTimeType type)
+{
+    psTime *outTime = NULL;
+
+
+    // Error checks
+    if(type!=PS_TIME_TAI && type!=PS_TIME_UTC) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psTime_TYPE_UNKNOWN,
+                type);
+        return NULL;
+    }
+
+    outTime = (psTime*)psAlloc(sizeof(psTime));
+
+    outTime->sec = 0;
+    outTime->nsec = 0;
+    outTime->type = type;
+    outTime->leapsecond = false;
+
+    return outTime;
+}
+
+psTime* psTimeGetNow(psTimeType type)
+{
+    struct timeval now;
+    psTime *time = NULL;
+
+
+    // Allocate psTime struct
+    time = psTimeAlloc(type);
+
+    if (gettimeofday(&now, (struct timezone *)0) == -1) {
+        psError(PS_ERR_OS_CALL_FAILED, true,
+                PS_ERRORTEXT_psTime_GET_TOD_FAILED);
+        return NULL;
+    }
+
+    // Convert timeval time to psTime
+    time->sec = now.tv_sec;
+    time->nsec = now.tv_usec*1000;
+
+    // Add most leapseconds to UTC time to get TAI time if necessary
+    if(type == PS_TIME_TAI) {
+        time->sec += psTimeGetTAIDelta(time);
+    }
+
+    return time;
+}
+
+psTime* psTimeConvert(psTime *time, psTimeType type)
+{
+    double delta = 0.0;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    if (time->type == type) { // time already right type.  That was easy!
+        return time;
+    }
+
+    delta = psTimeGetTAIDelta(time);
+
+    if (type == PS_TIME_UTC) {
+        time->sec = time->sec - delta;    // User wants UTC time. Subtract leapseconds.
+    } else if(type == PS_TIME_TAI) {
+        time->sec = time->sec + delta;    // User wants TAI time. Add leapseconds.
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TYPE_UNKNOWN, type);
+    }
+
+    return time;
+}
+
+double psTimeToLMST(psTime *time, double longitude)
+{
+    psF64  jdTdtDays    =  0.0;
+    psF64  jdUt1Days    =  0.0;
+    psF64  mjdUt1Days   =  0.0;
+    psF64  lmstRad      =  0.0;
+    psF64  fracDays     =  0.0;
+    psF64  gmstRad      =  0.0;
+    psF64  t            =  0.0;
+    psF64  tu           =  0.0;
+    psF64  ut1UtcDbl    =  0.0;
+    psF64  const1       =  24110.5493771;
+    psF64  const2       =  8639877.3173760;
+    psF64  const3       =  307.4771600;
+    psF64  const4       =  0.0931118;
+    psF64  const5       = -0.0000062;
+    psF64  const6       =  0.0000013;
+    psTime *ut1UtcDelta = NULL;
+    psTime *tdtTime     = NULL;
+    psTime *taiTime     = NULL;
+    psTime *utcTime     = NULL;
+    psTime *ut1Time     = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Calculate TAI or UTC time based on type of time user passes
+    if(time->type == PS_TIME_TAI) {
+        taiTime = psMemIncrRefCounter(time);
+        utcTime = psTimeAlloc(PS_TIME_UTC);
+        utcTime->sec = taiTime->sec - psTimeGetTAIDelta(time);
+        utcTime->nsec = taiTime->nsec;
+    } else if(time->type == PS_TIME_UTC) {
+        utcTime = psMemIncrRefCounter(time);
+        taiTime = psTimeAlloc(PS_TIME_TAI);
+        taiTime->sec = utcTime->sec + psTimeGetTAIDelta(time);
+        taiTime->nsec = utcTime->nsec;
+    }
+
+    // Convert Universal Time (UTC) to  UT1
+    ut1UtcDbl = psTimeGetUT1Delta(taiTime);
+    utcTime->type = PS_TIME_TAI; // Don't allow addition of leapseconds to ut1Time
+    ut1Time = psTimeMath(utcTime, ut1UtcDbl);
+    utcTime->type = PS_TIME_UTC;
+
+
+    // Calculate UT1 as Julian Centuries since J2000.0
+    jdUt1Days = psTimeToJD(ut1Time);
+    mjdUt1Days = psTimeToMJD(ut1Time);
+    t = (jdUt1Days - 2451545.0)/36525.0;
+
+    // Calculate Terrestial Dynamical Time (TDT)
+    tdtTime = psTimeMath(taiTime, 32.184);
+
+    // Calculate TDT as Julian centuries since J2000.0
+    jdTdtDays = psTimeToJD(tdtTime);
+    tu = (jdTdtDays - 2451545.0)/36525.0;
+
+    // Calculate fractional part of MJD
+    fracDays = fmod(mjdUt1Days, 1.0);
+
+    // Calculate Greenwich Mean Sidereal Time (GMST) in radians. Equation set up to minimize multiplications.
+    gmstRad = fracDays*TWOPI+(const1+const2*tu+t*(const3+t*(const4+t*(const5+const6*t))))*S2R;
+
+    // Place GMST between 0 and 2*pi
+    gmstRad = fmod(gmstRad, TWOPI);
+
+    // Calculate Local Mean Sidereal Time (LMST) in radians
+    lmstRad = gmstRad + longitude;
+
+    // Free temporary structs
+    psFree(ut1UtcDelta);
+    psFree(ut1Time);
+    psFree(tdtTime);
+    psFree(utcTime);
+    psFree(taiTime);
+
+    return lmstRad;
+}
+
+double psTimeGetUT1Delta(const psTime *time)
+{
+    psU32 nTables = 3;
+    psF64 mjd = 0.0;
+    psF64 result = 0.0;
+    psF64 dut2ut1 = 0.0;
+    psF64 t = 0.0;
+    psVector *dut = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+    psLookupStatusType status = PS_LOOKUP_SUCCESS;
+    char *metadataTableNames[3] = {"ser7Table", "eopcTable",  "finalsTable"};
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TYPE_INCORRECT, time->type);
+        return 0.0;
+    }
+
+    // Attempt to find value through table lookup and interpolation
+    mjd = psTimeToMJD(time);
+    result = searchTables(mjd, 0, &status, metadataTableNames, nTables);
+
+    // Value could not be found through table lookup and interpolation
+    if(status == PS_LOOKUP_PAST_TOP) {
+
+        // Date too early for tables. Get default time delta value from metadata, and issue warning.
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_PREDATES_TABLES, mjd, "UT1-UTC");
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.dut");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.before.dut");
+            return 0.0;
+        }
+        result = tableMetadataItem->data.F64;
+
+    } else if(status == PS_LOOKUP_PAST_BOTTOM) {
+        /* Date too late for tables. Issue warning and use following formulae for predicting
+           ahead of the most recent available table entry.
+             ut1-utc = [0] + [1]*(MJD - [2]) - (ut2-ut1)
+             [0, 1, 2] = @psLib.time.predict.dut
+             ut2-ut1 = 0.022 sin(2*pi*t) - 0.012 cos(2*pi*t) - 0.006 sin(4*pi*t) + 0.007 cos(4*pi*t)
+             t = 2000.0 + (MJD - 51544.03)/365.2422
+        */
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_POSTDATES_TABLES, mjd, "UT1-UTC");
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.dut");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.predict.dut");
+            return 0.0;
+        }
+        dut = (psVector*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(dut,0.0);
+
+        t = 2000.0 + (mjd - 51544.03)/365.2422;
+        dut2ut1 = 0.022*sin(TWOPI*t) - 0.012*cos(TWOPI*t) - 0.006*sin(4.0*M_PI*t) + 0.007*cos(4.0*M_PI*t);
+        result = dut->data.F64[0] + dut->data.F64[1]*(mjd - dut->data.F64[2]) - dut2ut1;
+
+    } else if(status != PS_LOOKUP_SUCCESS) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED);
+        return 0.0;
+    }
+
+    return result;
+}
+
+struct psSphere* psTimeGetPoleCoords(const psTime* time)
+{
+    psU32 nTables = 3;
+    psF64 x = 0.0;
+    psF64 y = 0.0;
+    psF64 mjd = 0.0;
+    psF64 a = 0.0;
+    psF64 c = 0.0;
+    psF64 mjdPred = 0.0;
+    struct psSphere* output = NULL;
+    psLookupStatusType xStatus = PS_LOOKUP_SUCCESS;
+    psLookupStatusType yStatus = PS_LOOKUP_SUCCESS;
+    psMetadataItem *tableMetadataItem = NULL;
+    char *metadataTableNames[3] = {"ser7Table", "eopcTable",  "finalsTable"};
+    psVector *xp = NULL;
+    psVector *yp = NULL;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    if(time->type != PS_TIME_TAI)
+    {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TYPE_INCORRECT, time->type);
+        return NULL;
+    }
+
+    // Attempt to find value through table lookup and interpolation
+    mjd = psTimeToMJD(time);
+    x = searchTables(mjd, 0, &xStatus, metadataTableNames, nTables);
+    y = searchTables(mjd, 0, &yStatus, metadataTableNames, nTables);
+
+    // Value could not be found through table lookup and interpolation
+    if(xStatus==PS_LOOKUP_PAST_TOP && yStatus==PS_LOOKUP_PAST_TOP)
+    {
+
+        // Date too earlier for tables. Get default polar coodinate values from metadata, and issue warning.
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_PREDATES_TABLES, mjd, "polar motion");
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.xp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.before.xp");
+            return NULL;
+        }
+        x = tableMetadataItem->data.F64;
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.yp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.before.yp");
+            return NULL;
+        }
+        y = tableMetadataItem->data.F64;
+
+    } else if(xStatus==PS_LOOKUP_PAST_BOTTOM && yStatus==PS_LOOKUP_PAST_BOTTOM)
+    {
+
+        /* Date too late for tables. Issue warning and use following formulae for predicting
+           ahead of the most recent available table entry.
+              x = [0] + [1]*cos a + [2]*sin a + [3]*cos c + [4]*sin c
+              [0], [1], [2], [3] = @psLib.time.predict.xp
+              y = [0] + [1]*cos a + [2]*sin a + [3]*cos c + [4]*sin c
+              [0], [1], [2], [3] = @psLib.time.predict.yp
+              a = 2*pi*(mjd - pslib.time.predict.mjd)/365.25
+              c = 2*pi*(mjd - pslib.time.predict.mjd)/435.0
+        */
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_POSTDATES_TABLES, mjd, "polar motion");
+
+
+        // Get predicted MJD
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.mjd");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                    "psLib.time.predict.mjd");
+            return NULL;
+        }
+        mjdPred = tableMetadataItem->data.F64;
+
+        // Get xp
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.xp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.predict.xp");
+            return NULL;
+        }
+        xp = (psVector*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(xp,NULL);
+
+        // Get yp
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.yp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.predict.yp");
+            return NULL;
+        }
+        yp = (psVector*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(yp,NULL);
+
+        // Calculate "a" and "c" constants
+        a = TWOPI*(mjd - mjdPred)/365.25;
+        c = TWOPI*(mjd - mjdPred)/435.0;
+
+        // Calculate x and y polar coordinates
+        x = xp->data.F64[0] +
+            xp->data.F64[1]*cos(a) +
+            xp->data.F64[2]*sin(a) +
+            xp->data.F64[3]*cos(c) +
+            xp->data.F64[4]*sin(c);
+
+        y = yp->data.F64[0] +
+            yp->data.F64[1]*cos(a) +
+            yp->data.F64[2]*sin(a) +
+            yp->data.F64[3]*cos(c) +
+            yp->data.F64[4]*sin(c);
+
+    } else if(xStatus!=PS_LOOKUP_SUCCESS || yStatus!=PS_LOOKUP_SUCCESS)
+    {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED);
+        return NULL;
+    }
+
+    // Create output sphere and convert arcsec to radians (i.e. x/60/60*M_PI/180)
+    output = psAlloc(sizeof(psSphere));
+    output->r = x * M_PI / 648000.0;
+    output->d = y * M_PI / 648000.0;
+
+    return output;
+}
+
+double psTimeGetTAIDelta(const psTime *time)
+{
+    psU64 i = 0;
+    psF64 jd = 0.0;
+    psF64 mjd = 0.0;
+    psF64 out = 0.0;
+    psF64 const1 = 0.0;
+    psF64 const2 = 0.0;
+    psF64 const3 = 0.0;
+    psLookupTable* table = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+    psVector *stats = NULL;
+    psVector *results = NULL;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Check time metadata
+    if(timeMetadata == NULL) {
+        if(!p_psTimeInit(p_psGetConfigFileName())) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_FILE_NOT_FOUND, "psTime.config");
+            return 0.0;
+        }
+    }
+
+    // Get table from metadata
+    tableMetadataItem = psMetadataLookup(timeMetadata, "taiTable");
+    if(tableMetadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "taiTable");
+        return 0.0;
+    }
+    table = (psLookupTable*)tableMetadataItem->data.V;
+    PS_PTR_CHECK_NULL(table,0);
+
+    // Determine Julian and modified Julian dates used in table lookup and time delta calculation
+    jd = psTimeToJD(time);
+    mjd = psTimeToMJD(time);
+
+    stats = psVectorAlloc(table->numCols, PS_TYPE_U32);
+    results = psLookupTableInterpolateAll(table, jd, stats);
+
+    // Check for successful interpolation
+    for(i=0; i<stats->n; i++) {
+        if(stats->data.U32[i] == PS_LOOKUP_ERROR) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED);
+        }
+    }
+
+    const1 = results->data.F64[0];
+    const2 = results->data.F64[1];
+    const3 = results->data.F64[2];
+    out = const1 + (mjd - const2) * const3;
+
+    psFree(stats);
+    psFree(results);
+
+    return out;
+}
+
+
+psS64 psTimeLeapSecondDelta(const psTime *time1, const psTime *time2)
+{
+    psS64 diff = 0;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time1,0);
+    PS_PTR_CHECK_NULL(time2,0);
+    PS_INT_CHECK_RANGE(time1->nsec,0,1e9-1,0);
+    PS_INT_CHECK_RANGE(time2->nsec,0,1e9-1,0);
+    diff = abs((psS64)psTimeGetTAIDelta((psTime*)time1)-(psS64)psTimeGetTAIDelta((psTime*)time2));
+
+    return diff;
+}
+
+double psTimeToJD(const psTime *time)
+{
+    double jd = 0.0;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Julian date conversion
+    if(time->sec < 0) {
+        jd = time->sec / SEC_PER_DAY - time->nsec / NSEC_PER_DAY + 2440587.5; // psTime earlier than epoch
+    } else {
+        jd = time->sec / SEC_PER_DAY + time->nsec / NSEC_PER_DAY + 2440587.5; // psTime greater than epoch
+    }
+
+    return jd;
+}
+
+double psTimeToMJD(const psTime *time)
+{
+    double mjd = 0.0;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Modified Julian date conversion
+    if(time->sec < 0) {
+        mjd = time->sec / SEC_PER_DAY - time->nsec / NSEC_PER_DAY + 40587.0; // psTime earlier than epoch
+    } else {
+        mjd = time->sec / SEC_PER_DAY + time->nsec / NSEC_PER_DAY + 40587.0; // psTime greater than epoch
+    }
+
+    return mjd;
+}
+
+char* psTimeToISO(const psTime *time)
+{
+    psS32 ms = 0;
+    char *timeString = NULL;
+    char *tempString = NULL;
+    struct tm *tmTime = NULL;
+    time_t sec;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    tempString = psAlloc(MAX_TIME_STRING_LENGTH);
+    timeString = psAlloc(MAX_TIME_STRING_LENGTH);
+
+    ms = time->nsec / 1000000;
+    sec = time->sec;
+
+    // tmTime variable is statically allocated, no need to free
+    tmTime = gmtime(&sec);
+
+    // Converts psTime to YYYY-MM-DDThh:mm:ss.sss in string form
+    if (!strftime(tempString, MAX_TIME_STRING_LENGTH, "%Y-%m-%dT%H:%M:%S", tmTime)) {
+        psError(PS_ERR_OS_CALL_FAILED, true, PS_ERRORTEXT_psTime_CONVERT_TIME_TO_STRING_FAILED);
+    }
+
+    if (snprintf(timeString, MAX_TIME_STRING_LENGTH, "%s.%3.3dZ", tempString, ms) < 0) {
+        psError(PS_ERR_OS_CALL_FAILED, true, PS_ERRORTEXT_psTime_APPEND_MSEC_FAILED);
+    }
+    psFree(tempString);
+
+    return timeString;
+}
+
+struct timeval psTimeToTimeval(const psTime *time)
+{
+    struct timeval timevalTime;
+
+    timevalTime.tv_sec = 0;
+    timevalTime.tv_usec = 0;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,timevalTime);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,timevalTime);
+
+    timevalTime.tv_sec = time->sec;
+    timevalTime.tv_usec = time->nsec / 1000;
+
+    return timevalTime;
+}
+
+struct tm* psTimeToTM(const psTime *time)
+{
+    psS64 cent = 0;
+    psS64 year = 0;
+    psS64 month = 0;
+    psS64 day = 0;
+    psS64 hour = 0;
+    psS64 minute = 0;
+    psS64 seconds = 0;
+    psS64 temp = 0;
+    struct tm* tmTime = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    seconds = time->sec%60;
+    minute = time->sec/60%60;
+    hour = time->sec/3600%24;
+    day = (time->sec+62135596800)/86400;
+
+    // Add 306 days to make relative to Mar 1, 0; also adjust day to be within a range (1..2**28-1) where our
+    // calculations will work with 32bit ints
+    if(day > (pow(2, 28)-307))
+    {
+        temp = (day - 146097+306)/146097+1;         // Avoid overflow if day close to maxint
+        day -= temp * 146097-306;
+    } else if((day += 306) <= 0)
+    {
+        temp = -( -day / 146097 + 1);               // Avoid ambiguity in C division of negatives
+        day -= temp * 146097;
+    }
+
+    cent = (day*4-1)/146097;                        // Calc number of centuries day is after 29 Feb of yr 0
+    day -= cent*146097/4;                           // 4 centuries = 146097 days
+    year = (day*4-1)/1461;                          // Calc number of years into the century
+    day -= year*1461/4;                             // Again March-based (4 yrs =\u02dc 146[01] days)
+    month = (day*12+1093)/367;                      // Get the month (3..14 represent March through
+    day -= (month*367-1094)/12;                     // February of following year)
+    year += cent*100+temp*400;                      // Get the real year, which is off by
+
+    // One if month is January or February
+    if(month > 12)
+    {
+        year++;
+        month -= 12;
+    }
+
+    // Allocate output
+    tmTime = (struct tm*)psAlloc(sizeof(struct tm));
+
+    tmTime->tm_year = year - 1900;
+    tmTime->tm_mon = month - 1;
+    tmTime->tm_mday = day + 1;
+    tmTime->tm_hour = hour;
+    tmTime->tm_min = minute;
+    tmTime->tm_sec = seconds;
+    tmTime->tm_isdst = -1;
+
+    return tmTime;
+}
+
+psTime* psTimeFromJD(double time)
+{
+    double days = 0.0;
+    double seconds = 0.0;
+    psTime *outTime = NULL;
+
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Julian date conversion courtesy of Eugene Magnier
+    days = time - 2440587.5;
+    seconds = days * SEC_PER_DAY;
+    if(seconds < 0.0) {
+        outTime->nsec = (seconds - (psS64)seconds) * -1000000000.0;  // psTime earlier than epoch
+    } else {
+        outTime->nsec = (seconds - (psS64)seconds) * 1000000000.0;   // psTime greater than epoch
+    }
+    outTime->sec = seconds;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromMJD(double time)
+{
+    double days = 0.0;
+    double seconds = 0.0;
+    psTime *outTime = NULL;
+
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Modified Julian date conversion courtesy of Eugene Magnier
+    days = time - 40587.0;
+    seconds = days * SEC_PER_DAY;
+    if(seconds < 0.0) {
+        outTime->nsec = (seconds - (psS64)seconds) * -1000000000.0;  // psTime earlier than epoch
+    } else {
+        outTime->nsec = (seconds - (psS64)seconds) * 1000000000.0;   // psTime greater than epoch
+    }
+    outTime->sec = seconds;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromISO(const char *time)
+{
+    psS32 millisecond;
+    struct tm tmTime;
+    psTime *outTime = NULL;
+
+    // Convert YYYY-MM-DDThh:mm:ss.sss in string form to tm time
+    if (sscanf(time, "%d-%d-%dT%d:%d:%d.%d", &tmTime.tm_year, &tmTime.tm_mon, &tmTime.tm_mday,
+               &tmTime.tm_hour, &tmTime.tm_min, &tmTime.tm_sec,&millisecond) < 7) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_ISOTIME_MALFORMED, time);
+        return NULL;
+    }
+
+    PS_INT_CHECK_NON_NEGATIVE(tmTime.tm_year, outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_mon,1,12,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_mday,1,31,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_hour,0,23,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_min,0,59,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_sec,0,59,outTime);
+    PS_INT_CHECK_RANGE(millisecond,0,999,outTime);
+
+    tmTime.tm_year -= 1900;
+    tmTime.tm_mon--;
+    tmTime.tm_isdst = -1;
+
+    // Convert tm time to psTime
+    outTime = psTimeFromTM(&tmTime);
+    outTime->nsec = millisecond * 1000000;
+
+    return outTime;
+}
+
+psTime* psTimeFromTimeval(const struct timeval *time)
+{
+    psTime *outTime = NULL;
+
+
+    // Error check
+    PS_PTR_CHECK_NULL(time,NULL);
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Convert to psTime
+    outTime->sec = time->tv_sec;
+    outTime->nsec = time->tv_usec * 1000;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromTM(const struct tm* time)
+{
+    psS64 year;
+    psS64 month;
+    psS64 day;
+    psS64 hour;
+    psS64 minute;
+    psS64 seconds;
+    psS64 temp;
+    psTime *outTime = NULL;
+
+
+    // Error check
+    PS_PTR_CHECK_NULL(time,NULL);
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Extract data from TM struct
+    year = time->tm_year + 1900;
+    month = time->tm_mon + 1;
+    day = time->tm_mday;
+    hour = time->tm_hour;
+    minute = time->tm_min;
+    seconds = time->tm_sec;
+
+    // Make month in range 3..14 (treat Jan & Feb as months 13..14 of prev year)
+    if( month <= 2 )
+    {
+        year -= (temp = (14 - month) / 12);
+        month += 12 * temp;
+    } else if(month > 14)
+    {
+        year += (temp = (month - 3) / 12);
+        month -= 12 * temp;
+    }
+
+    // Make year positive
+    if (year < 0 )
+    {
+        day -= 146097 * (temp = (399 - year) / 400);
+        year += 400 * temp;
+    }
+
+    // Add day of month, days of previous 0-11 month period that began w/March, days of previous 0-399 year
+    // period that began w/March of a 400-multiple year), days of any 400-year periods before that, and 306
+    // days to adjust from Mar 1, year 0-relative to Jan 1, year 1-relative. Add hours, minutes, and seconds.
+    day += (month * 367 - 1094) / 12 + year % 100 * 1461 / 4 + (year/100 * 36524 + year/400) - 306;
+    outTime->sec = (((day - 1) * SEC_PER_DAY) - 62135596800) + hour*SEC_PER_HOUR + minute*SEC_PER_MINUTE + seconds;
+
+    // C's TM does not define a microsecond field. Microseconds must be manipulated by calling function.
+    outTime->nsec = 0;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeMath(const psTime *time, psF64 delta)
+{
+    psF64 sec = 0.0;
+    psTime *outTime = NULL;
+    psTime *tempTime = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    // Convert time to TAI if necessary, but without changing input arguments
+    if(time->type == PS_TIME_UTC) {
+        tempTime = psTimeAlloc(PS_TIME_UTC);
+        tempTime->sec = time->sec;
+        tempTime->nsec = time->nsec;
+        tempTime = psTimeConvert(tempTime, PS_TIME_TAI);
+    } else {
+        tempTime = psMemIncrRefCounter((psTime*)time);
+    }
+
+    // Create output time
+    outTime = psTimeAlloc(PS_TIME_TAI);
+    sec = delta + (psF64)tempTime->sec + (psF64)tempTime->nsec/1e9;
+    outTime->sec = sec;
+    outTime->nsec = (sec - outTime->sec)*1e9;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    // Convert result to same time type as input
+    if(time->type == PS_TIME_UTC) {
+        outTime = psTimeConvert(outTime, PS_TIME_UTC);
+    }
+
+    psFree(tempTime);
+
+    return outTime;
+}
+
+psF64 psTimeDelta(const psTime *time1, const psTime *time2)
+{
+    psF64 out = 0.0;
+    psF64 uSec1 = 0.0;
+    psF64 uSec2 = 0.0;
+    psTime *tempTime1 = NULL;
+    psTime *tempTime2 = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time1,0.0);
+    PS_INT_CHECK_RANGE(time1->nsec,0,1e9-1,0.0);
+    PS_PTR_CHECK_NULL(time2,0.0);
+    PS_INT_CHECK_RANGE(time2->nsec,0,1e9-1,0.0);
+
+    // Convert time to TAI if necessary, but without changing input arguments
+    if(time1->type == PS_TIME_UTC) {
+        tempTime1 = psTimeAlloc(PS_TIME_UTC);
+        tempTime1->sec = time1->sec;
+        tempTime1->nsec = time1->nsec;
+        tempTime1 = psTimeConvert(tempTime1, PS_TIME_TAI);
+    } else {
+        tempTime1 = psMemIncrRefCounter((psTime*)time1);
+    }
+    if(time2->type == PS_TIME_UTC) {
+        tempTime2 = psTimeAlloc(PS_TIME_UTC);
+        tempTime2->sec = time2->sec;
+        tempTime2->nsec = time2->nsec;
+        tempTime2 = psTimeConvert(tempTime2, PS_TIME_TAI);
+    } else {
+        tempTime2 = psMemIncrRefCounter((psTime*)time2);
+    }
+
+    uSec1 = tempTime1->sec >= 0 ? 1.0 : -1.0;
+    uSec1 = uSec1*tempTime1->nsec/1e9;
+    uSec2 = tempTime2->sec >= 0 ? 1.0 : -1.0;
+    uSec2 = uSec2*tempTime2->nsec/1e9;
+    out = (tempTime1->sec-tempTime2->sec) + (uSec1-uSec2);
+
+    psFree(tempTime1);
+    psFree(tempTime2);
+
+    return out;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astro/psTime.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astro/psTime.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astro/psTime.h	(revision 22271)
@@ -0,0 +1,308 @@
+/** @file  psTime.h
+ *
+ *  @brief Definitions for time, time utilities, and conversion functions for use with psLib astronomy
+ *  functions.
+ *
+ *  A collection of functions are required by psLib to manipulate time data. These functions primarily consist
+ *  of conversions between specific time formats.  They use the UNIX timeval time system as the
+ *  base upon which International Atomic Time (TAI) and Universal Time Coordinated (UTC) are calculated.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.28.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+#ifndef PSTIME_H
+#define PSTIME_H
+
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "psType.h"
+#include "psImage.h"
+
+struct psSphere;
+
+/// @addtogroup Time
+/// @{
+
+
+/** Time type.
+ *
+ * Enumeration for psTime types, TAI or UTC time.
+ */
+typedef enum {
+    PS_TIME_TAI,        ///< Temps Atomique International (TAI) time (time with leapseconds).
+    PS_TIME_UTC,        ///< Universal Time Coordinated (UTC) time (time without leapseconds).
+    PS_TIME_UT1,        ///< Universal Time corrected for polar motion
+    PS_TIME_TT,         ///< Terrestrial Time
+} psTimeType;
+
+/** Time Bulletin type
+ *
+ * Enumeration for psTimeBulletin type, A or B.
+ */
+typedef enum {
+    PS_IERS_A,          ///< IERS Bulletin A
+    PS_IERS_B,          ///< IERS Bulletin B
+} psTimeBulletin;
+
+/** Definition of psTime.
+ *
+ *  The psTime struct is used by psLib to represent time values critical to
+ *  astronomical calculations.  This structure represents a time which is
+ *  equivalent to TAI (International Atomic Time) and is measured in both
+ *  seconds and microseconds.
+ */
+typedef struct psTime
+{
+    psS64 sec;          ///< Seconds since epoch, Jan 1, 1970.
+    psU32 nsec;         ///< Nanoseconds since last second.
+    psBool leapsecond;  ///< if time falls on UTC leapsecond
+    psTimeType type;    ///< Type of time.
+}
+psTime;
+
+
+/** Initialize time data.
+ *
+ * Reads config and data files associated with various time conversions.
+ *
+ * @return  bool: True for success, false for failure.
+ */
+psBool p_psTimeInit(const char *fileName);
+
+/** Free memory persistant time data.
+ *
+ * Frees time data to be held in memory until the end of successful program execution.
+ *
+ * @return  void: void.
+ */
+psBool p_psTimeFinalize(void);
+
+/** Allocate time struct.
+ *
+ * Allocates an empty time struct. User must specify the psTimeType (PS_TIME_TAI or PS_TIME_UTC) in the argument.
+ * The seconds and microseconds members of the struct are set to zero.
+ *
+ * @return  psTime*: Struct with empty time.
+ */
+psTime* psTimeAlloc(
+    psTimeType type                     ///< Type of time to create (UTC or TAI).
+);
+
+/** Get current time.
+ *
+ * Gets current time from the system clock. User must specify the psTimeType (PS_TIME_TAI or PS_TIME_UTC) in
+ * the argument.
+ *
+ *  @return  psTime*: Struct with current time.
+ */
+psTime* psTimeGetNow(
+    psTimeType type                     ///< Type of time to get (UTC or TAI).
+);
+
+/** Convert psTime to UTC or TAI time.
+ *
+ *  Converts psTime to UTC or TAI time based on the psTimeType argument.
+ *
+ *  @return  psTime*: Pointer to psTime.
+ */
+psTime* psTimeConvert(
+    psTime *time,                       ///< Time to be converted.
+    psTimeType type                     ///< Type to be converted to.
+);
+
+/** Convert psTime to Local Mean Sidereal Time (LMST).
+ *
+ *  Converts psTime at the given longitude to LMST time. If the input time is not in UTC format, then it is
+ *  converted.
+ *
+ *  @return  double: LST Time.
+ */
+double psTimeToLMST(
+    psTime *time,   ///< psTime to be converted.
+    double longitude                    ///< Longitude.
+);
+
+/** Determine UT1 - UTC from table lookup.
+ *
+ *  This function is necessary to for various SLALIB functions.
+ *
+ *  @return  double: Time difference.
+ */
+double psTimeGetUT1Delta(
+    const psTime *time                  ///< psTime to be looked up.
+);
+
+/** Determine TAI - UTC from table lookup.
+ *
+ *  This function is necessary to for various psTime functions.
+ *
+ *  @return  double: Time difference.
+ */
+double psTimeGetTAIDelta(
+    const psTime *time                  ///< psTime to be looked up.
+);
+
+/** Determine polar coordinates at a given time.
+ *
+ *  Determines the orientation of the polar axis at the given time.
+ *
+ *  @return  psSphere*: Spherical coordinates of Earth's polar axias.
+ */
+struct psSphere* psTimeGetPoleCoords(
+                const psTime *time                  ///< psTime determine polar orientation.
+            );
+
+/** Calculate the number of leapseconds between two times.
+ *
+ *  Calculates the number of leapseconds between two times.
+ *
+ *  @return  psS64: leapseconds added between given times
+ */
+psS64 psTimeLeapSecondDelta(
+    const psTime* time1,                ///< First input time.
+    const psTime* time2                 ///< Second input time.
+);
+
+/** Convert psTime to Julian date time.
+ *
+ *  Converts psTime to Julian date (JD) time. This function does not add or subtract leapseconds.
+ *
+ *  @return  double: Julian Date (JD) time.
+ */
+double psTimeToJD(
+    const psTime* time                  ///< Input time to be converted.
+);
+/** Convert psTime to modified Julian date time.
+ *
+ *  Converts psTime to modified Julian date (MJD) time. This function does not add or subtract leapseconds.
+ *
+ *  @return  double: Modified Julian Days (MJD) time.
+ */
+double psTimeToMJD(
+    const psTime* time                  ///< Input time to be converted.
+);
+
+/** Convert psTime to ISO8601 formatted string.
+ *
+ *  Converts psTime to a null terminated string in the form of YYYY-MM-DDThh:mm:ss.sss. This function does not
+ *  add or subtract leapseconds.
+ *
+ *  @return  char*: Pointer null terminated array of chars in ISO time.
+ */
+char* psTimeToISO(
+    const psTime* time                  ///< Input time to be converted.
+);
+
+/** Convert psTime to timeval time.
+ *
+ *  Converts psTime to timeval time. This function does not add or subtract leapseconds.
+ *
+ *  @return  timeval: timeval struct time.
+ */
+struct timeval psTimeToTimeval(
+                const psTime* time                  ///< Input time to be converted.
+            );
+
+/** Convert psTime to tm time.
+ *
+ * Converts psTime to tm time. This function is based on a Perl algorithm availble in the Pan-STARRS Image
+ * processing Algorithm Design Description (ADD). This function does not add or subtract leapseconds.
+ *
+ *  @return  tm: tm struct time.
+ */
+struct tm* psTimeToTM(
+                const psTime *time                  ///< Input time to be converted.
+            );
+
+/** Convert JD to psTime.
+ *
+ *  Converts JD time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime: time.
+ */
+psTime* psTimeFromJD(
+    double time                         ///< Input time to be converted.
+);
+
+/** Convert MJD to psTime.
+ *
+ *  Converts MJD time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime: time.
+ */
+psTime* psTimeFromMJD(
+    double time                         ///< Input time to be converted.
+);
+
+/** Convert ISO to psTime.
+ *
+ *  Converts ISO time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time
+ */
+psTime* psTimeFromISO(
+    const char* time                    ///< Input time to be converted.
+);
+
+/** Convert timeval to psTime.
+ *
+ *  Converts timeval time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeFromTimeval(
+    const struct timeval *time          ///< Input time to be converted.
+);
+
+/** Convert tm time to psTime.
+ *
+ *  Converts tm time to psTime. This function is based on a Perl algorithm availble in the Pan-STARRS Image
+ * processing Algorithm Design Description (ADD). This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeFromTM(
+    const struct tm *time               ///< Input time to be converted.
+);
+
+/** Adds delta to time. Result is in TAI time.
+ *
+ *  Adds delta to time. Input time is converted to TAI format if necessary.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeMath(
+    const psTime *time,                 ///< Time.
+    psF64 delta                         ///< Time delta.
+);
+
+/** Determine difference between two times. Result is in TAI time.
+ *
+ *  Determine difference between two times. Input times are converted to TAI format if necessary.
+ *
+ *  @return psF64: Time difference.
+ */
+psF64 psTimeDelta(
+    const psTime *time1,                ///< First time.
+    const psTime *time2                 ///< Second time.
+);
+
+/** Get the filename of the psLib configuration file.
+ *
+ *  @return char*          If a PS_CONFIG_FILE environment variable exists,
+ *                         that is returned, otherwise the default location
+ *                         dependent on the installation location.
+ */
+char* p_psGetConfigFileName();
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/.cvsignore	(revision 22271)
@@ -0,0 +1,7 @@
+Makefile.in
+.deps
+.libs
+Makefile
+*.lo
+*.la
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/Makefile.am	(revision 22271)
@@ -0,0 +1,35 @@
+#Makefile for astronomy functions of psLib
+#
+AM_CFLAGS=$(CFLAGS) -DPS_CONFIG_FILE_DEFAULT=\"$(sysconfdir)/pslib/psTime.config\"
+
+INCLUDES = \
+	-I$(top_srcdir)/src/collections \
+	-I$(top_srcdir)/src/dataManip \
+	-I$(top_srcdir)/src/dataIO \
+	-I$(top_srcdir)/src/image \
+	-I$(top_srcdir)/src/sysUtils \
+	$(all_includes)
+
+noinst_LTLIBRARIES = libpslibastronomy.la
+libpslibastronomy_la_SOURCES = \
+	psTime.c \
+	psCoord.c \
+	psAstrometry.c \
+        aoppa.f aopqk.f oapqk.f airmas.f eqeqx.f geoc.f refco.f aoppat.f \
+        dranrm.f dcs2c.f refz.f refro.f dcc2s.f gmst.f atms.f atmt.f nutc.f drange.f
+
+BUILT_SOURCES = psAstronomyErrors.h
+
+EXTRA_DIST = psAstronomyErrors.dat psAstronomyErrors.h astronomy.i
+
+psAstronomyErrors.h: psAstronomyErrors.dat
+	perl $(top_srcdir)/src/parseErrorCodes.pl --data=$? $@
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psTime.h \
+	psCoord.h \
+	psAstrometry.h \
+	psPhotometry.h \
+	slalib.h
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/airmas.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/airmas.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/airmas.f	(revision 22271)
@@ -0,0 +1,75 @@
+      DOUBLE PRECISION FUNCTION sla_AIRMAS (ZD)
+*+
+*     - - - - - - -
+*      A I R M A S
+*     - - - - - - -
+*
+*  Air mass at given zenith distance (double precision)
+*
+*  Given:
+*     ZD     d     Observed zenith distance (radians)
+*
+*  The result is an estimate of the air mass, in units of that
+*  at the zenith.
+*
+*  Notes:
+*
+*  1)  The "observed" zenith distance referred to above means "as
+*      affected by refraction".
+*
+*  2)  Uses Hardie's (1962) polynomial fit to Bemporad's data for
+*      the relative air mass, X, in units of thickness at the zenith
+*      as tabulated by Schoenberg (1929). This is adequate for all
+*      normal needs as it is accurate to better than 0.1% up to X =
+*      6.8 and better than 1% up to X = 10. Bemporad's tabulated
+*      values are unlikely to be trustworthy to such accuracy
+*      because of variations in density, pressure and other
+*      conditions in the atmosphere from those assumed in his work.
+*
+*  3)  The sign of the ZD is ignored.
+*
+*  4)  At zenith distances greater than about ZD = 87 degrees the
+*      air mass is held constant to avoid arithmetic overflows.
+*
+*  References:
+*     Hardie, R.H., 1962, in "Astronomical Techniques"
+*        ed. W.A. Hiltner, University of Chicago Press, p180.
+*     Schoenberg, E., 1929, Hdb. d. Ap.,
+*        Berlin, Julius Springer, 2, 268.
+*
+*  Original code by P.W.Hill, St Andrews
+*
+*  P.T.Wallace   Starlink   18 March 1999
+*
+*  Copyright (C) 1999 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION ZD
+
+      DOUBLE PRECISION SECZM1
+
+
+      SECZM1 = 1D0/(COS(MIN(1.52D0,ABS(ZD))))-1D0
+      sla_AIRMAS = 1D0 + SECZM1*(0.9981833D0
+     :             - SECZM1*(0.002875D0 + 0.0008083D0*SECZM1))
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aoppa.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aoppa.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aoppa.f	(revision 22271)
@@ -0,0 +1,193 @@
+      SUBROUTINE sla_AOPPA (DATE, DUT, ELONGM, PHIM, HM,
+     :                      XP, YP, TDK, PMB, RH, WL, TLR, AOPRMS)
+*+
+*     - - - - - -
+*      A O P P A
+*     - - - - - -
+*
+*  Precompute apparent to observed place parameters required by
+*  sla_AOPQK and sla_OAPQK.
+*
+*  Given:
+*     DATE   d      UTC date/time (modified Julian Date, JD-2400000.5)
+*     DUT    d      delta UT:  UT1-UTC (UTC seconds)
+*     ELONGM d      mean longitude of the observer (radians, east +ve)
+*     PHIM   d      mean geodetic latitude of the observer (radians)
+*     HM     d      observer's height above sea level (metres)
+*     XP     d      polar motion x-coordinate (radians)
+*     YP     d      polar motion y-coordinate (radians)
+*     TDK    d      local ambient temperature (DegK; std=273.15D0)
+*     PMB    d      local atmospheric pressure (mB; std=1013.25D0)
+*     RH     d      local relative humidity (in the range 0D0-1D0)
+*     WL     d      effective wavelength (micron, e.g. 0.55D0)
+*     TLR    d      tropospheric lapse rate (DegK/metre, e.g. 0.0065D0)
+*
+*  Returned:
+*     AOPRMS d(14)  star-independent apparent-to-observed parameters:
+*
+*       (1)      geodetic latitude (radians)
+*       (2,3)    sine and cosine of geodetic latitude
+*       (4)      magnitude of diurnal aberration vector
+*       (5)      height (HM)
+*       (6)      ambient temperature (TDK)
+*       (7)      pressure (PMB)
+*       (8)      relative humidity (RH)
+*       (9)      wavelength (WL)
+*       (10)     lapse rate (TLR)
+*       (11,12)  refraction constants A and B (radians)
+*       (13)     longitude + eqn of equinoxes + sidereal DUT (radians)
+*       (14)     local apparent sidereal time (radians)
+*
+*  Notes:
+*
+*   1)  It is advisable to take great care with units, as even
+*       unlikely values of the input parameters are accepted and
+*       processed in accordance with the models used.
+*
+*   2)  The DATE argument is UTC expressed as an MJD.  This is,
+*       strictly speaking, improper, because of leap seconds.  However,
+*       as long as the delta UT and the UTC are consistent there
+*       are no difficulties, except during a leap second.  In this
+*       case, the start of the 61st second of the final minute should
+*       begin a new MJD day and the old pre-leap delta UT should
+*       continue to be used.  As the 61st second completes, the MJD
+*       should revert to the start of the day as, simultaneously,
+*       the delta UTC changes by one second to its post-leap new value.
+*
+*   3)  The delta UT (UT1-UTC) is tabulated in IERS circulars and
+*       elsewhere.  It increases by exactly one second at the end of
+*       each UTC leap second, introduced in order to keep delta UT
+*       within +/- 0.9 seconds.
+*
+*   4)  IMPORTANT -- TAKE CARE WITH THE LONGITUDE SIGN CONVENTION.
+*       The longitude required by the present routine is east-positive,
+*       in accordance with geographical convention (and right-handed).
+*       In particular, note that the longitudes returned by the
+*       sla_OBS routine are west-positive, following astronomical
+*       usage, and must be reversed in sign before use in the present
+*       routine.
+*
+*   5)  The polar coordinates XP,YP can be obtained from IERS
+*       circulars and equivalent publications.  The maximum amplitude
+*       is about 0.3 arcseconds.  If XP,YP values are unavailable,
+*       use XP=YP=0D0.  See page B60 of the 1988 Astronomical Almanac
+*       for a definition of the two angles.
+*
+*   6)  The height above sea level of the observing station, HM,
+*       can be obtained from the Astronomical Almanac (Section J
+*       in the 1988 edition), or via the routine sla_OBS.  If P,
+*       the pressure in millibars, is available, an adequate
+*       estimate of HM can be obtained from the expression
+*
+*             HM ~ -29.3D0*TSL*LOG(P/1013.25D0).
+*
+*       where TSL is the approximate sea-level air temperature in
+*       deg K (see Astrophysical Quantities, C.W.Allen, 3rd edition,
+*       section 52).  Similarly, if the pressure P is not known,
+*       it can be estimated from the height of the observing
+*       station, HM as follows:
+*
+*             P ~ 1013.25D0*EXP(-HM/(29.3D0*TSL)).
+*
+*       Note, however, that the refraction is proportional to the
+*       pressure and that an accurate P value is important for
+*       precise work.
+*
+*   7)  Repeated, computationally-expensive, calls to sla_AOPPA for
+*       times that are very close together can be avoided by calling
+*       sla_AOPPA just once and then using sla_AOPPAT for the subsequent
+*       times.  Fresh calls to sla_AOPPA will be needed only when
+*       changes in the precession have grown to unacceptable levels or
+*       when anything affecting the refraction has changed.
+*
+*  Called:  sla_GEOC, sla_REFCO, sla_EQEQX, sla_AOPPAT
+*
+*  P.T.Wallace   Starlink   24 October 2003
+*
+*  Copyright (C) 2003 P.T.Wallace and CCLRC
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION DATE,DUT,ELONGM,PHIM,HM,XP,YP,TDK,PMB,
+     :                 RH,WL,TLR,AOPRMS(14)
+
+      DOUBLE PRECISION sla_EQEQX
+
+*  2Pi
+      DOUBLE PRECISION D2PI
+      PARAMETER (D2PI=6.283185307179586476925287D0)
+
+*  Seconds of time to radians
+      DOUBLE PRECISION S2R
+      PARAMETER (S2R=7.272205216643039903848712D-5)
+
+*  Speed of light (AU per day)
+      DOUBLE PRECISION C
+      PARAMETER (C=173.14463331D0)
+
+*  Ratio between solar and sidereal time
+      DOUBLE PRECISION SOLSID
+      PARAMETER (SOLSID=1.00273790935D0)
+
+      DOUBLE PRECISION CPHIM,XT,YT,ZT,XC,YC,ZC,ELONG,PHI,UAU,VAU
+
+
+
+*  Observer's location corrected for polar motion
+      CPHIM = COS(PHIM)
+      XT = COS(ELONGM)*CPHIM
+      YT = SIN(ELONGM)*CPHIM
+      ZT = SIN(PHIM)
+      XC = XT-XP*ZT
+      YC = YT+YP*ZT
+      ZC = XP*XT-YP*YT+ZT
+      IF (XC.EQ.0D0.AND.YC.EQ.0D0) THEN
+         ELONG = 0D0
+      ELSE
+         ELONG = ATAN2(YC,XC)
+      END IF
+      PHI = ATAN2(ZC,SQRT(XC*XC+YC*YC))
+      AOPRMS(1) = PHI
+      AOPRMS(2) = SIN(PHI)
+      AOPRMS(3) = COS(PHI)
+
+*  Magnitude of the diurnal aberration vector
+      CALL sla_GEOC(PHI,HM,UAU,VAU)
+      AOPRMS(4) = D2PI*UAU*SOLSID/C
+
+*  Copy the refraction parameters and compute the A & B constants
+      AOPRMS(5) = HM
+      AOPRMS(6) = TDK
+      AOPRMS(7) = PMB
+      AOPRMS(8) = RH
+      AOPRMS(9) = WL
+      AOPRMS(10) = TLR
+      CALL sla_REFCO(HM,TDK,PMB,RH,WL,PHI,TLR,1D-10,
+     :               AOPRMS(11),AOPRMS(12))
+
+*  Longitude + equation of the equinoxes + sidereal equivalent of DUT
+*  (ignoring change in equation of the equinoxes between UTC and TDB)
+      AOPRMS(13) = ELONG+sla_EQEQX(DATE)+DUT*SOLSID*S2R
+
+*  Sidereal time
+      CALL sla_AOPPAT(DATE,AOPRMS)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aoppat.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aoppat.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aoppat.f	(revision 22271)
@@ -0,0 +1,62 @@
+      SUBROUTINE sla_AOPPAT (DATE, AOPRMS)
+*+
+*     - - - - - - -
+*      A O P P A T
+*     - - - - - - -
+*
+*  Recompute the sidereal time in the apparent to observed place
+*  star-independent parameter block.
+*
+*  Given:
+*     DATE   d      UTC date/time (modified Julian Date, JD-2400000.5)
+*                   (see AOPPA source for comments on leap seconds)
+*
+*     AOPRMS d(14)  star-independent apparent-to-observed parameters
+*
+*       (1-12)   not required
+*       (13)     longitude + eqn of equinoxes + sidereal DUT
+*       (14)     not required
+*
+*  Returned:
+*     AOPRMS d(14)  star-independent apparent-to-observed parameters:
+*
+*       (1-13)   not changed
+*       (14)     local apparent sidereal time (radians)
+*
+*  For more information, see sla_AOPPA.
+*
+*  Called:  sla_GMST
+*
+*  P.T.Wallace   Starlink   1 July 1993
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION DATE,AOPRMS(14)
+
+      DOUBLE PRECISION sla_GMST
+
+
+
+      AOPRMS(14) = sla_GMST(DATE)+AOPRMS(13)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aopqk.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aopqk.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/aopqk.f	(revision 22271)
@@ -0,0 +1,259 @@
+      SUBROUTINE sla_AOPQK (RAP, DAP, AOPRMS, AOB, ZOB, HOB, DOB, ROB)
+*+
+*     - - - - - -
+*      A O P Q K
+*     - - - - - -
+*
+*  Quick apparent to observed place (but see note 8, below, for
+*  remarks about speed).
+*
+*  Given:
+*     RAP    d      geocentric apparent right ascension
+*     DAP    d      geocentric apparent declination
+*     AOPRMS d(14)  star-independent apparent-to-observed parameters:
+*
+*       (1)      geodetic latitude (radians)
+*       (2,3)    sine and cosine of geodetic latitude
+*       (4)      magnitude of diurnal aberration vector
+*       (5)      height (HM)
+*       (6)      ambient temperature (T)
+*       (7)      pressure (P)
+*       (8)      relative humidity (RH)
+*       (9)      wavelength (WL)
+*       (10)     lapse rate (TLR)
+*       (11,12)  refraction constants A and B (radians)
+*       (13)     longitude + eqn of equinoxes + sidereal DUT (radians)
+*       (14)     local apparent sidereal time (radians)
+*
+*  Returned:
+*     AOB    d      observed azimuth (radians: N=0,E=90)
+*     ZOB    d      observed zenith distance (radians)
+*     HOB    d      observed Hour Angle (radians)
+*     DOB    d      observed Declination (radians)
+*     ROB    d      observed Right Ascension (radians)
+*
+*  Notes:
+*
+*   1)  This routine returns zenith distance rather than elevation
+*       in order to reflect the fact that no allowance is made for
+*       depression of the horizon.
+*
+*   2)  The accuracy of the result is limited by the corrections for
+*       refraction.  Providing the meteorological parameters are
+*       known accurately and there are no gross local effects, the
+*       observed RA,Dec predicted by this routine should be within
+*       about 0.1 arcsec for a zenith distance of less than 70 degrees.
+*       Even at a topocentric zenith distance of 90 degrees, the
+*       accuracy in elevation should be better than 1 arcmin;  useful
+*       results are available for a further 3 degrees, beyond which
+*       the sla_REFRO routine returns a fixed value of the refraction.
+*       The complementary routines sla_AOP (or sla_AOPQK) and sla_OaAP
+*       (or sla_OAPQK) are self-consistent to better than 1 micro-
+*       arcsecond all over the celestial sphere.
+*
+*   3)  It is advisable to take great care with units, as even
+*       unlikely values of the input parameters are accepted and
+*       processed in accordance with the models used.
+*
+*   4)  "Apparent" place means the geocentric apparent right ascension
+*       and declination, which is obtained from a catalogue mean place
+*       by allowing for space motion, parallax, precession, nutation,
+*       annual aberration, and the Sun's gravitational lens effect.  For
+*       star positions in the FK5 system (i.e. J2000), these effects can
+*       be applied by means of the sla_MAP etc routines.  Starting from
+*       other mean place systems, additional transformations will be
+*       needed;  for example, FK4 (i.e. B1950) mean places would first
+*       have to be converted to FK5, which can be done with the
+*       sla_FK425 etc routines.
+*
+*   5)  "Observed" Az,El means the position that would be seen by a
+*       perfect theodolite located at the observer.  This is obtained
+*       from the geocentric apparent RA,Dec by allowing for Earth
+*       orientation and diurnal aberration, rotating from equator
+*       to horizon coordinates, and then adjusting for refraction.
+*       The HA,Dec is obtained by rotating back into equatorial
+*       coordinates, using the geodetic latitude corrected for polar
+*       motion, and is the position that would be seen by a perfect
+*       equatorial located at the observer and with its polar axis
+*       aligned to the Earth's axis of rotation (n.b. not to the
+*       refracted pole).  Finally, the RA is obtained by subtracting
+*       the HA from the local apparent ST.
+*
+*   6)  To predict the required setting of a real telescope, the
+*       observed place produced by this routine would have to be
+*       adjusted for the tilt of the azimuth or polar axis of the
+*       mounting (with appropriate corrections for mount flexures),
+*       for non-perpendicularity between the mounting axes, for the
+*       position of the rotator axis and the pointing axis relative
+*       to it, for tube flexure, for gear and encoder errors, and
+*       finally for encoder zero points.  Some telescopes would, of
+*       course, exhibit other properties which would need to be
+*       accounted for at the appropriate point in the sequence.
+*
+*   7)  The star-independent apparent-to-observed-place parameters
+*       in AOPRMS may be computed by means of the sla_AOPPA routine.
+*       If nothing has changed significantly except the time, the
+*       sla_AOPPAT routine may be used to perform the requisite
+*       partial recomputation of AOPRMS.
+*
+*   8)  At zenith distances beyond about 76 degrees, the need for
+*       special care with the corrections for refraction causes a
+*       marked increase in execution time.  Moreover, the effect
+*       gets worse with increasing zenith distance.  Adroit
+*       programming in the calling application may allow the
+*       problem to be reduced.  Prepare an alternative AOPRMS array,
+*       computed for zero air-pressure;  this will disable the
+*       refraction corrections and cause rapid execution.  Using
+*       this AOPRMS array, a preliminary call to the present routine
+*       will, depending on the application, produce a rough position
+*       which may be enough to establish whether the full, slow
+*       calculation (using the real AOPRMS array) is worthwhile.
+*       For example, there would be no need for the full calculation
+*       if the preliminary call had already established that the
+*       source was well below the elevation limits for a particular
+*       telescope.
+*
+*  9)   The azimuths etc produced by the present routine are with
+*       respect to the celestial pole.  Corrections to the terrestrial
+*       pole can be computed using sla_POLMO.
+*
+*  Called:  sla_DCS2C, sla_REFZ, sla_REFRO, sla_DCC2S, sla_DRANRM
+*
+*  P.T.Wallace   Starlink   24 October 2003
+*
+*  Copyright (C) 2003 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION RAP,DAP,AOPRMS(14),AOB,ZOB,HOB,DOB,ROB
+
+*  Breakpoint for fast/slow refraction algorithm:
+*  ZD greater than arctan(4), (see sla_REFCO routine)
+*  or vector Z less than cosine(arctan(Z)) = 1/sqrt(17)
+      DOUBLE PRECISION ZBREAK
+      PARAMETER (ZBREAK=0.242535625D0)
+
+      INTEGER I
+
+      DOUBLE PRECISION SPHI,CPHI,ST,V(3),XHD,YHD,ZHD,DIURAB,F,
+     :                 XHDT,YHDT,ZHDT,XAET,YAET,ZAET,AZOBS,
+     :                 ZDT,REFA,REFB,ZDOBS,DZD,DREF,CE,
+     :                 XAEO,YAEO,ZAEO,HMOBS,DCOBS,RAOBS
+
+      DOUBLE PRECISION sla_DRANRM
+
+
+
+*  Sin, cos of latitude
+      SPHI = AOPRMS(2)
+      CPHI = AOPRMS(3)
+
+*  Local apparent sidereal time
+      ST = AOPRMS(14)
+
+*  Apparent RA,Dec to Cartesian -HA,Dec
+      CALL sla_DCS2C(RAP-ST,DAP,V)
+      XHD = V(1)
+      YHD = V(2)
+      ZHD = V(3)
+
+*  Diurnal aberration
+      DIURAB = AOPRMS(4)
+      F = (1D0-DIURAB*YHD)
+      XHDT = F*XHD
+      YHDT = F*(YHD+DIURAB)
+      ZHDT = F*ZHD
+
+*  Cartesian -HA,Dec to Cartesian Az,El (S=0,E=90)
+      XAET = SPHI*XHDT-CPHI*ZHDT
+      YAET = YHDT
+      ZAET = CPHI*XHDT+SPHI*ZHDT
+
+*  Azimuth (N=0,E=90)
+      IF (XAET.EQ.0D0.AND.YAET.EQ.0D0) THEN
+         AZOBS = 0D0
+      ELSE
+         AZOBS = ATAN2(YAET,-XAET)
+      END IF
+
+*  Topocentric zenith distance
+      ZDT = ATAN2(SQRT(XAET*XAET+YAET*YAET),ZAET)
+
+*
+*  Refraction
+*  ----------
+
+*  Fast algorithm using two constant model
+      REFA = AOPRMS(11)
+      REFB = AOPRMS(12)
+      CALL sla_REFZ(ZDT,REFA,REFB,ZDOBS)
+
+*  Large zenith distance?
+      IF (COS(ZDOBS).LT.ZBREAK) THEN
+
+*     Yes: use rigorous algorithm
+
+*     Initialize loop (maximum of 10 iterations)
+         I = 1
+         DZD = 1D1
+         DO WHILE (ABS(DZD).GT.1D-10.AND.I.LE.10)
+
+*        Compute refraction using current estimate of observed ZD
+            CALL sla_REFRO(ZDOBS,AOPRMS(5),AOPRMS(6),AOPRMS(7),
+     :                     AOPRMS(8),AOPRMS(9),AOPRMS(1),
+     :                     AOPRMS(10),1D-8,DREF)
+
+*        Remaining discrepancy
+            DZD = ZDOBS+DREF-ZDT
+
+*        Update the estimate
+            ZDOBS = ZDOBS-DZD
+
+*        Increment the iteration counter
+            I = I+1
+         END DO
+      END IF
+
+*  To Cartesian Az/ZD
+      CE = SIN(ZDOBS)
+      XAEO = -COS(AZOBS)*CE
+      YAEO = SIN(AZOBS)*CE
+      ZAEO = COS(ZDOBS)
+
+*  Cartesian Az/ZD to Cartesian -HA,Dec
+      V(1) = SPHI*XAEO+CPHI*ZAEO
+      V(2) = YAEO
+      V(3) = -CPHI*XAEO+SPHI*ZAEO
+
+*  To spherical -HA,Dec
+      CALL sla_DCC2S(V,HMOBS,DCOBS)
+
+*  Right Ascension
+      RAOBS = sla_DRANRM(ST+HMOBS)
+
+*  Return the results
+      AOB = AZOBS
+      ZOB = ZDOBS
+      HOB = -HMOBS
+      DOB = DCOBS
+      ROB = RAOBS
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/astronomy.i
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/astronomy.i	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/astronomy.i	(revision 22271)
@@ -0,0 +1,23 @@
+/* astronomy headers */
+%include "psAstrometry.h"
+%include "psAstronomyErrors.h"
+%include "psCoord.h"
+
+%include "psMetadata.h"
+%extend psMetadataItem {
+    const char *get_STR(void) {
+       if (self->type != PS_META_STR) {
+	  return NULL;
+       } else {
+	  return self->data.V;
+       }
+    }
+}
+
+%apply unsigned int *OUTPUT { unsigned int *nFail }; /* for psMetadataParseConfig */
+%include "psMetadataIO.h"
+%clear psU32 *nFail;
+
+%include "psPhotometry.h"
+%include "psTime.h"
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/atms.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/atms.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/atms.f	(revision 22271)
@@ -0,0 +1,57 @@
+      SUBROUTINE sla__ATMS (RT, TT, DNT, GAMAL, R, DN, RDNDR)
+*+
+*     - - - - -
+*      A T M S
+*     - - - - -
+*
+*  Internal routine used by REFRO
+*
+*  Refractive index and derivative with respect to height for the
+*  stratosphere.
+*
+*  Given:
+*    RT      d    height of tropopause from centre of the Earth (metre)
+*    TT      d    temperature at the tropopause (deg K)
+*    DNT     d    refractive index at the tropopause
+*    GAMAL   d    constant of the atmospheric model = G*MD/R
+*    R       d    current distance from the centre of the Earth (metre)
+*
+*  Returned:
+*    DN      d    refractive index at R
+*    RDNDR   d    R * rate the refractive index is changing at R
+*
+*  P.T.Wallace   Starlink   14 July 1995
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION RT,TT,DNT,GAMAL,R,DN,RDNDR
+
+      DOUBLE PRECISION B,W
+
+
+      B = GAMAL/TT
+      W = (DNT-1D0)*EXP(-B*(R-RT))
+      DN = 1D0+W
+      RDNDR = -R*B*W
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/atmt.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/atmt.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/atmt.f	(revision 22271)
@@ -0,0 +1,71 @@
+      SUBROUTINE sla__ATMT (R0, T0, ALPHA, GAMM2, DELM2,
+     :                      C1, C2, C3, C4, C5, C6, R, T, DN, RDNDR)
+*+
+*     - - - - -
+*      A T M T
+*     - - - - -
+*
+*  Internal routine used by REFRO
+*
+*  Refractive index and derivative with respect to height for the
+*  troposphere.
+*
+*  Given:
+*    R0      d    height of observer from centre of the Earth (metre)
+*    T0      d    temperature at the observer (deg K)
+*    ALPHA   d    alpha          )
+*    GAMM2   d    gamma minus 2  ) see HMNAO paper
+*    DELM2   d    delta minus 2  )
+*    C1      d    useful term  )
+*    C2      d    useful term  )
+*    C3      d    useful term  ) see source
+*    C4      d    useful term  ) of sla_REFRO
+*    C5      d    useful term  )
+*    C6      d    useful term  )
+*    R       d    current distance from the centre of the Earth (metre)
+*
+*  Returned:
+*    T       d    temperature at R (deg K)
+*    DN      d    refractive index at R
+*    RDNDR   d    R * rate the refractive index is changing at R
+*
+*  Note that in the optical case C5 and C6 are zero.
+*
+*  P.T.Wallace   Starlink   30 May 1997
+*
+*  Copyright (C) 1997 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION R0,T0,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,
+     :                 R,T,DN,RDNDR
+
+      DOUBLE PRECISION TT0,TT0GM2,TT0DM2
+
+
+      T = MAX(MIN(T0-ALPHA*(R-R0),320D0),100D0)
+      TT0 = T/T0
+      TT0GM2 = TT0**GAMM2
+      TT0DM2 = TT0**DELM2
+      DN = 1D0+(C1*TT0GM2-(C2-C5/T)*TT0DM2)*TT0
+      RDNDR = R*(-C3*TT0GM2+(C4-C6/TT0)*TT0DM2)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dcc2s.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dcc2s.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dcc2s.f	(revision 22271)
@@ -0,0 +1,70 @@
+      SUBROUTINE sla_DCC2S (V, A, B)
+*+
+*     - - - - - -
+*      D C C 2 S
+*     - - - - - -
+*
+*  Direction cosines to spherical coordinates (double precision)
+*
+*  Given:
+*     V     d(3)   x,y,z vector
+*
+*  Returned:
+*     A,B   d      spherical coordinates in radians
+*
+*  The spherical coordinates are longitude (+ve anticlockwise
+*  looking from the +ve latitude pole) and latitude.  The
+*  Cartesian coordinates are right handed, with the x axis
+*  at zero longitude and latitude, and the z axis at the
+*  +ve latitude pole.
+*
+*  If V is null, zero A and B are returned.
+*  At either pole, zero A is returned.
+*
+*  P.T.Wallace   Starlink   July 1989
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION V(3),A,B
+
+      DOUBLE PRECISION X,Y,Z,R
+
+
+      X = V(1)
+      Y = V(2)
+      Z = V(3)
+      R = SQRT(X*X+Y*Y)
+
+      IF (R.EQ.0D0) THEN
+         A = 0D0
+      ELSE
+         A = ATAN2(Y,X)
+      END IF
+
+      IF (Z.EQ.0D0) THEN
+         B = 0D0
+      ELSE
+         B = ATAN2(Z,R)
+      END IF
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dcs2c.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dcs2c.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dcs2c.f	(revision 22271)
@@ -0,0 +1,58 @@
+      SUBROUTINE sla_DCS2C (A, B, V)
+*+
+*     - - - - - -
+*      D C S 2 C
+*     - - - - - -
+*
+*  Spherical coordinates to direction cosines (double precision)
+*
+*  Given:
+*     A,B       dp      spherical coordinates in radians
+*                        (RA,Dec), (Long,Lat) etc
+*
+*  Returned:
+*     V         dp(3)   x,y,z unit vector
+*
+*  The spherical coordinates are longitude (+ve anticlockwise
+*  looking from the +ve latitude pole) and latitude.  The
+*  Cartesian coordinates are right handed, with the x axis
+*  at zero longitude and latitude, and the z axis at the
+*  +ve latitude pole.
+*
+*  P.T.Wallace   Starlink   October 1984
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION A,B,V(3)
+
+      DOUBLE PRECISION COSB
+
+
+
+      COSB=COS(B)
+
+      V(1)=COS(A)*COSB
+      V(2)=SIN(A)*COSB
+      V(3)=SIN(B)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/drange.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/drange.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/drange.f	(revision 22271)
@@ -0,0 +1,49 @@
+      DOUBLE PRECISION FUNCTION sla_DRANGE (ANGLE)
+*+
+*     - - - - - - -
+*      D R A N G E
+*     - - - - - - -
+*
+*  Normalize angle into range +/- pi  (double precision)
+*
+*  Given:
+*     ANGLE     dp      the angle in radians
+*
+*  The result (double precision) is ANGLE expressed in the range +/- pi.
+*
+*  P.T.Wallace   Starlink   23 November 1995
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION ANGLE
+
+      DOUBLE PRECISION DPI,D2PI
+      PARAMETER (DPI=3.141592653589793238462643D0)
+      PARAMETER (D2PI=6.283185307179586476925287D0)
+
+
+      sla_DRANGE=MOD(ANGLE,D2PI)
+      IF (ABS(sla_DRANGE).GE.DPI)
+     :          sla_DRANGE=sla_DRANGE-SIGN(D2PI,ANGLE)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dranrm.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dranrm.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/dranrm.f	(revision 22271)
@@ -0,0 +1,48 @@
+      DOUBLE PRECISION FUNCTION sla_DRANRM (ANGLE)
+*+
+*     - - - - - - -
+*      D R A N R M
+*     - - - - - - -
+*
+*  Normalize angle into range 0-2 pi  (double precision)
+*
+*  Given:
+*     ANGLE     dp      the angle in radians
+*
+*  The result is ANGLE expressed in the range 0-2 pi (double
+*  precision).
+*
+*  P.T.Wallace   Starlink   23 November 1995
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION ANGLE
+
+      DOUBLE PRECISION D2PI
+      PARAMETER (D2PI=6.283185307179586476925286766559D0)
+
+
+      sla_DRANRM=MOD(ANGLE,D2PI)
+      IF (sla_DRANRM.LT.0D0) sla_DRANRM=sla_DRANRM+D2PI
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/eqeqx.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/eqeqx.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/eqeqx.f	(revision 22271)
@@ -0,0 +1,74 @@
+      DOUBLE PRECISION FUNCTION sla_EQEQX (DATE)
+*+
+*     - - - - - -
+*      E Q E Q X
+*     - - - - - -
+*
+*  Equation of the equinoxes  (IAU 1994, double precision)
+*
+*  Given:
+*     DATE    dp      TDB (loosely ET) as Modified Julian Date
+*                                          (JD-2400000.5)
+*
+*  The result is the equation of the equinoxes (double precision)
+*  in radians:
+*
+*     Greenwich apparent ST = GMST + sla_EQEQX
+*
+*  References:  IAU Resolution C7, Recommendation 3 (1994)
+*               Capitaine, N. & Gontier, A.-M., Astron. Astrophys.,
+*               275, 645-650 (1993)
+*
+*  Called:  sla_NUTC
+*
+*  Patrick Wallace   Starlink   23 August 1996
+*
+*  Copyright (C) 1996 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION DATE
+
+*  Turns to arc seconds and arc seconds to radians
+      DOUBLE PRECISION T2AS,AS2R
+      PARAMETER (T2AS=1296000D0,
+     :           AS2R=0.484813681109535994D-5)
+
+      DOUBLE PRECISION T,OM,DPSI,DEPS,EPS0
+
+
+
+*  Interval between basic epoch J2000.0 and current epoch (JC)
+      T=(DATE-51544.5D0)/36525D0
+
+*  Longitude of the mean ascending node of the lunar orbit on the
+*   ecliptic, measured from the mean equinox of date
+      OM=AS2R*(450160.280D0+(-5D0*T2AS-482890.539D0
+     :         +(7.455D0+0.008D0*T)*T)*T)
+
+*  Nutation
+      CALL sla_NUTC(DATE,DPSI,DEPS,EPS0)
+
+*  Equation of the equinoxes
+      sla_EQEQX=DPSI*COS(EPS0)+AS2R*(0.00264D0*SIN(OM)+
+     :                               0.000063D0*SIN(OM+OM))
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/geoc.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/geoc.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/geoc.f	(revision 22271)
@@ -0,0 +1,74 @@
+      SUBROUTINE sla_GEOC (P, H, R, Z)
+*+
+*     - - - - -
+*      G E O C
+*     - - - - -
+*
+*  Convert geodetic position to geocentric (double precision)
+*
+*  Given:
+*     P     dp     latitude (geodetic, radians)
+*     H     dp     height above reference spheroid (geodetic, metres)
+*
+*  Returned:
+*     R     dp     distance from Earth axis (AU)
+*     Z     dp     distance from plane of Earth equator (AU)
+*
+*  Notes:
+*     1)  Geocentric latitude can be obtained by evaluating ATAN2(Z,R).
+*     2)  IAU 1976 constants are used.
+*
+*  Reference:
+*     Green,R.M., Spherical Astronomy, CUP 1985, p98.
+*
+*  P.T.Wallace   Starlink   4th October 1989
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION P,H,R,Z
+
+*  Earth equatorial radius (metres)
+      DOUBLE PRECISION A0
+      PARAMETER (A0=6378140D0)
+
+*  Reference spheroid flattening factor and useful function
+      DOUBLE PRECISION F,B
+      PARAMETER (F=1D0/298.257D0,B=(1D0-F)**2)
+
+*  Astronomical unit in metres
+      DOUBLE PRECISION AU
+      PARAMETER (AU=1.49597870D11)
+
+      DOUBLE PRECISION SP,CP,C,S
+
+
+
+*  Geodetic to geocentric conversion
+      SP=SIN(P)
+      CP=COS(P)
+      C=1D0/SQRT(CP*CP+B*SP*SP)
+      S=B*C
+      R=(A0*C+H)*CP/AU
+      Z=(A0*S+H)*SP/AU
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/gmst.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/gmst.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/gmst.f	(revision 22271)
@@ -0,0 +1,77 @@
+      DOUBLE PRECISION FUNCTION sla_GMST (UT1)
+*+
+*     - - - - -
+*      G M S T
+*     - - - - -
+*
+*  Conversion from universal time to sidereal time (double precision)
+*
+*  Given:
+*    UT1    dp     universal time (strictly UT1) expressed as
+*                  modified Julian Date (JD-2400000.5)
+*
+*  The result is the Greenwich mean sidereal time (double
+*  precision, radians).
+*
+*  The IAU 1982 expression (see page S15 of 1984 Astronomical Almanac)
+*  is used, but rearranged to reduce rounding errors.  This expression
+*  is always described as giving the GMST at 0 hours UT.  In fact, it
+*  gives the difference between the GMST and the UT, which happens to
+*  equal the GMST (modulo 24 hours) at 0 hours UT each day.  In this
+*  routine, the entire UT is used directly as the argument for the
+*  standard formula, and the fractional part of the UT is added
+*  separately.  Note that the factor 1.0027379... does not appear in the
+*  IAU 1982 expression explicitly but in the form of the coefficient
+*  8640184.812866, which is 86400x36525x0.0027379...
+*
+*  See also the routine sla_GMSTA, which delivers better numerical
+*  precision by accepting the UT date and time as separate arguments.
+*
+*  Called:  sla_DRANRM
+*
+*  P.T.Wallace   Starlink   14 October 2001
+*
+*  Copyright (C) 2001 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION UT1
+
+      DOUBLE PRECISION sla_DRANRM
+
+      DOUBLE PRECISION D2PI,S2R
+      PARAMETER (D2PI=6.283185307179586476925286766559D0,
+     :           S2R=7.272205216643039903848711535369D-5)
+
+      DOUBLE PRECISION TU
+
+
+
+*  Julian centuries from fundamental epoch J2000 to this UT
+      TU=(UT1-51544.5D0)/36525D0
+
+*  GMST at this UT
+      sla_GMST=sla_DRANRM(MOD(UT1,1D0)*D2PI+
+     :                    (24110.54841D0+
+     :                    (8640184.812866D0+
+     :                    (0.093104D0-6.2D-6*TU)*TU)*TU)*S2R)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/nutc.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/nutc.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/nutc.f	(revision 22271)
@@ -0,0 +1,830 @@
+      SUBROUTINE sla_NUTC (DATE, DPSI, DEPS, EPS0)
+*+
+*     - - - - -
+*      N U T C
+*     - - - - -
+*
+*  Nutation:  longitude & obliquity components and mean obliquity,
+*  using the Shirai & Fukushima (2001) theory.
+*
+*  Given:
+*     DATE        d    TDB (loosely ET) as Modified Julian Date
+*                                            (JD-2400000.5)
+*  Returned:
+*     DPSI,DEPS   d    nutation in longitude,obliquity
+*     EPS0        d    mean obliquity
+*
+*  Notes:
+*
+*  1  The routine predicts forced nutation (but not free core nutation)
+*     plus corrections to the IAU 1976 precession model.
+*
+*  2  Earth attitude predictions made by combining the present nutation
+*     model with IAU 1976 precession are accurate to 1 mas (with respect
+*     to the ICRF) for a few decades around 2000.
+*
+*  3  The sla_NUTC80 routine is the equivalent of the present routine
+*     but using the IAU 1980 nutation theory.  The older theory is less
+*     accurate, leading to errors as large as 350 mas over the interval
+*     1900-2100, mainly because of the error in the IAU 1976 precession.
+*
+*  References:
+*
+*     Shirai, T. & Fukushima, T., Astron.J. 121, 3270-3283 (2001).
+*
+*     Fukushima, T., 1991, Astron.Astrophys. 244, L11 (1991).
+*
+*     Simon, J. L., Bretagnon, P., Chapront, J., Chapront-Touze, M.,
+*     Francou, G. & Laskar, J., Astron.Astrophys. 282, 663 (1994).
+*
+*  P.T.Wallace   Starlink   7 October 2001
+*
+*  Copyright (C) 2001 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION DATE,DPSI,DEPS,EPS0
+
+*  Degrees to radians
+      DOUBLE PRECISION DD2R
+      PARAMETER (DD2R=1.745329251994329576923691D-2)
+
+*  Arc seconds to radians
+      DOUBLE PRECISION DAS2R
+      PARAMETER (DAS2R=4.848136811095359935899141D-6)
+
+*  Arc seconds in a full circle
+      DOUBLE PRECISION TURNAS
+      PARAMETER (TURNAS=1296000D0)
+
+*  Reference epoch (J2000), MJD
+      DOUBLE PRECISION DJM0
+      PARAMETER (DJM0=51544.5D0 )
+
+*  Days per Julian century
+      DOUBLE PRECISION DJC
+      PARAMETER (DJC=36525D0)
+
+      INTEGER I,J
+      DOUBLE PRECISION T,EL,ELP,F,D,OM,VE,MA,JU,SA,THETA,C,S,DP,DE
+
+*  Number of terms in the nutation model
+      INTEGER NTERMS
+      PARAMETER (NTERMS=194)
+
+*  The SF2001 forced nutation model
+      INTEGER NA(9,NTERMS)
+      DOUBLE PRECISION PSI(4,NTERMS), EPS(4,NTERMS)
+
+*  Coefficients of fundamental angles
+      DATA ( ( NA(I,J), I=1,9 ), J=1,10 ) /
+     :    0,   0,   0,   0,  -1,   0,   0,   0,   0,
+     :    0,   0,   2,  -2,   2,   0,   0,   0,   0,
+     :    0,   0,   2,   0,   2,   0,   0,   0,   0,
+     :    0,   0,   0,   0,  -2,   0,   0,   0,   0,
+     :    0,   1,   0,   0,   0,   0,   0,   0,   0,
+     :    0,   1,   2,  -2,   2,   0,   0,   0,   0,
+     :    1,   0,   0,   0,   0,   0,   0,   0,   0,
+     :    0,   0,   2,   0,   1,   0,   0,   0,   0,
+     :    1,   0,   2,   0,   2,   0,   0,   0,   0,
+     :    0,  -1,   2,  -2,   2,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=11,20 ) /
+     :    0,   0,   2,  -2,   1,   0,   0,   0,   0,
+     :   -1,   0,   2,   0,   2,   0,   0,   0,   0,
+     :   -1,   0,   0,   2,   0,   0,   0,   0,   0,
+     :    1,   0,   0,   0,   1,   0,   0,   0,   0,
+     :    1,   0,   0,   0,  -1,   0,   0,   0,   0,
+     :   -1,   0,   2,   2,   2,   0,   0,   0,   0,
+     :    1,   0,   2,   0,   1,   0,   0,   0,   0,
+     :   -2,   0,   2,   0,   1,   0,   0,   0,   0,
+     :    0,   0,   0,   2,   0,   0,   0,   0,   0,
+     :    0,   0,   2,   2,   2,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=21,30 ) /
+     :    2,   0,   0,  -2,   0,   0,   0,   0,   0,
+     :    2,   0,   2,   0,   2,   0,   0,   0,   0,
+     :    1,   0,   2,  -2,   2,   0,   0,   0,   0,
+     :   -1,   0,   2,   0,   1,   0,   0,   0,   0,
+     :    2,   0,   0,   0,   0,   0,   0,   0,   0,
+     :    0,   0,   2,   0,   0,   0,   0,   0,   0,
+     :    0,   1,   0,   0,   1,   0,   0,   0,   0,
+     :   -1,   0,   0,   2,   1,   0,   0,   0,   0,
+     :    0,   2,   2,  -2,   2,   0,   0,   0,   0,
+     :    0,   0,   2,  -2,   0,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=31,40 ) /
+     :   -1,   0,   0,   2,  -1,   0,   0,   0,   0,
+     :    0,   1,   0,   0,  -1,   0,   0,   0,   0,
+     :    0,   2,   0,   0,   0,   0,   0,   0,   0,
+     :   -1,   0,   2,   2,   1,   0,   0,   0,   0,
+     :    1,   0,   2,   2,   2,   0,   0,   0,   0,
+     :    0,   1,   2,   0,   2,   0,   0,   0,   0,
+     :   -2,   0,   2,   0,   0,   0,   0,   0,   0,
+     :    0,   0,   2,   2,   1,   0,   0,   0,   0,
+     :    0,  -1,   2,   0,   2,   0,   0,   0,   0,
+     :    0,   0,   0,   2,   1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=41,50 ) /
+     :    1,   0,   2,  -2,   1,   0,   0,   0,   0,
+     :    2,   0,   0,  -2,  -1,   0,   0,   0,   0,
+     :    2,   0,   2,  -2,   2,   0,   0,   0,   0,
+     :    2,   0,   2,   0,   1,   0,   0,   0,   0,
+     :    0,   0,   0,   2,  -1,   0,   0,   0,   0,
+     :    0,  -1,   2,  -2,   1,   0,   0,   0,   0,
+     :   -1,  -1,   0,   2,   0,   0,   0,   0,   0,
+     :    2,   0,   0,  -2,   1,   0,   0,   0,   0,
+     :    1,   0,   0,   2,   0,   0,   0,   0,   0,
+     :    0,   1,   2,  -2,   1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=51,60 ) /
+     :    1,  -1,   0,   0,   0,   0,   0,   0,   0,
+     :   -2,   0,   2,   0,   2,   0,   0,   0,   0,
+     :    0,  -1,   0,   2,   0,   0,   0,   0,   0,
+     :    3,   0,   2,   0,   2,   0,   0,   0,   0,
+     :    0,   0,   0,   1,   0,   0,   0,   0,   0,
+     :    1,  -1,   2,   0,   2,   0,   0,   0,   0,
+     :    1,   0,   0,  -1,   0,   0,   0,   0,   0,
+     :   -1,  -1,   2,   2,   2,   0,   0,   0,   0,
+     :   -1,   0,   2,   0,   0,   0,   0,   0,   0,
+     :    2,   0,   0,   0,  -1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=61,70 ) /
+     :    0,  -1,   2,   2,   2,   0,   0,   0,   0,
+     :    1,   1,   2,   0,   2,   0,   0,   0,   0,
+     :    2,   0,   0,   0,   1,   0,   0,   0,   0,
+     :    1,   1,   0,   0,   0,   0,   0,   0,   0,
+     :    1,   0,  -2,   2,  -1,   0,   0,   0,   0,
+     :    1,   0,   2,   0,   0,   0,   0,   0,   0,
+     :   -1,   1,   0,   1,   0,   0,   0,   0,   0,
+     :    1,   0,   0,   0,   2,   0,   0,   0,   0,
+     :   -1,   0,   1,   0,   1,   0,   0,   0,   0,
+     :    0,   0,   2,   1,   2,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=71,80 ) /
+     :   -1,   1,   0,   1,   1,   0,   0,   0,   0,
+     :   -1,   0,   2,   4,   2,   0,   0,   0,   0,
+     :    0,  -2,   2,  -2,   1,   0,   0,   0,   0,
+     :    1,   0,   2,   2,   1,   0,   0,   0,   0,
+     :    1,   0,   0,   0,  -2,   0,   0,   0,   0,
+     :   -2,   0,   2,   2,   2,   0,   0,   0,   0,
+     :    1,   1,   2,  -2,   2,   0,   0,   0,   0,
+     :   -2,   0,   2,   4,   2,   0,   0,   0,   0,
+     :   -1,   0,   4,   0,   2,   0,   0,   0,   0,
+     :    2,   0,   2,  -2,   1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=81,90 ) /
+     :    1,   0,   0,  -1,  -1,   0,   0,   0,   0,
+     :    2,   0,   2,   2,   2,   0,   0,   0,   0,
+     :    1,   0,   0,   2,   1,   0,   0,   0,   0,
+     :    3,   0,   0,   0,   0,   0,   0,   0,   0,
+     :    0,   0,   2,  -2,  -1,   0,   0,   0,   0,
+     :    3,   0,   2,  -2,   2,   0,   0,   0,   0,
+     :    0,   0,   4,  -2,   2,   0,   0,   0,   0,
+     :   -1,   0,   0,   4,   0,   0,   0,   0,   0,
+     :    0,   1,   2,   0,   1,   0,   0,   0,   0,
+     :    0,   0,   2,  -2,   3,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=91,100 ) /
+     :   -2,   0,   0,   4,   0,   0,   0,   0,   0,
+     :   -1,  -1,   0,   2,   1,   0,   0,   0,   0,
+     :   -2,   0,   2,   0,  -1,   0,   0,   0,   0,
+     :    0,   0,   2,   0,  -1,   0,   0,   0,   0,
+     :    0,  -1,   2,   0,   1,   0,   0,   0,   0,
+     :    0,   1,   0,   0,   2,   0,   0,   0,   0,
+     :    0,   0,   2,  -1,   2,   0,   0,   0,   0,
+     :    2,   1,   0,  -2,   0,   0,   0,   0,   0,
+     :    0,   0,   2,   4,   2,   0,   0,   0,   0,
+     :   -1,  -1,   0,   2,  -1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=101,110 ) /
+     :   -1,   1,   0,   2,   0,   0,   0,   0,   0,
+     :    1,  -1,   0,   0,   1,   0,   0,   0,   0,
+     :    0,  -1,   2,  -2,   0,   0,   0,   0,   0,
+     :    0,   1,   0,   0,  -2,   0,   0,   0,   0,
+     :    1,  -1,   2,   2,   2,   0,   0,   0,   0,
+     :    1,   0,   0,   2,  -1,   0,   0,   0,   0,
+     :   -1,   1,   2,   2,   2,   0,   0,   0,   0,
+     :    3,   0,   2,   0,   1,   0,   0,   0,   0,
+     :    0,   1,   2,   2,   2,   0,   0,   0,   0,
+     :    1,   0,   2,  -2,   0,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=111,120 ) /
+     :   -1,   0,  -2,   4,  -1,   0,   0,   0,   0,
+     :   -1,  -1,   2,   2,   1,   0,   0,   0,   0,
+     :    0,  -1,   2,   2,   1,   0,   0,   0,   0,
+     :    2,  -1,   2,   0,   2,   0,   0,   0,   0,
+     :    0,   0,   0,   2,   2,   0,   0,   0,   0,
+     :    1,  -1,   2,   0,   1,   0,   0,   0,   0,
+     :   -1,   1,   2,   0,   2,   0,   0,   0,   0,
+     :    0,   1,   0,   2,   0,   0,   0,   0,   0,
+     :    0,   1,   2,  -2,   0,   0,   0,   0,   0,
+     :    0,   3,   2,  -2,   2,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=121,130 ) /
+     :    0,   0,   0,   1,   1,   0,   0,   0,   0,
+     :   -1,   0,   2,   2,   0,   0,   0,   0,   0,
+     :    2,   1,   2,   0,   2,   0,   0,   0,   0,
+     :    1,   1,   0,   0,   1,   0,   0,   0,   0,
+     :    2,   0,   0,   2,   0,   0,   0,   0,   0,
+     :    1,   1,   2,   0,   1,   0,   0,   0,   0,
+     :   -1,   0,   0,   2,   2,   0,   0,   0,   0,
+     :    1,   0,  -2,   2,   0,   0,   0,   0,   0,
+     :    0,  -1,   0,   2,  -1,   0,   0,   0,   0,
+     :   -1,   0,   1,   0,   2,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=131,140 ) /
+     :    0,   1,   0,   1,   0,   0,   0,   0,   0,
+     :    1,   0,  -2,   2,  -2,   0,   0,   0,   0,
+     :    0,   0,   0,   1,  -1,   0,   0,   0,   0,
+     :    1,  -1,   0,   0,  -1,   0,   0,   0,   0,
+     :    0,   0,   0,   4,   0,   0,   0,   0,   0,
+     :    1,  -1,   0,   2,   0,   0,   0,   0,   0,
+     :    1,   0,   2,   1,   2,   0,   0,   0,   0,
+     :    1,   0,   2,  -1,   2,   0,   0,   0,   0,
+     :   -1,   0,   0,   2,  -2,   0,   0,   0,   0,
+     :    0,   0,   2,   1,   1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=141,150 ) /
+     :   -1,   0,   2,   0,  -1,   0,   0,   0,   0,
+     :   -1,   0,   2,   4,   1,   0,   0,   0,   0,
+     :    0,   0,   2,   2,   0,   0,   0,   0,   0,
+     :    1,   1,   2,  -2,   1,   0,   0,   0,   0,
+     :    0,   0,   1,   0,   1,   0,   0,   0,   0,
+     :   -1,   0,   2,  -1,   1,   0,   0,   0,   0,
+     :   -2,   0,   2,   2,   1,   0,   0,   0,   0,
+     :    2,  -1,   0,   0,   0,   0,   0,   0,   0,
+     :    4,   0,   2,   0,   2,   0,   0,   0,   0,
+     :    2,   1,   2,  -2,   2,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=151,160 ) /
+     :    0,   1,   2,   1,   2,   0,   0,   0,   0,
+     :    1,   0,   4,  -2,   2,   0,   0,   0,   0,
+     :    1,   1,   0,   0,  -1,   0,   0,   0,   0,
+     :   -2,   0,   2,   4,   1,   0,   0,   0,   0,
+     :    2,   0,   2,   0,   0,   0,   0,   0,   0,
+     :   -1,   0,   1,   0,   0,   0,   0,   0,   0,
+     :    1,   0,   0,   1,   0,   0,   0,   0,   0,
+     :    0,   1,   0,   2,   1,   0,   0,   0,   0,
+     :   -1,   0,   4,   0,   1,   0,   0,   0,   0,
+     :   -1,   0,   0,   4,   1,   0,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=161,170 ) /
+     :    2,   0,   2,   2,   1,   0,   0,   0,   0,
+     :    2,   1,   0,   0,   0,   0,   0,   0,   0,
+     :    0,   0,   5,  -5,   5,  -3,   0,   0,   0,
+     :    0,   0,   0,   0,   0,   0,   0,   2,   0,
+     :    0,   0,   1,  -1,   1,   0,   0,  -1,   0,
+     :    0,   0,  -1,   1,  -1,   1,   0,   0,   0,
+     :    0,   0,  -1,   1,   0,   0,   2,   0,   0,
+     :    0,   0,   3,  -3,   3,   0,   0,  -1,   0,
+     :    0,   0,  -8,   8,  -7,   5,   0,   0,   0,
+     :    0,   0,  -1,   1,  -1,   0,   2,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=171,180 ) /
+     :    0,   0,  -2,   2,  -2,   2,   0,   0,   0,
+     :    0,   0,  -6,   6,  -6,   4,   0,   0,   0,
+     :    0,   0,  -2,   2,  -2,   0,   8,  -3,   0,
+     :    0,   0,   6,  -6,   6,   0,  -8,   3,   0,
+     :    0,   0,   4,  -4,   4,  -2,   0,   0,   0,
+     :    0,   0,  -3,   3,  -3,   2,   0,   0,   0,
+     :    0,   0,   4,  -4,   3,   0,  -8,   3,   0,
+     :    0,   0,  -4,   4,  -5,   0,   8,  -3,   0,
+     :    0,   0,   0,   0,   0,   2,   0,   0,   0,
+     :    0,   0,  -4,   4,  -4,   3,   0,   0,   0 /
+      DATA ( ( NA(I,J), I=1,9 ), J=181,190 ) /
+     :    0,   1,  -1,   1,  -1,   0,   0,   1,   0,
+     :    0,   0,   0,   0,   0,   0,   0,   1,   0,
+     :    0,   0,   1,  -1,   1,   1,   0,   0,   0,
+     :    0,   0,   2,  -2,   2,   0,  -2,   0,   0,
+     :    0,  -1,  -7,   7,  -7,   5,   0,   0,   0,
+     :   -2,   0,   2,   0,   2,   0,   0,  -2,   0,
+     :   -2,   0,   2,   0,   1,   0,   0,  -3,   0,
+     :    0,   0,   2,  -2,   2,   0,   0,  -2,   0,
+     :    0,   0,   1,  -1,   1,   0,   0,   1,   0,
+     :    0,   0,   0,   0,   0,   0,   0,   0,   2 /
+      DATA ( ( NA(I,J), I=1,9 ), J=191,NTERMS ) /
+     :    0,   0,   0,   0,   0,   0,   0,   0,   1,
+     :    2,   0,  -2,   0,  -2,   0,   0,   3,   0,
+     :    0,   0,   1,  -1,   1,   0,   0,  -2,   0,
+     :    0,   0,  -7,   7,  -7,   5,   0,   0,   0 /
+
+*  Nutation series: longitude
+      DATA ( ( PSI(I,J), I=1,4 ), J=1,10 ) /
+     :  3341.5D0, 17206241.8D0,  3.1D0, 17409.5D0,
+     : -1716.8D0, -1317185.3D0,  1.4D0,  -156.8D0,
+     :   285.7D0,  -227667.0D0,  0.3D0,   -23.5D0,
+     :   -68.6D0,  -207448.0D0,  0.0D0,   -21.4D0,
+     :   950.3D0,   147607.9D0, -2.3D0,  -355.0D0,
+     :   -66.7D0,   -51689.1D0,  0.2D0,   122.6D0,
+     :  -108.6D0,    71117.6D0,  0.0D0,     7.0D0,
+     :    35.6D0,   -38740.2D0,  0.1D0,   -36.2D0,
+     :    85.4D0,   -30127.6D0,  0.0D0,    -3.1D0,
+     :     9.0D0,    21583.0D0,  0.1D0,   -50.3D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=11,20 ) /
+     :    22.1D0,    12822.8D0,  0.0D0,    13.3D0,
+     :     3.4D0,    12350.8D0,  0.0D0,     1.3D0,
+     :   -21.1D0,    15699.4D0,  0.0D0,     1.6D0,
+     :     4.2D0,     6313.8D0,  0.0D0,     6.2D0,
+     :   -22.8D0,     5796.9D0,  0.0D0,     6.1D0,
+     :    15.7D0,    -5961.1D0,  0.0D0,    -0.6D0,
+     :    13.1D0,    -5159.1D0,  0.0D0,    -4.6D0,
+     :     1.8D0,     4592.7D0,  0.0D0,     4.5D0,
+     :   -17.5D0,     6336.0D0,  0.0D0,     0.7D0,
+     :    16.3D0,    -3851.1D0,  0.0D0,    -0.4D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=21,30 ) /
+     :    -2.8D0,     4771.7D0,  0.0D0,     0.5D0,
+     :    13.8D0,    -3099.3D0,  0.0D0,    -0.3D0,
+     :     0.2D0,     2860.3D0,  0.0D0,     0.3D0,
+     :     1.4D0,     2045.3D0,  0.0D0,     2.0D0,
+     :    -8.6D0,     2922.6D0,  0.0D0,     0.3D0,
+     :    -7.7D0,     2587.9D0,  0.0D0,     0.2D0,
+     :     8.8D0,    -1408.1D0,  0.0D0,     3.7D0,
+     :     1.4D0,     1517.5D0,  0.0D0,     1.5D0,
+     :    -1.9D0,    -1579.7D0,  0.0D0,     7.7D0,
+     :     1.3D0,    -2178.6D0,  0.0D0,    -0.2D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=31,40 ) /
+     :    -4.8D0,     1286.8D0,  0.0D0,     1.3D0,
+     :     6.3D0,     1267.2D0,  0.0D0,    -4.0D0,
+     :    -1.0D0,     1669.3D0,  0.0D0,    -8.3D0,
+     :     2.4D0,    -1020.0D0,  0.0D0,    -0.9D0,
+     :     4.5D0,     -766.9D0,  0.0D0,     0.0D0,
+     :    -1.1D0,      756.5D0,  0.0D0,    -1.7D0,
+     :    -1.4D0,    -1097.3D0,  0.0D0,    -0.5D0,
+     :     2.6D0,     -663.0D0,  0.0D0,    -0.6D0,
+     :     0.8D0,     -714.1D0,  0.0D0,     1.6D0,
+     :     0.4D0,     -629.9D0,  0.0D0,    -0.6D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=41,50 ) /
+     :     0.3D0,      580.4D0,  0.0D0,     0.6D0,
+     :    -1.6D0,      577.3D0,  0.0D0,     0.5D0,
+     :    -0.9D0,      644.4D0,  0.0D0,     0.0D0,
+     :     2.2D0,     -534.0D0,  0.0D0,    -0.5D0,
+     :    -2.5D0,      493.3D0,  0.0D0,     0.5D0,
+     :    -0.1D0,     -477.3D0,  0.0D0,    -2.4D0,
+     :    -0.9D0,      735.0D0,  0.0D0,    -1.7D0,
+     :     0.7D0,      406.2D0,  0.0D0,     0.4D0,
+     :    -2.8D0,      656.9D0,  0.0D0,     0.0D0,
+     :     0.6D0,      358.0D0,  0.0D0,     2.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=51,60 ) /
+     :    -0.7D0,      472.5D0,  0.0D0,    -1.1D0,
+     :    -0.1D0,     -300.5D0,  0.0D0,     0.0D0,
+     :    -1.2D0,      435.1D0,  0.0D0,    -1.0D0,
+     :     1.8D0,     -289.4D0,  0.0D0,     0.0D0,
+     :     0.6D0,     -422.6D0,  0.0D0,     0.0D0,
+     :     0.8D0,     -287.6D0,  0.0D0,     0.6D0,
+     :   -38.6D0,     -392.3D0,  0.0D0,     0.0D0,
+     :     0.7D0,     -281.8D0,  0.0D0,     0.6D0,
+     :     0.6D0,     -405.7D0,  0.0D0,     0.0D0,
+     :    -1.2D0,      229.0D0,  0.0D0,     0.2D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=61,70 ) /
+     :     1.1D0,     -264.3D0,  0.0D0,     0.5D0,
+     :    -0.7D0,      247.9D0,  0.0D0,    -0.5D0,
+     :    -0.2D0,      218.0D0,  0.0D0,     0.2D0,
+     :     0.6D0,     -339.0D0,  0.0D0,     0.8D0,
+     :    -0.7D0,      198.7D0,  0.0D0,     0.2D0,
+     :    -1.5D0,      334.0D0,  0.0D0,     0.0D0,
+     :     0.1D0,      334.0D0,  0.0D0,     0.0D0,
+     :    -0.1D0,     -198.1D0,  0.0D0,     0.0D0,
+     :  -106.6D0,        0.0D0,  0.0D0,     0.0D0,
+     :    -0.5D0,      165.8D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=71,80 ) /
+     :     0.0D0,      134.8D0,  0.0D0,     0.0D0,
+     :     0.9D0,     -151.6D0,  0.0D0,     0.0D0,
+     :     0.0D0,     -129.7D0,  0.0D0,     0.0D0,
+     :     0.8D0,     -132.8D0,  0.0D0,    -0.1D0,
+     :     0.5D0,     -140.7D0,  0.0D0,     0.0D0,
+     :    -0.1D0,      138.4D0,  0.0D0,     0.0D0,
+     :     0.0D0,      129.0D0,  0.0D0,    -0.3D0,
+     :     0.5D0,     -121.2D0,  0.0D0,     0.0D0,
+     :    -0.3D0,      114.5D0,  0.0D0,     0.0D0,
+     :    -0.1D0,      101.8D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=81,90 ) /
+     :    -3.6D0,     -101.9D0,  0.0D0,     0.0D0,
+     :     0.8D0,     -109.4D0,  0.0D0,     0.0D0,
+     :     0.2D0,      -97.0D0,  0.0D0,     0.0D0,
+     :    -0.7D0,      157.3D0,  0.0D0,     0.0D0,
+     :     0.2D0,      -83.3D0,  0.0D0,     0.0D0,
+     :    -0.3D0,       93.3D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       92.1D0,  0.0D0,     0.0D0,
+     :    -0.5D0,      133.6D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       81.5D0,  0.0D0,     0.0D0,
+     :     0.0D0,      123.9D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=91,100 ) /
+     :    -0.3D0,      128.1D0,  0.0D0,     0.0D0,
+     :     0.1D0,       74.1D0,  0.0D0,    -0.3D0,
+     :    -0.2D0,      -70.3D0,  0.0D0,     0.0D0,
+     :    -0.4D0,       66.6D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -66.7D0,  0.0D0,     0.0D0,
+     :    -0.7D0,       69.3D0,  0.0D0,    -0.3D0,
+     :     0.0D0,      -70.4D0,  0.0D0,     0.0D0,
+     :    -0.1D0,      101.5D0,  0.0D0,     0.0D0,
+     :     0.5D0,      -69.1D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       58.5D0,  0.0D0,     0.2D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=101,110 ) /
+     :     0.1D0,      -94.9D0,  0.0D0,     0.2D0,
+     :     0.0D0,       52.9D0,  0.0D0,    -0.2D0,
+     :     0.1D0,       86.7D0,  0.0D0,    -0.2D0,
+     :    -0.1D0,      -59.2D0,  0.0D0,     0.2D0,
+     :     0.3D0,      -58.8D0,  0.0D0,     0.1D0,
+     :    -0.3D0,       49.0D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       56.9D0,  0.0D0,    -0.1D0,
+     :     0.3D0,      -50.2D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       53.4D0,  0.0D0,    -0.1D0,
+     :     0.1D0,      -76.5D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=111,120 ) /
+     :    -0.2D0,       45.3D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -46.8D0,  0.0D0,     0.0D0,
+     :     0.2D0,      -44.6D0,  0.0D0,     0.0D0,
+     :     0.2D0,      -48.7D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -46.8D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -42.0D0,  0.0D0,     0.0D0,
+     :     0.0D0,       46.4D0,  0.0D0,    -0.1D0,
+     :     0.2D0,      -67.3D0,  0.0D0,     0.1D0,
+     :     0.0D0,      -65.8D0,  0.0D0,     0.2D0,
+     :    -0.1D0,      -43.9D0,  0.0D0,     0.3D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=121,130 ) /
+     :     0.0D0,      -38.9D0,  0.0D0,     0.0D0,
+     :    -0.3D0,       63.9D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       41.2D0,  0.0D0,     0.0D0,
+     :     0.0D0,      -36.1D0,  0.0D0,     0.2D0,
+     :    -0.3D0,       58.5D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       36.1D0,  0.0D0,     0.0D0,
+     :     0.0D0,      -39.7D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -57.7D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       33.4D0,  0.0D0,     0.0D0,
+     :    36.4D0,        0.0D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=131,140 ) /
+     :    -0.1D0,       55.7D0,  0.0D0,    -0.1D0,
+     :     0.1D0,      -35.4D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -31.0D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       30.1D0,  0.0D0,     0.0D0,
+     :    -0.3D0,       49.2D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       49.1D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       33.6D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -33.5D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -31.0D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       28.0D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=141,150 ) /
+     :     0.1D0,      -25.2D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -26.2D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       41.5D0,  0.0D0,     0.0D0,
+     :     0.0D0,       24.5D0,  0.0D0,     0.1D0,
+     :   -16.2D0,        0.0D0,  0.0D0,     0.0D0,
+     :     0.0D0,      -22.3D0,  0.0D0,     0.0D0,
+     :     0.0D0,       23.1D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       37.5D0,  0.0D0,     0.0D0,
+     :     0.2D0,      -25.7D0,  0.0D0,     0.0D0,
+     :     0.0D0,       25.2D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=151,160 ) /
+     :     0.1D0,      -24.5D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       24.3D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -20.7D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -20.8D0,  0.0D0,     0.0D0,
+     :    -0.2D0,       33.4D0,  0.0D0,     0.0D0,
+     :    32.9D0,        0.0D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -32.6D0,  0.0D0,     0.0D0,
+     :     0.0D0,       19.9D0,  0.0D0,     0.0D0,
+     :    -0.1D0,       19.6D0,  0.0D0,     0.0D0,
+     :     0.0D0,      -18.7D0,  0.0D0,     0.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=161,170 ) /
+     :     0.1D0,      -19.0D0,  0.0D0,     0.0D0,
+     :     0.1D0,      -28.6D0,  0.0D0,     0.0D0,
+     :     4.0D0,      178.8D0,-11.8D0,     0.3D0,
+     :    39.8D0,     -107.3D0, -5.6D0,    -1.0D0,
+     :     9.9D0,      164.0D0, -4.1D0,     0.1D0,
+     :    -4.8D0,     -135.3D0, -3.4D0,    -0.1D0,
+     :    50.5D0,       75.0D0,  1.4D0,    -1.2D0,
+     :    -1.1D0,      -53.5D0,  1.3D0,     0.0D0,
+     :   -45.0D0,       -2.4D0, -0.4D0,     6.6D0,
+     :   -11.5D0,      -61.0D0, -0.9D0,     0.4D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=171,180 ) /
+     :     4.4D0,      -68.4D0, -3.4D0,     0.0D0,
+     :     7.7D0,      -47.1D0, -4.7D0,    -1.0D0,
+     :   -42.9D0,      -12.6D0, -1.2D0,     4.2D0,
+     :   -42.8D0,       12.7D0, -1.2D0,    -4.2D0,
+     :    -7.6D0,      -44.1D0,  2.1D0,    -0.5D0,
+     :   -64.1D0,        1.7D0,  0.2D0,     4.5D0,
+     :    36.4D0,      -10.4D0,  1.0D0,     3.5D0,
+     :    35.6D0,       10.2D0,  1.0D0,    -3.5D0,
+     :    -1.7D0,       39.5D0,  2.0D0,     0.0D0,
+     :    50.9D0,       -8.2D0, -0.8D0,    -5.0D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=181,190 ) /
+     :     0.0D0,       52.3D0,  1.2D0,     0.0D0,
+     :   -42.9D0,      -17.8D0,  0.4D0,     0.0D0,
+     :     2.6D0,       34.3D0,  0.8D0,     0.0D0,
+     :    -0.8D0,      -48.6D0,  2.4D0,    -0.1D0,
+     :    -4.9D0,       30.5D0,  3.7D0,     0.7D0,
+     :     0.0D0,      -43.6D0,  2.1D0,     0.0D0,
+     :     0.0D0,      -25.4D0,  1.2D0,     0.0D0,
+     :     2.0D0,       40.9D0, -2.0D0,     0.0D0,
+     :    -2.1D0,       26.1D0,  0.6D0,     0.0D0,
+     :    22.6D0,       -3.2D0, -0.5D0,    -0.5D0 /
+      DATA ( ( PSI(I,J), I=1,4 ), J=191,NTERMS ) /
+     :    -7.6D0,       24.9D0, -0.4D0,    -0.2D0,
+     :    -6.2D0,       34.9D0,  1.7D0,     0.3D0,
+     :     2.0D0,       17.4D0, -0.4D0,     0.1D0,
+     :    -3.9D0,       20.5D0,  2.4D0,     0.6D0 /
+
+*  Nutation series: obliquity
+      DATA ( ( EPS(I,J), I=1,4 ), J=1,10 ) /
+     : 9205365.8D0, -1506.2D0,  885.7D0, -0.2D0,
+     :  573095.9D0,  -570.2D0, -305.0D0, -0.3D0,
+     :   97845.5D0,   147.8D0,  -48.8D0, -0.2D0,
+     :  -89753.6D0,    28.0D0,   46.9D0,  0.0D0,
+     :    7406.7D0,  -327.1D0,  -18.2D0,  0.8D0,
+     :   22442.3D0,   -22.3D0,  -67.6D0,  0.0D0,
+     :    -683.6D0,    46.8D0,    0.0D0,  0.0D0,
+     :   20070.7D0,    36.0D0,    1.6D0,  0.0D0,
+     :   12893.8D0,    39.5D0,   -6.2D0,  0.0D0,
+     :   -9593.2D0,    14.4D0,   30.2D0, -0.1D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=11,20 ) /
+     :   -6899.5D0,     4.8D0,   -0.6D0,  0.0D0,
+     :   -5332.5D0,    -0.1D0,    2.7D0,  0.0D0,
+     :    -125.2D0,    10.5D0,    0.0D0,  0.0D0,
+     :   -3323.4D0,    -0.9D0,   -0.3D0,  0.0D0,
+     :    3142.3D0,     8.9D0,    0.3D0,  0.0D0,
+     :    2552.5D0,     7.3D0,   -1.2D0,  0.0D0,
+     :    2634.4D0,     8.8D0,    0.2D0,  0.0D0,
+     :   -2424.4D0,     1.6D0,   -0.4D0,  0.0D0,
+     :    -123.3D0,     3.9D0,    0.0D0,  0.0D0,
+     :    1642.4D0,     7.3D0,   -0.8D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=21,30 ) /
+     :      47.9D0,     3.2D0,    0.0D0,  0.0D0,
+     :    1321.2D0,     6.2D0,   -0.6D0,  0.0D0,
+     :   -1234.1D0,    -0.3D0,    0.6D0,  0.0D0,
+     :   -1076.5D0,    -0.3D0,    0.0D0,  0.0D0,
+     :     -61.6D0,     1.8D0,    0.0D0,  0.0D0,
+     :     -55.4D0,     1.6D0,    0.0D0,  0.0D0,
+     :     856.9D0,    -4.9D0,   -2.1D0,  0.0D0,
+     :    -800.7D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     685.1D0,    -0.6D0,   -3.8D0,  0.0D0,
+     :     -16.9D0,    -1.5D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=31,40 ) /
+     :     695.7D0,     1.8D0,    0.0D0,  0.0D0,
+     :     642.2D0,    -2.6D0,   -1.6D0,  0.0D0,
+     :      13.3D0,     1.1D0,   -0.1D0,  0.0D0,
+     :     521.9D0,     1.6D0,    0.0D0,  0.0D0,
+     :     325.8D0,     2.0D0,   -0.1D0,  0.0D0,
+     :    -325.1D0,    -0.5D0,    0.9D0,  0.0D0,
+     :      10.1D0,     0.3D0,    0.0D0,  0.0D0,
+     :     334.5D0,     1.6D0,    0.0D0,  0.0D0,
+     :     307.1D0,     0.4D0,   -0.9D0,  0.0D0,
+     :     327.2D0,     0.5D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=41,50 ) /
+     :    -304.6D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     304.0D0,     0.6D0,    0.0D0,  0.0D0,
+     :    -276.8D0,    -0.5D0,    0.1D0,  0.0D0,
+     :     268.9D0,     1.3D0,    0.0D0,  0.0D0,
+     :     271.8D0,     1.1D0,    0.0D0,  0.0D0,
+     :     271.5D0,    -0.4D0,   -0.8D0,  0.0D0,
+     :      -5.2D0,     0.5D0,    0.0D0,  0.0D0,
+     :    -220.5D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -20.1D0,     0.3D0,    0.0D0,  0.0D0,
+     :    -191.0D0,     0.1D0,    0.5D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=51,60 ) /
+     :      -4.1D0,     0.3D0,    0.0D0,  0.0D0,
+     :     130.6D0,    -0.1D0,    0.0D0,  0.0D0,
+     :       3.0D0,     0.3D0,    0.0D0,  0.0D0,
+     :     122.9D0,     0.8D0,    0.0D0,  0.0D0,
+     :       3.7D0,    -0.3D0,    0.0D0,  0.0D0,
+     :     123.1D0,     0.4D0,   -0.3D0,  0.0D0,
+     :     -52.7D0,    15.3D0,    0.0D0,  0.0D0,
+     :     120.7D0,     0.3D0,   -0.3D0,  0.0D0,
+     :       4.0D0,    -0.3D0,    0.0D0,  0.0D0,
+     :     126.5D0,     0.5D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=61,70 ) /
+     :     112.7D0,     0.5D0,   -0.3D0,  0.0D0,
+     :    -106.1D0,    -0.3D0,    0.3D0,  0.0D0,
+     :    -112.9D0,    -0.2D0,    0.0D0,  0.0D0,
+     :       3.6D0,    -0.2D0,    0.0D0,  0.0D0,
+     :     107.4D0,     0.3D0,    0.0D0,  0.0D0,
+     :     -10.9D0,     0.2D0,    0.0D0,  0.0D0,
+     :      -0.9D0,     0.0D0,    0.0D0,  0.0D0,
+     :      85.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :       0.0D0,   -88.8D0,    0.0D0,  0.0D0,
+     :     -71.0D0,    -0.2D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=71,80 ) /
+     :     -70.3D0,     0.0D0,    0.0D0,  0.0D0,
+     :      64.5D0,     0.4D0,    0.0D0,  0.0D0,
+     :      69.8D0,     0.0D0,    0.0D0,  0.0D0,
+     :      66.1D0,     0.4D0,    0.0D0,  0.0D0,
+     :     -61.0D0,    -0.2D0,    0.0D0,  0.0D0,
+     :     -59.5D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     -55.6D0,     0.0D0,    0.2D0,  0.0D0,
+     :      51.7D0,     0.2D0,    0.0D0,  0.0D0,
+     :     -49.0D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     -52.7D0,    -0.1D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=81,90 ) /
+     :     -49.6D0,     1.4D0,    0.0D0,  0.0D0,
+     :      46.3D0,     0.4D0,    0.0D0,  0.0D0,
+     :      49.6D0,     0.1D0,    0.0D0,  0.0D0,
+     :      -5.1D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -44.0D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     -39.9D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     -39.5D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      -3.9D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -42.1D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     -17.2D0,     0.1D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=91,100 ) /
+     :      -2.3D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -39.2D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -38.4D0,     0.1D0,    0.0D0,  0.0D0,
+     :      36.8D0,     0.2D0,    0.0D0,  0.0D0,
+     :      34.6D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -32.7D0,     0.3D0,    0.0D0,  0.0D0,
+     :      30.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :       0.4D0,     0.1D0,    0.0D0,  0.0D0,
+     :      29.3D0,     0.2D0,    0.0D0,  0.0D0,
+     :      31.6D0,     0.1D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=101,110 ) /
+     :       0.8D0,    -0.1D0,    0.0D0,  0.0D0,
+     :     -27.9D0,     0.0D0,    0.0D0,  0.0D0,
+     :       2.9D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -25.3D0,     0.0D0,    0.0D0,  0.0D0,
+     :      25.0D0,     0.1D0,    0.0D0,  0.0D0,
+     :      27.5D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -24.4D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      24.9D0,     0.2D0,    0.0D0,  0.0D0,
+     :     -22.8D0,    -0.1D0,    0.0D0,  0.0D0,
+     :       0.9D0,    -0.1D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=111,120 ) /
+     :      24.4D0,     0.1D0,    0.0D0,  0.0D0,
+     :      23.9D0,     0.1D0,    0.0D0,  0.0D0,
+     :      22.5D0,     0.1D0,    0.0D0,  0.0D0,
+     :      20.8D0,     0.1D0,    0.0D0,  0.0D0,
+     :      20.1D0,     0.0D0,    0.0D0,  0.0D0,
+     :      21.5D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -20.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :       1.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -0.2D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      19.0D0,     0.0D0,   -0.1D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=121,130 ) /
+     :      20.5D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -2.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -17.6D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      19.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -2.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -18.4D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      17.1D0,     0.0D0,    0.0D0,  0.0D0,
+     :       0.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :      18.4D0,     0.1D0,    0.0D0,  0.0D0,
+     :       0.0D0,    17.4D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=131,140 ) /
+     :      -0.6D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -15.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -16.8D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      16.3D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -2.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -1.5D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -14.3D0,    -0.1D0,    0.0D0,  0.0D0,
+     :      14.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -13.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -14.3D0,    -0.1D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=141,150 ) /
+     :     -13.7D0,     0.0D0,    0.0D0,  0.0D0,
+     :      13.1D0,     0.1D0,    0.0D0,  0.0D0,
+     :      -1.7D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -12.8D0,     0.0D0,    0.0D0,  0.0D0,
+     :       0.0D0,   -14.4D0,    0.0D0,  0.0D0,
+     :      12.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -12.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -0.8D0,     0.0D0,    0.0D0,  0.0D0,
+     :      10.9D0,     0.1D0,    0.0D0,  0.0D0,
+     :     -10.8D0,     0.0D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=151,160 ) /
+     :      10.5D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -10.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -11.2D0,     0.0D0,    0.0D0,  0.0D0,
+     :      10.5D0,     0.1D0,    0.0D0,  0.0D0,
+     :      -1.4D0,     0.0D0,    0.0D0,  0.0D0,
+     :       0.0D0,     0.1D0,    0.0D0,  0.0D0,
+     :       0.7D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -10.3D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -10.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :       9.6D0,     0.0D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=161,170 ) /
+     :       9.4D0,     0.1D0,    0.0D0,  0.0D0,
+     :       0.6D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -87.7D0,     4.4D0,   -0.4D0, -6.3D0,
+     :      46.3D0,    22.4D0,    0.5D0, -2.4D0,
+     :      15.6D0,    -3.4D0,    0.1D0,  0.4D0,
+     :       5.2D0,     5.8D0,    0.2D0, -0.1D0,
+     :     -30.1D0,    26.9D0,    0.7D0,  0.0D0,
+     :      23.2D0,    -0.5D0,    0.0D0,  0.6D0,
+     :       1.0D0,    23.2D0,    3.4D0,  0.0D0,
+     :     -12.2D0,    -4.3D0,    0.0D0,  0.0D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=171,180 ) /
+     :      -2.1D0,    -3.7D0,   -0.2D0,  0.1D0,
+     :     -18.6D0,    -3.8D0,   -0.4D0,  1.8D0,
+     :       5.5D0,   -18.7D0,   -1.8D0, -0.5D0,
+     :      -5.5D0,   -18.7D0,    1.8D0, -0.5D0,
+     :      18.4D0,    -3.6D0,    0.3D0,  0.9D0,
+     :      -0.6D0,     1.3D0,    0.0D0,  0.0D0,
+     :      -5.6D0,   -19.5D0,    1.9D0,  0.0D0,
+     :       5.5D0,   -19.1D0,   -1.9D0,  0.0D0,
+     :     -17.3D0,    -0.8D0,    0.0D0,  0.9D0,
+     :      -3.2D0,    -8.3D0,   -0.8D0,  0.3D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=181,190 ) /
+     :      -0.1D0,     0.0D0,    0.0D0,  0.0D0,
+     :      -5.4D0,     7.8D0,   -0.3D0,  0.0D0,
+     :     -14.8D0,     1.4D0,    0.0D0,  0.3D0,
+     :      -3.8D0,     0.4D0,    0.0D0, -0.2D0,
+     :      12.6D0,     3.2D0,    0.5D0, -1.5D0,
+     :       0.1D0,     0.0D0,    0.0D0,  0.0D0,
+     :     -13.6D0,     2.4D0,   -0.1D0,  0.0D0,
+     :       0.9D0,     1.2D0,    0.0D0,  0.0D0,
+     :     -11.9D0,    -0.5D0,    0.0D0,  0.3D0,
+     :       0.4D0,    12.0D0,    0.3D0, -0.2D0 /
+      DATA ( ( EPS(I,J), I=1,4 ), J=191,NTERMS ) /
+     :       8.3D0,     6.1D0,   -0.1D0,  0.1D0,
+     :       0.0D0,     0.0D0,    0.0D0,  0.0D0,
+     :       0.4D0,   -10.8D0,    0.3D0,  0.0D0,
+     :       9.6D0,     2.2D0,    0.3D0, -1.2D0 /
+
+
+
+*  Interval between fundamental epoch J2000.0 and given epoch (JC).
+      T = (DATE-DJM0)/DJC
+
+*  Mean anomaly of the Moon.
+      EL  = 134.96340251D0*DD2R+
+     :      MOD(T*(1717915923.2178D0+
+     :          T*(        31.8792D0+
+     :          T*(         0.051635D0+
+     :          T*(       - 0.00024470D0)))),TURNAS)*DAS2R
+
+*  Mean anomaly of the Sun.
+      ELP = 357.52910918D0*DD2R+
+     :      MOD(T*( 129596581.0481D0+
+     :          T*(       - 0.5532D0+
+     :          T*(         0.000136D0+
+     :          T*(       - 0.00001149D0)))),TURNAS)*DAS2R
+
+*  Mean argument of the latitude of the Moon.
+      F   =  93.27209062D0*DD2R+
+     :      MOD(T*(1739527262.8478D0+
+     :          T*(      - 12.7512D0+
+     :          T*(      -  0.001037D0+
+     :          T*(         0.00000417D0)))),TURNAS)*DAS2R
+
+*  Mean elongation of the Moon from the Sun.
+      D   = 297.85019547D0*DD2R+
+     :      MOD(T*(1602961601.2090D0+
+     :          T*(       - 6.3706D0+
+     :          T*(         0.006539D0+
+     :          T*(       - 0.00003169D0)))),TURNAS)*DAS2R
+
+*  Mean longitude of the ascending node of the Moon.
+      OM  = 125.04455501D0*DD2R+
+     :      MOD(T*( - 6962890.5431D0+
+     :          T*(         7.4722D0+
+     :          T*(         0.007702D0+
+     :          T*(       - 0.00005939D0)))),TURNAS)*DAS2R
+
+*  Mean longitude of Venus.
+      VE    = 181.97980085D0*DD2R+MOD(210664136.433548D0*T,TURNAS)*DAS2R
+
+*  Mean longitude of Mars.
+      MA    = 355.43299958D0*DD2R+MOD( 68905077.493988D0*T,TURNAS)*DAS2R
+
+*  Mean longitude of Jupiter.
+      JU    =  34.35151874D0*DD2R+MOD( 10925660.377991D0*T,TURNAS)*DAS2R
+
+*  Mean longitude of Saturn.
+      SA    =  50.07744430D0*DD2R+MOD(  4399609.855732D0*T,TURNAS)*DAS2R
+
+*  Geodesic nutation (Fukushima 1991) in microarcsec.
+      DP = -153.1D0*SIN(ELP)-1.9D0*SIN(2D0*ELP)
+      DE = 0D0
+
+*  Shirai & Fukushima (2001) nutation series.
+      DO J=NTERMS,1,-1
+         THETA = DBLE(NA(1,J))*EL+
+     :           DBLE(NA(2,J))*ELP+
+     :           DBLE(NA(3,J))*F+
+     :           DBLE(NA(4,J))*D+
+     :           DBLE(NA(5,J))*OM+
+     :           DBLE(NA(6,J))*VE+
+     :           DBLE(NA(7,J))*MA+
+     :           DBLE(NA(8,J))*JU+
+     :           DBLE(NA(9,J))*SA
+         C = COS(THETA)
+         S = SIN(THETA)
+         DP = DP+(PSI(1,J)+PSI(3,J)*T)*C+(PSI(2,J)+PSI(4,J)*T)*S
+         DE = DE+(EPS(1,J)+EPS(3,J)*T)*C+(EPS(2,J)+EPS(4,J)*T)*S
+      END DO
+
+*  Change of units, and addition of the precession correction.
+      DPSI = (DP*1D-6-0.042888D0-0.29856D0*T)*DAS2R
+      DEPS = (DE*1D-6-0.005171D0-0.02408D0*T)*DAS2R
+
+*  Mean obliquity of date (Simon et al. 1994).
+      EPS0 = (84381.412D0+
+     :         (-46.80927D0+
+     :          (-0.000152D0+
+     :           (0.0019989D0+
+     :          (-0.00000051D0+
+     :          (-0.000000025D0)*T)*T)*T)*T)*T)*DAS2R
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/oapqk.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/oapqk.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/oapqk.f	(revision 22271)
@@ -0,0 +1,250 @@
+      SUBROUTINE sla_OAPQK (TYPE, OB1, OB2, AOPRMS, RAP, DAP)
+*+
+*     - - - - - -
+*      O A P Q K
+*     - - - - - -
+*
+*  Quick observed to apparent place
+*
+*  Given:
+*     TYPE   c*(*)  type of coordinates - 'R', 'H' or 'A' (see below)
+*     OB1    d      observed Az, HA or RA (radians; Az is N=0,E=90)
+*     OB2    d      observed ZD or Dec (radians)
+*     AOPRMS d(14)  star-independent apparent-to-observed parameters:
+*
+*       (1)      geodetic latitude (radians)
+*       (2,3)    sine and cosine of geodetic latitude
+*       (4)      magnitude of diurnal aberration vector
+*       (5)      height (HM)
+*       (6)      ambient temperature (T)
+*       (7)      pressure (P)
+*       (8)      relative humidity (RH)
+*       (9)      wavelength (WL)
+*       (10)     lapse rate (TLR)
+*       (11,12)  refraction constants A and B (radians)
+*       (13)     longitude + eqn of equinoxes + sidereal DUT (radians)
+*       (14)     local apparent sidereal time (radians)
+*
+*  Returned:
+*     RAP    d      geocentric apparent right ascension
+*     DAP    d      geocentric apparent declination
+*
+*  Notes:
+*
+*  1)  Only the first character of the TYPE argument is significant.
+*      'R' or 'r' indicates that OBS1 and OBS2 are the observed Right
+*      Ascension and Declination;  'H' or 'h' indicates that they are
+*      Hour Angle (West +ve) and Declination;  anything else ('A' or
+*      'a' is recommended) indicates that OBS1 and OBS2 are Azimuth
+*      (North zero, East is 90 deg) and zenith distance.  (Zenith
+*      distance is used rather than elevation in order to reflect the
+*      fact that no allowance is made for depression of the horizon.)
+*
+*  2)  The accuracy of the result is limited by the corrections for
+*      refraction.  Providing the meteorological parameters are
+*      known accurately and there are no gross local effects, the
+*      predicted apparent RA,Dec should be within about 0.1 arcsec
+*      for a zenith distance of less than 70 degrees.  Even at a
+*      topocentric zenith distance of 90 degrees, the accuracy in
+*      elevation should be better than 1 arcmin;  useful results
+*      are available for a further 3 degrees, beyond which the
+*      sla_REFRO routine returns a fixed value of the refraction.
+*      The complementary routines sla_AOP (or sla_AOPQK) and sla_OAP
+*      (or sla_OAPQK) are self-consistent to better than 1 micro-
+*      arcsecond all over the celestial sphere.
+*
+*  3)  It is advisable to take great care with units, as even
+*      unlikely values of the input parameters are accepted and
+*      processed in accordance with the models used.
+*
+*  5)  "Observed" Az,El means the position that would be seen by a
+*      perfect theodolite located at the observer.  This is
+*      related to the observed HA,Dec via the standard rotation, using
+*      the geodetic latitude (corrected for polar motion), while the
+*      observed HA and RA are related simply through the local
+*      apparent ST.  "Observed" RA,Dec or HA,Dec thus means the
+*      position that would be seen by a perfect equatorial located
+*      at the observer and with its polar axis aligned to the
+*      Earth's axis of rotation (n.b. not to the refracted pole).
+*      By removing from the observed place the effects of
+*      atmospheric refraction and diurnal aberration, the
+*      geocentric apparent RA,Dec is obtained.
+*
+*  5)  Frequently, mean rather than apparent RA,Dec will be required,
+*      in which case further transformations will be necessary.  The
+*      sla_AMP etc routines will convert the apparent RA,Dec produced
+*      by the present routine into an "FK5" (J2000) mean place, by
+*      allowing for the Sun's gravitational lens effect, annual
+*      aberration, nutation and precession.  Should "FK4" (1950)
+*      coordinates be needed, the routines sla_FK524 etc will also
+*      need to be applied.
+*
+*  6)  To convert to apparent RA,Dec the coordinates read from a
+*      real telescope, corrections would have to be applied for
+*      encoder zero points, gear and encoder errors, tube flexure,
+*      the position of the rotator axis and the pointing axis
+*      relative to it, non-perpendicularity between the mounting
+*      axes, and finally for the tilt of the azimuth or polar axis
+*      of the mounting (with appropriate corrections for mount
+*      flexures).  Some telescopes would, of course, exhibit other
+*      properties which would need to be accounted for at the
+*      appropriate point in the sequence.
+*
+*  7)  The star-independent apparent-to-observed-place parameters
+*      in AOPRMS may be computed by means of the sla_AOPPA routine.
+*      If nothing has changed significantly except the time, the
+*      sla_AOPPAT routine may be used to perform the requisite
+*      partial recomputation of AOPRMS.
+*
+*  8) The azimuths etc used by the present routine are with respect
+*     to the celestial pole.  Corrections from the terrestrial pole
+*     can be computed using sla_POLMO.
+*
+*  Called:  sla_DCS2C, sla_DCC2S, sla_REFRO, sla_DRANRM
+*
+*  P.T.Wallace   Starlink   23 June 1997
+*
+*  Copyright (C) 1996 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      CHARACTER*(*) TYPE
+      DOUBLE PRECISION OB1,OB2,AOPRMS(14),RAP,DAP
+
+*  Breakpoint for fast/slow refraction algorithm:
+*  ZD greater than arctan(4), (see sla_REFCO routine)
+*  or vector Z less than cosine(arctan(Z)) = 1/sqrt(17)
+      DOUBLE PRECISION ZBREAK
+      PARAMETER (ZBREAK=0.242535625D0)
+
+      CHARACTER C
+      DOUBLE PRECISION C1,C2,SPHI,CPHI,ST,CE,XAEO,YAEO,ZAEO,V(3),
+     :                 XMHDO,YMHDO,ZMHDO,AZ,SZ,ZDO,TZ,DREF,ZDT,
+     :                 XAET,YAET,ZAET,XMHDA,YMHDA,ZMHDA,DIURAB,F,HMA
+
+      DOUBLE PRECISION sla_DRANRM
+
+
+
+*  Coordinate type
+      C = TYPE(1:1)
+
+*  Coordinates
+      C1 = OB1
+      C2 = OB2
+
+*  Sin, cos of latitude
+      SPHI = AOPRMS(2)
+      CPHI = AOPRMS(3)
+
+*  Local apparent sidereal time
+      ST = AOPRMS(14)
+
+*  Standardise coordinate type
+      IF (C.EQ.'R'.OR.C.EQ.'r') THEN
+         C = 'R'
+      ELSE IF (C.EQ.'H'.OR.C.EQ.'h') THEN
+         C = 'H'
+      ELSE
+         C = 'A'
+      END IF
+
+*  If Az,ZD convert to Cartesian (S=0,E=90)
+      IF (C.EQ.'A') THEN
+         CE = SIN(C2)
+         XAEO = -COS(C1)*CE
+         YAEO = SIN(C1)*CE
+         ZAEO = COS(C2)
+      ELSE
+
+*     If RA,Dec convert to HA,Dec
+         IF (C.EQ.'R') THEN
+            C1 = ST-C1
+         END IF
+
+*     To Cartesian -HA,Dec
+         CALL sla_DCS2C(-C1,C2,V)
+         XMHDO = V(1)
+         YMHDO = V(2)
+         ZMHDO = V(3)
+
+*     To Cartesian Az,El (S=0,E=90)
+         XAEO = SPHI*XMHDO-CPHI*ZMHDO
+         YAEO = YMHDO
+         ZAEO = CPHI*XMHDO+SPHI*ZMHDO
+      END IF
+
+*  Azimuth (S=0,E=90)
+      IF (XAEO.NE.0D0.OR.YAEO.NE.0D0) THEN
+         AZ = ATAN2(YAEO,XAEO)
+      ELSE
+         AZ = 0D0
+      END IF
+
+*  Sine of observed ZD, and observed ZD
+      SZ = SQRT(XAEO*XAEO+YAEO*YAEO)
+      ZDO = ATAN2(SZ,ZAEO)
+
+*
+*  Refraction
+*  ----------
+
+*  Large zenith distance?
+      IF (ZAEO.GE.ZBREAK) THEN
+
+*     Fast algorithm using two constant model
+         TZ = SZ/ZAEO
+         DREF = AOPRMS(11)*TZ+AOPRMS(12)*TZ*TZ*TZ
+
+      ELSE
+
+*     Rigorous algorithm for large ZD
+         CALL sla_REFRO(ZDO,AOPRMS(5),AOPRMS(6),AOPRMS(7),AOPRMS(8),
+     :                  AOPRMS(9),AOPRMS(1),AOPRMS(10),1D-8,DREF)
+      END IF
+
+      ZDT = ZDO+DREF
+
+*  To Cartesian Az,ZD
+      CE = SIN(ZDT)
+      XAET = COS(AZ)*CE
+      YAET = SIN(AZ)*CE
+      ZAET = COS(ZDT)
+
+*  Cartesian Az,ZD to Cartesian -HA,Dec
+      XMHDA = SPHI*XAET+CPHI*ZAET
+      YMHDA = YAET
+      ZMHDA = -CPHI*XAET+SPHI*ZAET
+
+*  Diurnal aberration
+      DIURAB = -AOPRMS(4)
+      F = (1D0-DIURAB*YMHDA)
+      V(1) = F*XMHDA
+      V(2) = F*(YMHDA+DIURAB)
+      V(3) = F*ZMHDA
+
+*  To spherical -HA,Dec
+      CALL sla_DCC2S(V,HMA,DAP)
+
+*  Right Ascension
+      RAP = sla_DRANRM(ST+HMA)
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstrometry.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstrometry.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstrometry.c	(revision 22271)
@@ -0,0 +1,830 @@
+/** @file  psAstrometry.c
+ *
+ *  @brief This file defines the basic types for astronomical coordinate
+ *  transformation
+ *
+ *  @ingroup AstroImage
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.65 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-19 00:17:05 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <string.h>
+#include <math.h>
+
+#include "psFunctions.h"
+#include "psAstrometry.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psConstants.h"
+#include "psAstronomyErrors.h"
+#include "psMatrix.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+
+#include "slalib.h"
+
+/*****************************************************************************
+checkValidImageCoords(): this is a private function which simply
+determines if the supplied x,y coordinates are in the range for the supplied
+psImage.
+ *****************************************************************************/
+static psS32 checkValidImageCoords(double x,
+                                   double y,
+                                   psImage* tmpImage)
+{
+    PS_IMAGE_CHECK_NULL(tmpImage, 0);
+
+    if ((x < 0.0) || (x > (double)tmpImage->numCols) ||
+            (y < 0.0) || (y > (double)tmpImage->numRows)) {
+        return (0);
+    }
+
+    return (1);
+}
+
+
+static void FPAFree(psFPA* fpa)
+{
+    if (fpa != NULL) {
+        psFree(fpa->chips);
+        psFree(fpa->grommit);
+        psFree((psExposure*)fpa->exposure);
+        psFree(fpa->metadata);
+        psFree(fpa->fromTangentPlane);
+        psFree(fpa->toTangentPlane);
+        psFree(fpa->pattern);
+        psFree(fpa->colorPlus);
+        psFree(fpa->colorMinus);
+        psFree(fpa->projection);
+    }
+}
+
+static void chipFree(psChip* chip)
+{
+    if (chip != NULL) {
+        psFree(chip->cells);
+        psFree(chip->metadata);
+        psFree(chip->toFPA);
+        psFree(chip->fromFPA);
+    }
+}
+
+static void cellFree(psCell* cell)
+{
+    if (cell != NULL) {
+        psFree(cell->readouts);
+        psFree(cell->metadata);
+        psFree(cell->toChip);
+        psFree(cell->fromChip);
+        psFree(cell->toFPA);
+        psFree(cell->toTP);
+        psFree(cell->toSky);
+    }
+}
+
+static void readoutFree(psReadout* readout)
+{
+    if (readout != NULL) {
+        psFree(readout->image);
+        psFree(readout->mask);
+        psFree(readout->objects);
+        psFree(readout->metadata);
+    }
+}
+
+static void observatoryFree(psObservatory* obs)
+{
+    if (obs != NULL) {
+        psFree((psPtr)obs->name);
+    }
+}
+
+static void exposureFree(psExposure* exp)
+{
+    if (exp != NULL) {
+        psFree((psPtr)exp->time);
+        psFree((psPtr)exp->observatory);
+        psFree((psPtr)exp->cameraName);
+        psFree((psPtr)exp->telescopeName);
+    }
+}
+
+static void fixedPatternFree(psFixedPattern* fp)
+{
+    if (fp != NULL) {
+        for (psS32 i = 0; i < fp->p_ps_xRows; i++) {
+            psFree(fp->x[i]);
+        }
+
+        for (psS32 j = 0; j < fp->p_ps_yRows; j++) {
+            psFree(fp->y[j]);
+        }
+
+        psFree(fp->x);
+        psFree(fp->y);
+    }
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+/*
+ * XXX: Verify that you interpreted the SDR correctly.
+ *
+ * XXX: This assumes that x,y must be of type F64
+ */
+psFixedPattern* psFixedPatternAlloc(double x0,
+                                    double y0,
+                                    double xScale,
+                                    double yScale,
+                                    const psImage *x,
+                                    const psImage *y)
+{
+    psFixedPattern *tmp;
+    psS32 i;
+    psS32 j;
+
+    PS_IMAGE_CHECK_NULL(x, NULL);
+    PS_IMAGE_CHECK_NULL(y, NULL);
+    PS_IMAGE_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_IMAGE_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+
+    tmp = (psFixedPattern *) psAlloc(sizeof(psFixedPattern));
+    // XXX: Is this correct?
+    tmp->nX = (x->numCols * x->numRows);
+    tmp->nY = (y->numCols * y->numRows);
+    tmp->x0 = x0;
+    tmp->y0 = y0;
+    tmp->xScale = xScale;
+    tmp->yScale = yScale;
+    tmp->p_ps_xRows = x->numRows;
+    tmp->p_ps_xCols = x->numCols;
+    tmp->p_ps_yRows = y->numRows;
+    tmp->p_ps_yCols = y->numCols;
+    tmp->x = (double **) psAlloc(x->numRows * sizeof(double *));
+    for (i=0;i<x->numRows;i++) {
+        (tmp->x)[i] = (double *) psAlloc(x->numCols * sizeof(double));
+    }
+    for (i=0;i<x->numRows;i++) {
+        for (j=0;j<x->numCols;j++) {
+            (tmp->x)[i][j] = x->data.F64[i][j];
+        }
+    }
+
+    tmp->y = (double **) psAlloc(y->numRows * sizeof(double *));
+    for (i=0;i<y->numRows;i++) {
+        (tmp->y)[i] = (double *) psAlloc(y->numCols * sizeof(double));
+    }
+    for (i=0;i<y->numRows;i++) {
+        for (j=0;j<y->numCols;j++) {
+            (tmp->y)[i][j] = y->data.F64[i][j];
+        }
+    }
+
+    psMemSetDeallocator(tmp,(psFreeFcn)fixedPatternFree);
+
+    return(tmp);
+}
+
+
+psExposure* psExposureAlloc(double ra,
+                            double dec,
+                            double hourAngle,
+                            double zenithDistance,
+                            double azimuth,
+                            const psTime* time,
+                            float rotAngle,
+                            float temperature,
+                            float pressure,
+                            float humidity,
+                            float exposureTime,
+                            float wavelength,
+                            const psObservatory* observatory)
+{
+    PS_PTR_CHECK_NULL(observatory, NULL);
+
+    psExposure* exp = psAlloc(sizeof(psExposure));
+    *(double *)&exp->ra = ra;
+    *(double *)&exp->dec = dec;
+    *(double *)&exp->hourAngle = hourAngle;
+    *(double *)&exp->zenithDistance = zenithDistance;
+    *(double *)&exp->azimuth = azimuth;
+    *(float *)&exp->rotAngle = rotAngle;
+    *(float *)&exp->temperature = temperature;
+    *(float *)&exp->pressure = pressure;
+    *(float *)&exp->humidity = humidity;
+    *(float *)&exp->exposureTime = exposureTime;
+    *(float *)&exp->wavelength = wavelength;
+
+    exp->time = psMemIncrRefCounter((psPtr)time);
+    exp->observatory = psMemIncrRefCounter((psPtr)observatory);
+
+    // XXX: how is this value derived?
+    *(double *)&exp->lst = psTimeToLMST((psTime*)time,observatory->longitude);
+    *(float *)&exp->positionAngle = 0.0f; // XXX: need input, see Bug #207
+    *(float *)&exp->parallacticAngle = 0.0f; // XXX: need input, see Bug #207
+    *(float *)&exp->airmass = slaAirmas(zenithDistance);
+    *(float *)&exp->parallacticFactor = 0.0f;
+    exp->cameraName = NULL;
+    exp->telescopeName = NULL;
+
+    psMemSetDeallocator(exp,(psFreeFcn)exposureFree);
+
+    return exp;
+}
+
+psObservatory* psObservatoryAlloc(const char* name,
+                                  double latitude,
+                                  double longitude,
+                                  double height,
+                                  double tlr)
+{
+    psObservatory* obs = psAlloc(sizeof(psObservatory));
+
+    if (name == NULL) {
+        obs->name = NULL;
+    } else {
+        obs->name = psAlloc(strlen(name)+1);
+        strcpy((char*)obs->name, name);
+    }
+
+    *(double *)&obs->latitude = latitude;
+    *(double *)&obs->longitude = longitude;
+    *(double *)&obs->height = height;
+    *(double *)&obs->tlr = tlr;
+
+    psMemSetDeallocator(obs,(psFreeFcn)observatoryFree);
+
+    return obs;
+}
+
+psFPA* psFPAAlloc(psS32 nChips,
+                  const psExposure* exp)
+{
+    PS_INT_CHECK_NON_NEGATIVE(nChips, NULL);
+
+    psFPA* newFPA = psAlloc(sizeof(psFPA));
+
+    // create array of NULL chips of the size nChips
+    newFPA->chips = psArrayAlloc(nChips);
+    psPtr* chips = newFPA->chips->data;
+    for (psS32 i=0;i<nChips;i++) {
+        chips[i] = NULL;
+    }
+    newFPA->chips->n = 0; // per requirement
+
+    newFPA->metadata = NULL;
+    newFPA->fromTangentPlane = NULL;
+    newFPA->toTangentPlane = NULL;
+    newFPA->pattern = NULL;
+
+    if (exp != NULL) {
+        newFPA->exposure = psMemIncrRefCounter((psExposure*)exp);
+        newFPA->grommit = psGrommitAlloc(exp);
+    } else {
+        newFPA->exposure = NULL;
+        newFPA->grommit = NULL;
+    }
+
+    newFPA->colorPlus = NULL;
+    newFPA->colorMinus = NULL;
+    newFPA->projection = NULL;
+
+    newFPA->rmsX = 0.0f;
+    newFPA->rmsY = 0.0f;
+    newFPA->chi2 = 0.0f;
+
+    psMemSetDeallocator(newFPA,(psFreeFcn)FPAFree);
+
+    return newFPA;
+}
+
+/*
+ * psChip constructor
+ */
+psChip* psChipAlloc(psS32 nCells,
+                    psFPA *parentFPA)
+{
+    PS_INT_CHECK_NON_NEGATIVE(nCells, NULL);
+
+    psChip* chip = psAlloc(sizeof(psChip));
+
+    // create array of NULL psCells
+    int n = (nCells > 0) ? nCells : 1;
+    chip->cells = psArrayAlloc(n);
+    psPtr* cells = chip->cells->data;
+    for (psS32 i=0;i<n;i++) {
+        cells[i] = NULL;
+    }
+    chip->cells->n = 0; // per requirement
+
+    *(int*)&chip->row0 = 0;
+    *(int*)&chip->col0 = 0;
+
+    chip->metadata = NULL;
+
+    chip->toFPA = NULL;
+    chip->fromFPA = NULL;
+
+    chip->parent = parentFPA;
+
+    psMemSetDeallocator(chip,(psFreeFcn)chipFree);
+
+    return chip;
+
+}
+
+/*
+ * psCell constructor
+ */
+psCell* psCellAlloc(psS32 nReadouts,
+                    psChip* parentChip)
+{
+    PS_INT_CHECK_NON_NEGATIVE(nReadouts, NULL);
+
+    psCell* cell = psAlloc(sizeof(psCell));
+
+    // create array of NULL psReadouts
+    int n = (nReadouts > 0) ? nReadouts : 1;
+    cell->readouts = psArrayAlloc(n);
+    psPtr* readouts = cell->readouts->data;
+    for (psS32 i=0;i<n;i++) {
+        readouts[i] = NULL;
+    }
+    cell->readouts->n = 0; // per requirement
+
+    *(int*)&cell->row0 = 0;
+    *(int*)&cell->col0 = 0;
+
+    cell->metadata = NULL;
+
+    cell->toChip = NULL;
+    cell->fromChip = NULL;
+    cell->toFPA = NULL;
+    cell->toTP = NULL;
+    cell->toSky = NULL;
+
+    cell->parent = parentChip;
+
+    psMemSetDeallocator(cell,(psFreeFcn)cellFree);
+
+    return cell;
+
+
+}
+
+psReadout* psReadoutAlloc()
+{
+    psReadout* readout = psAlloc(sizeof(psReadout));
+
+    *(psU32*)&readout->colBins = 1;
+    *(psU32*)&readout->rowBins = 1;
+    *(psU32*)&readout->rowParity = 0;
+    *(psU32*)&readout->colParity = 0;
+    *(psS32*)&readout->col0 = 0;
+    *(psS32*)&readout->row0 = 0;
+
+    readout->image = NULL;
+    readout->mask = NULL;
+    readout->objects = NULL;
+    readout->metadata = NULL;
+
+    psMemSetDeallocator(readout,(psFreeFcn)readoutFree);
+
+    return readout;
+}
+
+psGrommit* psGrommitAlloc(const psExposure* exp)
+{
+    PS_PTR_CHECK_NULL(exp, NULL);
+
+    double date = psTimeToMJD(exp->time);
+    double dut = psTimeGetUT1Delta(exp->time);
+    double elongm = exp->observatory->longitude;
+    double phim = exp->observatory->latitude;
+    double hm = exp->observatory->height;
+
+    psSphere* polarMotion = psTimeGetPoleCoords(exp->time);
+    double xp = polarMotion->r;
+    double yp = polarMotion->d;
+    psFree(polarMotion);
+
+    double tdk = exp->temperature;
+    double pmb = exp->pressure;
+    double rh = exp->humidity;
+    double wl = exp->wavelength;
+    double tlr = exp->observatory->tlr;
+
+    psGrommit* grommit = (psGrommit* ) psAlloc(sizeof(psGrommit));
+
+    slaAoppa(date, dut, elongm, phim, hm, xp, yp,
+             tdk, pmb, rh, wl, tlr, (double*)grommit);
+
+    return (grommit);
+}
+
+psCell* psCellInFPA(const psPlane* fpaCoord,
+                    const psFPA* FPA)
+{
+    PS_PTR_CHECK_NULL(fpaCoord, NULL);
+    PS_PTR_CHECK_NULL(FPA, NULL);
+
+    psChip* tmpChip = NULL;
+    psPlane chipCoord;
+    psCell* outCell = NULL;
+
+    // Determine which chip contains the fpaCoords.
+    tmpChip = psChipInFPA(fpaCoord, FPA);
+    if (tmpChip == NULL) {
+        return(NULL);
+    }
+
+    // Convert to those chip coordinates.
+    psCoordFPAToChip(&chipCoord, fpaCoord, tmpChip);
+
+    // Determine which cell contains those chip coordinates.
+    outCell = psCellInChip(&chipCoord, tmpChip);
+
+    return (outCell);
+}
+
+psChip* psChipInFPA(const psPlane* fpaCoord,
+                    const psFPA* FPA)
+{
+    PS_PTR_CHECK_NULL(fpaCoord, NULL);
+    PS_PTR_CHECK_NULL(FPA, NULL);
+    PS_PTR_CHECK_NULL(FPA->chips, NULL);
+
+    psArray* chips = FPA->chips;
+    psS32 nChips = chips->n;
+    psPlane chipCoord;
+    psCell *tmpCell = NULL;
+
+    // Loop through every chip in this FPA.  Convert the original FPA
+    // coordinates to chip coordinates for that chip.  Then, determine if any
+    // cells in that chip contain those chip coordinates.
+
+    for (psS32 i = 0; i < nChips; i++) {
+        psChip* tmpChip = chips->data[i];
+        PS_PTR_CHECK_NULL(tmpChip, NULL);
+        PS_PTR_CHECK_NULL(tmpChip->fromFPA, NULL);
+
+        psPlaneTransformApply(&chipCoord, tmpChip->fromFPA, fpaCoord);
+
+        tmpCell = psCellInChip(&chipCoord, tmpChip);
+        if (tmpCell != NULL) {
+            return(tmpChip);
+        }
+    }
+
+    // XXX: Print warning here?
+    return (NULL);
+}
+
+psCell* psCellInChip(const psPlane* chipCoord,
+                     const psChip* chip)
+{
+    PS_PTR_CHECK_NULL(chipCoord, NULL);
+    PS_PTR_CHECK_NULL(chip, NULL);
+
+    psPlane cellCoord;
+    psArray* cells;
+
+    cells = chip->cells;
+    if (cells == NULL) {
+        return NULL;
+    }
+
+    // We loop over each cell in the chip.  We transform the chipCoord into
+    // a cellCoord for that cell and determine if that cellCoord is valid.
+    // If so, then we return that cell.
+
+    for (psS32 i = 0; i < cells->n; i++) {
+        psCell* tmpCell = (psCell* ) cells->data[i];
+        PS_PTR_CHECK_NULL(tmpCell, NULL);
+        PS_PTR_CHECK_NULL(tmpCell->fromChip, NULL);
+        psArray* readouts = tmpCell->readouts;
+
+        if (readouts != NULL) {
+            for (psS32 j = 0; j < readouts->n; j++) {
+                psReadout* tmpReadout = readouts->data[j];
+                PS_READOUT_CHECK_NULL(tmpReadout, NULL);
+
+                psPlaneTransformApply(&cellCoord,
+                                      tmpCell->fromChip,
+                                      chipCoord);
+
+                if (checkValidImageCoords(cellCoord.x,
+                                          cellCoord.y,
+                                          tmpReadout->image)) {
+                    return (tmpCell);
+                }
+            }
+        }
+    }
+
+    return (NULL);
+}
+
+psPlane* psCoordCellToChip(psPlane* outCoord,
+                           const psPlane* inCoord,
+                           const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(inCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+
+    return (psPlaneTransformApply(outCoord, cell->toChip, inCoord));
+}
+
+psPlane* psCoordChipToFPA(psPlane* outCoord,
+                          const psPlane* inCoord,
+                          const psChip* chip)
+{
+    PS_PTR_CHECK_NULL(inCoord, NULL);
+    PS_PTR_CHECK_NULL(chip, NULL);
+
+    return (psPlaneTransformApply(outCoord, chip->toFPA, inCoord));
+}
+
+psPlane* psCoordFPAToTP(psPlane* outCoord,
+                        const psPlane* inCoord,
+                        double color,
+                        double magnitude,
+                        const psFPA* fpa)
+{
+    PS_PTR_CHECK_NULL(inCoord, NULL);
+    PS_PTR_CHECK_NULL(fpa, NULL);
+
+    return(psPlaneDistortApply(outCoord, fpa->toTangentPlane, inCoord,
+                               color, magnitude));
+}
+
+/*****************************************************************************
+XXX: What about units for the (x,y) coords?
+ *****************************************************************************/
+psSphere* psCoordTPToSky(psSphere* outSphere,
+                         const psPlane* tpCoord,
+                         const psGrommit* grommit)
+{
+    PS_PTR_CHECK_NULL(tpCoord, NULL);
+    PS_PTR_CHECK_NULL(grommit, NULL);
+
+    double AOB = 0.0;
+    double ZOB = 0.0;
+    double HOB = 0.0;
+    if (outSphere == NULL) {
+        outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
+    }
+
+    slaAopqk(tpCoord->x, tpCoord->y, (double*)grommit,
+             &AOB, &ZOB, &HOB, &outSphere->r, &outSphere->d);
+
+    return (outSphere);
+}
+
+psPlane* psCoordCellToFPA(psPlane* fpaCoord,
+                          const psPlane* cellCoord,
+                          const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(cellCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+
+    return (psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord));
+}
+
+psSphere* psCoordCellToSky(psSphere* skyCoord,
+                           const psPlane* cellCoord,
+                           double color,
+                           double magnitude,
+                           const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(cellCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+    PS_PTR_CHECK_NULL(cell->toFPA, NULL);
+    PS_PTR_CHECK_NULL(cell->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent->toTangentPlane, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent->exposure, NULL);
+
+    psPlane* fpaCoord = NULL;
+    psPlane* tpCoord = NULL;
+    psFPA* parFPA = (cell->parent)->parent;
+    psGrommit* tmpGrommit = NULL;
+
+    // Convert the input cell coordinates to FPA coordinates.
+    fpaCoord = psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord);
+
+    // Convert the FPA coordinates to tangent plane Coordinates.
+    tpCoord = psPlaneDistortApply(tpCoord, parFPA->toTangentPlane,
+                                  fpaCoord, color, magnitude);
+
+    // Generate a grommit for this FPA.
+    tmpGrommit = psGrommitAlloc(parFPA->exposure);
+
+    // Convert the tangent plane Coordinates to sky coordinates.
+    skyCoord = psCoordTPToSky(skyCoord, tpCoord, tmpGrommit);
+
+    psFree(fpaCoord);
+    psFree(tpCoord);
+    psFree(tmpGrommit);
+
+    return(skyCoord);
+}
+
+psSphere* psCoordCellToSkyQuick(psSphere* outSphere,
+                                const psPlane* cellCoord,
+                                const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(cellCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+    PS_PTR_CHECK_NULL(cell->toSky, NULL);
+    PS_PTR_CHECK_NULL(cell->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent->projection, NULL);
+    if (cell->toSky) {
+        // XXX: Should we use toTP or toSky?
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psCoordCellToSkyQuick(): The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
+    }
+
+    psPlane *tpCoord = NULL;
+    psChip *chip = cell->parent;
+    psFPA *FPA = chip->parent;
+    psProjectionType oldProjectionType;
+
+    if (outSphere == NULL) {
+        outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
+    }
+
+    // Determine the tangent plane coordinates.
+    tpCoord = psPlaneTransformApply(NULL, cell->toTP, cellCoord);
+
+    // Save the old projection type and set the new projection type to TAN.
+    oldProjectionType = FPA->projection->type;
+    FPA->projection->type = PS_PROJ_TAN;
+
+    // Deproject the tangent plane coordinates a sphere.
+    outSphere = psDeproject(tpCoord, FPA->projection);
+
+    // Restore old projection type.  Free memory.
+    FPA->projection->type = oldProjectionType;
+    psFree(tpCoord);
+
+    return (outSphere);
+}
+
+/*****************************************************************************
+XXX: What about units for the (x,y) coords?
+ *****************************************************************************/
+psPlane* psCoordSkyToTP(psPlane* tpCoord,
+                        const psSphere* in,
+                        const psGrommit* grommit)
+{
+    PS_PTR_CHECK_NULL(in, NULL);
+    PS_PTR_CHECK_NULL(grommit, NULL);
+
+    char* type = "RA";
+
+    if (tpCoord == NULL) {
+        tpCoord = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+
+    slaOapqk(type, in->r, in->d, (double*)grommit, &tpCoord->x, &tpCoord->y);
+
+    return(tpCoord);
+}
+
+
+psPlane* psCoordTPToFPA(psPlane* fpaCoord,
+                        const psPlane* tpCoord,
+                        double color,
+                        double magnitude,
+                        const psFPA* fpa)
+{
+    PS_PTR_CHECK_NULL(tpCoord, NULL);
+    PS_PTR_CHECK_NULL(fpa, NULL);
+    PS_PTR_CHECK_NULL(fpa->fromTangentPlane, NULL);
+
+    return (psPlaneDistortApply(fpaCoord, fpa->fromTangentPlane,
+                                tpCoord, color, magnitude));
+}
+
+psPlane* psCoordFPAToChip(psPlane* chipCoord,
+                          const psPlane* fpaCoord,
+                          const psChip* chip)
+{
+    PS_PTR_CHECK_NULL(fpaCoord, NULL);
+    PS_PTR_CHECK_NULL(chip, NULL);
+    PS_PTR_CHECK_NULL(chip->fromFPA, NULL);
+
+    chipCoord = psPlaneTransformApply(chipCoord, chip->fromFPA, fpaCoord);
+    return(chipCoord);
+}
+
+psPlane* psCoordChipToCell(psPlane* cellCoord,
+                           const psPlane* chipCoord,
+                           const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(chipCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+    PS_PTR_CHECK_NULL(cell->fromChip, NULL);
+
+    cellCoord = psPlaneTransformApply(cellCoord, cell->fromChip, chipCoord);
+    return(cellCoord);
+}
+
+psPlane* psCoordSkyToCell(psPlane* cellCoord,
+                          const psSphere* skyCoord,
+                          double color,
+                          double magnitude,
+                          const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(skyCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+    PS_PTR_CHECK_NULL(cell->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent->grommit, NULL);
+
+    psChip *parChip = cell->parent;
+    psFPA *parFPA = parChip->parent;
+    psGrommit* grommit = parFPA->grommit;
+
+    // Convert the skyCoords to tangent plane coords.
+    psPlane *tpCoord = psCoordSkyToTP(tpCoord, skyCoord, grommit);
+
+    // Convert the tangent plane coords to FPA coords.
+    psPlane *fpaCoord = psCoordTPToFPA(fpaCoord, tpCoord, color,
+                                       magnitude, parFPA);
+
+    // Convert the FPA coords to chip coords.
+    psPlane *chipCoord = psCoordFPAToChip(chipCoord, fpaCoord, parChip);
+
+    // Convert the chip coords to cell coords.
+    cellCoord = psCoordChipToCell(cellCoord, chipCoord, cell);
+
+    psFree(tpCoord);
+    psFree(fpaCoord);
+    psFree(chipCoord);
+
+    return (cellCoord);
+}
+
+psPlane* psCoordSkyToCellQuick(psPlane* cellCoord,
+                               const psSphere* skyCoord,
+                               const psCell* cell)
+{
+    PS_PTR_CHECK_NULL(skyCoord, NULL);
+    PS_PTR_CHECK_NULL(cell, NULL);
+    PS_PTR_CHECK_NULL(cell->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent, NULL);
+    PS_PTR_CHECK_NULL(cell->parent->parent->projection, NULL);
+    PS_PTR_CHECK_NULL(cell->toTP, NULL);
+    if (cell->toSky) {
+        // XXX: Should we use toTP or toSky?
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psCoordSkyToCellQuick(): The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
+    }
+
+    psPlane *tpCoord = NULL;
+    psChip *whichChip = cell->parent;
+    psFPA *whichFPA = whichChip->parent;
+    psProjectionType oldProjectionType;
+    psPlaneTransform *TPtoCell = NULL;
+
+    // Save the old projection type and set the new projection type to TAN.
+    oldProjectionType = whichFPA->projection->type;
+    whichFPA->projection->type = PS_PROJ_TAN;
+
+    if (cellCoord == NULL) {
+        cellCoord = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+
+    tpCoord = psProject(skyCoord, whichFPA->projection);
+
+    // generate an error if cell->toTP is not linear.
+    if (0 == p_psIsProjectionLinear(cell->toTP)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psAstrometry_NONLINEAR_TRANSFORM,
+                "cell to tangent plane");
+    }
+
+    TPtoCell = p_psPlaneTransformLinearInvert(cell->toTP);
+    cellCoord = psPlaneTransformApply(cellCoord, TPtoCell, tpCoord);
+
+    // Restore old projection type.  Free memory.
+    whichFPA->projection->type = oldProjectionType;
+    psFree(tpCoord);
+    return (cellCoord);
+}
+
+
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstrometry.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstrometry.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstrometry.h	(revision 22271)
@@ -0,0 +1,537 @@
+/** @file  psAstrometry.h
+*
+*  @brief This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup AstroImage
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.39 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-03-31 23:01:46 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_ASTROMETRY_H
+#define PS_ASTROMETRY_H
+
+#include "psType.h"
+#include "psImage.h"
+#include "psArray.h"
+#include "psList.h"
+#include "psFunctions.h"
+#include "psMetadata.h"
+#include "psCoord.h"
+#include "psPhotometry.h"
+
+struct psCell;
+struct psChip;
+struct psFPA;
+struct psExposure;
+
+/// @addtogroup AstroImage
+/// @{
+
+/** Wallace's Grommit
+ *
+ *  SLALib requires several elements to perform the transformations between
+ *  the tangent plane and the sky.  Pre-computing these quantities for each
+ *  exposure means that subsequent transformations are faster.  For historical
+ *  reasons, this structure is known colloquially as "Wallace's Grommit".
+ *
+ */
+typedef struct
+{
+    const double latitude;             ///< geodetic latitude (radians)
+    const double sinLat;               ///< sine of geodetic latitude
+    const double cosLat;               ///< cosine of geodetic latitude
+    const double abberationMag;        ///< magnitude of diurnal aberration vector
+    const double height;               ///< height (HM)
+    const double temperature;          ///< ambient temperature (TDK)
+    const double pressure;             ///< pressure (PMB)
+    const double humidity;             ///< relative humidity (RH)
+    const double wavelength;           ///< wavelength (WL)
+    const double lapseRate;            ///< lapse rate (TLR)
+    const double refractA;             ///< refraction constant A (radians)
+    const double refractB;             ///< refraction constant B (radians)
+    const double longitudeOffset;      ///< longitude + ... (radians)
+    const double siderealTime;         ///< local apparent sidereal time (radians)
+}
+psGrommit;
+
+/** Fixed Pattern Corrections
+ *
+ *  The fixed pattern is a correction to the general astrometric solution
+ *  formed by summing the residuals from many observations. The intent is to
+ *  correct for higher-order distortions in the camera system on a coarse
+ *  grid (larger than individual pixels, but smaller than a single cell).
+ *  Hence, in addition to the offsets, we need to specify the size and scale
+ *  of the grid in x and y as well as the origin of the grid.
+ */
+typedef struct
+{
+    psS32 nX;                            ///< Number of elements in x direction
+    psS32 nY;                            ///< Number of elements in y direction
+    double x0;                         ///< X Position of 0,0 corner on focal plane
+    double y0;                         ///< Y Position of 0,0 corner on focal plane
+    double xScale;                     ///< Scale of the grid in x direction
+    double yScale;                     ///< Scale of the grid in x direction
+    /// XXX: I added the following memvers to facilitate the psFreeing of the x,y data structures.
+    psS32 p_ps_xRows;                    ///< Number of rows in the x member
+    psS32 p_ps_xCols;                    ///< Number of cols in the x member
+    psS32 p_ps_yRows;                    ///< Number of rows in the y member
+    psS32 p_ps_yCols;                    ///< Number of cols in the y member
+    double **x;                        ///< The grid of offsets in x
+    double **y;                        ///< The grid of offsets in y
+}
+psFixedPattern;
+
+/** Readout data structure.
+ *
+ *  A readout is the result of a single read of a cell (or a portion thereof).
+ *  It contains a pointer to the pixel data, and additional pointers to the
+ *  objects found in the readout, and the readout metadata.  It also contains
+ *  the offset from the lower-left corner of the chip, in the case that the
+ *  CCD was windowed.
+ *
+ */
+typedef struct
+{
+    const psS32 col0;                  ///< Offset from the left of chip.
+    const psS32 row0;                  ///< Offset from the bottom of chip.
+
+    const int colParity;               ///< Column Readout Direction
+    const int rowParity;               ///< Row Readout Direction
+
+    const psU32 colBins;               ///< Amount of binning in x-dimension
+    const psU32 rowBins;               ///< Amount of binning in y-dimension
+
+    psImage* image;                    ///< Imaging area of readout
+    psImage* mask;                     ///< Mask area for readout
+    psList* objects;                   ///< Objects derived from Readout
+    psMetadata* metadata;              ///< Readout-level metadata
+}
+psReadout;
+
+/** Cell data structure
+ *
+ *  A cell consists of one or more readouts.  It also contains a pointer to the
+ *  cell's metadata, and its parent chip.  On the astrometry side, it also
+ *  contains coordinate transforms from the cell to chip, from the cell to
+ *  focal-plane, as well as a "quick and dirty" tranform from the cell to
+ *  sky coordinates.
+ *
+ */
+typedef struct
+{
+    const psS32 col0;                  ///< Offset from the left of chip
+    const psS32 row0;                  ///< Offset from the bottom of chip
+
+    psArray* readouts;                 ///< readouts from the cell
+
+    psMetadata* metadata;              ///< cell-level metadata
+
+    psPlaneTransform* toChip;          ///< transformations from cell to chip coordinates
+    psPlaneTransform* fromChip;        ///< transformations from cell to chip coordinates
+    psPlaneTransform* toFPA;           ///< transformations from cell to FPA coordinates
+    psPlaneTransform* toTP;            ///< transformations from cell to FPA coordinates
+    psPlaneTransform* toSky;           ///< transformations from cell to tangent plane coordinates
+
+    struct psChip* parent;             ///< chip in which contains this cell
+}
+psCell;
+
+/** Chip data structure
+ *
+ *  A chip consists of one or more cells (according to the number of amplifiers
+ *  on the CCD). It contains a pointer to the chip's metadata, and a pointer
+ *  to the parent focal plane.  For astrometry, it contains a coordinate
+ *  transform from the chip to the focal plane, and vis-versa.
+ *
+ */
+typedef struct psChip
+{
+    const psS32 col0;                  ///< Offset from the left of FPA
+    const psS32 row0;                  ///< Offset from the bottom of FPA
+
+    psArray* cells;                    ///< cells in the chip
+
+    psMetadata* metadata;              ///< chip-level metadata
+
+    psPlaneTransform* toFPA;           ///< transformation from chip to FPA coordinates
+    psPlaneTransform* fromFPA;         ///< transformation from FPA to chip coordinates
+
+    struct psFPA* parent;              ///< FPA which contains this chip
+}
+psChip;
+
+/** A Focal-Plane
+ *
+ *  A focal plane consists of one or more chips (according to the number of
+ *  contiguous silicon).  It contains pointers to the focal-plane's metadata
+ *  and the exposure information.  For astrometry, it contains a transformation
+ *  from the focal plane to the tangent plane and the fixed pattern residuals.
+ *  Since colors are involved in the transformation, it is necessary to specify
+ *  the color the transformation is defined.  We also include some values to
+ *  characterize the quality of the transformation: the root square deviation
+ *  for the x and y transformation fits, and the chi-squared for the
+ *  transformation fit.
+ *
+ */
+typedef struct psFPA
+{
+    psArray* chips;                    ///< chips in the focal plane array
+    psMetadata* metadata;              ///< focal-plane's metadata
+
+    psPlaneDistort* fromTangentPlane;  ///< transformation from tangent plane to focal plane
+    psPlaneDistort* toTangentPlane;    ///< transformation from focal plane to tangent plane
+    psFixedPattern* pattern;           ///< fixed pattern residual offsets
+
+    const struct psExposure* exposure; ///< information about this exposure
+    psGrommit *grommit;                ///< Wallace's grommit
+
+    psPhotSystem* colorPlus;           ///< Color reference
+    psPhotSystem* colorMinus;          ///< Color reference
+    psProjection *projection;          ///< projection
+
+    float rmsX;                        ///< RMS for x transformation fits
+    float rmsY;                        ///< RMS for y transformation fits
+    float chi2;                        ///< chi^2 of astrometric solution
+}
+psFPA;
+
+/** Observatory Information
+ *
+ *  A container for the observatory data that doesn't change per exposure.
+ *
+ */
+typedef struct
+{
+    const char* name;                  ///< Name of observatory
+    const double latitude;             ///< Latitude of observatory, east positive (degrees?)
+    const double longitude;            ///< Longitude of observatory (degrees?)
+    const double height;               ///< Height of observatory in meters
+    const double tlr;                  ///< Tropospheric Lapse Rate
+}
+psObservatory;
+
+/** Exposure Information
+ *
+ *  Several quantities from the telescope in order to make a first guess at
+ *  the astrometric solution.  From these quantities, further quantities can
+ *  be derivedand stored for later use.
+ *
+ */
+typedef struct psExposure
+{
+    const double ra;                   ///< Telescope boresight, right ascention
+    const double dec;                  ///< Telescope boresight, declination
+    const double hourAngle;            ///< Hour angle
+    const double zenithDistance;       ///< Zenith distance
+    const double azimuth;              ///< Azimuth
+    const psTime* time;                ///< Time of observation
+    const float rotAngle;              ///< Rotator position angle in degrees? XXX: see bug#209
+    const float temperature;           ///< Air temperature in Kelvin
+    const float pressure;              ///< Air pressure in mB
+    const float humidity;              ///< Relative humidity, for refraction
+    const float exposureTime;          ///< Exposure time
+    const float wavelength;            ///< Wavelength in microns
+    const psObservatory* observatory;  ///< Observatory data
+
+    /* Derived quantities */
+    const double lst;                  ///< Local Sidereal Time
+    const float positionAngle;         ///< Position angle
+    const float parallacticAngle;      ///< Parallactic angle
+    const float airmass;               ///< Airmass, calculated from zenith distance
+    const float parallacticFactor;     ///< Parallactic factor
+    const char* cameraName;            ///< name of camera which provided exposure
+    const char* telescopeName;         ///< name of telescope which provided exposure
+}
+psExposure;
+
+/** Allocator for psFixedPattern struct
+ *
+ *  Allocates a new psFixedPattern struct with the attributes coorsponding
+ *  to the parameters set to the said input values.
+ *
+ *  @return psFixedPattern*     New psFixedPattern struct.
+ */
+psFixedPattern* psFixedPatternAlloc(
+    double x0,           ///< X Position of 0,0 corner on focal plane
+    double y0,           ///< Y Position of 0,0 corner on focal plane
+    double xScale,       ///< Scale of the grid in x direction
+    double yScale,       ///< Scale of the grid in x direction
+    const psImage *x,    ///< The grid of offsets in x
+    const psImage *y     ///< The grid of offsets in y
+);
+
+
+/** Allocator for psExposure
+ *
+ *  We need several quantities from the telescope in order to make a first
+ *  guess at the astrometric solution. From these quantities, further
+ *  quantities can be derived and stored for later use.
+ *
+ *  @return     psExposure*    New psExposure struct
+ */
+psExposure* psExposureAlloc(
+    double ra,                         ///< Telescope boresight, right ascention
+    double dec,                        ///< Telescope boresight, declination
+    double hourAngle,                  ///< Hour angle
+    double zenithDistance,             ///< Zenith distance
+    double azimuth,                    ///< Azimuth
+    const psTime* time,                ///< time of observation
+    float rotAngle,                    ///< Rotator position angle
+    float temperature,                 ///< Temperature
+    float pressure,                    ///< Pressure
+    float humidity,                    ///< Relative humidity
+    float exposureTime,                ///< Exposure time
+    float wavelength,                  ///< wavelength
+    const psObservatory* observatory   ///< Observatory data
+);
+
+/** Allocator for psObservatory
+ *
+ *  This function shall construct a new psObservatory with attributes
+ *  cooresponding to the function parameters.
+ *
+ *  @return psObservatory*    new psObservatory struct
+ */
+psObservatory* psObservatoryAlloc(
+    const char* name,                  ///< Name of observatory
+    double latitude,                   ///< Latitude of observatory, east positive
+    double longitude,                  ///< Longitude of observatory
+    double height,                     ///< Height of observatory
+    double tlr                         ///< Tropospheric Lapse Rate
+);
+
+/** Allocator for psFPA
+ *
+ *  This function shall make an empty psFPA, with the nChips allocated
+ *  pointers to psChips being set to NULL; all other pointers in the structure
+ *  shall be initialized to NULL, apart from the grommit, which shall be
+ *  constructed on the basis of the exp parameter.
+ *
+ *  @return psFPA*    a newly allocated psFPA
+ */
+psFPA* psFPAAlloc(
+    psS32 nChips,                        ///< number of chips in the FPA
+    const psExposure* exp              ///< the exposure information
+);
+
+/** Allocates a psChip
+ *
+ *  This allocator shall make an empty psChip, with the nCells allocated
+ *  pointers to psCells being set to NULL; all other pointers in the structure
+ *  shall be initialized to NULL.
+ *
+ *  @return psChip*    newly allocated psChip
+ */
+psChip* psChipAlloc(
+    psS32 nCells,                        ///< number of cells in Chip
+    psFPA* parentFPA                   ///< parent FPA
+);
+
+/** Allocates a psCell
+ *
+ *  The constructor shall make an empty psCell, with the nReadouts allocated
+ *  pointers to psReadouts being set to NULL; all other pointers in the
+ *  structure shall be initialized to NULL.
+ *
+ *  @return psCell*    newly allocated psCell
+ */
+psCell* psCellAlloc(
+    psS32 nReadouts,                     ///< number of readouts in cell
+    psChip* parentChip                 ///< parent Chip
+);
+
+/** Allocates a psReadout
+ *
+ *  All pointers in the structure other than the image shall be initialized
+ *  to NULL.
+ *
+ *  @return psReadout*    newly allocated psReadout with all internal pointers set to NULL
+ */
+psReadout* psReadoutAlloc();
+
+/** Allocates a Wallace's Grommit structure.
+ *
+ *  The psGrommit is calculated from telescope information for the particular
+ *  exposure.
+ *
+ *  @return psGrommit* New grommit structure.
+ */
+psGrommit* psGrommitAlloc(
+    const psExposure* exp              ///< the cooresponding exposure structure.
+);
+
+/** Find cooresponding cell for given FPA coordinate
+ *
+ *  @return psCell*    the cell cooresponding to the coord in FPA
+ */
+psCell* psCellInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const psFPA* FPA                   ///< the FPA to search for the cell
+);
+
+/** Find cooresponding chip for given FPA coordinate
+ *
+ *  @return psChip*    the chip cooresponding to coord
+ */
+psChip* psChipInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const psFPA* FPA                   ///< the FPA to search for the cell
+);
+
+/** Find cooresponding cell for given Chip coordinate
+ *
+ *  @return psCell*    the cell cooresponding to coord
+ */
+psCell* psCellInChip(
+    const psPlane* coord,              ///< the coordinate in Chip plane
+    const psChip* chip                 ///< the chip to search for the cell
+);
+
+/** Translate a cell coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* psCoordCellToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Cell
+    const psCell* cell                 ///< the Cell in interest
+);
+
+/** Translate a chip coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordChipToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Chip
+    const psChip* chip                 ///< the chip in interest
+);
+
+/** Translate a FPA coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* psCoordFPAToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within FPA
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const psFPA* fpa                   ///< the FPA in interest
+);
+
+/** Translate a Tangent Plane coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordTPToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Tangent Plane
+    const psGrommit* grommit           ///< the grommit of the tangent plane
+);
+
+/** Translate a cell coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordCellToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const psCell* cell                 ///< the cell in interest
+);
+
+/** Translate a cell coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordCellToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const psCell* cell                 ///< the cell in interest
+);
+
+/** Translate a cell coordinate into a Sky coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordCellToSkyQuick(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const psCell* cell                 ///< the cell in interest
+);
+
+/** Translate a Sky coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* psCoordSkyToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the sky coordinate
+    const psGrommit* grommit           ///< the grommit
+);
+
+/** Translate a Tangent Plane coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordTPToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within tangent plane
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const psFPA* fpa                   ///< the FPA of interest
+);
+
+/** Translate a FPA coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* psCoordFPAToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the FPA coordinate
+    const psChip* chip                 ///< the chip of interest
+);
+
+/** Translate a chip coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordChipToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the Chip coordinate
+    const psCell* cell                 ///< the cell of interest
+);
+
+/** Translate a sky coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordSkyToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const psCell* cell                 ///< the cell of interest
+);
+
+/** Translate a sky coordinate into a cell coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordSkyToCellQuick(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    const psCell* cell                 ///< the cell of interest
+);
+
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstronomyErrors.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstronomyErrors.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstronomyErrors.dat	(revision 22271)
@@ -0,0 +1,68 @@
+#
+#  This file is used to generate psAstronomyErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERROR_TEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+psTime_FILE_NOT_FOUND                  Failed to open file %s.
+psTime_FILE_TOO_MANY_ROWS              Too many rows found in file %s. Max number of rows allowed is %d.
+psTime_TIME_POSTDATES_TABLE            Specified psTime postdates (%g) the table of %s information.
+psTime_TIME_PREDATES_TABLE             Specified psTime predates (%g) the table of %s information.
+psTime_TIME_POSTDATES_TABLES           Specified psTime postdates (%g) all tables of %s information.
+psTime_TIME_PREDATES_TABLES            Specified psTime predates (%g) all tables of %s information.
+psTime_TABLE_DUPLICATE_ROWS            The %s table was found to have two rows of the same time value.
+psTime_TYPE_UNKNOWN                    Specified type, %d, is not supported.
+psTime_TYPE_INCORRECT                  Specified type, %d, is incorrect.
+psTime_TYPE_MISMATCH                   Specified psTime parameters must have same type.
+psTime_GET_TOD_FAILED                  Failed to determine the current time from gettimeofday function.
+psTime_CONVERT_TIME_TO_STRING_FAILED   Failed to convert a time via strftime function.
+psTime_APPEND_MSEC_FAILED              Failed to append millisecond to time string with snprintf function.
+psTime_USEC_INVALID                    The psTime usec attribute value, %u, is invalid.  Must be less than 1e6.
+psTime_ISOTIME_MALFORMED               Specified ISO Time string, '%s', is malformed.  Must be in 'YYYY-MM-DDThh:mm:ss.sss' format.
+psTime_INTERPOLATION_FAILED            Failed time table interpolation.
+psTime_INTERPOLATION_FAILED_NAME       Failed time table interpolation for '%s'.
+psTime_LOOKUP_METADATA_FAILED          Failed find '%s' in time metadata.
+psTime_BAD_TABLE_COUNT                 Incorrect number of table files entered. Found: %d. Expected: %d.
+psTime_BAD_VECTOR                      Incorrect vector size. Size: %d, Expected %d.
+#
+psCoord_PROJECTION_TYPE_UNDEFINED      The projection type, %s, is undefined.
+psCoord_PROJECTION_TYPE_UNKNOWN        The projection type, %d, is unknown.
+psCoord_UNITS_UNKNOWN                  Specified units, 0x%x, is not supported.
+psCoord_OFFSET_MODE_UNKNOWN            Specified offset mode, 0x%x, is not supported.
+psCoord_INVALID_MJD                    Specified time is less than 1900.
+#
+psAstrometry_NONLINEAR_TRANSFORM       The %s transfrom is not linear.  Only linear transforms are supported.
+#
+psMetadata_METATYPE_INVALID            Specified psMetadataType, %d, is not supported.
+psMetadata_FORMAT_INVALID              Specified print format, %%%c, is not supported.
+psMetadata_METATYPE_MISMATCH           Specified psMetadataType, %d, is incorrect. Expected %d.
+psMetadata_ADD_LIST_FAILED             Failed to add metadata item, %s, to items list.
+psMetadata_ADD_TABLE_FAILED            Failed to add metadata item, %s, to items table.
+psMetadata_REMOVE_LIST_FAILED          Failed to remove metadata item, %s, from metadata list.
+psMetadata_REMOVE_LIST_INDEX_FAILED    Failed to remove metadata item, at index %d, from metadata list.
+psMetadata_REMOVE_TABLE_FAILED         Failed to remove metadata item, %s, from metadata table.
+psMetadata_ADD_COLLECTION_FAILED       Failed to add metadata item, %s, to metadata collection list.
+psMetadata_ADD_FAILED                  Failed to add metadata item to metadata collection list.
+psMetadata_FIND_FAILED                 Could not find metadata item, %s.
+psMetadata_FIND_INDEX_FAILED           Could not find metadata item at index %d.
+psMetadata_DUPLICATE_NOT_ALLOWED       Duplicate metadata item name is not allowed.  Use a psMetadataFlags option to allow such action.
+psMetadata_REGEX_INVALID               Specified regular expression is invalid.  %s.
+psMetadata_LOCATION_INVALID            Specified location, %d, is invalid.
+#
+psMetadataIO_TYPE_INVALID              Specified type, %d, is not supported.
+psMetadataIO_EXTNUM_NOTPOSITIVE        Specified extension number, %d, is invalid.  Value must be positive if no extension name is given.
+psMetadataIO_FITS_METATYPE_INVALID     Specified FITS metadata type, %c, is not supported.
+psMetadataIO_ADD_FAILED                Failed to add metadata item, %s.
+psMetadataIO_FILE_OPEN_FAILED          Failed to open file '%s'. Check if it exists and it has the proper permissions.
+psMetadataIO_FILE_MULTIPLE_CHAR        More than one '%c' character not allowed.  Found on line %u of %s.
+psMetadataIO_FILE_ELEMENT_NULL         Failed to read a metadata %s on line %u of %s.
+psMetadataIO_FILE_TYPE_INVALID         Metadata type '%s', found on line %u of %s, is not invalid.
+psMetadataIO_OVERWRITE_ITEM            Duplicate Metadata item, %s, found on line %u of %s.  Overwrite not allowed.
+psMetadataIO_PARSE_FAILED              Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s.
+psMetadataIO_NO_NAME                   Failed to find key 'name' in table on line %u of %s.
+psMetadataIO_TYPE_INVALID_LINE_FILE    Specified type, %s, is not supported on line %u of %s.
+psMetadataIO_TAG_MISMATCH              Start tag, %s and end tag, %s do not agree.
+psMetadataIO_TAG_UNKNOWN               Invalid end tag name, %s.
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstronomyErrors.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstronomyErrors.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psAstronomyErrors.h	(revision 22271)
@@ -0,0 +1,87 @@
+/** @file  psAstronomyErrors.h
+ *
+ *  @brief Contains the error text for the astronomy functions
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-17 19:01:01 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ASTRONOMY_ERRORS_H
+#define PS_ASTRONOMY_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psAstronomyErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psAstronomyErrors.dat lines)
+ *     $2  The error text (rest of the line in psAstronomyErrors.dat)
+ *     $n  The order of the source line in psAstronomyErrors.dat (comments excluded)
+ *
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_psTime_FILE_NOT_FOUND "Failed to open file %s."
+#define PS_ERRORTEXT_psTime_FILE_TOO_MANY_ROWS "Too many rows found in file %s. Max number of rows allowed is %d."
+#define PS_ERRORTEXT_psTime_TIME_POSTDATES_TABLE "Specified psTime postdates (%g) the table of %s information."
+#define PS_ERRORTEXT_psTime_TIME_PREDATES_TABLE "Specified psTime predates (%g) the table of %s information."
+#define PS_ERRORTEXT_psTime_TIME_POSTDATES_TABLES "Specified psTime postdates (%g) all tables of %s information."
+#define PS_ERRORTEXT_psTime_TIME_PREDATES_TABLES "Specified psTime predates (%g) all tables of %s information."
+#define PS_ERRORTEXT_psTime_TABLE_DUPLICATE_ROWS "The %s table was found to have two rows of the same time value."
+#define PS_ERRORTEXT_psTime_TYPE_UNKNOWN "Specified type, %d, is not supported."
+#define PS_ERRORTEXT_psTime_TYPE_INCORRECT "Specified type, %d, is incorrect."
+#define PS_ERRORTEXT_psTime_TYPE_MISMATCH "Specified psTime parameters must have same type."
+#define PS_ERRORTEXT_psTime_GET_TOD_FAILED "Failed to determine the current time from gettimeofday function."
+#define PS_ERRORTEXT_psTime_CONVERT_TIME_TO_STRING_FAILED "Failed to convert a time via strftime function."
+#define PS_ERRORTEXT_psTime_APPEND_MSEC_FAILED "Failed to append millisecond to time string with snprintf function."
+#define PS_ERRORTEXT_psTime_USEC_INVALID "The psTime usec attribute value, %u, is invalid.  Must be less than 1e6."
+#define PS_ERRORTEXT_psTime_ISOTIME_MALFORMED "Specified ISO Time string, '%s', is malformed.  Must be in 'YYYY-MM-DDThh:mm:ss.sss' format."
+#define PS_ERRORTEXT_psTime_INTERPOLATION_FAILED "Failed time table interpolation."
+#define PS_ERRORTEXT_psTime_INTERPOLATION_FAILED_NAME "Failed time table interpolation for '%s'."
+#define PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED "Failed find '%s' in time metadata."
+#define PS_ERRORTEXT_psTime_BAD_TABLE_COUNT "Incorrect number of table files entered. Found: %d. Expected: %d."
+#define PS_ERRORTEXT_psTime_BAD_VECTOR "Incorrect vector size. Size: %d, Expected %d."
+#define PS_ERRORTEXT_psCoord_PROJECTION_TYPE_UNDEFINED "The projection type, %s, is undefined."
+#define PS_ERRORTEXT_psCoord_PROJECTION_TYPE_UNKNOWN "The projection type, %d, is unknown."
+#define PS_ERRORTEXT_psCoord_UNITS_UNKNOWN "Specified units, 0x%x, is not supported."
+#define PS_ERRORTEXT_psCoord_OFFSET_MODE_UNKNOWN "Specified offset mode, 0x%x, is not supported."
+#define PS_ERRORTEXT_psCoord_INVALID_MJD "Specified time is less than 1900."
+#define PS_ERRORTEXT_psAstrometry_NONLINEAR_TRANSFORM "The %s transfrom is not linear.  Only linear transforms are supported."
+#define PS_ERRORTEXT_psMetadata_METATYPE_INVALID "Specified psMetadataType, %d, is not supported."
+#define PS_ERRORTEXT_psMetadata_FORMAT_INVALID "Specified print format, %%%c, is not supported."
+#define PS_ERRORTEXT_psMetadata_METATYPE_MISMATCH "Specified psMetadataType, %d, is incorrect. Expected %d."
+#define PS_ERRORTEXT_psMetadata_ADD_LIST_FAILED "Failed to add metadata item, %s, to items list."
+#define PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED "Failed to add metadata item, %s, to items table."
+#define PS_ERRORTEXT_psMetadata_REMOVE_LIST_FAILED "Failed to remove metadata item, %s, from metadata list."
+#define PS_ERRORTEXT_psMetadata_REMOVE_LIST_INDEX_FAILED "Failed to remove metadata item, at index %d, from metadata list."
+#define PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED "Failed to remove metadata item, %s, from metadata table."
+#define PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED "Failed to add metadata item, %s, to metadata collection list."
+#define PS_ERRORTEXT_psMetadata_ADD_FAILED "Failed to add metadata item to metadata collection list."
+#define PS_ERRORTEXT_psMetadata_FIND_FAILED "Could not find metadata item, %s."
+#define PS_ERRORTEXT_psMetadata_FIND_INDEX_FAILED "Could not find metadata item at index %d."
+#define PS_ERRORTEXT_psMetadata_DUPLICATE_NOT_ALLOWED "Duplicate metadata item name is not allowed.  Use a psMetadataFlags option to allow such action."
+#define PS_ERRORTEXT_psMetadata_REGEX_INVALID "Specified regular expression is invalid.  %s."
+#define PS_ERRORTEXT_psMetadata_LOCATION_INVALID "Specified location, %d, is invalid."
+#define PS_ERRORTEXT_psMetadataIO_TYPE_INVALID "Specified type, %d, is not supported."
+#define PS_ERRORTEXT_psMetadataIO_EXTNUM_NOTPOSITIVE "Specified extension number, %d, is invalid.  Value must be positive if no extension name is given."
+#define PS_ERRORTEXT_psMetadataIO_FITS_METATYPE_INVALID "Specified FITS metadata type, %c, is not supported."
+#define PS_ERRORTEXT_psMetadataIO_ADD_FAILED "Failed to add metadata item, %s."
+#define PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED "Failed to open file '%s'. Check if it exists and it has the proper permissions."
+#define PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR "More than one '%c' character not allowed.  Found on line %u of %s."
+#define PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL "Failed to read a metadata %s on line %u of %s."
+#define PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID "Metadata type '%s', found on line %u of %s, is not invalid."
+#define PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM "Duplicate Metadata item, %s, found on line %u of %s.  Overwrite not allowed."
+#define PS_ERRORTEXT_psMetadataIO_PARSE_FAILED "Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."
+#define PS_ERRORTEXT_psMetadataIO_NO_NAME "Failed to find key 'name' in table on line %u of %s."
+#define PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE "Specified type, %s, is not supported on line %u of %s."
+#define PS_ERRORTEXT_psMetadataIO_TAG_MISMATCH "Start tag, %s and end tag, %s do not agree."
+#define PS_ERRORTEXT_psMetadataIO_TAG_UNKNOWN "Invalid end tag name, %s."
+//~End
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psCoord.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psCoord.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psCoord.c	(revision 22271)
@@ -0,0 +1,1225 @@
+/** @file  psCoord.c
+*
+*  @brief Contains basic coordinate transformation definitions and operations
+*
+*  This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup CoordinateTransform
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.66 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-11 22:02:15 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include "psType.h"
+#include "psCoord.h"
+#include "psMemory.h"
+#include "psTime.h"
+#include "psConstants.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psAstronomyErrors.h"
+#include "psAstrometry.h"
+#include "psMatrix.h"
+#include <math.h>
+#include <float.h>
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+// Modified Julian Day 01/01/1900 00:00:00
+#define MJD_1900 15021.0
+
+// Days in Julian century
+#define JULIAN_CENTURY 36525.0
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+static void planeFree(psPlane *p)
+{
+    // There are non dynamic allocated items
+}
+
+/*****************************************************************************
+p_psPlaneTransformLinearInvert(transform): : this is a private function which
+simply inverts the supplied psPlaneTransform transform.  It assumes that
+"transform" is linear.
+ 
+This program assumes that the inverse of the following linear equations:
+        X2 = A + (B * X1) + (C * Y1);
+        Y2 = D + (E * X1) + (F * Y1);
+is
+        Y1 = (Y2 - ((E/B) * X2) - D + ((E*A)/B)) / (F - ((C*E)/B));
+        X1 = (Y2 - ((F/C) * X2) - D + ((F*A)/C)) / (E - ((F*B)/C));
+or
+ X1 = (-D + ((F*A)/C)) / (E - ((F*B)/C)) +
+      (X2 * -((F/C) / (E - ((F*B)/C)))) +
+      (Y2 * (1.0 / (E - ((F*B)/C))));
+ Y1 = (-D + ((E*A)/B))/(F - ((C*E)/B)) +
+      (X2 * -((E/B) / (F - ((C*E)/B)))) +
+      (Y2 * (1.0 / (F - ((C*E)/B))));
+ 
+XXX: Since thre is now a general psPlaneTransformInvert() function, we
+should rename this.
+ *****************************************************************************/
+psPlaneTransform *p_psPlaneTransformLinearInvert(psPlaneTransform *transform)
+{
+    PS_PTR_CHECK_NULL(transform, 0);
+    PS_PTR_CHECK_NULL(transform->x, 0);
+    PS_PTR_CHECK_NULL(transform->y, 0);
+
+    psF64 A = 0.0;
+    psF64 B = 0.0;
+    psF64 C = 0.0;
+    psF64 D = 0.0;
+    psF64 E = 0.0;
+    psF64 F = 0.0;
+
+    A = transform->x->coeff[0][0];
+    if (transform->x->nX >= 2) {
+        B = transform->x->coeff[1][0];
+    }
+    if (transform->x->nY >= 2) {
+        C = transform->x->coeff[0][1];
+    }
+    D = transform->y->coeff[0][0];
+    if (transform->y->nX >= 2) {
+        E = transform->y->coeff[1][0];
+    }
+    if (transform->y->nY >= 2) {
+        F = transform->y->coeff[0][1];
+    }
+
+    psPlaneTransform *out = psPlaneTransformAlloc(2, 2);
+
+    out->x->coeff[0][0] = (-D + ((F*A)/C)) / (E - ((F*B)/C));
+    out->x->coeff[1][0] = -(F/C) / (E - ((F*B)/C));
+    out->x->coeff[0][1] =  1.0 / (E - ((F*B)/C));
+    out->y->coeff[0][0] = (-D + ((E*A)/B)) / (F - ((C*E)/B));
+    out->y->coeff[1][0] = -(E/B) / (F - ((C*E)/B));
+    out->y->coeff[0][1] =  1.0 / (F - ((C*E)/B));
+
+    return(out);
+}
+
+/*****************************************************************************
+p_psIsProjectionLinear(): this is a private function which simply determines
+if the supplied psPlaneTransform transform is linear: if any of the
+cooefficients of order 2 are higher are non-zero, then it is not linear.
+ *****************************************************************************/
+psS32 p_psIsProjectionLinear(psPlaneTransform *transform)
+{
+    PS_PTR_CHECK_NULL(transform, 0);
+    PS_PTR_CHECK_NULL(transform->x, 0);
+    PS_PTR_CHECK_NULL(transform->y, 0);
+
+    for (psS32 i=0;i<(transform->x->nX);i++) {
+        for (psS32 j=0;j<(transform->x->nY);j++) {
+            if (transform->x->coeff[i][j] != 0.0) {
+                if (!(((i == 0) && (j == 0)) ||
+                        ((i == 0) && (j == 1)) ||
+                        ((i == 1) && (j == 0)))) {
+                    return(0);
+                }
+            }
+        }
+    }
+
+    for (psS32 i=0;i<(transform->y->nX);i++) {
+        for (psS32 j=0;j<(transform->y->nY);j++) {
+            if (transform->y->coeff[i][j] != 0.0) {
+                if (!(((i == 0) && (j == 0)) ||
+                        ((i == 0) && (j == 1)) ||
+                        ((i == 1) && (j == 0)))) {
+                    return(0);
+                }
+            }
+        }
+    }
+
+    return(1);
+}
+
+// XXX: Must test psPlaneAlloc() and planeFree().
+// XXX: Must rewrite code and tests to use these functions.
+psPlane* psPlaneAlloc(void)
+{
+    psPlane *p = psAlloc(sizeof(psPlane));
+
+    psMemSetDeallocator(p, (psFreeFcn) planeFree);
+    return(p);
+}
+
+
+static void sphereFree(psSphere *s)
+{
+    // There are non dynamic allocated items
+}
+
+psSphere* psSphereAlloc(void)
+{
+    psSphere *s = psAlloc(sizeof(psSphere));
+
+    psMemSetDeallocator(s, (psFreeFcn) sphereFree);
+    return(s);
+}
+
+static void planeTransformFree(psPlaneTransform *pt)
+{
+    psFree(pt->x);
+    psFree(pt->y);
+}
+
+psPlaneTransform* psPlaneTransformAlloc(psS32 n1, psS32 n2)
+{
+    PS_INT_CHECK_NON_NEGATIVE(n1, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n2, NULL);
+
+    psPlaneTransform *pt = psAlloc(sizeof(psPlaneTransform));
+    pt->x = psDPolynomial2DAlloc(n1, n2, PS_POLYNOMIAL_ORD);
+    pt->y = psDPolynomial2DAlloc(n1, n2, PS_POLYNOMIAL_ORD);
+
+    psMemSetDeallocator(pt, (psFreeFcn) planeTransformFree);
+    return(pt);
+}
+
+psPlane* psPlaneTransformApply(psPlane* out,
+                               const psPlaneTransform* transform,
+                               const psPlane* coords)
+{
+    PS_PTR_CHECK_NULL(transform, NULL);
+    PS_PTR_CHECK_NULL(transform->x, NULL);
+    PS_PTR_CHECK_NULL(transform->y, NULL);
+    PS_PTR_CHECK_NULL(coords, NULL);
+
+    if (out == NULL) {
+        out = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+    out->x = psDPolynomial2DEval(
+                 transform->x,
+                 coords->x,
+                 coords->y
+             );
+    out->y = psDPolynomial2DEval(
+                 transform->y,
+                 coords->x,
+                 coords->y
+             );
+    return (out);
+}
+
+static void planeDistortFree(psPlaneDistort *pt)
+{
+    psFree(pt->x);
+    psFree(pt->y);
+}
+
+psPlaneDistort* psPlaneDistortAlloc(psS32 n1, psS32 n2, psS32 n3, psS32 n4)
+{
+    PS_INT_CHECK_NON_NEGATIVE(n1, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n2, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n3, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(n4, NULL);
+
+    psPlaneDistort *pt = psAlloc(sizeof(psPlaneDistort));
+    pt->x = psDPolynomial4DAlloc(n1, n2, n3, n4, PS_POLYNOMIAL_ORD);
+    pt->y = psDPolynomial4DAlloc(n1, n2, n3, n4, PS_POLYNOMIAL_ORD);
+
+    psMemSetDeallocator(pt, (psFreeFcn) planeDistortFree);
+    return(pt);
+}
+
+/******************************************************************************
+This transformation takes into account parameters beyond an objects spatial
+coordinates: term3 and term4 (magnitude and color).
+ *****************************************************************************/
+psPlane* psPlaneDistortApply(psPlane* out,
+                             const psPlaneDistort* transform,
+                             const psPlane* coords,
+                             float color,
+                             float magnitude)
+{
+    PS_PTR_CHECK_NULL(transform, NULL);
+    PS_PTR_CHECK_NULL(transform->x, NULL);
+    PS_PTR_CHECK_NULL(transform->y, NULL);
+    PS_PTR_CHECK_NULL(coords, NULL);
+
+    if (out == NULL) {
+        out = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+    out->x = psDPolynomial4DEval(
+                 transform->x,
+                 coords->x,
+                 coords->y,
+                 color,
+                 magnitude
+             );
+    out->y = psDPolynomial4DEval(
+                 transform->y,
+                 coords->x,
+                 coords->y,
+                 color,
+                 magnitude
+             );
+    return (out);
+}
+
+/******************************************************************************
+alpha is LONGITUDE
+delta is LATITUDE
+ 
+    alphaP: Take the target pole in the source system; calculate its LONGITUDE
+     in the target system.  That longitude is alphaP.
+    DeltaP: Take the target pole in the source system; calculate its LATITUDE
+     in the target system.  That longitude is deltaP.
+    phiP:   This is the LONGITUDE of the ascending node in the target system.
+ *****************************************************************************/
+psSphereTransform* psSphereTransformAlloc(psF64 alphaP,
+        psF64 deltaP,
+        psF64 phiP)
+{
+    psSphereTransform* tmp = (psSphereTransform* ) psAlloc(sizeof(psSphereTransform));
+
+    tmp->cosDeltaP = cos(deltaP);
+    tmp->sinDeltaP = sin(deltaP);
+    tmp->alphaP = alphaP;
+    tmp->phiP = phiP;
+
+    return (tmp);
+}
+
+/******************************************************************************
+XXX: Private Function.
+ 
+piNormalize(): take an input angle in radians and convert it to the range 0:2*PI.
+ *****************************************************************************/
+psF32 piNormalize(psF32 angle)
+{
+    while (angle < FLT_EPSILON) {
+        angle+=M_PI*2;
+    }
+
+    while (angle >= (M_PI*2)) {
+        angle-=M_PI*2;
+    }
+    return(angle);
+}
+
+/******************************************************************************
+XXX: We convert Right Ascension angles to the range 0:PI.  Is that acceptable?
+XXX: Should we do something for Declination as well?
+ *****************************************************************************/
+psSphere* psSphereTransformApply(psSphere* out,
+                                 const psSphereTransform* transform,
+                                 const psSphere* coord)
+{
+    PS_PTR_CHECK_NULL(transform, NULL);
+    PS_PTR_CHECK_NULL(coord, NULL);
+
+    if (out == NULL) {
+        out = (psSphere* ) psAlloc(sizeof(psSphere));
+    }
+
+    psF64 alpha = coord->r;
+    psF64 delta = coord->d;
+    psF64 alphaMinusAlphaP = alpha - transform->alphaP;
+
+    psF64 eq55 = (sin(delta) * transform->cosDeltaP) -
+                 (cos(delta) * transform->sinDeltaP * sin(alphaMinusAlphaP));
+    psF64 eq56 = (cos(delta) * transform->cosDeltaP * sin(alphaMinusAlphaP)) +
+                 (sin(delta) * transform->sinDeltaP);
+    psF64 eq57 = cos(delta) * cos(alphaMinusAlphaP);
+
+    psF64 theta = asin(eq55);
+    psF64 phi = atan2(eq56, eq57) + transform->phiP;
+    out->r = piNormalize(phi);
+    out->d = theta;
+
+    return(out);
+}
+
+psSphereTransform* psSphereTransformICRSToEcliptic(psTime *time)
+{
+    psF64 T;
+
+    // Check for null parameter
+    PS_PTR_CHECK_NULL(time, NULL);
+
+    // Convert psTime to MJD
+    psF64 MJD = psTimeToMJD(time);
+
+    // Check the specified MJD is greater than 1900
+    if ( MJD < MJD_1900 ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE,true,PS_ERRORTEXT_psCoord_INVALID_MJD);
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 1900
+    T = ( MJD - MJD_1900 ) / JULIAN_CENTURY;
+
+    psF64 alphaP = 0.0;
+    psF64 deltaP = DEG_TO_RAD(23.0) +
+                   MIN_TO_RAD(27.0) +
+                   SEC_TO_RAD(8.26) -
+                   (SEC_TO_RAD(46.845) * T) -
+                   (SEC_TO_RAD(0.0059) * T * T) +
+                   (SEC_TO_RAD(0.00181) * T * T * T);
+    psF64 phiP = 0.0;
+
+    // Don't neglect the minus sign on deltaP (bug 244):
+    return (psSphereTransformAlloc(alphaP, deltaP, phiP));
+}
+
+
+psSphereTransform* psSphereTransformEclipticToICRS(psTime *time)
+{
+    psF64 T;
+
+    // Check for null parameter
+    PS_PTR_CHECK_NULL(time, NULL);
+
+    // Convert psTime to MJD
+    psF64 MJD = psTimeToMJD(time);
+
+    // Check the specified MJD is greater than 1900
+    if ( MJD < MJD_1900 ) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE,true,PS_ERRORTEXT_psCoord_INVALID_MJD);
+        return NULL;
+    }
+
+    // Calculate number of Julian centuries since 1900
+    T = ( MJD - MJD_1900 ) / JULIAN_CENTURY;
+
+    psF64 alphaP = 0.0;
+    psF64 deltaP = DEG_TO_RAD(23.0) +
+                   MIN_TO_RAD(27.0) +
+                   SEC_TO_RAD(8.26) -
+                   (SEC_TO_RAD(46.845) * T) -
+                   (SEC_TO_RAD(0.0059) * T * T) +
+                   (SEC_TO_RAD(0.00181) * T * T * T);
+    psF64 phiP = 0.0;
+
+    return (psSphereTransformAlloc(alphaP, -deltaP, phiP));
+}
+
+// XXX: This is bug 245: alphaP swaps with phiP from psSphereTransformGalacticToICRS()
+psSphereTransform* psSphereTransformGalacticToICRS(void)
+{
+    psF64 alphaP = DEG_TO_RAD(32.93192);
+    psF64 deltaP = DEG_TO_RAD(-62.87175);
+    psF64 phiP = DEG_TO_RAD(282.85948);
+
+    return (psSphereTransformAlloc(alphaP, deltaP, phiP));
+}
+
+psSphereTransform* psSphereTransformICRSToGalactic(void)
+{
+    psF64 alphaP = DEG_TO_RAD(282.85948);
+    psF64 deltaP = DEG_TO_RAD(62.87175);
+    psF64 phiP = DEG_TO_RAD(32.93192);
+
+    return (psSphereTransformAlloc(alphaP, deltaP, phiP));
+}
+
+void projectionFree(psProjection *p)
+{
+    // There are no dynamically allocated items
+}
+
+psProjection* psProjectionAlloc(
+    psF64 R,
+    psF64 D,
+    psF64 Xs,
+    psF64 Ys,
+    psProjectionType type)
+{
+    psProjection *p = psAlloc(sizeof(psProjection));
+    p->D = D;
+    p->R = R;
+    p->Xs = Xs;
+    p->Ys = Ys;
+    p->type = type;
+
+    psMemSetDeallocator(p, (psFreeFcn) projectionFree);
+    return(p);
+}
+
+psPlane* psProject(const psSphere* coord,
+                   const psProjection* projection)
+{
+    PS_PTR_CHECK_NULL(coord, NULL);
+    PS_PTR_CHECK_NULL(projection, NULL);
+
+    psF64   theta = 0.0;
+    psF64   phi   = 0.0;
+
+    // Allocate return value
+    psPlane* out = psPlaneAlloc();
+
+    // Convert to projection spherical coordinate system
+    theta = asin( sin(coord->d)*sin(projection->D) +
+                  cos(coord->d)*cos(projection->D)*cos(coord->r-projection->R));
+    phi = atan2( -1.0*cos(coord->d)*sin(coord->r-projection->R),
+                 sin(coord->d)*cos(projection->D) - cos(coord->d)*sin(projection->D)*cos(coord->r-projection->R) );
+
+    // Perform the specified projection
+    // Gnomonic projection
+    if (projection->type == PS_PROJ_TAN) {
+        out->x = (cos(theta)*sin(phi))/sin(theta);
+        out->y = (-1.0*cos(theta)*cos(phi))/sin(theta);
+        // Othrographic projection
+    } else if (projection->type == PS_PROJ_SIN) {
+        out->x = cos(theta)*sin(phi);
+        out->y = -1.0*cos(theta)*cos(phi);
+        // Hammer-Aitoff projection
+    } else if ( projection->type == PS_PROJ_AIT) {
+        psF64 zeta = 1.0/sqrt(0.5*(1.0+cos(theta)*cos(phi/2.0)));
+        out->x = 2.0*zeta*cos(theta)*sin(phi/2.0);
+        out->y = zeta*sin(theta);
+        // Parabolic projection
+    } else if ( projection->type == PS_PROJ_PAR) {
+        out->x = phi*(2.0*cos(2.0*theta/3.0) - 1.0);
+        out->y = M_PI*sin(theta/3.0);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psCoord_PROJECTION_TYPE_UNKNOWN,
+                projection->type);
+        psFree(out);
+        return NULL;
+    }
+
+    // Apply plate scales
+    out->x *= projection->Xs;
+    out->y *= projection->Ys;
+
+    // Return output
+    return out;
+}
+
+psSphere* psDeproject(const psPlane* coord,
+                      const psProjection* projection)
+{
+    PS_PTR_CHECK_NULL(coord, NULL);
+    PS_PTR_CHECK_NULL(projection, NULL);
+
+    psF64  theta = 0.0;
+    psF64  phi   = 0.0;
+
+    // Allocate return sphere structure
+    psSphere* out = psSphereAlloc();
+
+    // Remove plate scales
+    psF64  x = coord->x/projection->Xs;
+    psF64  y = coord->y/projection->Ys;
+
+    // Perform inverse projection
+    // Gnonomic deprojection
+    if ( projection->type == PS_PROJ_TAN) {
+        phi = atan(-1.0*x/y);
+        theta = atan(1.0/sqrt(x*x+y*y));
+        // Orhtographic deprojection
+    } else if ( projection->type == PS_PROJ_SIN) {
+        phi = atan((-1.0*x)/y);
+        theta = atan( sqrt(1.0-(x*x+y*y)) / sqrt(x*x+y*y));
+        // Hammer-Aitoff deprojection
+    } else if ( projection->type == PS_PROJ_AIT) {
+        psF64 z = sqrt(1.0 - ((x/4.0)*(x/4.0)) - ((y/2.0)*(y/2.0)));
+        phi = 2.0*atan((z*x) / (2.0*(2.0*z*z-1.0)) );
+        theta = asin(y*z);
+        // Parabolic deprojection
+    } else if ( projection->type == PS_PROJ_PAR) {
+        psF64 rho = y/M_PI;
+        phi = x/(1.0 - 4.0*rho*rho);
+        theta = 3.0*asin(rho);
+        // Invalid deprojection type
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psCoord_PROJECTION_TYPE_UNKNOWN,
+                projection->type);
+        psFree(out);
+        return NULL;
+    }
+
+    // Convert from projection spherical coordinates
+    out->d = asin( sin(theta)*sin(projection->D) +
+                   cos(theta)*cos(projection->D)*cos(phi) );
+    out->r = projection->R + atan2( -1.0*cos(theta)*sin(phi),
+                                    sin(theta)*cos(projection->D) -
+                                    cos(theta)*sin(projection->D)*cos(phi) );
+
+    // Return sphere coordinate
+    return out;
+}
+
+/******************************************************************************
+The basic idea is to project both positions onto the linear plane, with
+position1 at the center, then calculate the linear offset between those
+projections.
+ 
+XXX: Do I need to check for unacceptable transformation parameters?  Maybe,
+     if the points are on the North/South Pole, etc?
+ 
+XXX: Do I need to somehow scale this projection?
+ 
+XXX: Does PS_LINEAR mode make sense?  The result must be returned in psSphere
+     regardless of the mode.
+ 
+XXX: How to compound errors?
+ *****************************************************************************/
+psSphere* psSphereGetOffset(const psSphere* position1,
+                            const psSphere* position2,
+                            psSphereOffsetMode mode,
+                            psSphereOffsetUnit unit)
+{
+    PS_PTR_CHECK_NULL(position1, NULL);
+    PS_PTR_CHECK_NULL(position2, NULL);
+
+    // Check positions near 90 degree and issue warnings if necessary
+    if (position1->d >= DEG_TO_RAD(90.0)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psDeproject(): position1->d is larger than 90 degrees.  Returning NULL.");
+        return NULL;
+    }
+    if (position2->d >= DEG_TO_RAD(90.0)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psDeproject(): position2->d is larger than 90 degrees.  Returning NULL.");
+        return NULL;
+    }
+
+    // Allocate return structure
+    psSphere* tmp = psSphereAlloc();
+
+    // Mode is LINEAR - Use first position as projection center and project second point
+    // onto tangent plane, set point projected into psSphere structure x->r y->d
+    if (mode == PS_LINEAR) {
+        psProjection* proj = psProjectionAlloc(position1->r,
+                                               position1->d,
+                                               1.0,
+                                               1.0,
+                                               PS_PROJ_TAN);
+
+        // Perform projection onto tangent plane
+        psPlane* lin = psProject(position2, proj);
+
+        // Set return values
+        tmp->r = lin->x;
+        tmp->d = lin->y;
+
+        // Free data structures allocated
+        psFree(proj);
+        psFree(lin);
+
+        // Mode is SPHERICAL - Get difference between positiion 1 and position 2 and convert
+        // offset value from radians to desired units and return
+    } else if (mode == PS_SPHERICAL) {
+        tmp->r = position2->r - position1->r;
+        tmp->d = position2->d - position1->d;
+
+        // Wrap these to an acceptable range.  This assumes that all
+        // angles are in radians.
+        tmp->r = fmod(tmp->r, 2*M_PI);
+        tmp->d = fmod(tmp->d, 2*M_PI);
+        tmp->rErr = 0.0;
+        tmp->dErr = 0.0;
+
+        // Convert to desired units
+        if (unit == PS_ARCSEC) {
+            tmp->r = RAD_TO_SEC(tmp->r);
+            tmp->d = RAD_TO_SEC(tmp->d);
+        } else if (unit == PS_ARCMIN) {
+            tmp->r = RAD_TO_MIN(tmp->r);
+            tmp->d = RAD_TO_MIN(tmp->d);
+        } else if (unit == PS_DEGREE) {
+            tmp->r = RAD_TO_DEG(tmp->r);
+            tmp->d = RAD_TO_DEG(tmp->d);
+        } else if (unit == PS_RADIAN) {}
+        else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psCoord_UNITS_UNKNOWN,
+                    unit);
+            psFree(tmp);
+            return NULL;
+        }
+        // Invalid mode
+    } else {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psCoord_OFFSET_MODE_UNKNOWN,
+                mode);
+        psFree(tmp);
+        return NULL;
+    }
+
+    // Return value
+    return tmp;
+}
+
+/******************************************************************************
+XXX: Do we need to check for unacceptable transformation parameters?  Maybe,
+     if the points are on the North/South Pole, etc?
+ 
+XXX: Do we need to somehow scale this projection?
+ 
+XXX: I copied the algorithm from the ADD exactly.
+ 
+XXX: Should we compound errors?
+ *****************************************************************************/
+
+psSphere* psSphereSetOffset(const psSphere* position,
+                            const psSphere* offset,
+                            psSphereOffsetMode mode,
+                            psSphereOffsetUnit unit)
+{
+    PS_PTR_CHECK_NULL(position, NULL);
+    PS_PTR_CHECK_NULL(offset, NULL);
+
+    psSphere* tmp;
+    psF64 tmpR = 0.0;
+    psF64 tmpD = 0.0;
+
+    // If mode is linear then set position to projection center
+    // and offset to linear coordinate then deproject to obtain
+    // new sphere coordinate
+    if (mode == PS_LINEAR) {
+
+        // Allocate plane coordinate and set coordinate
+        psPlane*  lin = psPlaneAlloc();
+        lin->x = offset->r;
+        lin->y = offset->d;
+
+        // Allocate and set projection structure
+        psProjection* proj = psProjectionAlloc(position->r,
+                                               position->d,
+                                               1.0,
+                                               1.0,
+                                               PS_PROJ_TAN);
+
+        // Project tangent plane coord to spherical coord
+        tmp = psDeproject(lin, proj);
+
+        // Free data structures used
+        psFree(proj);
+        psFree(lin);
+
+        // If mode is spherical then convert offset to radians, add the offset
+        // to the position and wrap to 0 to 2pi
+    } else if (mode == PS_SPHERICAL) {
+
+        // Convert offset unit to radians
+        if (unit == PS_ARCSEC) {
+            tmpR = SEC_TO_RAD(offset->r);
+            tmpD = SEC_TO_RAD(offset->d);
+        } else if (unit == PS_ARCMIN) {
+            tmpR = MIN_TO_RAD(offset->r);
+            tmpD = MIN_TO_RAD(offset->d);
+        } else if (unit == PS_DEGREE) {
+            tmpR = DEG_TO_RAD(offset->r);
+            tmpD = DEG_TO_RAD(offset->d);
+        } else if (unit == PS_RADIAN) {
+            tmpR = offset->r;
+            tmpD = offset->d;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psCoord_UNITS_UNKNOWN,
+                    unit);
+            return NULL;
+        }
+
+        // Allocate sphere structure to return
+        tmp = psSphereAlloc();
+
+        // Add offset and wrap to 0 to 2PI if necessary
+        tmp->r = position->r + tmpR;
+        tmp->r = fmod(tmp->r, 2.0*M_PI);
+        tmp->d = position->d + tmpD;
+        tmp->d = fmod(tmp->d, 2.0*M_PI);
+        tmp->rErr = 0.0;
+        tmp->dErr = 0.0;
+
+        // Invalid mode report error
+    } else {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psCoord_OFFSET_MODE_UNKNOWN,
+                mode);
+        return NULL;
+    }
+
+    return tmp;
+}
+
+
+
+/******************************************************************************
+psSpherePrecess(coords, fromTime, toTime):
+ 
+XXX: Use static memory for tmpST.
+ *****************************************************************************/
+psSphere *psSpherePrecess(psSphere *coords,
+                          const psTime *fromTime,
+                          const psTime *toTime)
+{
+    // Check input for NULL pointers
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(fromTime, NULL);
+    PS_PTR_CHECK_NULL(toTime, NULL);
+
+    // Calculate Julian centuries
+    psF64 fromMJD = psTimeToMJD(fromTime);
+    psF64 toMJD = psTimeToMJD(toTime);
+    psF64 T = (toMJD - fromMJD) / JULIAN_CENTURY;
+
+    // Calculate conversion constants
+    psF64 alphaP = DEG_TO_RAD(90.0) - ((DEG_TO_RAD(0.6406161) * T) +
+                                       (DEG_TO_RAD(0.0000839) * T * T) +
+                                       (DEG_TO_RAD(0.000005) * T * T * T));
+
+    psF64 deltaP = (DEG_TO_RAD(0.5567530) * T) -
+                   (DEG_TO_RAD(0.0001185) * T * T) -
+                   (DEG_TO_RAD(0.0000116) * T * T * T);
+
+    psF64 phiP = DEG_TO_RAD(90.0) + ((DEG_TO_RAD(0.6406161) * T) +
+                                     (DEG_TO_RAD(0.0003041) * T * T) +
+                                     (DEG_TO_RAD(0.0000051) * T * T * T));
+
+    // Create transform with proper constants
+    psSphereTransform *tmpST = psSphereTransformAlloc(alphaP, deltaP, phiP);
+
+    // Apply transform to coordinates
+    psSphere *out = psSphereTransformApply(NULL, tmpST, coords);
+
+    psFree(tmpST);
+
+    return(out);
+}
+
+/*****************************************************************************
+multiplyDPoly2D(trans1, trans2): Takes two 2-D polynomials as input and
+multiplies them.  Basically, for each non-zero coeff in the trans1 coeff[][]
+array, you must multiply by all non-zero coeffs in trans2.
+ 
+XXX: Inefficient in that the out polynomial is allocated every time.
+ *****************************************************************************/
+
+psDPolynomial2D *multiplyDPoly2D(psDPolynomial2D *trans1,
+                                 psDPolynomial2D *trans2)
+{
+    //TRACE: printf("multiplyDPoly2D(%d %d: %d %d)\n", trans1->nX, trans1->nY, trans2->nX, trans2->nY);
+    psS32 orderX = (trans1->nX + trans2->nX) - 1;
+    psS32 orderY = (trans1->nY + trans2->nY) - 1;
+
+    psDPolynomial2D *out = psDPolynomial2DAlloc(orderX, orderY, PS_POLYNOMIAL_ORD);
+    //TRACE: printf("Creating poly (%d, %d)\n", orderX, orderY);
+    for (psS32 i = 0 ; i < out->nX; i++) {
+        for (psS32 j = 0 ; j < out->nY; j++) {
+            out->coeff[i][j] = 0.0;
+            out->mask[i][j] = 0;
+        }
+    }
+
+    for (psS32 t1x = 0 ; t1x < trans1->nX ; t1x++) {
+        for (psS32 t1y = 0 ; t1y < trans1->nY ; t1y++) {
+            if (0.0 != trans1->coeff[t1x][t1y]) {
+                for (psS32 t2x = 0 ; t2x < trans2->nX ; t2x++) {
+                    for (psS32 t2y = 0 ; t2y < trans2->nY ; t2y++) {
+                        /* Possible debug-only macro which checks these coords?
+                        if ((t1x+t2x) >= orderX)
+                            printf("BAD 1\n");
+                        if ((t1y+t2y) >= orderY)
+                            printf("BAD 2\n");
+                        */
+                        out->coeff[t1x+t2x][t1y+t2y]+= (trans1->coeff[t1x][t1y] * trans2->coeff[t2x][t2y]);
+                    }
+                }
+            }
+        }
+    }
+    return(out);
+}
+
+
+/*****************************************************************************
+psPlaneTransformCombine(out, trans1, trans2)
+ 
+XXX: Much room for optimization.  Currently, we call the polyMultiply
+routine far too many times.
+ *****************************************************************************/
+psPlaneTransform *psPlaneTransformCombine(psPlaneTransform *out,
+        const psPlaneTransform *trans1,
+        const psPlaneTransform *trans2)
+{
+    PS_PTR_CHECK_NULL(trans1, NULL);
+    PS_PTR_CHECK_NULL(trans2, NULL);
+    //TRACE: printf("psPlaneTransformCombine(%d, %d, %d, %d: %d, %d, %d, %d)\n", trans1->x->nX, trans1->x->nY, trans1->y->nX, trans1->y->nY, trans2->x->nX, trans2->x->nY, trans2->y->nX, trans2->y->nY);
+    //
+    // Determine the size of the new psPlaneTransform.
+    //
+    // PS_MAX(  Number of x terms in T2->x * number of x terms in T1->x,
+    //          Number of y terms in T2->x * number of x terms in T1->y,
+    psS32 orderXnX = PS_MAX((trans2->x->nX * trans1->x->nX),
+                            (trans2->x->nY * trans1->y->nX));
+    psS32 orderXnY = PS_MAX((trans2->x->nX * trans1->x->nY),
+                            (trans2->x->nY * trans1->y->nY));
+
+    psS32 orderYnX = PS_MAX((trans2->y->nX * trans1->x->nX),
+                            (trans2->y->nY * trans1->y->nX));
+    psS32 orderYnY = PS_MAX((trans2->y->nX * trans1->x->nY),
+                            (trans2->y->nY * trans1->y->nY));
+    psS32 orderX = PS_MAX(orderXnX, orderYnX);
+    psS32 orderY = PS_MAX(orderXnY, orderYnY);
+
+    //
+    // Allocate the new psPlaneTransform, if necessary.
+    //
+    psPlaneTransform *myPT = NULL;
+    if (out == NULL) {
+        myPT = psPlaneTransformAlloc(orderX, orderY);
+    } else {
+        if ((out->x->nX == orderX) && (out->x->nY == orderY) &&
+                (out->y->nX == orderX) && (out->y->nY == orderY)) {
+            myPT = out;
+        } else {
+            psFree(out);
+            myPT = psPlaneTransformAlloc(orderX, orderY);
+        }
+    }
+
+    //
+    // Initialize the new psPlaneTransform, if necessary.
+    //
+    for (psS32 i = 0 ; i < orderX ; i++) {
+        for (psS32 j = 0 ; j < orderY ; j++) {
+            myPT->x->coeff[i][j] = 0.0;
+            myPT->x->mask[i][j] = 0;
+            myPT->y->coeff[i][j] = 0.0;
+            myPT->y->mask[i][j] = 0;
+        }
+    }
+
+    //
+    // For each term (a * x^i * y^j) in trans2, we substitute the appropriate
+    // equation from trans1, and raise it to the appropriate power.  This is
+    // done via the multiplyDPoly2D().  The result is a polynomial (currPoly)
+    // and its coefficients are added into the myPT coeff matrix.
+    //
+    // XXX: This is horribly inefficient in that the trans1 polys are repeatedly
+    // multiplied against themselves.  This can easily be improved.
+    //
+
+    for (psS32 t2x = 0 ; t2x < trans2->x->nX ; t2x++) {
+        for (psS32 t2y = 0 ; t2y < trans2->x->nY ; t2y++) {
+            psDPolynomial2D *currPoly = psDPolynomial2DAlloc(1, 1, PS_POLYNOMIAL_ORD);
+
+            currPoly->coeff[0][0] = 1.0;
+            currPoly->mask[0][0] = 0;
+            psDPolynomial2D *newPoly = NULL;
+
+            if (trans2->x->mask[t2x][t2y] == 0) {
+                // Must raise trans1->y to the t2y-power.
+                for (psS32 c = 0 ; c < t2y; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->y);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Must raise trans1->x to the t2x-power.
+                for (psS32 c = 0 ; c < t2x; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->x);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Set the appropriate coeffs in myPT->x
+                for (psS32 i = 0 ; i < currPoly->nX ; i++) {
+                    for (psS32 j = 0 ; j < currPoly->nY ; j++) {
+                        myPT->x->coeff[i][j]+= currPoly->coeff[i][j] * trans2->x->coeff[t2x][t2y];
+                    }
+                }
+            }
+            psFree(currPoly);
+        }
+    }
+
+
+    for (psS32 t2x = 0 ; t2x < trans2->y->nX ; t2x++) {
+        for (psS32 t2y = 0 ; t2y < trans2->y->nY ; t2y++) {
+            psDPolynomial2D *currPoly = psDPolynomial2DAlloc(1, 1, PS_POLYNOMIAL_ORD);
+            currPoly->coeff[0][0] = 1.0;
+            currPoly->mask[0][0] = 0;
+            psDPolynomial2D *newPoly = NULL;
+
+            if (trans2->y->mask[t2x][t2y] == 0) {
+
+                // Must raise trans1->y to the t2y-power.
+                for (psS32 c = 0 ; c < t2y; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->y);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Must raise trans1->x to the t2x-power.
+                for (psS32 c = 0 ; c < t2x; c++) {
+                    newPoly = multiplyDPoly2D(currPoly, trans1->x);
+                    psFree(currPoly);
+                    currPoly = newPoly;
+                }
+
+                // Set the appropriate coeffs in myPT->x
+                for (psS32 i = 0 ; i < currPoly->nX ; i++) {
+                    for (psS32 j = 0 ; j < currPoly->nY ; j++) {
+                        myPT->y->coeff[i][j]+= currPoly->coeff[i][j] * trans2->y->coeff[t2x][t2y];
+                    }
+                }
+            }
+            psFree(currPoly);
+        }
+    }
+
+    //TRACE: printf("Exiting combine()\n");
+    return(myPT);
+}
+
+/*****************************************************************************
+psPlaneTransformFit(trans, source, dest, nRejIter, sigmaClip)
+ 
+XXX: What about nRejIter?  Iterations?
+XXX: Use static vectors for internal data.
+ *****************************************************************************/
+bool psPlaneTransformFit(psPlaneTransform *trans,
+                         const psArray *source,
+                         const psArray *dest,
+                         int nRejIter,
+                         float sigmaClip)
+{
+    PS_PTR_CHECK_NULL(trans, NULL);
+    PS_PTR_CHECK_NULL(source, NULL);
+    PS_PTR_CHECK_NULL(dest, NULL);
+
+    psS32 numCoords = PS_MIN(source->n, dest->n);
+    psS32 order = PS_MAX(trans->x->nX, trans->x->nY);
+
+    //
+    // Create fake polynomial to use in evaluation
+    //
+    psDPolynomial2D *fakePoly = psDPolynomial2DAlloc(order, order, PS_POLYNOMIAL_ORD);
+    for (int i = 0; i < order; i++) {
+        for (int j = 0; j < order; j++) {
+            fakePoly->coeff[i][j] = 1.0;
+            fakePoly->mask[i][j] = 1;       // Mask all coefficients; unmask to evaluate
+        }
+    }
+
+    //
+    // Initialize the matrix and vectors
+    //
+    psS32 nCoeff = order * (order + 1) / 2; // Number of polynomial coefficients
+    psImage *matrix = psImageAlloc(nCoeff, nCoeff, PS_TYPE_F64); // Matrix for solution
+    psVector *xVector = psVectorAlloc(nCoeff, PS_TYPE_F64); // Vector for solution in x
+    psVector *yVector = psVectorAlloc(nCoeff, PS_TYPE_F64); // Vector for solution in y
+    for (psS32 i = 0; i < nCoeff; i++) {
+        for (psS32 j = 0; j < nCoeff; j++) {
+            matrix->data.F64[i][j] = 0.0;
+        }
+        xVector->data.F64[i] = 0.0;
+        yVector->data.F64[i] = 0.0;
+    }
+
+    //
+    // Iterate over the grid points
+    //
+    for (psS32 g = 0; g < numCoords; g++) {
+        // Iterate over the polynomial coefficients, accumulating the matrix and vectors
+
+        for (psS32 i = 0, ijIndex = 0; i < order; i++) {
+            for (psS32 j = 0; j < order - i; j++, ijIndex++) {
+                fakePoly->mask[i][j] = 0;
+                psF64 xIn = ((psPlane *) source->data[g])->x;
+                psF64 yIn = ((psPlane *) source->data[g])->y;
+                psF64 xOut = ((psPlane *) dest->data[g])->x;
+                psF64 yOut = ((psPlane *) dest->data[g])->y;
+                psF64 ijPoly = psDPolynomial2DEval(fakePoly, xIn, yIn);
+                fakePoly->mask[i][j] = 1;
+
+                for (psS32 m = 0, mnIndex = 0; m < order; m++) {
+                    for (psS32 n = 0; n < order - m; n++, mnIndex++) {
+                        fakePoly->mask[m][n] = 0;
+                        psF64 mnPoly = psDPolynomial2DEval(fakePoly, xIn, yIn);
+                        fakePoly->mask[m][n] = 1;
+
+                        matrix->data.F64[ijIndex][mnIndex] += ijPoly * mnPoly;
+                    }
+                }
+
+                xVector->data.F64[ijIndex] += ijPoly * xOut;
+                yVector->data.F64[ijIndex] += ijPoly * yOut;
+            }
+        }
+    }
+
+    //
+    // Solution via LU Decomposition
+    //
+    psVector *permutation = psVectorAlloc(nCoeff, PS_TYPE_F64); // Permutation vector for LU Decomposition
+    psImage *luMatrix = psMatrixLUD(NULL, &permutation, matrix); // LU decomposed matrix
+    psVector *xSolution = psMatrixLUSolve(NULL, luMatrix, xVector, permutation); // Solution in x
+    psVector *ySolution = psMatrixLUSolve(NULL, luMatrix, yVector, permutation); // Solution in y
+
+    //
+    // XXX: Should check the output of the matrix routines and return false if bad.
+    //
+
+    //
+    // Stuff coefficients into transformation
+    //
+    for (psS32 i = 0, ijIndex = 0; i < order; i++) {
+        for (psS32 j = 0; j < order - i; j++, ijIndex++) {
+            trans->x->coeff[i][j] = xSolution->data.F64[ijIndex];
+            trans->y->coeff[i][j] = ySolution->data.F64[ijIndex];
+        }
+    }
+
+    psFree(fakePoly);
+    psFree(permutation);
+    psFree(luMatrix);
+    psFree(xSolution);
+    psFree(ySolution);
+    psFree(matrix);
+    psFree(xVector);
+    psFree(yVector);
+
+    return(true);
+}
+
+
+/*****************************************************************************
+psPlaneTransformInvert(out, in, region, nSamples)
+ 
+// XXX: Use static data structures.
+ *****************************************************************************/
+psPlaneTransform *psPlaneTransformInvert(psPlaneTransform *out,
+        const psPlaneTransform *in,
+        psRegion *region,
+        int nSamples)
+{
+    PS_PTR_CHECK_NULL(in, NULL);
+    //
+    // If the transform is linear, then invert it exactly and return.
+    //
+    if (p_psIsProjectionLinear((psPlaneTransform *) in)) {
+        return(p_psPlaneTransformLinearInvert((psPlaneTransform *) in));
+    }
+    PS_PTR_CHECK_NULL(region, NULL);
+    PS_INT_COMPARE(1, nSamples, NULL);
+
+    // Ensure that the input transformation is symmetrical.
+    if ((in->x->nX != in->x->nY) ||
+            (in->y->nX != in->y->nY) ||
+            (in->x->nX != in->y->nX)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Input transformation must have same nX==nY.");
+    }
+    psS32 order = PS_MAX(in->x->nX, in->x->nY);
+
+    psPlaneTransform *myPT = NULL;
+    psPlane *inCoord = psPlaneAlloc();
+    psPlane *outCoord = psPlaneAlloc();
+
+    //
+    // Allocate a new psPlaneTransform if "out" is NULL, or has the wrong size.
+    //
+    if (out == NULL) {
+        myPT = psPlaneTransformAlloc(order, order);
+    } else {
+        if ((out->x->nX == order) && (out->x->nY == order) &&
+                (out->y->nX == order) && (out->y->nY == order)) {
+            myPT = out;
+        } else {
+            psFree(out);
+            myPT = psPlaneTransformAlloc(order, order);
+        }
+    }
+
+    //
+    // Copy the input transform to myPT.
+    //
+    for (psS32 i = 0 ; i < in->x->nX ; i++) {
+        for (psS32 j = 0 ; j < in->x->nY ; j++) {
+            myPT->x->coeff[i][j] = in->x->coeff[i][j];
+        }
+    }
+    for (psS32 i = 0 ; i < in->y->nX ; i++) {
+        for (psS32 j = 0 ; j < in->y->nY ; j++) {
+            myPT->y->coeff[i][j] = in->y->coeff[i][j];
+        }
+    }
+
+    //
+    // Create a grid of xin,yin --> xout,yout
+    //
+    psArray *inData = psArrayAlloc(nSamples * nSamples);
+    psArray *outData = psArrayAlloc(nSamples * nSamples);
+    for (psS32 i = 0 ; i < inData->n; i++) {
+        inData->data[i] = (psPtr *) psPlaneAlloc();
+        outData->data[i] = (psPtr *) psPlaneAlloc();
+    }
+
+    //
+    // Initialize the grid.
+    //
+    psS32 cnt = 0;
+    for (int yint = 0; yint < nSamples; yint++) {
+        inCoord->y = region->y0 + ((psF32) yint) * ((region->y1 - region->y0) / ((psF32) nSamples));
+        for (int xint = 0; xint < nSamples; xint++) {
+            inCoord->x = region->x0 + ((psF32) xint) * ((region->x1 - region->x0) / ((psF32) nSamples));
+            (void)psPlaneTransformApply(outCoord, in, inCoord);
+
+            ((psPlane *) outData->data[cnt])->x = inCoord->x;
+            ((psPlane *) outData->data[cnt])->y = inCoord->y;
+            ((psPlane *) inData->data[cnt])->x = outCoord->x;
+            ((psPlane *) inData->data[cnt])->y = outCoord->y;
+
+            cnt++;
+        }
+    }
+    bool rc = psPlaneTransformFit(myPT, inData, outData, 10, 100.0);
+
+    psFree(inCoord);
+    psFree(outCoord);
+    psFree(inData);
+    psFree(outData);
+
+    if (rc == true) {
+        return(myPT);
+    }
+
+    // XXX: Generate an error message, or warning message.
+    return(NULL);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psCoord.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psCoord.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psCoord.h	(revision 22271)
@@ -0,0 +1,436 @@
+/** @file  psCoord.h
+*
+*  @brief Contains basic coordinate transformation definitions and operations
+*
+*  This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup CoordinateTransform
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.30 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-03-31 23:01:46 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_COORD_H
+#define PS_COORD_H
+
+#include "psType.h"
+#include "psImage.h"
+#include "psArray.h"
+#include "psList.h"
+#include "psFunctions.h"
+#include "psTime.h"
+
+/// @addtogroup CoordinateTransform
+/// @{
+
+/** Euclidiean Coordinate System.
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is linear coordinates which conform to
+ *  Euclidean geometry.
+ *
+ */
+typedef struct
+{
+    double x;                   ///< x position
+    double y;                   ///< y position
+    double xErr;                ///< Error in x position
+    double yErr;                ///< Error in y position
+}
+psPlane;
+
+/** Angular Coordinate System
+ *
+ *  Both detector and sky positions will be used extensively in the IPP. One
+ *  coordinate system to be used is angular coordinates for which additional
+ *  care must often be taken in comparison to a euclidiean coordinate system.
+ *
+ */
+typedef struct psSphere
+{
+    double r;                   ///< RA
+    double d;                   ///< Dec
+    double rErr;                ///< Error in RA
+    double dErr;                ///< Error in Dec
+}
+psSphere;
+
+/** 2D Polynomial Transform
+ *
+ *  A transform between coordinate systems that consists simply of two 2D
+ *  polynomials to transform both components - the output coordinates depend
+ *  only on the input coordinates and no other quantities of objects at those
+ *  coordinates.
+ *
+ */
+typedef struct
+{
+    psDPolynomial2D* x;         ///< 2D polynomial transform of X coordinates
+    psDPolynomial2D* y;         ///< 2D polynomial transform of Y coordinates
+}
+psPlaneTransform;
+
+/** 4D Polynomial Transform
+ *
+ *  A transform between coordinate systems that consists of two 4D polynomials
+ *  in which the output coordinates are also specified to be a function of the
+ *  magnitude and color of the object with the given coordinates. This type of
+ *  coordinate transformation is necessary to represent the (color-dependent)
+ *  optical distortions caused by the atmosphere and camera optics, and the
+ *  possibly effects of charge transfer inefficiency.
+ *
+ *  The lowest two terms are the x and y axis of the target system.  The higher
+ *  two terms may represent magnitude and color terms.
+ */
+typedef struct
+{
+    psDPolynomial4D* x;         ///< 4D polynomial transform of X coordinates
+    psDPolynomial4D* y;         ///< 4D polynomial transform of Y coordinates
+}
+psPlaneDistort;
+
+/** Spherical Transform Definition
+ *
+ *  We need to be able to convert between ICRS, Galactic and Ecliptic
+ *  coordinates, and potentially between arbitrary spherical coordinate
+ *  systems. All of these basic spherical transformations represent rotations
+ *  of the spherical coordinate reference. We specify a general
+ *  transformation function which takes a structure, psSphereTransform,
+ *  defining the transformation between two spherical coordinate systems
+ *
+ */
+typedef struct
+{
+    double alphaP;                    ///< Longitude of the target system pole in the source system
+    double cosDeltaP;                 ///< Cosine of target pole latitude in the source system
+    double sinDeltaP;                 ///< Sine of target pole latitude in the source system
+    double phiP;                      ///< Longitude of the ascending node in the target system
+}
+psSphereTransform;
+
+/** Projection type for projection/deprojection
+ *
+ *  @see psProject, psDeproject
+ *
+ */
+typedef enum {
+    PS_PROJ_TAN,                ///< Tangent projection
+    PS_PROJ_SIN,                ///< Sine projection
+    PS_PROJ_AIT,                ///< Aitoff projection
+    PS_PROJ_PAR,                ///< Par projection
+    //    PS_PROJ_GLS,                ///< GLS projection
+    //    PS_PROJ_CAR,                ///< CAR projection
+    //    PS_PROJ_MER,                ///< MER projection
+    PS_PROJ_NTYPE               ///< Number of types; must be last.
+} psProjectionType;
+
+/** Parameter set for projection/deprojection
+ *
+ *  @see psProject, psDeproject
+ *
+ */
+typedef struct
+{
+    double R;                   ///< Coordinates of projection center
+    double D;                   ///< Coordinates of projection center
+    double Xs;                  ///< plate-scale in X direction
+    double Ys;                  ///< plate-scale in Y direction
+    psProjectionType type;      ///< Projection type
+}
+psProjection;
+
+/** Mode for Offset calculation between two sky positions
+ *
+ *  @see  psSphereGetOffset, psSphereSetOffset
+ *
+ */
+typedef enum {
+    PS_SPHERICAL,               ///< offset corresponds to an angular offset
+    PS_LINEAR                   ///< offset corresponds to a linear offset
+} psSphereOffsetMode;
+
+/** The units of the offset
+ *
+ *  @see  psSphereGetOffset, psSphereSetOffset
+ *
+ */
+typedef enum {
+    PS_ARCSEC,                  ///< Arcseconds
+    PS_ARCMIN,                  ///< Arcminutes
+    PS_DEGREE,                  ///< Degrees
+    PS_RADIAN                   ///< Radians
+} psSphereOffsetUnit;
+
+/** Allocates a psPlane
+ *
+ *  @return psPlane*     resulting plane structure.
+ */
+
+psPlane* psPlaneAlloc(void);
+
+/** Allocates a psSphere
+ *
+ *  @return psSphere*     resulting sphere structure.
+ */
+
+psSphere* psSphereAlloc(void);
+
+
+/** Allocates a psPlaneTransform transform.
+ *
+ *  @return psPlaneTransform*     resulting plane transform
+ */
+
+psPlaneTransform* psPlaneTransformAlloc(
+    psS32 n1,  ///< The order of the x term in the transform.
+    psS32 n2   ///< The order of the y term in the transform.
+);
+
+/** Applies the psPlaneTransform transform to a specified coordinate
+ *
+ *  @return psPlane*     resulting coordinate based on transform
+ */
+psPlane* psPlaneTransformApply(
+    psPlane* out,                      ///< a psPlane to recycle.  If NULL, a new one is generated.
+    const psPlaneTransform* transform, ///< the transform to apply
+    const psPlane* coords              ///< the coordinate to apply the transform above.
+);
+
+/** Allocates a psPlaneDistort transform.
+ *
+ *  @return psPlaneDistort*     resulting plane distort transform
+ */
+
+psPlaneDistort* psPlaneDistortAlloc(
+    psS32 n1,  ///< The order of the w term in the transform.
+    psS32 n2,  ///< The order of the x term in the transform.
+    psS32 n3,  ///< The order of the y term in the transform.
+    psS32 n4   ///< The order of the z term in the transform.
+);
+
+
+/** Applies the psPlaneDistort transform to a specified coordinate
+ *
+ *  @return psPlane*     resulting coordinate based on transform
+ */
+psPlane* psPlaneDistortApply(
+    psPlane* out,                      ///< a psPlane to recycle.  If NULL, a new one is generated.
+    const psPlaneDistort* transform,   ///< the transform to apply
+    const psPlane* coords,             ///< the coordinate to apply the transform above.
+    float term3,                       ///< third term -- maybe magnitude
+    float term4                        ///< forth term -- maybe color
+);
+
+/** Allocator for psSphereTransform
+ *
+ *  @return psSphereTransform*         newly allocated struct
+ */
+
+psSphereTransform* psSphereTransformAlloc(
+    double alphaP,                      ///< north pole latitude
+    double deltaP,                      ///< north pole longitude?
+    double phiP                         ///< defines the longitude in the input system of the equatorial intersection between the two systems (e.g, the first point of Ares).
+);
+
+/** Applies the psSphereTransform transform for a specified coordinate
+ *
+ *  @return psSphere*      resulting coordinate based on transform
+ */
+psSphere* psSphereTransformApply(
+    psSphere* out,                     ///< a psSphere to recycle.  If NULL, a new one is generated.
+    const psSphereTransform* transform,///< the transform to apply
+    const psSphere* coord              ///< the coordinate to apply the transform above.x
+);
+
+/** Creates the appropriate transform for converting from ICRS to Ecliptic
+ *  coordinate systems.
+ *
+ *  @return psSphereTransform*     transform for ICRS->Ecliptic coordinate systems
+ */
+psSphereTransform* psSphereTransformICRSToEcliptic(
+    psTime *time                        ///< the time for which the resulting transform will be valid
+);
+
+/** Creates the appropriate transform for converting from Ecliptic to ICRS
+ *  coordinate systems.
+ *
+ *  @return psSphereTransform*     transform for Ecliptic->ICRS coordinate systems
+ */
+psSphereTransform* psSphereTransformEclipticToICRS(
+    psTime *time                        ///< the time for which the resulting transform will be valid
+);
+
+/** Creates the appropriate transform for converting from ICRS to Galactic
+ *  coordinate systems.
+ *
+ */
+psSphereTransform* psSphereTransformICRSToGalactic(void);
+
+/** Creates the appropriate transform for converting from Galactic to ICRS
+ *  coordinate systems.
+ *
+ */
+psSphereTransform* psSphereTransformGalacticToICRS(void);
+
+/** Allocates memory for a psProjection structure
+ *
+ *  @return psProjection*    psProjection structure
+ */
+psProjection* psProjectionAlloc(
+    psF64 R,                   ///< Right-ascension of projection center.
+    psF64 D,                   ///< Declination of projection center.
+    psF64 Xs,                  ///< Scale in x-dimension
+    psF64 Ys,                  ///< Scale in y-dimension
+    psProjectionType type
+);
+
+/** Projects a spherical coordinate to a linear coordinate system
+ *
+ *  @return psPlane*    projected coordinate
+ */
+psPlane* psProject(
+    const psSphere* coord,             ///< coordinate to project
+    const psProjection* projection     ///< parameters of the projection
+);
+
+/** Reverse projection of a linear coordinate to a spherical coordinate system
+ *
+ *  @return psPlane*    projected coordinate
+ */
+psSphere* psDeproject(
+    const psPlane* coord,              ///< coordinate to project
+    const psProjection* projection     ///< parameters of the projection
+);
+
+/** Determines the offset (RA,Dec) on the sky between two positions.
+ *
+ *  Both an offset mode and an offset unit may be defined. The mode may be
+ *  either PS_SPHERICAL, in which case the specified offset corresponds to an
+ *  offset in angles, or it may be PS_LINEAR, in which case the offset
+ *  corresponds to a linear offset in a local projection. The offset unit may
+ *  be in one of PS_ARCSEC, PS_ARCMIN, PS_DEGREE, and PS_RADIAN, which
+ *  specifies the units of the offset only.
+ *
+ *  @return psSphere*    the offset between position1 and position2
+ */
+psSphere* psSphereGetOffset(
+    const psSphere* position1,
+    const psSphere* position2,
+    psSphereOffsetMode mode,
+    psSphereOffsetUnit unit
+);
+
+/** Applies the given offset to a coordinate.
+ *
+ *  Both an offset mode and an offset unit may be defined. The mode may be
+ *  either PS_SPHERICAL, in which case the specified offset corresponds to an
+ *  offset in angles, or it may be PS_LINEAR, in which case the offset
+ *  corresponds to a linear offset in a local projection. The offset unit may
+ *  be in one of PS_ARCSEC, PS_ARCMIN, PS_DEGREE, and PS_RADIAN, which
+ *  specifies the units of the offset only.
+ *
+ *  @return psSphere*    the given position with the given offset applied.
+ */
+psSphere* psSphereSetOffset(
+    const psSphere* position,
+    const psSphere* offset,
+    psSphereOffsetMode mode,
+    psSphereOffsetUnit unit
+);
+
+/** Generates the complete spherical rotation to account for precession
+ *  between two times.  The equinoxes shall be Julian equinoxes.
+ *
+ *  @return psSphere* the resulting spherical rotation
+ */
+psSphere* psSpherePrecess(
+    psSphere *coords,                  ///< coordinates (modified in-place)
+    const psTime *fromTime,            ///< equinox of coords input
+    const psTime *toTime               ///< equinox of coords output
+);
+
+// XXX: Doxygenate.
+psPlaneTransform *p_psPlaneTransformLinearInvert(
+    psPlaneTransform *transform
+);
+
+// XXX: Doxygenate
+psS32 p_psIsProjectionLinear(
+    psPlaneTransform *transform
+);
+
+/** inverts a given transformation.
+ *
+ *  It may assume that the input transformation is one-to-one, and that the
+ *  inverse transformation may be specified through using polynomials of the
+ *  same type and order as the forward transformation. In the event that the
+ *  input transformation is linear, an exact solution may be calculated;
+ *  otherwise nSamples samples in each axis, covering the region specified by
+ *  region shall be used as a grid to fit the best inverse transformation. The
+ *  function shall return NULL if it was unable to generate the inverse
+ *  transformation; otherwise it shall return the inverse transformation. In
+ *  the event that out is NULL, a new psPlaneTransform shall be allocated and
+ *  returned.
+ *
+ *  @return psPlaneTransform*  the resulting inverted transform
+ */
+psPlaneTransform* psPlaneTransformInvert(
+    psPlaneTransform *out,             ///< a transform to recycle, or NULL if one is to be created.
+    const psPlaneTransform *in,        ///< transform to invert
+    psRegion *region,                  ///< region to fit for non-linear transform inversion
+    int nSamples                       ///< number of samples in each axis for fit
+);
+
+/** Creates a single transformation that has the effect of performing trans1
+ *  followed by trans2.
+ *
+ *  psPlaneTransformCombine takes two transformations (trans1 and trans2) and
+ *  returns a single transformation that has the effect of performing trans1
+ *  followed by trans2. In the event that the input transformation is linear,
+ *  an exact solution may be calculated; otherwise nSamples samples in each
+ *  axis, covering the region specified by region shall be used as a grid to
+ *  fit the best inverse transformation. The function shall return NULL if it
+ *  was unable to generate the transformation; otherwise it shall return the
+ *  transformation.
+ *
+ *  @return psPlaneTransform*    resulting transformation
+ */
+psPlaneTransform* psPlaneTransformCombine(
+    psPlaneTransform *out,             ///< a transform to recycle, or NULL if one is to be created.
+    const psPlaneTransform *trans1,    ///< first transform to combine
+    const psPlaneTransform *trans2     ///< first transform to combine
+);
+
+
+/** takes two arrays containing matched coordinates and returns the
+ *  best-fitting transformation.
+ *
+ *  psPlaneTransformFit takes two arrays containing matched coordinates (i.e.,
+ *  coordinates in the source array correspond to the coordinates in the dest
+ *  array) and returns the best-fitting transformation. The source and dest
+ *  will contain psCoords. In the event that the number of coordinates in each
+ *  is not identical, the function shall generate a warning, and extra
+ *  coordinates in the longer of the two shall be ignored. The trans transform
+ *  may not be NULL, since it specifies the desired order, polynomial type and
+ *  any polynomial terms to mask. nRejIter rejection iterations shall be
+ *  performed, wherein coordinates lying more than sigmaClip standard
+ *  deviations from the fit shall be rejected.
+ *
+ *  @return bool        TRUE if successful, otherwise FALSE.
+ */
+bool psPlaneTransformFit(
+    psPlaneTransform *trans,
+    const psArray *source,
+    const psArray *dest,
+    int nRejIter,
+    float sigmaClip
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psPhotometry.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psPhotometry.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psPhotometry.h	(revision 22271)
@@ -0,0 +1,75 @@
+
+/** @file  psPhotometry.h
+*
+*  @brief Contains basic photometric structures.
+*
+*  This file defines the basic photometric structures.
+*
+*  @ingroup Photometry
+*
+*  @author George Gusciora, MHPCC
+*
+*  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:23 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_PHOTOMETRIC_H
+#define PS_PHOTOMETRIC_H
+
+#include "psType.h"
+#include "psImage.h"
+#include "psArray.h"
+#include "psList.h"
+#include "psFunctions.h"
+
+/// @addtogroup Photometry
+/// @{
+
+/** The photometric system description
+ *
+ *  The photometric system is defined by the psPhotSystem structure. A 
+ *  photometric system is identified by a human-readable name (ie, SDSS.g, 
+ *  Landolt92.B, GPC1.OTA32.r). Each photometric system is given a unique 
+ *  identifier ID. Observations taken with a specific camera, detector, and 
+ *  filter represent their own photometric system, and it may be necessary to 
+ *  perform transformations between these systems. Photometric systems 
+ *  associated with observations from a specific camera/ detector/filter 
+ *  combination can be associated with those components.
+ *
+ */
+
+typedef struct
+{
+    const psS32 ID;                    ///< ID number for this photometric system
+    const char *name;                  ///< Name of photometric system
+    const char *camera;                ///< Camera for photometric system
+    const char *filter;                ///< Filter used for photometric system
+    const char *detector;              ///< Detector used for photometric system
+}
+psPhotSystem;
+
+/** Photometric system transformation
+ *
+ *  This structure defines the transformation between two photometric systems.
+ *
+ */
+
+typedef struct
+{
+    const psPhotSystem src;            ///< Source photometric system
+    const psPhotSystem dst;            ///< Destination photometric system
+    const psPhotSystem pP;             ///< Primary color reference
+    const psPhotSystem pM;             ///< Primary color reference
+    const psPhotSystem sP;             ///< Secondary color reference
+    const psPhotSystem sM;             ///< Secondary color reference
+    float pA;                          ///< Color offset for references
+    float sA;                          ///< Color offset for references
+    psPolynomial3D transform;          ///< Transformation from source to destination
+}
+psPhotTransform;
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psTime.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psTime.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psTime.c	(revision 22271)
@@ -0,0 +1,1178 @@
+/** @file  psTime.c
+ *
+ *  @brief Definitions for time, time utilities, and conversion functions for use with psLib astronomy
+ *  functions.
+ *
+ *  A collection of functions are required by psLib to manipulate time data. These functions primarily consist
+ *  of conversions between specific time formats.  They use the UNIX timeval time system as the
+ *  base upon which International Atomic Time (TAI) and Universal Time Coordinated (UTC) are calculated.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.60 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:15 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "psTime.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psAbort.h"
+#include "psImage.h"
+#include "psCoord.h"
+#include "psString.h"
+#include "psMetadata.h"
+#include "psMetadataIO.h"
+#include "psLookupTable.h"
+#include "psConstants.h"
+#include "psAstronomyErrors.h"
+
+#include "config.h"
+
+#define MAX_STRING_LENGTH 256
+
+/** Sidereal angular conversion from seconds to radians for GMST in seconds (i.e. pi/(180*240)) */
+#define S2R (7.272205216643039903848711535369e-5)
+
+/** Two times pi with double precision accuracy */
+#define TWOPI (2.0*M_PI)
+
+/** Conversion from radians to degrees */
+#define R2DEG = (180.0/M_PI)
+
+                /** Maximum length of time string */
+                #define MAX_TIME_STRING_LENGTH 256
+
+                /** Seconds per minute */
+                #define  SEC_PER_MINUTE 60.0
+
+                /** Seconds per hour */
+                #define  SEC_PER_HOUR (60.0*SEC_PER_MINUTE)
+
+                /** Seconds per day */
+                #define  SEC_PER_DAY (24.0*SEC_PER_HOUR)
+
+                /** Seconds per year */
+                #define  SEC_PER_YEAR (365.0*SEC_PER_DAY)
+
+                /** Microseconds per day */
+                #define NSEC_PER_DAY 86400000000000.0
+
+                /** Time metadata read from config file */
+                static psMetadata *timeMetadata = NULL;
+
+/** Static function prototypes */
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+psF64 searchTables(psF64 index, psU64 column, psLookupStatusType *status, char *metadataTableNames[], psU32 nTables);
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+static char *cleanString(char *inString, int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = PS_PARSE_ERROR_GENERAL;
+    }
+
+    return cleanToken;
+}
+
+// get the psTime.config filename by checking environment variable first, then original installation area.
+char* p_psGetConfigFileName()
+{
+    char* filename = getenv("PS_CONFIG_FILE");
+
+    if (filename == NULL) { // environment variable not found
+        filename = PS_CONFIG_FILE_DEFAULT; // this should come from configure.ac
+    }
+
+    return filename;
+}
+
+
+/** Searches time tables in priority order and performs interpolation if input index value is within a table.
+ * If the index value is out of range, the status is set accordingly. */
+psF64 searchTables(psF64 index, psU64 column, psLookupStatusType *status, char *metadataTableNames[], psU32 nTables)
+{
+    psU32 i = 0;
+    char *tableName = NULL;
+    psF64 result = 0.0;
+    psLookupTable *table = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+
+
+
+    // Check time metadata. Function call reports errors.
+    if(timeMetadata == NULL) {
+        if(!p_psTimeInit(p_psGetConfigFileName()))
+            return 0.0;
+    }
+
+    // Search each table in priority order: ser7, eopc,finals
+    for(i=0; i<nTables; i++) {
+
+        // Get table from metadata
+        tableName = metadataTableNames[i];
+        tableMetadataItem = psMetadataLookup(timeMetadata, tableName);
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                    tableName);
+            return 0.0;
+        }
+        table = (psLookupTable*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(table,0.0);
+
+        // Attempt to interpolate table
+        *status = PS_LOOKUP_SUCCESS;
+        result = psLookupTableInterpolate(table, index, 2, status);
+        if(*status == PS_LOOKUP_SUCCESS) {
+            return result;
+        } else if(*status == PS_LOOKUP_ERROR) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED_NAME,
+                    tableName);
+            return 0.0;
+        }
+    }
+
+    return result;
+}
+
+bool p_psTimeInit(const char *fileName)
+{
+    bool foundTable = false;
+    char *tableDir = NULL;
+    char *tableNames = NULL;
+    char *namesPtr = NULL;
+    char *metadataNamesPtr = NULL;
+    char *tableName = NULL;
+    char *fullTableName = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 numTables = 0;
+    psU32 nFail = 0;
+    psVector *tablesFrom = NULL;
+    psVector *tablesTo = NULL;
+    psMetadataItem *metadataItem = NULL;
+    psLookupTable *table = NULL;
+    psParseErrorType status = PS_PARSE_SUCCESS;
+    char metadataTableNames[4][MAX_STRING_LENGTH] = {"ser7", "eopc",  "finals", "tai"};
+
+    // Read time config file
+    timeMetadata = psMetadataParseConfig(timeMetadata, &nFail, fileName, true);
+    if(timeMetadata == NULL) {
+        return false;
+    } else if(nFail != 0) {
+        return false;
+    }
+
+    // Get number of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.n");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.n");
+        return false;
+    }
+    numTables = (psS32)metadataItem->data.S32;
+
+    // Get lower range of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.from");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.from");
+        return false;
+    }
+    tablesFrom = psVectorCopy(tablesFrom, metadataItem->data.V, PS_TYPE_F64);
+    if(tablesFrom->n != numTables) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_VECTOR, tablesFrom->n, numTables);
+        psFree(tablesFrom);
+        return false;
+    }
+
+    // Get upper range of tables
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.to");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.to");
+        return false;
+    }
+    tablesTo = psVectorCopy(tablesTo, metadataItem->data.V, PS_TYPE_F64);
+    if(tablesTo->n != numTables) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_VECTOR, tablesTo->n, numTables);
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        return false;
+    }
+
+    // Get path to time data files
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.dir");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.dir");
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        return false;
+    }
+    tableDir = psStringCopy(metadataItem->data.V);
+
+    // Table file names
+    metadataItem = psMetadataLookup(timeMetadata, "psLib.time.tables.files");
+    if(metadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                "psLib.time.tables.files");
+
+        psFree(tablesFrom);
+        psFree(tablesTo);
+        psFree(tableDir);
+        return false;
+    }
+    tableNames = psStringCopy(metadataItem->data.V);
+
+    // Read time tables
+    namesPtr = tableNames;
+    while((tableName=getToken(&namesPtr, " ", &status)) != NULL) {
+
+        // Form path with table name, adding one to length for last '/' that may not occur in string in cong file
+        fullTableName = (char*)psAlloc(strlen(tableDir)+strlen(tableName)+1+1);
+
+        // Old strings may come back from psAlloc(), so set initial position to EOL
+        fullTableName[0]='\0';
+        strcat(fullTableName, tableDir);
+        strcat(fullTableName, "/");
+        strcat(fullTableName, tableName);
+
+        // Create and read table
+        if(i < numTables) {
+            table = psLookupTableAlloc(fullTableName, tablesFrom->data.F64[i], tablesTo->data.F64[i]);
+            table = psLookupTableRead(table);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_TABLE_COUNT, i+1, numTables);
+        }
+
+        // Place tables into metadata slightly altered names as keys to create consistent naming conventions
+        foundTable = false;
+        for(j=0; j<numTables; j++) {
+            metadataNamesPtr = strstr(tableName, metadataTableNames[j]);
+            if(metadataNamesPtr != NULL) {
+                psMetadataAdd(timeMetadata, PS_LIST_TAIL, strcat(metadataTableNames[j], "Table"),
+                              PS_META_LOOKUPTABLE, NULL, table);
+                foundTable = true;
+            } else if(foundTable==false && j==numTables-1) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_TABLE_COUNT, j, numTables);
+            }
+        }
+
+        psFree(fullTableName);
+        psFree(tableName);
+        psFree(table);
+        i++;
+    }
+
+    if(numTables != i) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_BAD_TABLE_COUNT, i, numTables);
+    }
+
+    psFree(tableDir);
+    psFree(tableNames);
+    psFree(tablesFrom);
+    psFree(tablesTo);
+
+    return true;
+}
+
+bool p_psTimeFinalize(void)
+{
+    if(timeMetadata != NULL) {
+        psFree(timeMetadata);
+        timeMetadata = NULL;
+    }
+
+    return true;
+}
+
+psTime* psTimeAlloc(psTimeType type)
+{
+    psTime *outTime = NULL;
+
+
+    // Error checks
+    if(type!=PS_TIME_TAI && type!=PS_TIME_UTC) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psTime_TYPE_UNKNOWN,
+                type);
+        return NULL;
+    }
+
+    outTime = (psTime*)psAlloc(sizeof(psTime));
+
+    outTime->sec = 0;
+    outTime->nsec = 0;
+    outTime->type = type;
+    outTime->leapsecond = false;
+
+    return outTime;
+}
+
+psTime* psTimeGetNow(psTimeType type)
+{
+    struct timeval now;
+    psTime *time = NULL;
+
+
+    // Allocate psTime struct
+    time = psTimeAlloc(type);
+
+    if (gettimeofday(&now, (struct timezone *)0) == -1) {
+        psError(PS_ERR_OS_CALL_FAILED, true,
+                PS_ERRORTEXT_psTime_GET_TOD_FAILED);
+        return NULL;
+    }
+
+    // Convert timeval time to psTime
+    time->sec = now.tv_sec;
+    time->nsec = now.tv_usec*1000;
+
+    // Add most leapseconds to UTC time to get TAI time if necessary
+    if(type == PS_TIME_TAI) {
+        time->sec += psTimeGetTAIDelta(time);
+    }
+
+    return time;
+}
+
+psTime* psTimeConvert(psTime *time, psTimeType type)
+{
+    double delta = 0.0;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    if (time->type == type) { // time already right type.  That was easy!
+        return time;
+    }
+
+    delta = psTimeGetTAIDelta(time);
+
+    if (type == PS_TIME_UTC) {
+        time->sec = time->sec - delta;    // User wants UTC time. Subtract leapseconds.
+    } else if(type == PS_TIME_TAI) {
+        time->sec = time->sec + delta;    // User wants TAI time. Add leapseconds.
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TYPE_UNKNOWN, type);
+    }
+
+    return time;
+}
+
+double psTimeToLMST(psTime *time, double longitude)
+{
+    psF64  jdTdtDays    =  0.0;
+    psF64  jdUt1Days    =  0.0;
+    psF64  mjdUt1Days   =  0.0;
+    psF64  lmstRad      =  0.0;
+    psF64  fracDays     =  0.0;
+    psF64  gmstRad      =  0.0;
+    psF64  t            =  0.0;
+    psF64  tu           =  0.0;
+    psF64  ut1UtcDbl    =  0.0;
+    psF64  const1       =  24110.5493771;
+    psF64  const2       =  8639877.3173760;
+    psF64  const3       =  307.4771600;
+    psF64  const4       =  0.0931118;
+    psF64  const5       = -0.0000062;
+    psF64  const6       =  0.0000013;
+    psTime *ut1UtcDelta = NULL;
+    psTime *tdtTime     = NULL;
+    psTime *taiTime     = NULL;
+    psTime *utcTime     = NULL;
+    psTime *ut1Time     = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Calculate TAI or UTC time based on type of time user passes
+    if(time->type == PS_TIME_TAI) {
+        taiTime = psMemIncrRefCounter(time);
+        utcTime = psTimeAlloc(PS_TIME_UTC);
+        utcTime->sec = taiTime->sec - psTimeGetTAIDelta(time);
+        utcTime->nsec = taiTime->nsec;
+    } else if(time->type == PS_TIME_UTC) {
+        utcTime = psMemIncrRefCounter(time);
+        taiTime = psTimeAlloc(PS_TIME_TAI);
+        taiTime->sec = utcTime->sec + psTimeGetTAIDelta(time);
+        taiTime->nsec = utcTime->nsec;
+    }
+
+    // Convert Universal Time (UTC) to  UT1
+    ut1UtcDbl = psTimeGetUT1Delta(taiTime);
+    utcTime->type = PS_TIME_TAI; // Don't allow addition of leapseconds to ut1Time
+    ut1Time = psTimeMath(utcTime, ut1UtcDbl);
+    utcTime->type = PS_TIME_UTC;
+
+
+    // Calculate UT1 as Julian Centuries since J2000.0
+    jdUt1Days = psTimeToJD(ut1Time);
+    mjdUt1Days = psTimeToMJD(ut1Time);
+    t = (jdUt1Days - 2451545.0)/36525.0;
+
+    // Calculate Terrestial Dynamical Time (TDT)
+    tdtTime = psTimeMath(taiTime, 32.184);
+
+    // Calculate TDT as Julian centuries since J2000.0
+    jdTdtDays = psTimeToJD(tdtTime);
+    tu = (jdTdtDays - 2451545.0)/36525.0;
+
+    // Calculate fractional part of MJD
+    fracDays = fmod(mjdUt1Days, 1.0);
+
+    // Calculate Greenwich Mean Sidereal Time (GMST) in radians. Equation set up to minimize multiplications.
+    gmstRad = fracDays*TWOPI+(const1+const2*tu+t*(const3+t*(const4+t*(const5+const6*t))))*S2R;
+
+    // Place GMST between 0 and 2*pi
+    gmstRad = fmod(gmstRad, TWOPI);
+
+    // Calculate Local Mean Sidereal Time (LMST) in radians
+    lmstRad = gmstRad + longitude;
+
+    // Free temporary structs
+    psFree(ut1UtcDelta);
+    psFree(ut1Time);
+    psFree(tdtTime);
+    psFree(utcTime);
+    psFree(taiTime);
+
+    return lmstRad;
+}
+
+double psTimeGetUT1Delta(const psTime *time)
+{
+    psU32 nTables = 3;
+    psF64 mjd = 0.0;
+    psF64 result = 0.0;
+    psF64 dut2ut1 = 0.0;
+    psF64 t = 0.0;
+    psVector *dut = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+    psLookupStatusType status = PS_LOOKUP_SUCCESS;
+    char *metadataTableNames[3] = {"ser7Table", "eopcTable",  "finalsTable"};
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    if(time->type != PS_TIME_TAI) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TYPE_INCORRECT, time->type);
+        return 0.0;
+    }
+
+    // Attempt to find value through table lookup and interpolation
+    mjd = psTimeToMJD(time);
+    result = searchTables(mjd, 0, &status, metadataTableNames, nTables);
+
+    // Value could not be found through table lookup and interpolation
+    if(status == PS_LOOKUP_PAST_TOP) {
+
+        // Date too early for tables. Get default time delta value from metadata, and issue warning.
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_PREDATES_TABLES, mjd, "UT1-UTC");
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.dut");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.before.dut");
+            return 0.0;
+        }
+        result = tableMetadataItem->data.F64;
+
+    } else if(status == PS_LOOKUP_PAST_BOTTOM) {
+        /* Date too late for tables. Issue warning and use following formulae for predicting
+           ahead of the most recent available table entry.
+             ut1-utc = [0] + [1]*(MJD - [2]) - (ut2-ut1)
+             [0, 1, 2] = @psLib.time.predict.dut
+             ut2-ut1 = 0.022 sin(2*pi*t) - 0.012 cos(2*pi*t) - 0.006 sin(4*pi*t) + 0.007 cos(4*pi*t)
+             t = 2000.0 + (MJD - 51544.03)/365.2422
+        */
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_POSTDATES_TABLES, mjd, "UT1-UTC");
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.dut");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.predict.dut");
+            return 0.0;
+        }
+        dut = (psVector*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(dut,0.0);
+
+        t = 2000.0 + (mjd - 51544.03)/365.2422;
+        dut2ut1 = 0.022*sin(TWOPI*t) - 0.012*cos(TWOPI*t) - 0.006*sin(4.0*M_PI*t) + 0.007*cos(4.0*M_PI*t);
+        result = dut->data.F64[0] + dut->data.F64[1]*(mjd - dut->data.F64[2]) - dut2ut1;
+
+    } else if(status != PS_LOOKUP_SUCCESS) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED);
+        return 0.0;
+    }
+
+    return result;
+}
+
+struct psSphere* psTimeGetPoleCoords(const psTime* time)
+{
+    psU32 nTables = 3;
+    psF64 x = 0.0;
+    psF64 y = 0.0;
+    psF64 mjd = 0.0;
+    psF64 a = 0.0;
+    psF64 c = 0.0;
+    psF64 mjdPred = 0.0;
+    struct psSphere* output = NULL;
+    psLookupStatusType xStatus = PS_LOOKUP_SUCCESS;
+    psLookupStatusType yStatus = PS_LOOKUP_SUCCESS;
+    psMetadataItem *tableMetadataItem = NULL;
+    char *metadataTableNames[3] = {"ser7Table", "eopcTable",  "finalsTable"};
+    psVector *xp = NULL;
+    psVector *yp = NULL;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    if(time->type != PS_TIME_TAI)
+    {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TYPE_INCORRECT, time->type);
+        return NULL;
+    }
+
+    // Attempt to find value through table lookup and interpolation
+    mjd = psTimeToMJD(time);
+    x = searchTables(mjd, 0, &xStatus, metadataTableNames, nTables);
+    y = searchTables(mjd, 0, &yStatus, metadataTableNames, nTables);
+
+    // Value could not be found through table lookup and interpolation
+    if(xStatus==PS_LOOKUP_PAST_TOP && yStatus==PS_LOOKUP_PAST_TOP)
+    {
+
+        // Date too earlier for tables. Get default polar coodinate values from metadata, and issue warning.
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_PREDATES_TABLES, mjd, "polar motion");
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.xp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.before.xp");
+            return NULL;
+        }
+        x = tableMetadataItem->data.F64;
+
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.before.yp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.before.yp");
+            return NULL;
+        }
+        y = tableMetadataItem->data.F64;
+
+    } else if(xStatus==PS_LOOKUP_PAST_BOTTOM && yStatus==PS_LOOKUP_PAST_BOTTOM)
+    {
+
+        /* Date too late for tables. Issue warning and use following formulae for predicting
+           ahead of the most recent available table entry.
+              x = [0] + [1]*cos a + [2]*sin a + [3]*cos c + [4]*sin c
+              [0], [1], [2], [3] = @psLib.time.predict.xp
+              y = [0] + [1]*cos a + [2]*sin a + [3]*cos c + [4]*sin c
+              [0], [1], [2], [3] = @psLib.time.predict.yp
+              a = 2*pi*(mjd - pslib.time.predict.mjd)/365.25
+              c = 2*pi*(mjd - pslib.time.predict.mjd)/435.0
+        */
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_TIME_POSTDATES_TABLES, mjd, "polar motion");
+
+
+        // Get predicted MJD
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.mjd");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED,
+                    "psLib.time.predict.mjd");
+            return NULL;
+        }
+        mjdPred = tableMetadataItem->data.F64;
+
+        // Get xp
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.xp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.predict.xp");
+            return NULL;
+        }
+        xp = (psVector*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(xp,NULL);
+
+        // Get yp
+        tableMetadataItem = psMetadataLookup(timeMetadata, "psLib.time.predict.yp");
+        if(tableMetadataItem == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "psLib.time.predict.yp");
+            return NULL;
+        }
+        yp = (psVector*)tableMetadataItem->data.V;
+        PS_PTR_CHECK_NULL(yp,NULL);
+
+        // Calculate "a" and "c" constants
+        a = TWOPI*(mjd - mjdPred)/365.25;
+        c = TWOPI*(mjd - mjdPred)/435.0;
+
+        // Calculate x and y polar coordinates
+        x = xp->data.F64[0] +
+            xp->data.F64[1]*cos(a) +
+            xp->data.F64[2]*sin(a) +
+            xp->data.F64[3]*cos(c) +
+            xp->data.F64[4]*sin(c);
+
+        y = yp->data.F64[0] +
+            yp->data.F64[1]*cos(a) +
+            yp->data.F64[2]*sin(a) +
+            yp->data.F64[3]*cos(c) +
+            yp->data.F64[4]*sin(c);
+
+    } else if(xStatus!=PS_LOOKUP_SUCCESS || yStatus!=PS_LOOKUP_SUCCESS)
+    {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED);
+        return NULL;
+    }
+
+    // Create output sphere and convert arcsec to radians (i.e. x/60/60*M_PI/180)
+    output = psAlloc(sizeof(psSphere));
+    output->r = x * M_PI / 648000.0;
+    output->d = y * M_PI / 648000.0;
+
+    return output;
+}
+
+double psTimeGetTAIDelta(const psTime *time)
+{
+    psU64 i = 0;
+    psF64 jd = 0.0;
+    psF64 mjd = 0.0;
+    psF64 out = 0.0;
+    psF64 const1 = 0.0;
+    psF64 const2 = 0.0;
+    psF64 const3 = 0.0;
+    psLookupTable* table = NULL;
+    psMetadataItem *tableMetadataItem = NULL;
+    psVector *stats = NULL;
+    psVector *results = NULL;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Check time metadata
+    if(timeMetadata == NULL) {
+        if(!p_psTimeInit(p_psGetConfigFileName())) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_FILE_NOT_FOUND, "psTime.config");
+            return 0.0;
+        }
+    }
+
+    // Get table from metadata
+    tableMetadataItem = psMetadataLookup(timeMetadata, "taiTable");
+    if(tableMetadataItem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_LOOKUP_METADATA_FAILED, "taiTable");
+        return 0.0;
+    }
+    table = (psLookupTable*)tableMetadataItem->data.V;
+    PS_PTR_CHECK_NULL(table,0);
+
+    // Determine Julian and modified Julian dates used in table lookup and time delta calculation
+    jd = psTimeToJD(time);
+    mjd = psTimeToMJD(time);
+
+    stats = psVectorAlloc(table->numCols, PS_TYPE_U32);
+    results = psLookupTableInterpolateAll(table, jd, stats);
+
+    // Check for successful interpolation
+    for(i=0; i<stats->n; i++) {
+        if(stats->data.U32[i] == PS_LOOKUP_ERROR) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_INTERPOLATION_FAILED);
+        }
+    }
+
+    const1 = results->data.F64[0];
+    const2 = results->data.F64[1];
+    const3 = results->data.F64[2];
+    out = const1 + (mjd - const2) * const3;
+
+    psFree(stats);
+    psFree(results);
+
+    return out;
+}
+
+
+psS64 psTimeLeapSecondDelta(const psTime *time1, const psTime *time2)
+{
+    psS64 diff = 0;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time1,0);
+    PS_PTR_CHECK_NULL(time2,0);
+    PS_INT_CHECK_RANGE(time1->nsec,0,1e9-1,0);
+    PS_INT_CHECK_RANGE(time2->nsec,0,1e9-1,0);
+    diff = abs((psS64)psTimeGetTAIDelta((psTime*)time1)-(psS64)psTimeGetTAIDelta((psTime*)time2));
+
+    return diff;
+}
+
+double psTimeToJD(const psTime *time)
+{
+    double jd = 0.0;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Julian date conversion
+    if(time->sec < 0) {
+        jd = time->sec / SEC_PER_DAY - time->nsec / NSEC_PER_DAY + 2440587.5; // psTime earlier than epoch
+    } else {
+        jd = time->sec / SEC_PER_DAY + time->nsec / NSEC_PER_DAY + 2440587.5; // psTime greater than epoch
+    }
+
+    return jd;
+}
+
+double psTimeToMJD(const psTime *time)
+{
+    double mjd = 0.0;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NAN);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NAN);
+
+    // Modified Julian date conversion
+    if(time->sec < 0) {
+        mjd = time->sec / SEC_PER_DAY - time->nsec / NSEC_PER_DAY + 40587.0; // psTime earlier than epoch
+    } else {
+        mjd = time->sec / SEC_PER_DAY + time->nsec / NSEC_PER_DAY + 40587.0; // psTime greater than epoch
+    }
+
+    return mjd;
+}
+
+char* psTimeToISO(const psTime *time)
+{
+    psS32 ms = 0;
+    char *timeString = NULL;
+    char *tempString = NULL;
+    struct tm *tmTime = NULL;
+    time_t sec;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    tempString = psAlloc(MAX_TIME_STRING_LENGTH);
+    timeString = psAlloc(MAX_TIME_STRING_LENGTH);
+
+    ms = time->nsec / 1000000;
+    sec = time->sec;
+
+    // tmTime variable is statically allocated, no need to free
+    tmTime = gmtime(&sec);
+
+    // Converts psTime to YYYY-MM-DDThh:mm:ss.sss in string form
+    if (!strftime(tempString, MAX_TIME_STRING_LENGTH, "%Y-%m-%dT%H:%M:%S", tmTime)) {
+        psError(PS_ERR_OS_CALL_FAILED, true, PS_ERRORTEXT_psTime_CONVERT_TIME_TO_STRING_FAILED);
+    }
+
+    if (snprintf(timeString, MAX_TIME_STRING_LENGTH, "%s.%3.3dZ", tempString, ms) < 0) {
+        psError(PS_ERR_OS_CALL_FAILED, true, PS_ERRORTEXT_psTime_APPEND_MSEC_FAILED);
+    }
+    psFree(tempString);
+
+    return timeString;
+}
+
+struct timeval psTimeToTimeval(const psTime *time)
+{
+    struct timeval timevalTime;
+
+    timevalTime.tv_sec = 0;
+    timevalTime.tv_usec = 0;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,timevalTime);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,timevalTime);
+
+    timevalTime.tv_sec = time->sec;
+    timevalTime.tv_usec = time->nsec / 1000;
+
+    return timevalTime;
+}
+
+struct tm* psTimeToTM(const psTime *time)
+{
+    psS64 cent = 0;
+    psS64 year = 0;
+    psS64 month = 0;
+    psS64 day = 0;
+    psS64 hour = 0;
+    psS64 minute = 0;
+    psS64 seconds = 0;
+    psS64 temp = 0;
+    struct tm* tmTime = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    seconds = time->sec%60;
+    minute = time->sec/60%60;
+    hour = time->sec/3600%24;
+    day = (time->sec+62135596800)/86400;
+
+    // Add 306 days to make relative to Mar 1, 0; also adjust day to be within a range (1..2**28-1) where our
+    // calculations will work with 32bit ints
+    if(day > (pow(2, 28)-307))
+    {
+        temp = (day - 146097+306)/146097+1;         // Avoid overflow if day close to maxint
+        day -= temp * 146097-306;
+    } else if((day += 306) <= 0)
+    {
+        temp = -( -day / 146097 + 1);               // Avoid ambiguity in C division of negatives
+        day -= temp * 146097;
+    }
+
+    cent = (day*4-1)/146097;                        // Calc number of centuries day is after 29 Feb of yr 0
+    day -= cent*146097/4;                           // 4 centuries = 146097 days
+    year = (day*4-1)/1461;                          // Calc number of years into the century
+    day -= year*1461/4;                             // Again March-based (4 yrs =\u02dc 146[01] days)
+    month = (day*12+1093)/367;                      // Get the month (3..14 represent March through
+    day -= (month*367-1094)/12;                     // February of following year)
+    year += cent*100+temp*400;                      // Get the real year, which is off by
+
+    // One if month is January or February
+    if(month > 12)
+    {
+        year++;
+        month -= 12;
+    }
+
+    // Allocate output
+    tmTime = (struct tm*)psAlloc(sizeof(struct tm));
+
+    tmTime->tm_year = year - 1900;
+    tmTime->tm_mon = month - 1;
+    tmTime->tm_mday = day + 1;
+    tmTime->tm_hour = hour;
+    tmTime->tm_min = minute;
+    tmTime->tm_sec = seconds;
+    tmTime->tm_isdst = -1;
+
+    return tmTime;
+}
+
+psTime* psTimeFromJD(double time)
+{
+    double days = 0.0;
+    double seconds = 0.0;
+    psTime *outTime = NULL;
+
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Julian date conversion courtesy of Eugene Magnier
+    days = time - 2440587.5;
+    seconds = days * SEC_PER_DAY;
+    if(seconds < 0.0) {
+        outTime->nsec = (seconds - (psS64)seconds) * -1000000000.0;  // psTime earlier than epoch
+    } else {
+        outTime->nsec = (seconds - (psS64)seconds) * 1000000000.0;   // psTime greater than epoch
+    }
+    outTime->sec = seconds;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromMJD(double time)
+{
+    double days = 0.0;
+    double seconds = 0.0;
+    psTime *outTime = NULL;
+
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Modified Julian date conversion courtesy of Eugene Magnier
+    days = time - 40587.0;
+    seconds = days * SEC_PER_DAY;
+    if(seconds < 0.0) {
+        outTime->nsec = (seconds - (psS64)seconds) * -1000000000.0;  // psTime earlier than epoch
+    } else {
+        outTime->nsec = (seconds - (psS64)seconds) * 1000000000.0;   // psTime greater than epoch
+    }
+    outTime->sec = seconds;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromISO(const char *time)
+{
+    psS32 millisecond;
+    struct tm tmTime;
+    psTime *outTime = NULL;
+
+    // Convert YYYY-MM-DDThh:mm:ss.sss in string form to tm time
+    if (sscanf(time, "%d-%d-%dT%d:%d:%d.%d", &tmTime.tm_year, &tmTime.tm_mon, &tmTime.tm_mday,
+               &tmTime.tm_hour, &tmTime.tm_min, &tmTime.tm_sec,&millisecond) < 7) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psTime_ISOTIME_MALFORMED, time);
+        return NULL;
+    }
+
+    PS_INT_CHECK_NON_NEGATIVE(tmTime.tm_year, outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_mon,1,12,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_mday,1,31,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_hour,0,23,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_min,0,59,outTime);
+    PS_INT_CHECK_RANGE(tmTime.tm_sec,0,59,outTime);
+    PS_INT_CHECK_RANGE(millisecond,0,999,outTime);
+
+    tmTime.tm_year -= 1900;
+    tmTime.tm_mon--;
+    tmTime.tm_isdst = -1;
+
+    // Convert tm time to psTime
+    outTime = psTimeFromTM(&tmTime);
+    outTime->nsec = millisecond * 1000000;
+
+    return outTime;
+}
+
+psTime* psTimeFromTimeval(const struct timeval *time)
+{
+    psTime *outTime = NULL;
+
+
+    // Error check
+    PS_PTR_CHECK_NULL(time,NULL);
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Convert to psTime
+    outTime->sec = time->tv_sec;
+    outTime->nsec = time->tv_usec * 1000;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeFromTM(const struct tm* time)
+{
+    psS64 year;
+    psS64 month;
+    psS64 day;
+    psS64 hour;
+    psS64 minute;
+    psS64 seconds;
+    psS64 temp;
+    psTime *outTime = NULL;
+
+
+    // Error check
+    PS_PTR_CHECK_NULL(time,NULL);
+
+    // Allocate psTime struct
+    outTime = psTimeAlloc(PS_TIME_TAI);
+
+    // Extract data from TM struct
+    year = time->tm_year + 1900;
+    month = time->tm_mon + 1;
+    day = time->tm_mday;
+    hour = time->tm_hour;
+    minute = time->tm_min;
+    seconds = time->tm_sec;
+
+    // Make month in range 3..14 (treat Jan & Feb as months 13..14 of prev year)
+    if( month <= 2 )
+    {
+        year -= (temp = (14 - month) / 12);
+        month += 12 * temp;
+    } else if(month > 14)
+    {
+        year += (temp = (month - 3) / 12);
+        month -= 12 * temp;
+    }
+
+    // Make year positive
+    if (year < 0 )
+    {
+        day -= 146097 * (temp = (399 - year) / 400);
+        year += 400 * temp;
+    }
+
+    // Add day of month, days of previous 0-11 month period that began w/March, days of previous 0-399 year
+    // period that began w/March of a 400-multiple year), days of any 400-year periods before that, and 306
+    // days to adjust from Mar 1, year 0-relative to Jan 1, year 1-relative. Add hours, minutes, and seconds.
+    day += (month * 367 - 1094) / 12 + year % 100 * 1461 / 4 + (year/100 * 36524 + year/400) - 306;
+    outTime->sec = (((day - 1) * SEC_PER_DAY) - 62135596800) + hour*SEC_PER_HOUR + minute*SEC_PER_MINUTE + seconds;
+
+    // C's TM does not define a microsecond field. Microseconds must be manipulated by calling function.
+    outTime->nsec = 0;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    return outTime;
+}
+
+psTime* psTimeMath(const psTime *time, psF64 delta)
+{
+    psF64 sec = 0.0;
+    psTime *outTime = NULL;
+    psTime *tempTime = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time,NULL);
+    PS_INT_CHECK_RANGE(time->nsec,0,1e9-1,NULL);
+
+    // Convert time to TAI if necessary, but without changing input arguments
+    if(time->type == PS_TIME_UTC) {
+        tempTime = psTimeAlloc(PS_TIME_UTC);
+        tempTime->sec = time->sec;
+        tempTime->nsec = time->nsec;
+        tempTime = psTimeConvert(tempTime, PS_TIME_TAI);
+    } else {
+        tempTime = psMemIncrRefCounter((psTime*)time);
+    }
+
+    // Create output time
+    outTime = psTimeAlloc(PS_TIME_TAI);
+    sec = delta + (psF64)tempTime->sec + (psF64)tempTime->nsec/1e9;
+    outTime->sec = sec;
+    outTime->nsec = (sec - outTime->sec)*1e9;
+
+    // Error check
+    PS_INT_CHECK_RANGE(outTime->nsec,0,1e9-1,outTime);
+
+    // Convert result to same time type as input
+    if(time->type == PS_TIME_UTC) {
+        outTime = psTimeConvert(outTime, PS_TIME_UTC);
+    }
+
+    psFree(tempTime);
+
+    return outTime;
+}
+
+psF64 psTimeDelta(const psTime *time1, const psTime *time2)
+{
+    psF64 out = 0.0;
+    psF64 uSec1 = 0.0;
+    psF64 uSec2 = 0.0;
+    psTime *tempTime1 = NULL;
+    psTime *tempTime2 = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(time1,0.0);
+    PS_INT_CHECK_RANGE(time1->nsec,0,1e9-1,0.0);
+    PS_PTR_CHECK_NULL(time2,0.0);
+    PS_INT_CHECK_RANGE(time2->nsec,0,1e9-1,0.0);
+
+    // Convert time to TAI if necessary, but without changing input arguments
+    if(time1->type == PS_TIME_UTC) {
+        tempTime1 = psTimeAlloc(PS_TIME_UTC);
+        tempTime1->sec = time1->sec;
+        tempTime1->nsec = time1->nsec;
+        tempTime1 = psTimeConvert(tempTime1, PS_TIME_TAI);
+    } else {
+        tempTime1 = psMemIncrRefCounter((psTime*)time1);
+    }
+    if(time2->type == PS_TIME_UTC) {
+        tempTime2 = psTimeAlloc(PS_TIME_UTC);
+        tempTime2->sec = time2->sec;
+        tempTime2->nsec = time2->nsec;
+        tempTime2 = psTimeConvert(tempTime2, PS_TIME_TAI);
+    } else {
+        tempTime2 = psMemIncrRefCounter((psTime*)time2);
+    }
+
+    uSec1 = tempTime1->sec >= 0 ? 1.0 : -1.0;
+    uSec1 = uSec1*tempTime1->nsec/1e9;
+    uSec2 = tempTime2->sec >= 0 ? 1.0 : -1.0;
+    uSec2 = uSec2*tempTime2->nsec/1e9;
+    out = (tempTime1->sec-tempTime2->sec) + (uSec1-uSec2);
+
+    psFree(tempTime1);
+    psFree(tempTime2);
+
+    return out;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psTime.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psTime.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/psTime.h	(revision 22271)
@@ -0,0 +1,308 @@
+/** @file  psTime.h
+ *
+ *  @brief Definitions for time, time utilities, and conversion functions for use with psLib astronomy
+ *  functions.
+ *
+ *  A collection of functions are required by psLib to manipulate time data. These functions primarily consist
+ *  of conversions between specific time formats.  They use the UNIX timeval time system as the
+ *  base upon which International Atomic Time (TAI) and Universal Time Coordinated (UTC) are calculated.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.28.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+#ifndef PSTIME_H
+#define PSTIME_H
+
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "psType.h"
+#include "psImage.h"
+
+struct psSphere;
+
+/// @addtogroup Time
+/// @{
+
+
+/** Time type.
+ *
+ * Enumeration for psTime types, TAI or UTC time.
+ */
+typedef enum {
+    PS_TIME_TAI,        ///< Temps Atomique International (TAI) time (time with leapseconds).
+    PS_TIME_UTC,        ///< Universal Time Coordinated (UTC) time (time without leapseconds).
+    PS_TIME_UT1,        ///< Universal Time corrected for polar motion
+    PS_TIME_TT,         ///< Terrestrial Time
+} psTimeType;
+
+/** Time Bulletin type
+ *
+ * Enumeration for psTimeBulletin type, A or B.
+ */
+typedef enum {
+    PS_IERS_A,          ///< IERS Bulletin A
+    PS_IERS_B,          ///< IERS Bulletin B
+} psTimeBulletin;
+
+/** Definition of psTime.
+ *
+ *  The psTime struct is used by psLib to represent time values critical to
+ *  astronomical calculations.  This structure represents a time which is
+ *  equivalent to TAI (International Atomic Time) and is measured in both
+ *  seconds and microseconds.
+ */
+typedef struct psTime
+{
+    psS64 sec;          ///< Seconds since epoch, Jan 1, 1970.
+    psU32 nsec;         ///< Nanoseconds since last second.
+    psBool leapsecond;  ///< if time falls on UTC leapsecond
+    psTimeType type;    ///< Type of time.
+}
+psTime;
+
+
+/** Initialize time data.
+ *
+ * Reads config and data files associated with various time conversions.
+ *
+ * @return  bool: True for success, false for failure.
+ */
+psBool p_psTimeInit(const char *fileName);
+
+/** Free memory persistant time data.
+ *
+ * Frees time data to be held in memory until the end of successful program execution.
+ *
+ * @return  void: void.
+ */
+psBool p_psTimeFinalize(void);
+
+/** Allocate time struct.
+ *
+ * Allocates an empty time struct. User must specify the psTimeType (PS_TIME_TAI or PS_TIME_UTC) in the argument.
+ * The seconds and microseconds members of the struct are set to zero.
+ *
+ * @return  psTime*: Struct with empty time.
+ */
+psTime* psTimeAlloc(
+    psTimeType type                     ///< Type of time to create (UTC or TAI).
+);
+
+/** Get current time.
+ *
+ * Gets current time from the system clock. User must specify the psTimeType (PS_TIME_TAI or PS_TIME_UTC) in
+ * the argument.
+ *
+ *  @return  psTime*: Struct with current time.
+ */
+psTime* psTimeGetNow(
+    psTimeType type                     ///< Type of time to get (UTC or TAI).
+);
+
+/** Convert psTime to UTC or TAI time.
+ *
+ *  Converts psTime to UTC or TAI time based on the psTimeType argument.
+ *
+ *  @return  psTime*: Pointer to psTime.
+ */
+psTime* psTimeConvert(
+    psTime *time,                       ///< Time to be converted.
+    psTimeType type                     ///< Type to be converted to.
+);
+
+/** Convert psTime to Local Mean Sidereal Time (LMST).
+ *
+ *  Converts psTime at the given longitude to LMST time. If the input time is not in UTC format, then it is
+ *  converted.
+ *
+ *  @return  double: LST Time.
+ */
+double psTimeToLMST(
+    psTime *time,   ///< psTime to be converted.
+    double longitude                    ///< Longitude.
+);
+
+/** Determine UT1 - UTC from table lookup.
+ *
+ *  This function is necessary to for various SLALIB functions.
+ *
+ *  @return  double: Time difference.
+ */
+double psTimeGetUT1Delta(
+    const psTime *time                  ///< psTime to be looked up.
+);
+
+/** Determine TAI - UTC from table lookup.
+ *
+ *  This function is necessary to for various psTime functions.
+ *
+ *  @return  double: Time difference.
+ */
+double psTimeGetTAIDelta(
+    const psTime *time                  ///< psTime to be looked up.
+);
+
+/** Determine polar coordinates at a given time.
+ *
+ *  Determines the orientation of the polar axis at the given time.
+ *
+ *  @return  psSphere*: Spherical coordinates of Earth's polar axias.
+ */
+struct psSphere* psTimeGetPoleCoords(
+                const psTime *time                  ///< psTime determine polar orientation.
+            );
+
+/** Calculate the number of leapseconds between two times.
+ *
+ *  Calculates the number of leapseconds between two times.
+ *
+ *  @return  psS64: leapseconds added between given times
+ */
+psS64 psTimeLeapSecondDelta(
+    const psTime* time1,                ///< First input time.
+    const psTime* time2                 ///< Second input time.
+);
+
+/** Convert psTime to Julian date time.
+ *
+ *  Converts psTime to Julian date (JD) time. This function does not add or subtract leapseconds.
+ *
+ *  @return  double: Julian Date (JD) time.
+ */
+double psTimeToJD(
+    const psTime* time                  ///< Input time to be converted.
+);
+/** Convert psTime to modified Julian date time.
+ *
+ *  Converts psTime to modified Julian date (MJD) time. This function does not add or subtract leapseconds.
+ *
+ *  @return  double: Modified Julian Days (MJD) time.
+ */
+double psTimeToMJD(
+    const psTime* time                  ///< Input time to be converted.
+);
+
+/** Convert psTime to ISO8601 formatted string.
+ *
+ *  Converts psTime to a null terminated string in the form of YYYY-MM-DDThh:mm:ss.sss. This function does not
+ *  add or subtract leapseconds.
+ *
+ *  @return  char*: Pointer null terminated array of chars in ISO time.
+ */
+char* psTimeToISO(
+    const psTime* time                  ///< Input time to be converted.
+);
+
+/** Convert psTime to timeval time.
+ *
+ *  Converts psTime to timeval time. This function does not add or subtract leapseconds.
+ *
+ *  @return  timeval: timeval struct time.
+ */
+struct timeval psTimeToTimeval(
+                const psTime* time                  ///< Input time to be converted.
+            );
+
+/** Convert psTime to tm time.
+ *
+ * Converts psTime to tm time. This function is based on a Perl algorithm availble in the Pan-STARRS Image
+ * processing Algorithm Design Description (ADD). This function does not add or subtract leapseconds.
+ *
+ *  @return  tm: tm struct time.
+ */
+struct tm* psTimeToTM(
+                const psTime *time                  ///< Input time to be converted.
+            );
+
+/** Convert JD to psTime.
+ *
+ *  Converts JD time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime: time.
+ */
+psTime* psTimeFromJD(
+    double time                         ///< Input time to be converted.
+);
+
+/** Convert MJD to psTime.
+ *
+ *  Converts MJD time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime: time.
+ */
+psTime* psTimeFromMJD(
+    double time                         ///< Input time to be converted.
+);
+
+/** Convert ISO to psTime.
+ *
+ *  Converts ISO time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time
+ */
+psTime* psTimeFromISO(
+    const char* time                    ///< Input time to be converted.
+);
+
+/** Convert timeval to psTime.
+ *
+ *  Converts timeval time to psTime. This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeFromTimeval(
+    const struct timeval *time          ///< Input time to be converted.
+);
+
+/** Convert tm time to psTime.
+ *
+ *  Converts tm time to psTime. This function is based on a Perl algorithm availble in the Pan-STARRS Image
+ * processing Algorithm Design Description (ADD). This function does not add or subtract leapseconds.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeFromTM(
+    const struct tm *time               ///< Input time to be converted.
+);
+
+/** Adds delta to time. Result is in TAI time.
+ *
+ *  Adds delta to time. Input time is converted to TAI format if necessary.
+ *
+ *  @return  psTime*: time.
+ */
+psTime* psTimeMath(
+    const psTime *time,                 ///< Time.
+    psF64 delta                         ///< Time delta.
+);
+
+/** Determine difference between two times. Result is in TAI time.
+ *
+ *  Determine difference between two times. Input times are converted to TAI format if necessary.
+ *
+ *  @return psF64: Time difference.
+ */
+psF64 psTimeDelta(
+    const psTime *time1,                ///< First time.
+    const psTime *time2                 ///< Second time.
+);
+
+/** Get the filename of the psLib configuration file.
+ *
+ *  @return char*          If a PS_CONFIG_FILE environment variable exists,
+ *                         that is returned, otherwise the default location
+ *                         dependent on the installation location.
+ */
+char* p_psGetConfigFileName();
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refco.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refco.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refco.f	(revision 22271)
@@ -0,0 +1,87 @@
+      SUBROUTINE sla_REFCO (HM, TDK, PMB, RH, WL, PHI, TLR, EPS,
+     :                      REFA, REFB)
+*+
+*     - - - - - -
+*      R E F C O
+*     - - - - - -
+*
+*  Determine the constants A and B in the atmospheric refraction
+*  model dZ = A tan Z + B tan**3 Z.
+*
+*  Z is the "observed" zenith distance (i.e. affected by refraction)
+*  and dZ is what to add to Z to give the "topocentric" (i.e. in vacuo)
+*  zenith distance.
+*
+*  Given:
+*    HM      d     height of the observer above sea level (metre)
+*    TDK     d     ambient temperature at the observer (deg K)
+*    PMB     d     pressure at the observer (millibar)
+*    RH      d     relative humidity at the observer (range 0-1)
+*    WL      d     effective wavelength of the source (micrometre)
+*    PHI     d     latitude of the observer (radian, astronomical)
+*    TLR     d     temperature lapse rate in the troposphere (degK/metre)
+*    EPS     d     precision required to terminate iteration (radian)
+*
+*  Returned:
+*    REFA    d     tan Z coefficient (radian)
+*    REFB    d     tan**3 Z coefficient (radian)
+*
+*  Called:  sla_REFRO
+*
+*  Notes:
+*
+*  1  Typical values for the TLR and EPS arguments might be 0.0065D0 and
+*     1D-10 respectively.
+*
+*  2  The radio refraction is chosen by specifying WL > 100 micrometres.
+*
+*  3  The routine is a slower but more accurate alternative to the
+*     sla_REFCOQ routine.  The constants it produces give perfect
+*     agreement with sla_REFRO at zenith distances arctan(1) (45 deg)
+*     and arctan(4) (about 76 deg).  It achieves 0.5 arcsec accuracy
+*     for ZD < 80 deg, 0.01 arcsec accuracy for ZD < 60 deg, and
+*     0.001 arcsec accuracy for ZD < 45 deg.
+*
+*  P.T.Wallace   Starlink   3 June 1997
+*
+*  Copyright (C) 1997 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION HM,TDK,PMB,RH,WL,PHI,TLR,EPS,REFA,REFB
+
+      DOUBLE PRECISION ATN1,ATN4,R1,R2
+
+*  Sample zenith distances: arctan(1) and arctan(4)
+      PARAMETER (ATN1=0.7853981633974483D0,
+     :           ATN4=1.325817663668033D0)
+
+
+
+*  Determine refraction for the two sample zenith distances
+      CALL sla_REFRO(ATN1,HM,TDK,PMB,RH,WL,PHI,TLR,EPS,R1)
+      CALL sla_REFRO(ATN4,HM,TDK,PMB,RH,WL,PHI,TLR,EPS,R2)
+
+*  Solve for refraction constants
+      REFA = (64D0*R1-R2)/60D0
+      REFB = (R2-4D0*R1)/60D0
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refro.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refro.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refro.f	(revision 22271)
@@ -0,0 +1,401 @@
+      SUBROUTINE sla_REFRO (ZOBS, HM, TDK, PMB, RH, WL, PHI, TLR,
+     :                      EPS, REF)
+*+
+*     - - - - - -
+*      R E F R O
+*     - - - - - -
+*
+*  Atmospheric refraction for radio and optical/IR wavelengths.
+*
+*  Given:
+*    ZOBS    d  observed zenith distance of the source (radian)
+*    HM      d  height of the observer above sea level (metre)
+*    TDK     d  ambient temperature at the observer (deg K)
+*    PMB     d  pressure at the observer (millibar)
+*    RH      d  relative humidity at the observer (range 0-1)
+*    WL      d  effective wavelength of the source (micrometre)
+*    PHI     d  latitude of the observer (radian, astronomical)
+*    TLR     d  temperature lapse rate in the troposphere (K/metre)
+*    EPS     d  precision required to terminate iteration (radian)
+*
+*  Returned:
+*    REF     d  refraction: in vacuo ZD minus observed ZD (radian)
+*
+*  Notes:
+*
+*  1  A suggested value for the TLR argument is 0.0065D0.  The
+*     refraction is significantly affected by TLR, and if studies
+*     of the local atmosphere have been carried out a better TLR
+*     value may be available.  The sign of the supplied TLR value
+*     is ignored.
+*
+*  2  A suggested value for the EPS argument is 1D-8.  The result is
+*     usually at least two orders of magnitude more computationally
+*     precise than the supplied EPS value.
+*
+*  3  The routine computes the refraction for zenith distances up
+*     to and a little beyond 90 deg using the method of Hohenkerk
+*     and Sinclair (NAO Technical Notes 59 and 63, subsequently adopted
+*     in the Explanatory Supplement, 1992 edition - see section 3.281).
+*
+*  4  The code is a development of the optical/IR refraction subroutine
+*     AREF of C.Hohenkerk (HMNAO, September 1984), with extensions to
+*     support the radio case.  Apart from merely cosmetic changes, the
+*     following modifications to the original HMNAO optical/IR refraction
+*     code have been made:
+*
+*     .  The angle arguments have been changed to radians.
+*
+*     .  Any value of ZOBS is allowed (see note 6, below).
+*
+*     .  Other argument values have been limited to safe values.
+*
+*     .  Murray's values for the gas constants have been used
+*        (Vectorial Astrometry, Adam Hilger, 1983).
+*
+*     .  The numerical integration phase has been rearranged for
+*        extra clarity.
+*
+*     .  A better model for Ps(T) has been adopted (taken from
+*        Gill, Atmosphere-Ocean Dynamics, Academic Press, 1982).
+*
+*     .  More accurate expressions for Pwo have been adopted
+*        (again from Gill 1982).
+*
+*     .  Provision for radio wavelengths has been added using
+*        expressions devised by A.T.Sinclair, RGO (private
+*        communication 1989).  The refractivity model currently
+*        used is from J.M.Rueger, "Refractive Index Formulae for
+*        Electronic Distance Measurement with Radio and Millimetre
+*        Waves", in Unisurv Report S-68 (2002), School of Surveying
+*        and Spatial Information Systems, University of New South
+*        Wales, Sydney, Australia.
+*
+*     .  Various small changes have been made to gain speed.
+*
+*     None of the changes significantly affects the optical/IR results
+*     with respect to the algorithm given in the 1992 Explanatory
+*     Supplement.  For example, at 70 deg zenith distance the present
+*     routine agrees with the ES algorithm to better than 0.05 arcsec
+*     for any reasonable combination of parameters.  However, the
+*     improved water-vapour expressions do make a significant difference
+*     in the radio band, at 70 deg zenith distance reaching almost
+*     4 arcsec for a hot, humid, low-altitude site during a period of
+*     low pressure.
+*
+*  5  The radio refraction is chosen by specifying WL > 100 micrometres.
+*     Because the algorithm takes no account of the ionosphere, the
+*     accuracy deteriorates at low frequencies, below about 30 MHz.
+*
+*  6  Before use, the value of ZOBS is expressed in the range +/- pi.
+*     If this ranged ZOBS is -ve, the result REF is computed from its
+*     absolute value before being made -ve to match.  In addition, if
+*     it has an absolute value greater than 93 deg, a fixed REF value
+*     equal to the result for ZOBS = 93 deg is returned, appropriately
+*     signed.
+*
+*  7  As in the original Hohenkerk and Sinclair algorithm, fixed values
+*     of the water vapour polytrope exponent, the height of the
+*     tropopause, and the height at which refraction is negligible are
+*     used.
+*
+*  8  The radio refraction has been tested against work done by
+*     Iain Coulson, JACH, (private communication 1995) for the
+*     James Clerk Maxwell Telescope, Mauna Kea.  For typical conditions,
+*     agreement at the 0.1 arcsec level is achieved for moderate ZD,
+*     worsening to perhaps 0.5-1.0 arcsec at ZD 80 deg.  At hot and
+*     humid sea-level sites the accuracy will not be as good.
+*
+*  9  It should be noted that the relative humidity RH is formally
+*     defined in terms of "mixing ratio" rather than pressures or
+*     densities as is often stated.  It is the mass of water per unit
+*     mass of dry air divided by that for saturated air at the same
+*     temperature and pressure (see Gill 1982).
+*
+*  10 The algorithm is designed for observers in the troposphere.  The
+*     supplied temperature, pressure and lapse rate are assumed to be
+*     for a point in the troposphere and are used to define a model
+*     atmosphere with the tropopause at 11km altitude and a constant
+*     temperature above that.  However, in practice, the refraction
+*     values returned for stratospheric observers, at altitudes up to
+*     25km, are quite usable.
+*
+*  Called:  sla_DRANGE, sla__ATMT, sla__ATMS
+*
+*  P.T.Wallace   Starlink   28 May 2002
+*
+*  Copyright (C) 2002 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION ZOBS,HM,TDK,PMB,RH,WL,PHI,TLR,EPS,REF
+
+*
+*  Fixed parameters
+*
+      DOUBLE PRECISION D93,GCR,DMD,DMW,S,DELTA,HT,HS
+      INTEGER ISMAX
+*  93 degrees in radians
+      PARAMETER (D93=1.623156204D0)
+*  Universal gas constant
+      PARAMETER (GCR=8314.32D0)
+*  Molecular weight of dry air
+      PARAMETER (DMD=28.9644D0)
+*  Molecular weight of water vapour
+      PARAMETER (DMW=18.0152D0)
+*  Mean Earth radius (metre)
+      PARAMETER (S=6378120D0)
+*  Exponent of temperature dependence of water vapour pressure
+      PARAMETER (DELTA=18.36D0)
+*  Height of tropopause (metre)
+      PARAMETER (HT=11000D0)
+*  Upper limit for refractive effects (metre)
+      PARAMETER (HS=80000D0)
+*  Numerical integration: maximum number of strips.
+      PARAMETER (ISMAX=16384)
+
+      INTEGER IS,K,N,I,J
+      LOGICAL OPTIC,LOOP
+      DOUBLE PRECISION ZOBS1,ZOBS2,HMOK,TDKOK,PMBOK,RHOK,WLOK,ALPHA,
+     :                 TOL,WLSQ,GB,A,GAMAL,GAMMA,GAMM2,DELM2,
+     :                 TDC,PSAT,PWO,W,
+     :                 C1,C2,C3,C4,C5,C6,R0,TEMPO,DN0,RDNDR0,SK0,F0,
+     :                 RT,TT,DNT,RDNDRT,SINE,ZT,FT,DNTS,RDNDRP,ZTS,FTS,
+     :                 RS,DNS,RDNDRS,ZS,FS,REFOLD,Z0,ZRANGE,FB,FF,FO,FE,
+     :                 H,R,SZ,RG,DR,TG,DN,RDNDR,T,F,REFP,REFT
+
+      DOUBLE PRECISION sla_DRANGE
+
+*  The refraction integrand
+      DOUBLE PRECISION REFI
+      REFI(DN,RDNDR) = RDNDR/(DN+RDNDR)
+
+
+
+*  Transform ZOBS into the normal range.
+      ZOBS1 = sla_DRANGE(ZOBS)
+      ZOBS2 = MIN(ABS(ZOBS1),D93)
+
+*  Keep other arguments within safe bounds.
+      HMOK = MIN(MAX(HM,-1D3),HS)
+      TDKOK = MIN(MAX(TDK,100D0),500D0)
+      PMBOK = MIN(MAX(PMB,0D0),10000D0)
+      RHOK = MIN(MAX(RH,0D0),1D0)
+      WLOK = MAX(WL,0.1D0)
+      ALPHA = MIN(MAX(ABS(TLR),0.001D0),0.01D0)
+
+*  Tolerance for iteration.
+      TOL = MIN(MAX(ABS(EPS),1D-12),0.1D0)/2D0
+
+*  Decide whether optical/IR or radio case - switch at 100 microns.
+      OPTIC = WLOK.LE.100D0
+
+*  Set up model atmosphere parameters defined at the observer.
+      WLSQ = WLOK*WLOK
+      GB = 9.784D0*(1D0-0.0026D0*COS(PHI+PHI)-0.00000028D0*HMOK)
+      IF (OPTIC) THEN
+         A = (287.604D0+(1.6288D0+0.0136D0/WLSQ)/WLSQ)
+     :                                              *273.15D-6/1013.25D0
+      ELSE
+         A = 77.6890D-6
+      END IF
+      GAMAL = (GB*DMD)/GCR
+      GAMMA = GAMAL/ALPHA
+      GAMM2 = GAMMA-2D0
+      DELM2 = DELTA-2D0
+      TDC = TDKOK-273.15D0
+      PSAT = 10D0**((0.7859D0+0.03477D0*TDC)/(1D0+0.00412D0*TDC))*
+     :                                (1D0+PMBOK*(4.5D-6+6D-10*TDC*TDC))
+      IF (PMBOK.GT.0D0) THEN
+         PWO = RHOK*PSAT/(1D0-(1D0-RHOK)*PSAT/PMBOK)
+      ELSE
+         PWO = 0D0
+      END IF
+      W = PWO*(1D0-DMW/DMD)*GAMMA/(DELTA-GAMMA)
+      C1 = A*(PMBOK+W)/TDKOK
+      IF (OPTIC) THEN
+         C2 = (A*W+11.2684D-6*PWO)/TDKOK
+      ELSE
+         C2 = (A*W+6.3938D-6*PWO)/TDKOK
+      END IF
+      C3 = (GAMMA-1D0)*ALPHA*C1/TDKOK
+      C4 = (DELTA-1D0)*ALPHA*C2/TDKOK
+      IF (OPTIC) THEN
+         C5 = 0D0
+         C6 = 0D0
+      ELSE
+         C5 = 375463D-6*PWO/TDKOK
+         C6 = C5*DELM2*ALPHA/(TDKOK*TDKOK)
+      END IF
+
+*  Conditions at the observer.
+      R0 = S+HMOK
+      CALL sla__ATMT(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,
+     :                                              R0,TEMPO,DN0,RDNDR0)
+      SK0 = DN0*R0*SIN(ZOBS2)
+      F0 = REFI(DN0,RDNDR0)
+
+*  Conditions in the troposphere at the tropopause.
+      RT = S+MAX(HT,HMOK)
+      CALL sla__ATMT(R0,TDKOK,ALPHA,GAMM2,DELM2,C1,C2,C3,C4,C5,C6,
+     :                                                 RT,TT,DNT,RDNDRT)
+      SINE = SK0/(RT*DNT)
+      ZT = ATAN2(SINE,SQRT(MAX(1D0-SINE*SINE,0D0)))
+      FT = REFI(DNT,RDNDRT)
+
+*  Conditions in the stratosphere at the tropopause.
+      CALL sla__ATMS(RT,TT,DNT,GAMAL,RT,DNTS,RDNDRP)
+      SINE = SK0/(RT*DNTS)
+      ZTS = ATAN2(SINE,SQRT(MAX(1D0-SINE*SINE,0D0)))
+      FTS = REFI(DNTS,RDNDRP)
+
+*  Conditions at the stratosphere limit.
+      RS = S+HS
+      CALL sla__ATMS(RT,TT,DNT,GAMAL,RS,DNS,RDNDRS)
+      SINE = SK0/(RS*DNS)
+      ZS = ATAN2(SINE,SQRT(MAX(1D0-SINE*SINE,0D0)))
+      FS = REFI(DNS,RDNDRS)
+
+*
+*  Integrate the refraction integral in two parts;  first in the
+*  troposphere (K=1), then in the stratosphere (K=2).
+*
+      DO K = 1,2
+
+*     Initialize previous refraction to ensure at least two iterations.
+         REFOLD = 1D0
+
+*     Start off with 8 strips.
+         IS = 8
+
+*     Start Z, Z range, and start and end values.
+         IF (K.EQ.1) THEN
+            Z0 = ZOBS2
+            ZRANGE = ZT-Z0
+            FB = F0
+            FF = FT
+         ELSE
+            Z0 = ZTS
+            ZRANGE = ZS-Z0
+            FB = FTS
+            FF = FS
+         END IF
+
+*     Sums of odd and even values.
+         FO = 0D0
+         FE = 0D0
+
+*     First time through the loop we have to do every point.
+         N = 1
+
+*     Start of iteration loop (terminates at specified precision).
+         LOOP = .TRUE.
+         DO WHILE (LOOP)
+
+*        Strip width.
+            H = ZRANGE/DBLE(IS)
+
+*        Initialize distance from Earth centre for quadrature pass.
+            IF (K.EQ.1) THEN
+               R = R0
+            ELSE
+               R = RT
+            END IF
+
+*        One pass (no need to compute evens after first time).
+            DO I=1,IS-1,N
+
+*           Sine of observed zenith distance.
+               SZ = SIN(Z0+H*DBLE(I))
+
+*           Find R (to the nearest metre, maximum four iterations).
+               IF (SZ.GT.1D-20) THEN
+                  W = SK0/SZ
+                  RG = R
+                  DR = 1D6
+                  J = 0
+                  DO WHILE (ABS(DR).GT.1D0.AND.J.LT.4)
+                     J=J+1
+                     IF (K.EQ.1) THEN
+                        CALL sla__ATMT(R0,TDKOK,ALPHA,GAMM2,DELM2,
+     :                                 C1,C2,C3,C4,C5,C6,RG,TG,DN,RDNDR)
+                     ELSE
+                        CALL sla__ATMS(RT,TT,DNT,GAMAL,RG,DN,RDNDR)
+                     END IF
+                     DR = (RG*DN-W)/(DN+RDNDR)
+                     RG = RG-DR
+                  END DO
+                  R = RG
+               END IF
+
+*           Find the refractive index and integrand at R.
+               IF (K.EQ.1) THEN
+                  CALL sla__ATMT(R0,TDKOK,ALPHA,GAMM2,DELM2,
+     :                                   C1,C2,C3,C4,C5,C6,R,T,DN,RDNDR)
+               ELSE
+                  CALL sla__ATMS(RT,TT,DNT,GAMAL,R,DN,RDNDR)
+               END IF
+               F = REFI(DN,RDNDR)
+
+*           Accumulate odd and (first time only) even values.
+               IF (N.EQ.1.AND.MOD(I,2).EQ.0) THEN
+                  FE = FE+F
+               ELSE
+                  FO = FO+F
+               END IF
+            END DO
+
+*        Evaluate the integrand using Simpson's Rule.
+            REFP = H*(FB+4D0*FO+2D0*FE+FF)/3D0
+
+*        Has the required precision been achieved (or can't be)?
+            IF (ABS(REFP-REFOLD).GT.TOL.AND.IS.LT.ISMAX) THEN
+
+*           No: prepare for next iteration.
+
+*           Save current value for convergence test.
+               REFOLD = REFP
+
+*           Double the number of strips.
+               IS = IS+IS
+
+*           Sum of all current values = sum of next pass's even values.
+               FE = FE+FO
+
+*           Prepare for new odd values.
+               FO = 0D0
+
+*           Skip even values next time.
+               N = 2
+            ELSE
+
+*           Yes: save troposphere component and terminate the loop.
+               IF (K.EQ.1) REFT = REFP
+               LOOP = .FALSE.
+            END IF
+         END DO
+      END DO
+
+*  Result.
+      REF = REFT+REFP
+      IF (ZOBS1.LT.0D0) REF = -REF
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refz.f
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refz.f	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/refz.f	(revision 22271)
@@ -0,0 +1,156 @@
+      SUBROUTINE sla_REFZ (ZU, REFA, REFB, ZR)
+*+
+*     - - - - -
+*      R E F Z
+*     - - - - -
+*
+*  Adjust an unrefracted zenith distance to include the effect of
+*  atmospheric refraction, using the simple A tan Z + B tan**3 Z
+*  model (plus special handling for large ZDs).
+*
+*  Given:
+*    ZU    dp    unrefracted zenith distance of the source (radian)
+*    REFA  dp    tan Z coefficient (radian)
+*    REFB  dp    tan**3 Z coefficient (radian)
+*
+*  Returned:
+*    ZR    dp    refracted zenith distance (radian)
+*
+*  Notes:
+*
+*  1  This routine applies the adjustment for refraction in the
+*     opposite sense to the usual one - it takes an unrefracted
+*     (in vacuo) position and produces an observed (refracted)
+*     position, whereas the A tan Z + B tan**3 Z model strictly
+*     applies to the case where an observed position is to have the
+*     refraction removed.  The unrefracted to refracted case is
+*     harder, and requires an inverted form of the text-book
+*     refraction models;  the formula used here is based on the
+*     Newton-Raphson method.  For the utmost numerical consistency
+*     with the refracted to unrefracted model, two iterations are
+*     carried out, achieving agreement at the 1D-11 arcseconds level
+*     for a ZD of 80 degrees.  The inherent accuracy of the model
+*     is, of course, far worse than this - see the documentation for
+*     sla_REFCO for more information.
+*
+*  2  At ZD 83 degrees, the rapidly-worsening A tan Z + B tan**3 Z
+*     model is abandoned and an empirical formula takes over.  Over a
+*     wide range of observer heights and corresponding temperatures and
+*     pressures, the following levels of accuracy (arcsec) are
+*     typically achieved, relative to numerical integration through a
+*     model atmosphere:
+*
+*              ZR    error
+*
+*              80      0.4
+*              81      0.8
+*              82      1.5
+*              83      3.2
+*              84      4.9
+*              85      5.8
+*              86      6.1
+*              87      7.1
+*              88     10
+*              89     20
+*              90     40
+*              91    100         } relevant only to
+*              92    200         } high-elevation sites
+*
+*     The high-ZD model is scaled to match the normal model at the
+*     transition point;  there is no glitch.
+*
+*  3  Beyond 93 deg zenith distance, the refraction is held at its
+*     93 deg value.
+*
+*  4  See also the routine sla_REFV, which performs the adjustment in
+*     Cartesian Az/El coordinates, and with the emphasis on speed
+*     rather than numerical accuracy.
+*
+*  P.T.Wallace   Starlink   19 September 1995
+*
+*  Copyright (C) 1995 Rutherford Appleton Laboratory
+*
+*  License:
+*    This program is free software; you can redistribute it and/or modify
+*    it under the terms of the GNU General Public License as published by
+*    the Free Software Foundation; either version 2 of the License, or
+*    (at your option) any later version.
+*
+*    This program is distributed in the hope that it will be useful,
+*    but WITHOUT ANY WARRANTY; without even the implied warranty of
+*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*    GNU General Public License for more details.
+*
+*    You should have received a copy of the GNU General Public License
+*    along with this program (see SLA_CONDITIONS); if not, write to the 
+*    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
+*    Boston, MA  02111-1307  USA
+*
+*-
+
+      IMPLICIT NONE
+
+      DOUBLE PRECISION ZU,REFA,REFB,ZR
+
+*  Radians to degrees
+      DOUBLE PRECISION R2D
+      PARAMETER (R2D=57.29577951308232D0)
+
+*  Largest usable ZD (deg)
+      DOUBLE PRECISION D93
+      PARAMETER (D93=93D0)
+
+*  Coefficients for high ZD model (used beyond ZD 83 deg)
+      DOUBLE PRECISION C1,C2,C3,C4,C5
+      PARAMETER (C1=+0.55445D0,
+     :           C2=-0.01133D0,
+     :           C3=+0.00202D0,
+     :           C4=+0.28385D0,
+     :           C5=+0.02390D0)
+
+*  ZD at which one model hands over to the other (radians)
+      DOUBLE PRECISION Z83
+      PARAMETER (Z83=83D0/R2D)
+
+*  High-ZD-model prediction (deg) for that point
+      DOUBLE PRECISION REF83
+      PARAMETER (REF83=(C1+C2*7D0+C3*49D0)/(1D0+C4*7D0+C5*49D0))
+
+      DOUBLE PRECISION ZU1,ZL,S,C,T,TSQ,TCU,REF,E,E2
+
+
+
+*  Perform calculations for ZU or 83 deg, whichever is smaller
+      ZU1 = MIN(ZU,Z83)
+
+*  Functions of ZD
+      ZL = ZU1
+      S = SIN(ZL)
+      C = COS(ZL)
+      T = S/C
+      TSQ = T*T
+      TCU = T*TSQ
+
+*  Refracted ZD (mathematically to better than 1 mas at 70 deg)
+      ZL = ZL-(REFA*T+REFB*TCU)/(1D0+(REFA+3D0*REFB*TSQ)/(C*C))
+
+*  Further iteration
+      S = SIN(ZL)
+      C = COS(ZL)
+      T = S/C
+      TSQ = T*T
+      TCU = T*TSQ
+      REF = ZU1-ZL+
+     :          (ZL-ZU1+REFA*T+REFB*TCU)/(1D0+(REFA+3D0*REFB*TSQ)/(C*C))
+
+*  Special handling for large ZU
+      IF (ZU.GT.ZU1) THEN
+         E = 90D0-MIN(D93,ZU*R2D)
+         E2 = E*E
+         REF = (REF/REF83)*(C1+C2*E+C3*E2)/(1D0+C4*E+C5*E2)
+      END IF
+
+*  Return refracted ZD
+      ZR = ZU-REF
+
+      END
Index: /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/slalib.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/slalib.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/astronomy/slalib.h	(revision 22271)
@@ -0,0 +1,129 @@
+/** @file  slalib.h
+*
+*  @brief Simple wrapper of the required fortran SLALIB functions
+*
+*  @ingroup AstroImage
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:23 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef SLALIB_H
+#define SLALIB_H
+
+#include "config.h"
+
+#define fslaAoppa F77_FUNC_(sla_aoppa,SLA_AOPPA)
+extern void fslaAoppa(
+        const double* date,
+        const double* dut,
+        const double* elongm,
+        const double* phim,
+        const double* him,
+        const double* xp,
+        const double* yp,
+        const double* tdk,
+        const double* pmb,
+        const double* rh,
+        const double* wl,
+        const double* tlr,
+        double* AOPRMS
+    );
+
+#define fslaAopqk F77_FUNC_(sla_aopqk,SLA_AOPQK)
+extern void fslaAopqk(
+        const double* RAP,
+        const double* DAP,
+        double* AOPRMS,
+        double* AOB,
+        double* ZOB,
+        double* HOB,
+        double* DOB,
+        double* ROB
+    );
+
+#define fslaOapqk F77_FUNC_(sla_oapqk,SLA_OAPQK)
+extern void fslaOapqk(
+        char* TYPE,
+        const double* OB1,
+        const double* OB2,
+        double* AOPRMS,
+        double* RAP,
+        double* DAP
+    );
+
+#define fslaAirmas F77_FUNC_(sla_airmas,SLA_AIRMAS)
+extern double fslaAirmas(
+        const double* zenithDistance
+    );
+
+/*
+    void slaAoppa(date, dut, elongm, phim, hm, xp, yp, tdk, pmb, rh, wl, tlr, aoprms)
+    
+    PARAMETERS
+    
+    double date
+    double dut
+    double elongm
+    double phim
+    double hm
+    double xp
+    double yp
+    double tdk
+    double pmb
+    double rh
+    double wl
+    double tlr
+    double *aoprms
+*/
+#define slaAoppa(date,dut,elongm,phim,him,xp,yp,tdk,pmb,rh,wl,tlr,AOPRMS) \
+fslaAoppa(&date,&dut,&elongm,&phim,&him,&xp,&yp,&tdk,&pmb,&rh,&wl,&tlr,AOPRMS)
+
+/*
+    void slaAopqk(rap, dap, aoprms, aob, zob, hob, dob, rob)
+    
+    PARAMETERS
+    
+    double rap
+    double dap
+    double *aoprms
+    double *aob
+    double *zob
+    double *hob
+    double *dob
+    double *rob
+*/
+
+#define slaAopqk(RAP,DAP,AOPRMS,AOB,ZOB,HOB,DOB,ROB) \
+fslaAopqk(&RAP,&DAP,AOPRMS,AOB,ZOB,HOB,DOB,ROB)
+
+/*
+    void slaOapqk(type, ob1, ob2, aoprms, rap, dap)
+    
+    PARAMETERS
+    
+    char *type
+    double ob1
+    double ob2
+    double *aoprms
+    double *rap
+    double *dap
+*/
+
+#define slaOapqk(TYPE, OB1, OB2, AOPRMS, RAP, DAP) \
+fslaOapqk(TYPE, &OB1, &OB2, AOPRMS, RAP, DAP)
+
+/*
+    double slaAirmas(zd)
+    
+    PARAMETERS
+    
+    double zd
+*/
+#define slaAirmas(ZD) fslaAirmas(&ZD)
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/.cvsignore	(revision 22271)
@@ -0,0 +1,7 @@
+Makefile.in
+.deps
+.libs
+Makefile
+*.lo
+*.la
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/Makefile.am	(revision 22271)
@@ -0,0 +1,44 @@
+#Makefile for collections functions of psLib
+#
+
+INCLUDES = \
+	-I$(top_srcdir)/src/astronomy \
+	-I$(top_srcdir)/src/dataManip \
+	-I$(top_srcdir)/src/dataIO \
+	-I$(top_srcdir)/src/image \
+	-I$(top_srcdir)/src/sysUtils \
+	$(all_includes)
+
+noinst_LTLIBRARIES = libpslibcollections.la
+
+libpslibcollections_la_SOURCES = \
+	psBitSet.c \
+	psVector.c \
+	psPixels.c \
+	psList.c \
+	psScalar.c \
+	psCompare.c \
+	psArray.c \
+	psHash.c \
+	psMetadata.c \
+	psMetadataIO.c
+
+BUILT_SOURCES = psCollectionsErrors.h
+EXTRA_DIST = psCollectionsErrors.dat psCollectionsErrors.h collections.i
+
+psCollectionsErrors.h:psCollectionsErrors.dat
+	perl $(top_srcdir)/src/parseErrorCodes.pl --data=$? $@
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psBitSet.h \
+	psVector.h \
+	psPixels.h \
+	psList.h \
+	psScalar.h \
+	psCompare.h \
+	psArray.h \
+	psHash.h \
+	psMetadata.h \
+	psMetadataIO.h 
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/collections.i
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/collections.i	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/collections.i	(revision 22271)
@@ -0,0 +1,9 @@
+/* collections headers */
+%include "psArray.h"
+%include "psBitSet.h"
+%include "psCollectionsErrors.h"
+%include "psCompare.h"
+%include "psHash.h"
+%include "psList.h"
+%include "psScalar.h"
+%include "psVector.h"
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psArray.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psArray.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psArray.c	(revision 22271)
@@ -0,0 +1,205 @@
+
+/** @file  psArray.c
+ *
+ *  @brief Contains support for basic vector types
+ *
+ *  This file defines the basic type for a vector struct and functions useful
+ *  in manupulating vectors.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.27.4.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************/
+
+/*  INCLUDE FILES                                                             */
+
+/******************************************************************************/
+#include<stdlib.h>                         // for qsort, etc.
+#include<string.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psArray.h"
+#include "psLogMsg.h"
+
+#include "psCollectionsErrors.h"
+
+/*****************************************************************************
+  FUNCTION IMPLEMENTATION - LOCAL
+ *****************************************************************************/
+static void arrayFree(psArray* psArr);
+
+static void arrayFree(psArray* psArr)
+{
+    if (psArr == NULL) {
+        return;
+    }
+
+    p_psArrayElementFree(psArr);
+
+    psFree(psArr->data);
+}
+
+/*****************************************************************************
+  FUNCTION IMPLEMENTATION - PUBLIC
+ *****************************************************************************/
+psArray* psArrayAlloc(psU32 nalloc)
+{
+    psArray* psArr = NULL;
+
+    // Create vector struct
+    psArr = (psArray* ) psAlloc(sizeof(psArray));
+    psMemSetDeallocator(psArr, (psFreeFcn) arrayFree);
+
+    psArr->nalloc = nalloc;
+    psArr->n = nalloc;
+
+    // Create vector data array
+    psArr->data = psAlloc(nalloc * sizeof(psPtr));
+
+    return psArr;
+}
+
+psArray* psArrayRealloc(psArray* in, psU32 nalloc)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,PS_ERRORTEXT_psArray_REALLOC_NULL);
+        return NULL;
+    } else if (in->nalloc != nalloc) {     // No need to realloc to same size
+        if (nalloc < in->n) {
+            for (psS32 i = nalloc; i < in->n; i++) {      // For reduction in vector size
+                psFree(in->data[i]);
+            }
+            in->n = nalloc;
+        }
+        // Realloc after decrementation to avoid accessing freed array elements
+        in->data = psRealloc(in->data, nalloc * sizeof(psPtr));
+        in->nalloc = nalloc;
+    }
+
+    return in;
+}
+
+psArray* psArrayAdd(psArray* psArr,
+                    int delta,
+                    const psPtr data)
+{
+    if (psArr == NULL) {
+        return psArr;
+    }
+
+    int n = psArr->n;
+
+    if (n >= psArr->nalloc) {
+        // array needs to be expanded to make room for more elements
+        int d = (delta > 0) ? delta : 10; // as spec'ed in SDRS.
+        psArr = psArrayRealloc(psArr, n+d);
+    }
+
+    // add the element to the end of the array.
+    psArr->data[n] = psMemIncrRefCounter(data);
+    psArr->n = n+1;
+
+    return psArr;
+}
+
+psBool psArrayRemove(psArray* psArr,
+                     const psPtr data)
+{
+    psBool success = false;
+
+    if (psArr == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_ARRAY_NULL);
+        return false;
+    }
+
+    psS32 n = psArr->n;
+    psPtr* psArrData = psArr->data;
+    for (psS32 i = n-1; i >= 0; i--) {
+        if (psArrData[i] == data) {
+            memmove(&psArr->data[i],&psArr->data[i+1],(n-i-1)*sizeof(psPtr));
+            n--;
+            success = true;
+        }
+    }
+    psArr->n = n; // reset the array size to indicate the removed item(s)
+
+    return success;
+}
+
+void p_psArrayElementFree(psArray* psArr)
+{
+
+    if (psArr == NULL) {
+        return;
+    }
+
+    for (psS32 i = 0; i < psArr->n; i++) {
+        psFree(psArr->data[i]);
+        psArr->data[i] = NULL;
+    }
+}
+
+psArray* psArraySort(psArray* in, psComparePtrFcn compare)
+{
+    if (in == NULL) {
+        return NULL;
+    }
+
+    qsort(in->data, in->n, sizeof(psPtr), (int (*)(const void* , const void*))compare);
+
+    return in;
+}
+
+/// Set an element in the array.
+psBool psArraySet(psArray* in,                       ///< input array to set element in
+                  psU32 position,                    ///< the element position to set
+                  const void* value) ///< the value to set it to
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_ARRAY_NULL);
+        return false;
+    }
+
+    if (position >= in->nalloc) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
+                position, in->nalloc);
+        return false;
+    }
+
+    psFree(in->data[position]);
+    in->data[position] = value;
+
+    return true;
+}
+
+/// Get an element in the array.
+void* psArrayGet(const psArray* in, psU32 position )
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_ARRAY_NULL);
+        return NULL;
+    }
+
+    if (position >= in->nalloc) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
+                position, in->nalloc);
+        return NULL;
+    }
+
+    return in->data[position];
+}
+
+
+
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psArray.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psArray.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psArray.h	(revision 22271)
@@ -0,0 +1,150 @@
+
+/** @file  psArray.h
+ *
+ *  @brief Contains basic array definitions and operations
+ *
+ *  This file defines the basic type for a array struct and functions useful
+ *  in manupulating arrays.
+ *
+ *  @ingroup Array
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.22.8.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ARRAY_H
+#define PS_ARRAY_H
+
+#include "psType.h"
+#include "psCompare.h"
+
+/// @addtogroup Array
+/// @{
+
+/** An array to support primitive types.
+ *
+ * Struct for maintaining an array of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psU32 nalloc;        ///< Total number of elements available.
+    psU32 n;             ///< Number of elements in use.
+    psPtr* data;                ///< An Array of pointer elements
+}
+psArray;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Allocate an array.
+ *
+ * Uses psLib memory allocation functions to create an array collection of 
+ * data
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+psArray* psArrayAlloc(
+    psU32 nalloc                       ///< Total number of elements to make available.
+);
+
+/** Reallocate an array.
+ *
+ * Uses psLib memory allocation functions to reallocate an array collection 
+ * of data. 
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+psArray* psArrayRealloc(
+    psArray* psArr,                    ///< array to reallocate.
+    psU32 nalloc                       ///< Total number of elements to make available.
+);
+
+/** Add an element to the end the array, expanding the array storage if
+ *  necessary.
+ *
+ *  @return psArray*        The array with the element added
+ */
+psArray* psArrayAdd(
+    psArray* psArr,                    ///< array to operate on
+    int delta,
+    ///< the amount to expand array, if necessary.  If less than one, 10 will be used.
+    const psPtr data   ///< the data pointer to add to psArray
+);
+
+/** Remove an element from the array
+ *
+ *  Finds and removes the specified data pointer from the list.  
+ *
+ * @return bool:  TRUE if the specified data pointer was found and removed, 
+ *                otherwise FALSE.
+ *
+ */
+psBool psArrayRemove(
+    psArray* psArr,                    ///< array to operate on
+    const psPtr data   ///< the data pointer to remove from psArray
+);
+
+/** Deallocate/Dereference elements of an array.
+ *
+ * Uses psLib memory allocation functions to deallocate/dereference elements 
+ * of a array of void pointers.  The array psArr is not freed, and its elements
+ * will all be set to NULL.
+ *
+ */
+void p_psArrayElementFree(
+    psArray* psArr                     ///< Void pointer array to destroy.
+);
+
+/** Sort the array according to an external compare function.
+ *
+ *  Sorts an array via the specification of a comparison function
+ *  to specify how the objects on the array should be sorted.
+ *
+ *  The comparison function must return an integer less than, equal to, or 
+ *  greater than zero if the first argument is considered to be respectively 
+ *  less than, equal to, or greater than the second. 
+ *
+ *  If two members compare as equal, their order in the sorted array is 
+ *  undefined.
+ *
+ *  @return psArray* The sorted array.
+ */
+psArray* psArraySort(
+    psArray* in,                       ///< input array to sort.
+    psComparePtrFcn compare            ///< the compare function
+);
+
+/** Set an element in the array.  If the current element is non-NULL, the old
+ *  element is freed.
+ *
+ *  @return psBool  TRUE if the element was set successfully, otherwise FALSE
+ */
+psBool psArraySet(
+    psArray* in,                       ///< input array to set element in
+    psU32 position,                    ///< the element position to set
+    const void* value   ///< the value to set it to
+);
+
+/** Get an element from the array.
+ *
+ *  @return void*   the element at given position.
+ */
+void* psArrayGet(
+    const psArray* in,   ///< input array to get element from
+    psU32 position                     ///< the element position to get
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psBitSet.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psBitSet.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psBitSet.c	(revision 22271)
@@ -0,0 +1,295 @@
+/** @file  psBitSet.c
+ *
+ *  @brief Creates an array of bytes of arbitrary length for storing individual bits.
+ *
+ *  Bit masks are useful tools for toggling various flags and options. This set of functions module provides
+ *  a mechanism to create an array of bits of arbitrary length and manipulate them with basic binary
+ *  operations. A print function is also provided to display the entire set of bits in binary format as a
+ *  string.
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.24.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "psBitSet.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psString.h"
+
+#include "psCollectionsErrors.h"
+
+enum {
+    UNKNOWN_OP,
+    AND_OP,
+    OR_OP,
+    XOR_OP,
+    NOT_OP
+};
+
+static void bitSetFree(psBitSet* inBitSet);
+
+/** Private function to create a mask.
+ *
+ *  Creates an eight bit mask with the given bit set. All other bits in the byte are zero. The input bit uses
+ *  zero-based indexing, and is the cumulitive index within the array, not the localized byte's bit position.
+ *
+ *  @return  char*: Pointer to byte in which bit is contained.
+ */
+static char mask(psS32 bit)
+{
+    char mask = (char)0x01;
+
+    // Ignore splint warning about negative bit shifts
+    /* @i@ */
+    mask = mask << (bit % 8);
+
+    return mask;
+}
+
+static void bitSetFree(psBitSet* inBitSet)
+{
+    if (inBitSet == NULL) {
+        return;
+    }
+    psFree(inBitSet->bits);
+}
+
+psBitSet* psBitSetAlloc(psS32 n)
+{
+    psS32 numBytes = 0;
+    psBitSet* newObj = NULL;
+
+    if (n < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_ALLOC_NEG_SIZE,
+                n);
+        return NULL;
+    }
+
+    numBytes = ceil(n / 8.0);
+    newObj = psAlloc(sizeof(psBitSet));
+    psMemSetDeallocator(newObj, (psFreeFcn) bitSetFree);
+    newObj->n = numBytes;
+
+    // Ignore splint warning about releasing pointer members, since they've not been allocated yet
+    /* @i@ */
+    newObj->bits = psAlloc(sizeof(char) * numBytes);
+
+    memset(newObj->bits, 0, numBytes);
+
+    return newObj;
+}
+
+psBitSet* psBitSetSet(psBitSet* inBitSet,
+                      psS32 bit)
+{
+    char *byte = NULL;
+
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_SET_NULL);
+        return inBitSet;
+    } else if ( (bit < 0) ||
+                (bit > inBitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE,
+                bit,inBitSet->n * 8 - 1);
+        return inBitSet;
+    }
+    // Variable byte is the byte in the array that contains the bit to be set
+    byte = inBitSet->bits + bit / 8;
+    *byte |= mask(bit);
+
+    return inBitSet;
+}
+
+psBitSet* psBitSetClear(psBitSet* inBitSet,
+                        psS32 bit)
+{
+    char *byte = NULL;
+
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_SET_NULL);
+        return inBitSet;
+    } else if ( (bit < 0) ||
+                (bit > inBitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE,
+                bit,inBitSet->n * 8 - 1);
+        return inBitSet;
+    }
+    // Variable byte is the byte in the array that contains the bit to be set
+    byte = inBitSet->bits + bit / 8;
+    *byte &= ! mask(bit);
+
+    return inBitSet;
+}
+
+psBool psBitSetTest(const psBitSet* inBitSet,
+                    psS32 bit)
+{
+    char *byte = NULL;
+
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_SET_NULL);
+        return false;
+    } else if ( (bit < 0) ||
+                (bit > inBitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE,
+                bit,inBitSet->n * 8 - 1);
+        return false;
+    }
+
+    // Variable byte is the byte in the array that contains the bit to be tested
+    byte = inBitSet->bits + bit / 8;
+    return ((*byte & mask(bit)) != 0);
+}
+
+psBitSet* psBitSetOp(psBitSet* outBitSet,
+                     const psBitSet* inBitSet1,
+                     const char *operator,
+                     const psBitSet* inBitSet2)
+{
+    psS32 i = 0;
+    psS32 n = 0;
+    char* outBits = NULL;
+    char* inBits1 = NULL;
+    char* inBits2 = NULL;
+    psS32 op = UNKNOWN_OP;
+
+    if (inBitSet1 == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_FIRST_OPERAND_NULL);
+        psFree(outBitSet);
+        return NULL;
+    }
+    inBits1 = inBitSet1->bits;
+
+    if (operator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_OPERATOR_NULL);
+        psFree(outBitSet);
+        return NULL;
+    }
+
+    // parse the operator
+    if (strcmp(operator,"AND")==0) {
+        op = AND_OP;
+    } else if (strcmp(operator,"OR")==0) {
+        op = OR_OP;
+    } else if (strcmp(operator,"XOR")==0) {
+        op = XOR_OP;
+    } else if (strcmp(operator,"NOT")==0) {
+        op = NOT_OP;
+    } else {
+        psFree(outBitSet);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_OPERATOR_INVALID,
+                operator);
+        return NULL;
+    }
+
+    if (op != NOT_OP) {
+        if (inBitSet2 == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    PS_ERRORTEXT_psBitSet_SECOND_OPERAND_NULL);
+            psFree(outBitSet);
+            return NULL;
+        }
+
+        if (inBitSet1->n != inBitSet2->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psBitSet_OPERANDS_SIZE_DIFFER);
+            psFree(outBitSet);
+            return NULL;
+        }
+        inBits2 = inBitSet2->bits;
+    }
+
+    if (outBitSet == NULL) {
+        outBitSet = psBitSetAlloc(inBitSet1->n*8);
+    } else if (outBitSet->n != inBitSet1->n) {
+        outBitSet->n = inBitSet1->n;
+        outBitSet->bits = psRealloc(outBitSet->bits, inBitSet1->n);
+    }
+
+    n = outBitSet->n;
+    outBits = outBitSet->bits;
+
+    switch (op) {
+    case AND_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] & inBits2[i];
+        }
+        break;
+    case OR_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] | inBits2[i];
+        }
+        break;
+    case XOR_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] ^ inBits2[i];
+        }
+        break;
+    case NOT_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = ~inBits1[i];
+        }
+        break;
+    default:
+        psAbort("psBitSetOp",
+                "Unexpected error - operator parsed successfully but not valid?");
+    }
+
+    return outBitSet;
+}
+
+psBitSet* psBitSetNot(psBitSet* outBitSet,
+                      const psBitSet* inBitSet)
+{
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_OPERAND_NULL);
+        psFree(outBitSet);
+        return NULL;
+    }
+
+    outBitSet = psBitSetOp(outBitSet,inBitSet,"NOT",NULL);
+
+    if (outBitSet == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                PS_ERRORTEXT_psBitSet_NOT_OP_FAILED);
+    }
+
+    return outBitSet;
+}
+
+char *psBitSetToString(const psBitSet* inBitSet)
+{
+    psS32 i = 0;
+    psS32 numBits = inBitSet->n * 8;
+    char *outString = psAlloc((size_t) numBits + 1);
+
+    for (i = 0; i < numBits; i++) {
+        outString[numBits - i - 1] = psBitSetTest(inBitSet, i) ? '1' : '0';
+    }
+
+    outString[numBits] = 0;
+
+    return outString;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psBitSet.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psBitSet.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psBitSet.h	(revision 22271)
@@ -0,0 +1,145 @@
+/** @file  psBitSet.h
+ *
+ *  @brief Creates an array of bytes of arbitrary length for storing individual bits.
+ *
+ *  Bit masks are useful tools for toggling various flags and options. This set of functions module provides
+ *  a mechanism to create an array of bits of arbitrary length and manipulate them with basic binary
+ *  operations. A print function is also provided to display the entire set of bits in binary format as a
+ *  string.
+ *
+ *  @ingroup BitSet
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.17.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSBITSET_H
+#define PSBITSET_H
+
+#include "psType.h"
+
+/// @addtogroup BitSet
+/// @{
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+/** Struct containing array of bytes to hold bit data and corresponding array length.
+ *
+ *  The bits in the struct are assembled in as an array of bytes with eight bits per byte. The bits are
+ *  arranged with the LSB in first (right most) position of the first array element.
+ */
+typedef struct
+{
+    psS32 n;                             ///< Number of bytes in the array
+    char *bits;                        ///< Aray of bytes holding bits
+}
+psBitSet;
+
+/*****************************************************************************/
+/* FUNCTION PROTOTYPES                                                       */
+/*****************************************************************************/
+
+/** Allocate a psBitSet.
+ *
+ *  Create a psBitSet with the number of bits specified by the user. All bits are set to zero upon
+ *  allocation.
+ *
+ *  @return  psBitSet* : Pointer to struct containing array of bits and size of array.
+ */
+
+/*@null@*/
+psBitSet* psBitSetAlloc(
+    psS32 n                            ///< Number of bits in psBitSet array
+);
+
+/** Set a bit.
+ *
+ *  Sets a bit at a given bit location. The bit is set based on a zero index with the
+ *  first bit set in the zero bit slot of the zero element of the byte array. As an example, setting bit 3 in
+ *  an array with two elements would result in an psBitSet that looks like 00000000 00001000.
+ *
+ *  @return  psBitSet* : Pointer to struct containing psBitSet.
+ */
+psBitSet* psBitSetSet(
+    /* @returned@ */
+    psBitSet* inMask,                  ///< Pointer to psBitSet to be set.
+    psS32 bit                          ///< Bit to be set.
+);
+
+/** Clear a bit.
+ *
+ *  Clear a bit at a given bit location. The bit is cleared based on a zero 
+ *  index with the first bit set in the zero bit slot of the zero element of 
+ *  the byte array. 
+ *
+ *  @return  psBitSet* : Pointer to struct containing psBitSet.
+ */
+psBitSet* psBitSetClear(
+    /* @returned@ */
+    psBitSet* inMask,                  ///< Pointer to psBitSet to be cleared.
+    psS32 bit                          ///< Bit to be cleared.
+);
+
+/** Test the value of a bit.
+ *
+ *  Prints the value of a bit at a given bit location, either one or zero. The resulting bit is based on a
+ *  zero index format with the first bit set in the zero bit slot of the zero element of the byte array
+ *  As an example, testing bit 3 in a psBitSet with two bytes that looks like 00000000 00001000 would return a
+ *  value of one, since that is the value that was set.
+ *
+ *  @return  int: Value of bit, either one or zero.
+ */
+
+psBool psBitSetTest(
+    const psBitSet* inMask,            ///< Pointer psBitSet to be tested.
+    psS32 bit                          ///< Bit to be tested.
+);
+
+/** Perform a binary operation on two psBitSets
+ *
+ *  Perform an AND, OR, or XOR on two psBitSets. If the BitMasks are not the same size, the operation will not
+ *  be performed and an error message will be logged.
+ *
+ *  @return  psBitSet* : Pointer to struct containing result of binary operation.
+ */
+psBitSet* psBitSetOp(
+    /* @returned@ */
+    psBitSet* outMask,                 ///< Resulting psBitSet from binary operation
+    const psBitSet* inMask1,           ///< First psBitSet on which to operate
+    const char *operator,  ///< Bit operation
+    const psBitSet* inMask2            ///< First psBitSet on which to operate
+);
+
+/** Perform a not operation on a psBitSet
+ *
+ *  Toggles bits in a psBitset. All zero bits are set to one and all one bits are set to zero.
+ *
+ *  @return  psBitSet* : Pointer to struct containing result of operation.
+ */
+
+psBitSet* psBitSetNot(
+    psBitSet* outBitSet,               ///< Resulting psBitSet from operation
+    const psBitSet* inBitSet           ///< Input psBitSet
+);
+
+/** Convert the psBitSet to a string of ones and zeros.
+ *
+ *  Converts the contents of a psBitSet to a string representation of its binary form of ones and zeros. The
+ *  LSB is the right-most chracter. Each set of eight characters represents one byte.
+ *
+ *  @return  char*: Pointer to character array containing string data.
+ */
+
+char *psBitSetToString(
+    const psBitSet* inMask             ///< psBitSet to convert */
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCollectionsErrors.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCollectionsErrors.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCollectionsErrors.dat	(revision 22271)
@@ -0,0 +1,44 @@
+#
+#  This file is used to generate psCollectionsErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERRORTEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+#
+psArray_REALLOC_NULL                   psArrayRealloc must be given a non-NULL psArray to resize.
+psArray_ARRAY_NULL                     Specified psArray can not be NULL.
+psArray_POSITION_BEYOND_NALLOC         Specified position, %d, is greater than the allocated size of the array, %d.
+#
+psVector_REALLOC_NULL                  psVectorRealloc must a given a non-NULL psVector to resize.  Desired datatype unknown.
+psVector_NOT_A_VECTOR                  The input psVector must have a vector dimension type.
+psVector_SORT_NULL                     psVectorSort can not sort a NULL psVector.
+psVector_UNSUPPORTED_TYPE              Input psVector is an unsupported type (0x%x).
+#
+psBitSet_ALLOC_NEG_SIZE                The number of bit in a psBitSet (%d) must be greater than zero.
+psBitSet_SET_NULL                      Can not operate on a NULL psBitSet.
+psBitSet_BIT_OUTOFRANGE                The specified bit position (%d) is invalid.  Position must be between 0 and %d.
+psBitSet_OPERATOR_NULL                 Specified operator is NULL.  Must specify desired operator.
+psBitSet_OPERATOR_INVALID              Specified operator, %s, is invalid.  Valid operators are AND, OR, and XOR.
+psBitSet_FIRST_OPERAND_NULL            First psBitSet operand can not be NULL.
+psBitSet_SECOND_OPERAND_NULL           Second psBitSet operand can not be NULL.
+psBitSet_OPERANDS_SIZE_DIFFER          The psBitSet operand must be the same size.
+psBitSet_NOT_OP_FAILED                 Could not perform NOT operation.
+psBitSet_OPERAND_NULL                  Operand can not be NULL.
+#
+psScalar_UNSUPPORTED_TYPE              Specified datatype (%d) is unsupported by psScalar.
+psScalar_COPY_NULL                     Can not copy a NULL psScalar.
+#
+psHash_KEY_NULL                        Input key can not be NULL.
+psHash_TABLE_NULL                      Input psHash can not be NULL.
+psHash_DATA_NULL                       Input data can not be NULL.
+#
+psList_LOCATION_INVALID                Specified location, %d, is invalid.
+psList_ITERATOR_INVALID                Specified iterator is not valid.
+psList_ITERATOR_NULL                   Specified iterator is NULL.
+psList_ITERATOR_NONMUTABLE             Specified iterator indicates list is non-mutable.
+psList_LIST_NULL                       Specified psList reference is NULL.
+psList_DATA_NULL                       Specified data item is NULL.
+psList_DATA_NOT_FOUND                  Specified data item is not found in the psList.
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCollectionsErrors.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCollectionsErrors.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCollectionsErrors.h	(revision 22271)
@@ -0,0 +1,63 @@
+/** @file  psCollectionsErrors.h
+ *
+ *  @brief Contains the error text for the collections functions
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-21 21:18:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_COLLECTIONS_ERRORS_H
+#define PS_COLLECTIONS_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psCollectionsErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psCollectionsErrors.dat lines)
+ *     $2  The error text (rest of the line in psCollectionsErrors.dat)
+ *     $n  The order of the source line in psCollecitonsErrors.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+#define PS_ERRORNAME_DOMAIN "psLib.collections."
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_psArray_REALLOC_NULL "psArrayRealloc must be given a non-NULL psArray to resize."
+#define PS_ERRORTEXT_psArray_ARRAY_NULL "Specified psArray can not be NULL."
+#define PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC "Specified position, %d, is greater than the allocated size of the array, %d."
+#define PS_ERRORTEXT_psVector_REALLOC_NULL "psVectorRealloc must a given a non-NULL psVector to resize.  Desired datatype unknown."
+#define PS_ERRORTEXT_psVector_NOT_A_VECTOR "The input psVector must have a vector dimension type."
+#define PS_ERRORTEXT_psVector_SORT_NULL "psVectorSort can not sort a NULL psVector."
+#define PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE "Input psVector is an unsupported type (0x%x)."
+#define PS_ERRORTEXT_psBitSet_ALLOC_NEG_SIZE "The number of bit in a psBitSet (%d) must be greater than zero."
+#define PS_ERRORTEXT_psBitSet_SET_NULL "Can not operate on a NULL psBitSet."
+#define PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE "The specified bit position (%d) is invalid.  Position must be between 0 and %d."
+#define PS_ERRORTEXT_psBitSet_OPERATOR_NULL "Specified operator is NULL.  Must specify desired operator."
+#define PS_ERRORTEXT_psBitSet_OPERATOR_INVALID "Specified operator, %s, is invalid.  Valid operators are AND, OR, and XOR."
+#define PS_ERRORTEXT_psBitSet_FIRST_OPERAND_NULL "First psBitSet operand can not be NULL."
+#define PS_ERRORTEXT_psBitSet_SECOND_OPERAND_NULL "Second psBitSet operand can not be NULL."
+#define PS_ERRORTEXT_psBitSet_OPERANDS_SIZE_DIFFER "The psBitSet operand must be the same size."
+#define PS_ERRORTEXT_psBitSet_NOT_OP_FAILED "Could not perform NOT operation."
+#define PS_ERRORTEXT_psBitSet_OPERAND_NULL "Operand can not be NULL."
+#define PS_ERRORTEXT_psScalar_UNSUPPORTED_TYPE "Specified datatype (%d) is unsupported by psScalar."
+#define PS_ERRORTEXT_psScalar_COPY_NULL "Can not copy a NULL psScalar."
+#define PS_ERRORTEXT_psHash_KEY_NULL "Input key can not be NULL."
+#define PS_ERRORTEXT_psHash_TABLE_NULL "Input psHash can not be NULL."
+#define PS_ERRORTEXT_psHash_DATA_NULL "Input data can not be NULL."
+#define PS_ERRORTEXT_psList_LOCATION_INVALID "Specified location, %d, is invalid."
+#define PS_ERRORTEXT_psList_ITERATOR_INVALID "Specified iterator is not valid."
+#define PS_ERRORTEXT_psList_ITERATOR_NULL "Specified iterator is NULL."
+#define PS_ERRORTEXT_psList_ITERATOR_NONMUTABLE "Specified iterator indicates list is non-mutable."
+#define PS_ERRORTEXT_psList_LIST_NULL "Specified psList reference is NULL."
+#define PS_ERRORTEXT_psList_DATA_NULL "Specified data item is NULL."
+#define PS_ERRORTEXT_psList_DATA_NOT_FOUND "Specified data item is not found in the psList."
+//~End
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCompare.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCompare.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCompare.c	(revision 22271)
@@ -0,0 +1,119 @@
+
+/** @file psCompare.c
+ *  @brief Comparison functions for sorting routines
+ *  @ingroup Compare
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-17 19:26:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psCompare.h"
+
+#define COMPARE_NUMERIC_PTR(TYPE) \
+int psCompare##TYPE##Ptr(const void** a, const void** b) { \
+    return **((ps##TYPE**)a) - **((ps##TYPE**)b); \
+}
+
+#define COMPARE_NUMERIC_PTR_DESCENDING(TYPE) \
+int psCompareDescending##TYPE##Ptr(const void** a, const void** b) { \
+    return **((ps##TYPE**)b) - **((ps##TYPE**)a); \
+}
+
+#define COMPARE_NUMERIC(TYPE) \
+int psCompare##TYPE(const void* a, const void* b) { \
+    return *((ps##TYPE*)a) - *((ps##TYPE*)b); \
+}
+
+#define COMPARE_NUMERIC_DESCENDING(TYPE) \
+int psCompareDescending##TYPE(const void* a, const void* b) { \
+    return *((ps##TYPE*)b) - *((ps##TYPE*)a); \
+}
+
+COMPARE_NUMERIC_PTR(S8)
+COMPARE_NUMERIC_PTR(S16)
+COMPARE_NUMERIC_PTR(S32)
+COMPARE_NUMERIC_PTR(S64)
+COMPARE_NUMERIC_PTR(U8)
+COMPARE_NUMERIC_PTR(U16)
+COMPARE_NUMERIC_PTR(U32)
+COMPARE_NUMERIC_PTR(U64)
+
+int psCompareF32Ptr(const void** a, const void** b)
+{
+    psF32 diff = **((psF32**)a) - **((psF32**)b);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareF64Ptr(const void** a, const void** b)
+{
+    psF64 diff = **((psF64**)a) - **((psF64**)b);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC_PTR_DESCENDING(S8)
+COMPARE_NUMERIC_PTR_DESCENDING(S16)
+COMPARE_NUMERIC_PTR_DESCENDING(S32)
+COMPARE_NUMERIC_PTR_DESCENDING(S64)
+COMPARE_NUMERIC_PTR_DESCENDING(U8)
+COMPARE_NUMERIC_PTR_DESCENDING(U16)
+COMPARE_NUMERIC_PTR_DESCENDING(U32)
+COMPARE_NUMERIC_PTR_DESCENDING(U64)
+
+int psCompareDescendingF32Ptr(const void** a, const void** b)
+{
+    psF32 diff = **((psF32**)b) - **((psF32**)a);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareDescendingF64Ptr(const void** a, const void** b)
+{
+    psF64 diff = **((psF64**)b) - **((psF64**)a);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC(S8)
+COMPARE_NUMERIC(S16)
+COMPARE_NUMERIC(S32)
+COMPARE_NUMERIC(S64)
+COMPARE_NUMERIC(U8)
+COMPARE_NUMERIC(U16)
+COMPARE_NUMERIC(U32)
+COMPARE_NUMERIC(U64)
+
+int psCompareF32(const void* a, const void* b)
+{
+    psF32 diff = *((psF32*)a) - *((psF32*)b);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareF64(const void* a, const void* b)
+{
+    psF64 diff = *((psF64*)a) - *((psF64*)b);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC_DESCENDING(S8)
+COMPARE_NUMERIC_DESCENDING(S16)
+COMPARE_NUMERIC_DESCENDING(S32)
+COMPARE_NUMERIC_DESCENDING(S64)
+COMPARE_NUMERIC_DESCENDING(U8)
+COMPARE_NUMERIC_DESCENDING(U16)
+COMPARE_NUMERIC_DESCENDING(U32)
+COMPARE_NUMERIC_DESCENDING(U64)
+
+int psCompareDescendingF32(const void* a, const void* b)
+{
+    psF32 diff = *((psF32*)b) - *((psF32*)a);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareDescendingF64(const void* a, const void* b)
+{
+    psF64 diff = *((psF64*)b) - *((psF64*)a);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCompare.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCompare.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psCompare.h	(revision 22271)
@@ -0,0 +1,508 @@
+/** @file psCompare.h
+ *  @brief Comparison functions for sorting routines
+ *
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @ingroup Compare
+ *
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_COMPARE_H)
+#define PS_COMPARE_H
+
+#include "psType.h"
+
+/** @addtogroup Compare
+*  @{
+*/
+
+/** A comparison function for sorting elements that are pointers to data,
+ *  e.g., for psList of pointers to numeric values.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+typedef int (*psComparePtrFcn) (
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** A comparison function for sorting.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+typedef int (*psCompareFcn) (
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+/** Compare function of psS8 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS16 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU8 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU16 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS8 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS16 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU8 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU16 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingU32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingU64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingF32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingF64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingF32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingF64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psHash.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psHash.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psHash.c	(revision 22271)
@@ -0,0 +1,466 @@
+
+/** @file  psHash.c
+*
+*  @brief Contains support for basic hashing functions.
+*
+*  This file will hold the functions for defining a hash table with arbitrary
+*  data types, allocating/deallocating that hash table, adding and removing
+*  data from that hash table, and listing all keys defined in the hash table.
+*
+*  @author Robert Lupton, Princeton University
+*  @author George Gusciora, MHPCC
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.15.2.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:56 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "psHash.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psTrace.h"
+#include "psError.h"
+
+#include "psCollectionsErrors.h"
+
+static psHashBucket* hashBucketAlloc(const char *key, psPtr data, psHashBucket* next);
+static void hashBucketFree(psHashBucket* bucket);
+static psPtr doHashWork(psHash* table, const char *key, psPtr data, psBool remove
+                           );
+static void hashFree(psHash* table);
+
+/******************************************************************************
+psHashKeyList(table): this function creates a linked list with an entry in
+that list for every key in the hash table.
+Inputs:
+    table: a hash table
+Return;
+    The linked list
+ *****************************************************************************/
+psList* psHashKeyList(const psHash* table)
+{
+    psS32 i = 0;                  // Loop index variable
+    psList* myLinkList = NULL;  // The output data structure
+    psHashBucket* ptr = NULL;   // Used to step thru linked list.
+
+    if (table == NULL) {
+        return NULL;
+    }
+    // Create the linked list
+    myLinkList = psListAlloc(NULL);
+
+    // Loop through every bucket in the hash table.  If that bucket is not
+    // NULL, then add the bucket's key to the linked list.
+    for (i = 0; i < table->nbucket; i++) {
+        if (table->buckets[i] != NULL) {
+            // Since a bucket contains a linked list of keys/data, we must
+            // step trough each key in that linked list:
+
+            ptr = table->buckets[i];
+            while (ptr != NULL) {
+                psListAdd(myLinkList, PS_LIST_HEAD, ptr->key);
+                ptr = ptr->next;
+            }
+        }
+    }
+
+    // Return the linked list
+    return (myLinkList);
+}
+
+/******************************************************************************
+hashBucketAlloc(key, data, next): This procedure creates a new hash bucket
+with the specified key, data, and next.
+Inputs:
+    key:  the new bucket's key pointer
+    data: the new bucket's data pointer
+    next: the new bucket's key pointer
+Return:
+    the new hash bucket.
+ *****************************************************************************/
+static psHashBucket* hashBucketAlloc(const char *key,
+                                     psPtr data,
+                                     psHashBucket* next)
+{
+    // Allocate memory for the new hash bucket.
+    psHashBucket* bucket = psAlloc(sizeof(psHashBucket));
+
+    psMemSetDeallocator(bucket, (psFreeFcn) hashBucketFree);
+
+    // Initialize the bucket.
+    bucket->key = psStringCopy(key);
+
+    if (data == NULL) {
+        // NOTE: Should we flag a warning message?
+        bucket->data = NULL;
+    } else {
+        bucket->data = psMemIncrRefCounter(data);
+    }
+
+    bucket->next = next;
+
+    return bucket;
+}
+
+/******************************************************************************
+hashBucketFree(bucket): This procedure deallocates the specified
+hash bucket.
+Inputs:
+    bucket: the hash bucket to be freed.
+Return:
+    NONE
+ *****************************************************************************/
+static void hashBucketFree(psHashBucket* bucket)
+{
+    if (bucket == NULL) {
+        return;
+    }
+
+    psFree(bucket->key);
+
+    psFree(bucket->data);
+}
+
+/******************************************************************************
+psHashAlloc(nbucket): this procedure creates a new hash table with the
+specified number of buckets.
+Inputs:
+    nbucket: initial number of buckets
+Return:
+    The new hash table.
+ *****************************************************************************/
+psHash* psHashAlloc(psS32 nbucket)        // initial number of buckets
+{
+    psS32 i = 0;                  // loop index variable
+
+    // Create the new hash table.
+    psHash* table = psAlloc(sizeof(psHash));
+
+    psMemSetDeallocator(table, (psFreeFcn) hashFree);
+
+    // Allocate memory for the buckets.
+    table->buckets = psAlloc(nbucket * sizeof(psHashBucket* ));
+    table->nbucket = nbucket;
+
+    psTrace("utils.hash", 1, "Creating %d-element hash table\n", nbucket);
+
+    // Initialize all buckets to NULL.
+    for (i = 0; i < nbucket; i++)
+    {
+        table->buckets[i] = NULL;
+    }
+
+    // Return the new hash table.
+    return table;
+}
+
+/******************************************************************************
+hashFree(table): This procedure deallocates the specified hash
+table.  It loops through each bucket, and calls hashBucketFree() on that
+bucket.
+ 
+Inputs:
+    table: a hash table
+Return:
+    NONE
+ *****************************************************************************/
+static void hashFree(psHash* table)
+{
+    psS32 i = 0;                  // Loop index variable.
+
+    if (table == NULL) {
+        return;
+    }
+    // Loop through each bucket in the hash table.  If that bucket is not
+    // NULL, then free the bucket via a function call to hashBucketFree();
+    for (i = 0; i < table->nbucket; i++) {
+
+        // A bucket is composed of a linked list of buckets.
+        while (table->buckets[i] != NULL) {
+            psHashBucket* bucket = table->buckets[i];
+            table->buckets[i] = bucket->next;
+            psFree(bucket);
+        }
+    }
+
+    // Free the bucket structure, then the hash table.
+    psFree(table->buckets);
+}
+
+/******************************************************************************
+doHashWork(table, key, data, remove): This is an internal
+procedure which does the bulk of the work in using the hash table.  Depending
+upon the input parameters, it will either insert a new key/data into the hash
+table, retrieve the data for a specified key, or remove a key/data item.  If
+we try to insert a key that already exists in the hash table, then we deallocate
+the existing data/key item.
+Inputs:
+    table: a hash table
+    key: the key to insert, retrieve, or remove.  Must not be NULL.
+    data: the data to insert, if not NULL
+    remove: set to non-zero if the key/data should be removed from the table.
+Return:
+    NONE
+ 
+NOTE: consider removing this private function and simply putting the code
+into the psHashInsert(), psHashLookup(), and psHashRemove().  Why?  Because
+there is little common code between those functions.
+  *****************************************************************************/
+static psPtr doHashWork(psHash* table,
+                        const char *key,
+                        psPtr data, psBool remove
+                           )
+{
+    psS64 hash = 1;          // This will contain an integer value
+
+    // "hashed" from the key.
+    char *tmpchar = NULL;       // Used in computing the hash function.
+    psHashBucket* ptr = NULL;   // Used to retrieve the hash bucket.
+    psHashBucket* optr = NULL;  // "original pointer": used to step
+
+    // thru the linked list for a bucket.
+
+    // The following condition should never be true, since this is a private
+    // function, but I'm checking it anyway since future coders might change
+    // the way this procedure is called.
+    if ((table == NULL) || (key == NULL)) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return NULL;
+    }
+    // NOTE: This is the originally supplied hash function.
+    // for (psS32 i = 0, len = strlen(key); i < len; i++) {
+    // hash = (hash << 1) ^ key[i];
+    // }
+    // hash &= (table->nbucket - 1);
+
+    // This hash algorithm is from Sedgewick.  NOTE: must reread to ensure that
+    // the size of the hash table is not required to be a prime number.
+    tmpchar = (char *)key;
+    for (hash = 0; *tmpchar != '\0'; tmpchar++) {
+        hash = (64 * hash + *tmpchar) % (table->nbucket);
+    }
+
+    // NOTE: This should not be necessary, but for now, I'm checking bounds
+    // anyway.
+    if ((hash < 0) || (hash >= table->nbucket)) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Internal hash function out of range (%d)", hash);
+    }
+    // ptr will have the correct hash bucket.
+    ptr = table->buckets[hash];
+
+    // We know the correct hash bucket, now we need to know what to do.
+    // If the data parameter is NULL, then, by definition, this is a retrieve
+    // or a remove operation on the hash table.
+
+    if (data == NULL) {
+        if (remove
+           ) {
+            // We search through the linked list for this bucket in
+            // the hash table and look for an entry for this key.
+
+            optr = ptr;
+            while (ptr != NULL) {
+                // Determine if this entry holds the correct key.
+                if (strcmp(key, ptr->key) == 0) {
+                    // The following lines of code are fairly standard ways
+                    // of removing an item from a single-linked list.
+
+                    psPtr data = ptr->data;
+
+                    optr->next = ptr->next;
+                    if (ptr == table->buckets[hash]) {
+                        table->buckets[hash] = ptr->next;
+                    }
+                    psFree(ptr);
+
+                    // By definition, the data associated with that key
+                    // must be returned, not freed.
+                    return data;
+                }
+                optr = ptr;
+                ptr = ptr->next;
+            }
+            return NULL;                   // not in hash
+        }
+        else {
+            // If we get here, then a retrieve operation is requested.  So,
+            // we step trough the linked list at this bucket, and return the
+            // data once we find it, or return NULL if we don't.
+            while (ptr != NULL) {
+                if (strcmp(key, ptr->key) == 0) {
+                    return ptr->data;
+                }
+                ptr = ptr->next;
+            }
+            return NULL;                   // not in hash
+        }
+    } else {
+        // We get here if this procedure was called with non-NULL data.
+        // Therefore, we should insert that data into the hash table.
+        // First, we search through the linked list for this bucket in
+        // the hash table and look for a duplicate entry for this key.
+
+        while (ptr != NULL) {
+            if (strcmp(key, ptr->key) == 0) {
+                // We have found this key in the hash table.
+
+                psTrace("utils.hash.insert", 3, "Replacing data for %s\n", key);
+
+                // NOTE: I have changed this behavior from the originally
+                // supplied code.  Formerly, if itemFree was NULL, then
+                // the new data was not inserted into the hash table.
+
+                psFree(ptr->data);
+
+                ptr->data = psMemIncrRefCounter(data);
+                return data;
+            }
+            ptr = ptr->next;
+        }
+        // We did not found key in the linked list for this bucket of the hash
+        // table.  So, we insert this data at the head of that linked list.
+
+        table->buckets[hash] = hashBucketAlloc(key, data, table->buckets[hash]);
+        return data;
+    }
+}
+
+/******************************************************************************
+psHashAdd(table, key, data): this procedure, which is part of
+the public API, inserts a new key/data pair into the hash table.
+Inputs:
+    table: a hash table
+    key: the key to use
+    data: the data to insert.
+Return:
+    boolean value defining success or failure
+ *****************************************************************************/
+psBool psHashAdd(psHash* table,
+                 const char *key,
+                 const psPtr data)
+{
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return false;
+    }
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return false;
+    }
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_DATA_NULL);
+        return false;
+    }
+
+    return (doHashWork(table, key, data, false) != NULL);
+}
+
+/******************************************************************************
+psHashLookup(table, key): this procedure, which is part of the public API,
+looks up the specified key in the hash table and returns the data associated
+with that key.
+ 
+Inputs:
+    table: a hash table
+    key: the key to use
+Return:
+    The data associated with that key.
+ *****************************************************************************/
+psPtr psHashLookup(const psHash* table, // table to lookup key in
+                   const char *key)     // key to lookup
+{
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return NULL;
+    }
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return NULL;
+    }
+
+    return doHashWork(table, key, NULL, false);
+}
+
+/******************************************************************************
+psHashRemove(table, key): this procedure, which is part of the
+public API, removes the specified key from the hash table.
+Inputs:
+    table: a hash table
+    key: the key to remove
+Return:
+    boolean value defining success or failure
+ *****************************************************************************/
+psBool psHashRemove(psHash* table,
+                    const char *key)
+{
+    psPtr data = NULL;
+    psBool retVal = false;
+
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return false;
+    }
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return false;
+    }
+
+    data = doHashWork(table, key, NULL, true);
+    if (data != NULL) {
+        retVal = true;
+    } else {
+        retVal = false;
+    }
+
+    return retVal;
+}
+
+psArray* psHashToArray(const psHash* table)
+{
+
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return NULL;
+    }
+
+    // first, let's just count the number of data elements to know what size
+    // psArray we need to allocate.
+    int nElements = 0;
+    int nbucket = table->nbucket;
+    for (int i = 0; i < nbucket; i++) {
+        psHashBucket* tmpBucket = table->buckets[i];
+        while (tmpBucket != NULL) {
+            nElements++;
+            tmpBucket = tmpBucket->next;
+        }
+    }
+
+    psArray* result = psArrayAlloc(nElements);
+    result->n = nElements;
+
+    // now fill in the array with the hash table's data
+    psPtr* data = result->data;
+    for (int i = 0; i < nbucket; i++) {
+        psHashBucket* tmpBucket = table->buckets[i];
+        while (tmpBucket != NULL) {
+            *(data++) = psMemIncrRefCounter(tmpBucket->data);
+            tmpBucket = tmpBucket->next;
+        }
+    }
+
+    return result;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psHash.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psHash.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psHash.h	(revision 22271)
@@ -0,0 +1,86 @@
+
+/** @file  psHash.h
+ *  @brief Contains support for basic hashing functions.
+ *  @ingroup HashTable
+ *
+ *  This file will hold the prototypes for defining a hash table with arbitrary
+ *  data types, allocating/deallocating that has table, adding and removing
+ *  data from that hash table, and listing all keys defined in the hash table.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author George Gusciora, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.7.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_HASH_H)
+#define PS_HASH_H
+
+/** \addtogroup HashTable
+ *  \{
+ */
+
+#include "psList.h"
+
+/** A bucket that holds an item of data. */
+typedef struct psHashBucket
+{
+    char *key;                         ///< key for this item of data
+    psPtr data;                        ///< the data itself
+    struct psHashBucket* next;         ///< list of other possible keys
+}
+psHashBucket;
+
+//typedef struct HashTable psHash; ///< Opaque type for a hash table
+
+/** The hash-table itself. */
+typedef struct psHash
+{
+    psS32 nbucket;                     ///< Number of buckets in hash table.
+    psHashBucket* *buckets;            ///< The bucket data.
+}
+psHash;
+
+/// Allocate hash buckets in table.
+psHash* psHashAlloc(
+    psS32 nbucket                  ///< The number of buckets to allocate.
+);
+
+/// Insert entry into table.
+psBool psHashAdd(
+    psHash* table,                 ///< table to insert in
+    const char *key,               ///< key to use
+    const psPtr data   ///< data to insert
+);
+
+/// Lookup key in table.
+psPtr psHashLookup(
+    const psHash* table,  ///< table to lookup key in
+    const char *key                ///< key to lookup
+);
+
+/// Remove key from table.
+psBool psHashRemove(
+    psHash* table,                 ///< table to lookup key in
+    const char *key                ///< key to lookup
+);
+
+/// List all keys in table.
+psList* psHashKeyList(
+    const psHash* table   ///< table to list keys from.
+);
+
+/** Create a psArray from a psHash contents.
+ *
+ *  @return psArray*       A new psArray with duplicate contents of the input psHash
+ */
+psArray* psHashToArray(
+    const psHash* table   ///< table to convert to psArray
+);
+
+/* \} */// End of DataGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psList.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psList.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psList.c	(revision 22271)
@@ -0,0 +1,639 @@
+/** @file psList.c
+ *  @brief Support for doubly linked lists
+ *  @ingroup LinkedList
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.35.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>                       // we need a mutex to make this stuff thread safe.
+
+#include "psError.h"
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psList.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+
+#include "psCollectionsErrors.h"
+
+#define ITER_INIT_HEAD ((psPtr )1)         // next iteration should return head
+#define ITER_INIT_TAIL ((psPtr )2)         // next iteration should return tail
+
+// private functions.
+static void listFree(psList* list);
+static void listIteratorFree(psListIterator* iter);
+static psBool listIteratorRemove(psListIterator* iterator);
+
+static void listFree(psList* list)
+{
+    if (list == NULL) {
+        return;
+    }
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    // remove the free function of iterators to avoid double removal from list
+    psArray* iterators = list->iterators;
+    for (int i = 0; i < iterators->n; i++) {
+        psMemSetDeallocator(iterators->data[i], NULL);
+    }
+
+    psFree(list->iterators);
+
+    for (psListElem* ptr = list->head; ptr != NULL;) {
+        psListElem* next = ptr->next;
+
+        psFree(ptr->data);
+        psFree(ptr);
+
+        ptr = next;
+    }
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    pthread_mutex_destroy(&list->lock)
+    ;
+
+}
+
+static void listIteratorFree(psListIterator* iter)
+{
+    if (iter == NULL) {
+        return;
+    }
+
+    // remove this iterator from the parent list
+    psArrayRemove(iter->list->iterators,iter);
+
+}
+
+static psBool listIteratorRemove(psListIterator* iterator)
+{
+    if (iterator == NULL || iterator->cursor == NULL) {
+        return false;
+    }
+
+    psListElem* elem = iterator->cursor;
+    psList* list = iterator->list;
+    int index = iterator->index;
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    if (elem == list->head) {        // head of list?
+        list->head = elem->next;
+    } else {
+        elem->prev->next = elem->next;
+    }
+
+    if (elem == list->tail) {        // tail of list?
+        list->tail = elem->prev;
+    } else {
+        elem->next->prev = elem->prev;
+    }
+
+    psArray* iterators = list->iterators;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->cursor == elem) {
+            iter->cursor = NULL;
+        } else if (iter->index > index && iter->index > 0) {
+            iter->index--;
+        }
+    }
+
+    list->size--;
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    // OK, delete orphaned list element and its data
+    psFree(elem->data);
+    psFree(elem);
+
+    return true;
+}
+
+psList* psListAlloc(const psPtr data)
+{
+    psList* list = psAlloc(sizeof(psList));
+
+    psMemSetDeallocator(list, (psFreeFcn) listFree);
+
+    list->size = 0;
+    list->head = list->tail = NULL;
+    list->iterators = psArrayAlloc(16);
+    list->iterators->n = 0;
+
+    // create a default iterator
+    psListIteratorAlloc(list,PS_LIST_HEAD,true);
+
+    pthread_mutex_init(&(list->lock), NULL)
+    ;
+
+    if (data != NULL) {
+        psListAdd(list, PS_LIST_TAIL, data);
+    }
+
+    return list;
+}
+
+psListIterator* psListIteratorAlloc(psList* list, int location, bool mutable)
+{
+    psListIterator* iter = psAlloc(sizeof(psListIterator));
+
+    psMemSetDeallocator(iter, (psFreeFcn) listIteratorFree);
+
+    // initialize the attributes
+    iter->list = list;
+    iter->cursor = NULL;
+    iter->index = 0;
+    iter->offEnd = false;
+    iter->mutable = mutable;
+
+    // add to the list's array of iterators
+    psArray* listIterators = list->iterators;
+    int num = listIterators->n;
+    if ( num >= listIterators->nalloc) {
+        // need to resize the array to make more room for another iterator.
+        list->iterators = psArrayRealloc(listIterators,listIterators->nalloc*2);
+        listIterators = list->iterators;
+    }
+    listIterators->data[num] = iter;
+    listIterators->n = num+1;
+
+    if (! psListIteratorSet(iter,location)) {
+        psFree(iter);
+        iter = NULL;
+    }
+
+    return iter;
+}
+
+psBool psListIteratorSet(psListIterator* iterator,
+                         int location)
+{
+    if (iterator == NULL) {
+        return false;
+    }
+
+    psList* list = iterator->list;
+
+    if (location == PS_LIST_TAIL) {
+        iterator->cursor = list->tail;
+        iterator->index = list->size - 1;
+        iterator->offEnd = false;
+        return true;
+    }
+
+    if (location == PS_LIST_HEAD) {
+        iterator->cursor = list->head;
+        iterator->index = 0;
+        iterator->offEnd = false;
+        return true;
+    }
+
+    if (location < 0) {
+        location = list->size + location;
+    }
+
+    if (location < 0 || location >= (int)list->size) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_LOCATION_INVALID,
+                location);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    int index = iterator->index;
+    if (cursor == NULL) {      // set the cursor to the head if it is NULL
+        if (location > list->size/2) { // closer to tail or head?
+            cursor = list->tail;
+            index = list->size - 1;
+        } else {
+            cursor = list->head;
+            index = 0;
+        }
+    }
+
+    if (location < index) {
+        psS32 diff = index - location;
+
+        for (psS32 count = 0; count < diff; count++) {
+            cursor = cursor->prev; // shouldn't need to check for NULL
+        }
+    } else {
+        psS32 diff = location - index;
+
+        for (psS32 count = 0; count < diff; count++) {
+            cursor = cursor->next; // shouldn't need to check for NULL
+        }
+    }
+    iterator->cursor = cursor;
+    iterator->index = location;
+    iterator->offEnd = false;
+
+    return true;
+}
+
+psBool psListAdd(psList* list, psS32 location, const psPtr data)
+{
+
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return false;
+    }
+
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    if (location > 0 && location >= (int)list->size) {
+        psLogMsg(__func__,PS_LOG_WARN,
+                 "Specified location, %d, is beyond the end of the list.  "
+                 "Adding data item to tail.",
+                 location);
+        location = PS_LIST_TAIL;
+    }
+
+    // move ourselves to the given position
+    if (! psListIteratorSet(list->iterators->data[0],location)) {
+        return false;
+    }
+
+    if (location == PS_LIST_TAIL) {
+        // insert the element at the end of the list
+        return psListAddAfter(list->iterators->data[0],data);
+    } else {
+        return psListAddBefore(list->iterators->data[0],data);
+    }
+}
+
+bool psListAddAfter(psListIterator* iterator, const void* data)
+{
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    if (iterator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NULL);
+        return false;
+    }
+
+    // Check if the list pointed by the iterator can be changed
+    if (!iterator->mutable) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NONMUTABLE);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    psList* list = iterator->list;
+
+    if (cursor == NULL && list->head != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_ITERATOR_INVALID);
+        return false;
+    }
+
+    psListElem* elem = psAlloc(sizeof(psListElem));
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    // set the new list element's attributes
+    if (cursor == NULL) { // must be an empty list
+        elem->prev = NULL;
+        elem->next = NULL;
+        list->head = elem;
+        list->tail = elem;
+    } else {
+        elem->prev = cursor;
+        elem->next = cursor->next;
+        cursor->next = elem;
+        if (elem->next == NULL) {
+            list->tail = elem;
+        } else {
+            elem->next->prev = elem;
+        }
+    }
+
+    elem->data = psMemIncrRefCounter(data);
+
+    list->size++;
+
+    if (cursor == list->tail) {
+        list->tail = elem;
+    }
+
+    psArray* iterators = list->iterators;
+    int index = iterator->index;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->index > index) {
+            iter->index++;
+        }
+    }
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    return true;
+}
+
+bool psListAddBefore(psListIterator* iterator, const void* data)
+{
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    if (iterator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NULL);
+        return false;
+    }
+
+    // Check if the list pointed by the iterator can be changed
+    if (!iterator->mutable) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NONMUTABLE);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    psList* list = iterator->list;
+
+    if (cursor == NULL && list->head != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_ITERATOR_INVALID);
+        return false;
+    }
+
+    psListElem* elem = psAlloc(sizeof(psListElem));
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    // set the new list element's attributes
+    if (cursor == NULL) { // empty list.
+        elem->prev = NULL;
+        elem->next = NULL;
+        list->head = elem;
+        list->tail = elem;
+    } else {
+        elem->prev = cursor->prev;
+        elem->next = cursor;
+        cursor->prev = elem;
+        if (elem->prev == NULL) {
+            list->head = elem;
+        } else {
+            elem->prev->next = elem;
+        }
+    }
+
+    elem->data = psMemIncrRefCounter(data);
+
+    list->size++;
+
+    if (cursor == list->head) {
+        list->head = elem;
+    }
+
+    psArray* iterators = list->iterators;
+    int index = iterator->index;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->index >= index) {
+            iter->index++;
+        }
+    }
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    return true;
+}
+
+psBool psListRemove(psList* list,
+                    psS32 location)
+{
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return false;
+    }
+
+    // move ourselves to the given position
+    psListIterator* defaultIterator = list->iterators->data[0];
+    if (! psListIteratorSet(defaultIterator,location)) {
+        return false;
+    }
+
+    return listIteratorRemove(defaultIterator);
+}
+
+psBool psListRemoveData(psList* list,
+                        const psPtr data)
+{
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return false;
+    }
+
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    psListElem* elem = list->head;
+    int index = 0;
+    while (elem != NULL && elem->data != data) {
+        elem = elem->next;
+        index++;
+    }
+    if (elem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NOT_FOUND);
+        return false;
+    }
+
+    psListIterator* iterator = (psListIterator*)list->iterators->data[0];
+    iterator->index = index;
+    iterator->cursor = elem;
+
+    return listIteratorRemove(iterator);
+}
+
+psPtr psListGet(psList* list, psS32 location)
+{
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return NULL;
+    }
+
+    if (list->head == NULL) { // list empty?
+        return NULL;
+    }
+
+    psListIterator* iterator = list->iterators->data[0];
+
+    if (! psListIteratorSet(iterator,location)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_LOCATION_INVALID,
+                location);
+        return NULL;
+    }
+
+    return iterator->cursor->data;
+}
+
+/*
+ * and now return the previous/next element of the list
+ */
+psPtr psListGetAndIncrement(psListIterator* iterator)
+{
+    if (iterator == NULL ) {
+        return NULL;
+    }
+    if (( iterator->cursor == NULL) && (iterator->offEnd)) {
+        return NULL;
+    }
+    if ( (iterator->cursor == NULL) && (!iterator->offEnd)) {
+        iterator->cursor = iterator->list->head;
+        iterator->index = 0;
+        return NULL;
+    }
+
+    psPtr data = iterator->cursor->data;
+
+    iterator->cursor = iterator->cursor->next;
+    iterator->index++;
+    if (iterator->cursor == NULL) {
+        iterator->offEnd = true;
+    }
+
+    return data;
+}
+
+psPtr psListGetAndDecrement(psListIterator* iterator)
+{
+    if (iterator == NULL ) {
+        return NULL;
+    }
+    if ((iterator->cursor == NULL) && (!iterator->offEnd))  {
+        psLogMsg(__func__,PS_LOG_WARN,"Attempt to get previous with itertator cursor NULL and offEnd false");
+        return NULL;
+    }
+    if ( (iterator->cursor == NULL) && (iterator->offEnd) ) {
+        iterator->cursor = iterator->list->tail;
+        iterator->index = iterator->list->size-1;
+        iterator->offEnd = false;
+        return NULL;
+    }
+
+    psPtr data = iterator->cursor->data;
+
+    iterator->cursor = iterator->cursor->prev;
+    iterator->index--;
+
+    return data;
+}
+
+/*
+ * Convert a psList to/from a psVoidPtrArray
+ */
+psArray* psListToArray(const psList* restrict list)
+{
+    psListElem* ptr;
+    psU32 n;
+    psArray* restrict arr;
+
+    if (list == NULL) {
+        return NULL;
+    }
+
+    if (list->size > 0) {
+        arr = psArrayAlloc(list->size);
+    } else {
+        arr = psArrayAlloc(1);
+    }
+
+    arr->n = list->size;
+
+    ptr = list->head;
+    n = list->size;
+    for (psS32 i = 0; i < n; i++) {
+        arr->data[i] = psMemIncrRefCounter(ptr->data);
+        ptr = ptr->next;
+    }
+
+    return arr;
+}
+
+psList* psArrayToList(const psArray* arr)
+{
+    psU32 n;
+    psList* list;               // list of elements
+
+    if (arr == NULL) {
+        return NULL;
+    }
+
+    list = psListAlloc(NULL);
+    n = arr->n;
+    for (psS32 i = 0; i < n; i++) {
+        psListAdd(list, PS_LIST_TAIL, arr->data[i]);
+    }
+
+    return list;
+}
+
+psList* psListSort(psList* list, psComparePtrFcn compare)
+{
+    psArray* arr;
+
+    if (list == NULL) {
+        return NULL;
+    }
+    // convert to indexable vector for use by qsort.
+    arr = psListToArray(list);
+    psArray* iterators = psMemIncrRefCounter(list->iterators);
+    psFree(list);
+
+    arr = psArraySort(arr, compare);
+
+    // convert back to linked list
+    list = psArrayToList(arr);
+    psFree(list->iterators);
+    list->iterators = iterators;
+    psFree(arr);
+
+    // sorting should invalidate all iterator positions.
+    for (int i = 0; i < iterators->n; i++) {
+        ((psListIterator*)iterators->data[i])->cursor = NULL;
+    }
+
+    return list;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psList.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psList.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psList.h	(revision 22271)
@@ -0,0 +1,228 @@
+#if !defined(PS_LIST_H)
+#define PS_LIST_H
+
+/** @file psList.h
+ *  @brief Support for doubly linked lists
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @ingroup LinkedList
+ *
+ *  @version $Revision: 1.23.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <pthread.h>                   // we need a mutex to make this stuff thread safe.
+
+#include "psCompare.h"
+#include "psArray.h"
+
+/** @addtogroup LinkedList
+ *  @{
+ */
+
+/** Special values of index into list
+ *
+ *  This list of possible list position values should be contiguous non-positive values ending with
+ *  PS_LIST_UNKNOWN.  Any value less-than-or-equal-to PS_LIST_UNKNOWN is considered a undefined position.
+ *
+ */
+enum {
+    PS_LIST_HEAD = 0,                  ///< at head
+    PS_LIST_TAIL = -1,                 ///< at tail
+};
+
+/** Doubly-linked list element */
+typedef struct psListElem
+{
+    struct psListElem* prev;           ///< previous link in list
+    struct psListElem* next;           ///< next link in list
+    psPtr data;                        ///< real data item
+}
+psListElem;
+
+/** The psList Linked list structure.  User should not allocate this struct
+ *  directly; rather the psListAlloc should be used.
+ *
+ *  @see psListAlloc
+ */
+typedef struct
+{
+    psU32 size;                        ///< number of elements on list
+    psListElem* head;                  ///< first element on list (may be NULL)
+    psListElem* tail;                  ///< last element on list (may be NULL)
+    psArray* iterators;
+    ///< array of all iterators associated with this list.  First iterator is
+    ///< used internally to improve performance when using indexed access, all
+    ///< others are user-level iterators created by psListIteratorAlloc.
+
+    pthread_mutex_t lock;              ///< mutex to lock a node during changes
+}
+psList;
+
+/** The psList iterator structure.  This should be allocated via
+ *  psListIteratorAlloc and not directly.
+ *
+ *  The life span of a psListIterator object is ended by either a psFree
+ *  of this structure OR psFree of the psList in which it operates on.
+ *
+ *  @see psListIteratorAlloc, psListIteratorSet, psListGetAndIncrement, psListGetAndDecrement
+ */
+typedef struct
+{
+psList* list;                      ///< List iterator to works on
+psListElem* cursor;                ///< current cursor position
+int index;                         ///< the index number in the list
+bool offEnd;                       ///< Iterator off the end?
+bool mutable;                      ///< Is it permissible to modify the list?
+}
+psListIterator;
+
+
+/** Creates a psList linked list object.
+ *
+ *  @return psList* A new psList object.
+ */
+psList* psListAlloc(
+    const psPtr data
+    ///< initial data item; may be NULL if no an empty psList is desired
+)
+;
+
+/** Creates a psListIterator object and associates it with a psList.
+ *
+ *  @return psListIterator* A new psListIterator object.
+ */
+psListIterator* psListIteratorAlloc(
+    psList* list,                      ///< the psList to iterate with
+    int location,                      ///< the initial starting point.
+    ///<  This can be a numeric index, PS_LIST_HEAD, or PS_LIST_TAIL.
+    bool mutable                       ///< Is it permissible to modify list?
+);
+
+/** Set the iterator of the list to a given position.  If location is invalid the
+ *  iterator position is not changed.
+ *
+ *  @return psBool        TRUE if iterator successfully set, otherwise FALSE.
+ */
+psBool psListIteratorSet(
+    psListIterator* iterator,            ///< list iterator
+    int location                         ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Adds an element to a psList at position given.
+ *
+ *  @return psBool        TRUE if item was successfully added, otherwise FALSE.
+ */
+psBool psListAdd(
+    psList* list,                      ///< list to add item to
+    psS32 location,                    ///< index, PS_LIST_HEAD, PS_LIST_TAIL, or numbered location.
+    const psPtr data   ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Adds an data item to a psList at position just after the list position given
+ *
+ *  @return psBool        TRUE if item was successfully added, otherwise FALSE.
+ */
+psBool psListAddAfter(
+    psListIterator* list,              ///< list position to add item to
+    const psPtr data   ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Adds an data item to a psList at position just before the list position given
+ *
+ *  @return psBool        TRUE if item was successfully added, otherwise FALSE.
+ */
+psBool psListAddBefore(
+    psListIterator* list,              ///< list position to add item to
+    const psPtr data   ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Remove an item at the specified location from a list.
+ *
+ *  @return psBool        TRUE if element is successfully removed, otherwise FALSE.
+ */
+psBool psListRemove(
+    psList* list,                      ///< list to remove element from
+    psS32 location                     ///< index of item
+);
+
+/** Remove an item from a list.
+ *
+ *  @return psBool        TRUE if element is successfully removed, otherwise FALSE.
+ */
+psBool psListRemoveData(
+    psList* list,                      ///< list to remove element from
+    const psPtr data   ///< data item to find and remove
+);
+
+/** Retrieve an item from a list.
+ *
+ *  @return psPtr       the item corresponding to the location parameter.  If
+ *                      location is invalid (e.g., a numbered index greater
+ *                      than the list size or if the list is empty), a
+ *                      NULL is returned.
+ */
+psPtr psListGet(
+    psList* list,                      ///< list to retrieve element from
+    psS32 location                     ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Position the specified iterator to the next item in list.
+ *
+ *  @return psPtr       the data item at the original iterator position or NULL if the
+ *                      iterator went past the end of the list.
+ */
+psPtr psListGetAndIncrement(
+    psListIterator* iterator           ///< iterator to move
+);
+
+/** Position the specified iterator to the previous item in list.
+ *
+ *  @return psPtr       the data item at the original iterator position or NULL if the
+ *                      iterator went past the beginning of the list.
+ */
+psPtr psListGetAndDecrement(
+    psListIterator* iterator           ///< iterator to move
+);
+
+/** Convert a linked list to an array
+ *
+ *  @return psArray* A new psArray populated with elements from the list,
+ *                      or NULL if the given dlist parameter is NULL.
+ */
+psArray* psListToArray(
+    const psList* dlist   ///< List to convert
+);
+
+/** Convert array to a doubly-linked list
+ *
+ *  @return psList* A new psList populated with elements formt the psArray,
+ *                      or NULL is the given arr parameter is NULL.
+ */
+psList* psArrayToList(
+    const psArray* arr   ///< vector to convert
+);
+
+/** Sort a list via a comparison function.
+ *
+ *  The comparison function must return an integer less than, equal to, or 
+ *  greater than zero if the first argument is considered to be respectively 
+ *  less than, equal to, or greater than the second. 
+ *
+ *  If two members compare as equal, their order in the sorted array is 
+ *  undefined.
+ *
+ *  @return psList*     Sorted list.
+ */
+psList* psListSort(
+    psList* list,                      ///< the list to sort
+    psComparePtrFcn compare            ///< the comparison function
+);
+
+/// @} End of DataGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadata.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadata.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadata.c	(revision 22271)
@@ -0,0 +1,694 @@
+/** @file  psMetadata.c
+*
+*
+*  @brief Contains metadata structures, enumerations and functions prototypes.
+*
+*  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+*  prototypes necessary creating psLib metadata APIs
+*
+*  @ingroup Metadata
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.61.2.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:56 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include<stdio.h>
+#include<stdarg.h>
+#include<string.h>
+
+#include "fitsio.h"
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psList.h"
+#include "psHash.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psLookupTable.h"
+#include "psString.h"
+#include "psAstronomyErrors.h"
+#include "psConstants.h"
+
+static psS32 metadataId = 0;
+
+static psMetadataItem* makeMetaMulti(psHash* table, const char* key, psMetadataItem* existing)
+{
+
+    if (existing != NULL && existing->type == PS_META_MULTI) {
+        return existing;
+    }
+
+
+    psMetadataItem* item = psMetadataItemAlloc(key,
+                           PS_META_MULTI,
+                           "List of Metadata Items",
+                           NULL);
+
+    psListAdd(item->data.list,PS_LIST_TAIL,existing);
+
+    if (existing != NULL) {
+        psHashRemove(table,key); // take out the old entry
+    }
+
+    psHashAdd(table, key, item); // put in the new MULTI list entry
+
+    // free local references of newly allocated item.
+    psFree(item);
+
+    return item;
+}
+
+static void metadataItemFree(psMetadataItem* metadataItem)
+{
+    psMetadataType type;
+
+    type = metadataItem->type;
+
+    if (metadataItem == NULL) {
+        return;
+    }
+
+    psFree(metadataItem->name);
+    psFree(metadataItem->comment);
+    if (! PS_META_IS_PRIMITIVE(type)) {
+        psFree(metadataItem->data.V);
+    }
+}
+
+static void metadataIteratorFree(psMetadataIterator* iter)
+{
+    if (iter == NULL) {
+        return;
+    }
+    psFree(iter->iter);
+
+    if (iter->preg != NULL) {
+        regfree(iter->preg);
+    }
+}
+
+static void metadataFree(psMetadata* metadata)
+{
+    if (metadata == NULL) {
+        return;
+    }
+    psFree(metadata->list);
+    psFree(metadata->table);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psMetadataItem* psMetadataItemAlloc(const char *name, psMetadataType type,
+                                    const char *comment, ...)
+{
+    va_list argPtr;
+    psMetadataItem* metadataItem = NULL;
+
+    // Get the variable list parameters to pass to allocation function
+    va_start(argPtr, comment);
+
+    // Call metadata item allocation
+    metadataItem = psMetadataItemAllocV(name, type, comment, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    return metadataItem;
+}
+
+#define METADATAITEM_ALLOC_TYPE(NAME,TYPE,METATYPE) \
+psMetadataItem* psMetadataItemAlloc##NAME(const char* name, \
+        const char* comment, \
+        TYPE value) \
+{ \
+    return psMetadataItemAlloc(name, METATYPE, comment, value); \
+}
+
+METADATAITEM_ALLOC_TYPE(Str,const char*,PS_META_STR)
+METADATAITEM_ALLOC_TYPE(F32,psF32,PS_META_F32)
+METADATAITEM_ALLOC_TYPE(F64,psF64,PS_META_F64)
+METADATAITEM_ALLOC_TYPE(S32,psS32,PS_META_S32)
+METADATAITEM_ALLOC_TYPE(Bool,psBool,PS_META_BOOL)
+
+psMetadataItem* psMetadataItemAllocV(const char *name, psMetadataType type,
+                                     const char *comment, va_list argPtr)
+{
+    psMetadataItem* metadataItem = NULL;
+    char tmp;
+    int nBytes;
+
+    PS_PTR_CHECK_NULL(name,NULL);
+
+    // Allocate metadata item
+    metadataItem = (psMetadataItem*) psAlloc(sizeof(psMetadataItem));
+    metadataItem->data.V = NULL;
+
+    // Set deallocator
+    psMemSetDeallocator(metadataItem, (psFreeFcn) metadataItemFree);
+
+    // set metadata item comment
+    if (comment == NULL) {
+        // Per SDRS, null isn't allowed, must use "" instead
+        metadataItem->comment = psStringCopy("");
+    } else {
+        metadataItem->comment = psStringCopy(comment);
+    }
+
+    // Set metadata item unique id
+    *(psS32 *)(&metadataItem->id) = ++metadataId;
+
+    // Set metadata item type
+    metadataItem->type = type & PS_METADATA_TYPE_MASK;
+
+    // Allocate and set metadata item name
+    nBytes = vsnprintf(&tmp, 0, name, argPtr) + 1;
+    metadataItem->name = (char *)psAlloc(sizeof(char) * nBytes);
+    vsprintf(metadataItem->name, name, argPtr);
+
+    // Set metadata item value
+    switch(metadataItem->type) {
+    case PS_META_BOOL:
+        metadataItem->data.B = (psBool)va_arg(argPtr, psS32);
+        break;
+    case PS_META_S32:
+        metadataItem->data.S32 = (psS32)va_arg(argPtr, psS32);
+        break;
+    case PS_META_F32:
+        metadataItem->data.F32 = (psF32)va_arg(argPtr, psF64);
+        break;
+    case PS_META_F64:
+        metadataItem->data.F64 = (psF64)va_arg(argPtr, psF64);
+        break;
+    case PS_META_STR:
+        // Perform copy of input strings
+        metadataItem->data.V = psStringCopy(va_arg(argPtr, char *));
+        break;
+    case PS_META_MULTI:
+        // MULTI needs to create a psList entry, value must be NULL
+        metadataItem->data.list = psListAlloc(NULL);
+        break;
+    case PS_META_LIST:
+    case PS_META_VEC:
+    case PS_META_HASH:
+    case PS_META_LOOKUPTABLE:
+    case PS_META_JPEG:
+    case PS_META_PNG:
+    case PS_META_ASTROM:
+    case PS_META_UNKNOWN:
+        // Copy of input data not performed due to variability of data types
+        metadataItem->data.V = psMemIncrRefCounter(va_arg(argPtr, psPtr));
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_METATYPE_INVALID, type);
+        psFree(metadataItem);
+        metadataItem = NULL;
+    }
+
+    return metadataItem;
+}
+
+psMetadata* psMetadataAlloc(void)
+{
+    psList* list = NULL;
+    psHash* table = NULL;
+    psMetadata* metadata = NULL;
+
+    // Allocate metadata
+    metadata = (psMetadata*) psAlloc(sizeof(psMetadata));
+    // Set deallocator
+    psMemSetDeallocator(metadata, (psFreeFcn) metadataFree);
+
+    // Allocate metadata's internal containers
+    list = (psList*) psListAlloc(NULL);
+    table = (psHash*) psHashAlloc(10);
+
+    metadata->list = list;
+    metadata->table = table;
+
+    return metadata;
+}
+
+psBool psMetadataAddItem(psMetadata *md, const psMetadataItem *metadataItem, psS32 location, psS32 flags)
+{
+    char * key = NULL;
+    psHash *mdTable = NULL;
+    psList *mdList = NULL;
+    psMetadataItem *existingEntry = NULL;
+
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->table,NULL);
+    PS_PTR_CHECK_NULL(md->list,NULL);
+    PS_PTR_CHECK_NULL(metadataItem,NULL);
+    PS_PTR_CHECK_NULL(metadataItem->name,NULL);
+
+    mdTable = md->table;
+    mdList = md->list;
+    key = metadataItem->name;
+
+    // See if key is already in table
+    existingEntry = psMetadataLookup(md, key);
+
+    // if replace is set, remove any existing items of the same key
+    if (existingEntry != NULL && (flags & PS_META_REPLACE) != 0) {
+        psMetadataRemove(md,0,key);
+        existingEntry = NULL;
+    }
+
+    // if the metadataItem is MULTI, just create a MULTI node.
+    if (metadataItem->type == PS_META_MULTI) {
+        if (metadataItem->data.list == NULL || metadataItem->data.list->size > 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                    "Specified PS_META_MULTI item is not defined properly "
+                    "(list allocated with zero size).");
+            return false;
+        }
+
+        // make sure the existing entry is PS_META_MULTI
+        existingEntry = makeMetaMulti(mdTable,key,existingEntry);
+
+        return true; // all done.
+    }
+
+    // how the item is added to the hash depends on prior existence, flags, etc.
+    if(existingEntry == NULL) { // no prior existence
+        // Node doesn't already exist - Add new metadata item to metadata collection's hash
+        if(!psHashAdd(mdTable, key, metadataItem)) {
+            psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
+            return false;
+        }
+    } else {
+        if (existingEntry->type == PS_META_MULTI || (flags & PS_META_DUPLICATE_OK) != 0) {
+            // duplicate entries allowed - add another entry.
+
+            // make sure the existing hash entry is PS_META_MULTI
+            existingEntry = makeMetaMulti(mdTable,key,existingEntry);
+
+            // add to the hash key's list of entries
+            if (! psListAdd(existingEntry->data.list, PS_LIST_TAIL, metadataItem) ) {
+                psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
+                return false;
+            }
+        } else {
+            // error on duplicate entry.
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psMetadata_DUPLICATE_NOT_ALLOWED);
+            return false;
+        }
+    }
+
+    // finally, add the metadataItem to the metadata's list.
+    if(!psListAdd(mdList, location, metadataItem)) {
+        psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
+        return false;
+    }
+
+    return true;
+}
+
+psBool psMetadataAdd(psMetadata *md, psS32 location, const char *name,
+                     psS32 type, const char *comment, ...)
+{
+    va_list argPtr;
+
+    va_start(argPtr, comment);
+    psBool result = psMetadataAddV(md,location,name,type,comment,argPtr);
+    va_end(argPtr);
+
+    return result;
+}
+
+psBool psMetadataAddV(psMetadata *md, psS32 location, const char *name,
+                      psS32 type, const char *comment, va_list list)
+{
+    psMetadataItem* metadataItem = NULL;
+
+    metadataItem = psMetadataItemAllocV(name, type & PS_METADATA_TYPE_MASK, comment, list);
+
+    if (!psMetadataAddItem(md, metadataItem, location, type & PS_METADATA_FLAGS_MASK)) {
+        psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_FAILED);
+        psFree(metadataItem);
+        return false;
+    }
+    // Decrement reference count, since the metadata item is now in metadata collection and no longer needed
+    psFree(metadataItem);
+
+    return true;
+}
+
+#define METADATA_ADD_TYPE(NAME,TYPE,METATYPE) \
+psBool psMetadataAdd##NAME(psMetadata* md, psS32 where, const char* name, \
+                           const char* comment, const TYPE value) { \
+    return psMetadataAdd(md,where,name, METATYPE,comment,value); \
+}
+
+METADATA_ADD_TYPE(S32,psS32,PS_META_S32)
+METADATA_ADD_TYPE(F32,psF32,PS_META_F32)
+METADATA_ADD_TYPE(F64,psF64,PS_META_F64)
+METADATA_ADD_TYPE(List,psList*,PS_META_LIST)
+METADATA_ADD_TYPE(Str,const char*,PS_META_STR)
+METADATA_ADD_TYPE(Vector,psVector*,PS_META_VEC)
+METADATA_ADD_TYPE(Image,psImage*,PS_META_IMG)
+METADATA_ADD_TYPE(Hash,psHash*,PS_META_HASH)
+METADATA_ADD_TYPE(LookupTable,psLookupTable*,PS_META_LOOKUPTABLE)
+METADATA_ADD_TYPE(Unknown,void*,PS_META_UNKNOWN)
+
+psBool psMetadataRemove(psMetadata *md, psS32 where, const char *key)
+{
+    PS_PTR_CHECK_NULL(md,NULL);
+
+    PS_PTR_CHECK_NULL(md->list,NULL);
+    psList* mdList = md->list;
+
+    PS_PTR_CHECK_NULL(md->table,NULL);
+    psHash* mdTable = md->table;
+
+    // Select removal by key or index
+    if (key != NULL) {
+        // Remove by key name
+        psMetadataItem* entry = psHashLookup(mdTable,key);
+        if (entry == NULL) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED, key);
+            return false;
+        }
+        if (entry->type == PS_META_MULTI) {
+            psMetadataItem* listItem;
+            psListIterator* iter = psListIteratorAlloc(
+                                       entry->data.list,
+                                       PS_LIST_HEAD,true);
+            while ((listItem=psListGetAndIncrement(iter)) != NULL) {
+                psListRemoveData(mdList, listItem);
+            }
+            psFree(iter);
+            psHashRemove(mdTable,key);
+
+        } else {
+            psListRemoveData(mdList, entry);
+            psHashRemove(mdTable, key);
+        }
+    } else {
+        // Remove by index
+        psMetadataItem* entry = psListGet(mdList, where);
+        if (entry == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_FIND_INDEX_FAILED, where);
+            return false;
+        }
+        key = entry->name;
+
+        if (key == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_REMOVE_LIST_INDEX_FAILED, where);
+            return false;
+        }
+
+        psMetadataItem* tableItem = psHashLookup(mdTable, key);
+        if (tableItem == NULL) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED, key);
+            return false;
+        }
+
+        if (tableItem->type == PS_META_MULTI) {
+            // multiple entries with same key, remove just the specified one
+            psListRemoveData(tableItem->data.list, entry);
+        } else {
+            if (!psHashRemove(mdTable, key)) {
+                psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED, key);
+                return false;
+            }
+        }
+        psListRemove(mdList, where);
+    }
+
+    return true;
+}
+
+psMetadataItem* psMetadataLookup(psMetadata *md, const char *key)
+{
+    psHash* mdTable = NULL;
+    psMetadataItem* entry = NULL;
+
+
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->table,NULL);
+    PS_PTR_CHECK_NULL(key,NULL);
+
+    mdTable = md->table;
+    entry = (psMetadataItem*)psHashLookup(mdTable, key);
+
+    return entry;
+}
+
+void* psMetadataLookupPtr(psBool *status, psMetadata *md, const char *key)
+{
+    psMetadataItem *metadataItem = NULL;
+
+    if (status) {
+        *status = true;
+    }
+
+    metadataItem = psMetadataLookup(md, key);
+    if(metadataItem == NULL) {
+        if (status) {
+            *status = false;
+        }
+        return NULL;
+    }
+    if (metadataItem->type == PS_META_MULTI) {
+        // if multiple keys found, use the first.
+        metadataItem = (psMetadataItem*)((metadataItem->data.list)->head);
+    }
+
+    if(PS_META_IS_PRIMITIVE(metadataItem->type)) {
+        if (status) {
+            *status = false;
+        }
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psMetadata_METATYPE_INVALID,
+                metadataItem->type);
+        return NULL;
+    } else {
+        return metadataItem->data.V;
+    }
+}
+
+#define psMetadataLookupNumTYPE(TYPE) \
+ps##TYPE psMetadataLookup##TYPE(psBool *status, psMetadata *md, const char *key) \
+{ \
+    psMetadataItem *metadataItem = NULL; \
+    ps##TYPE value = 0; \
+    \
+    if (status) { \
+        *status = true; \
+    } \
+    \
+    metadataItem = psMetadataLookup(md, key); \
+    if(metadataItem == NULL) { \
+        if (status) { \
+            *status = false; \
+        } \
+        return 0; \
+    } \
+    if (metadataItem->type == PS_META_MULTI) { \
+        /* if multiple keys found, use the first. */ \
+        metadataItem = (psMetadataItem*)((metadataItem->data.list)->head); \
+    } \
+    \
+    switch (metadataItem->type) { \
+    case PS_META_S32: \
+        value = (ps##TYPE)metadataItem->data.S32; \
+        break; \
+    case PS_META_F32: \
+        value = (ps##TYPE)metadataItem->data.F32; \
+        break; \
+    case PS_META_F64: \
+        value = (ps##TYPE)metadataItem->data.F64; \
+        break; \
+    case PS_META_BOOL: \
+        if (metadataItem->data.B) { \
+            value = 1; \
+        } \
+        break; \
+    default: \
+        /* if you get to this point, the value is not a number. */ \
+        if (status) { \
+            *status = false; \
+        } \
+        break; \
+    } \
+    \
+    /* psFree(metadataItem); currently, the lookup doesn't increment the ref count */ \
+    return value; \
+}
+
+psMetadataLookupNumTYPE(F32)
+psMetadataLookupNumTYPE(F64)
+psMetadataLookupNumTYPE(S32)
+psMetadataLookupNumTYPE(Bool)
+
+psMetadataItem* psMetadataGet(psMetadata *md, psS32 where)
+{
+    psMetadataItem* entry = NULL;
+
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->list,NULL);
+
+    entry = (psMetadataItem*) psListGet(md->list, where);
+    if (entry == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_FIND_INDEX_FAILED, where);
+        return NULL;
+    }
+
+    return entry;
+}
+
+psMetadataIterator* psMetadataIteratorAlloc(psMetadata* md,
+        int location,
+        const char* regex)
+{
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->list,NULL);
+
+    psMetadataIterator* newIter = psAlloc(sizeof(psMetadataIterator));
+    newIter->preg = NULL;
+    newIter->iter = NULL;
+
+    // Set deallocator
+    psMemSetDeallocator(newIter, (psFreeFcn) metadataIteratorFree);
+
+    if (regex == NULL) {
+        newIter->iter = psListIteratorAlloc(md->list, location, false);
+        return newIter;
+    } else {
+        int regRtn = regcomp(newIter->preg,regex,0);
+        if (regRtn != 0) {
+            char errMsg[256];
+            regerror(regRtn, newIter->preg, errMsg, 256);
+            regfree(newIter->preg);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psMetadata_REGEX_INVALID,
+                    errMsg);
+            psFree(newIter);
+            return NULL;
+        }
+    }
+
+    psMetadataIteratorSet(newIter, location); // XXX: do we error if no match is found?
+
+    return newIter;
+}
+
+psBool psMetadataIteratorSet(psMetadataIterator* iterator,
+                             int location)
+{
+    int match;
+    psMetadataItem* cursor;
+
+    PS_PTR_CHECK_NULL(iterator,NULL);
+
+    psListIterator* iter = iterator->iter;
+    PS_PTR_CHECK_NULL(iterator->iter,NULL);
+
+    regex_t* preg = iterator->preg;
+
+    // handle trivial case where no regex subsetting is required.
+    if (preg == NULL) {
+        return psListIteratorSet(iter,location);
+    }
+
+    if (location < 0) {
+        // match from the tail
+        match = 0;
+        psListIteratorSet(iter,PS_LIST_TAIL);
+        while ( (cursor=(psMetadataItem*)iter->cursor) != NULL) {
+            if (regexec(preg, cursor->name, 0, NULL, 0) == 0) {
+                // this key is a match
+                match--;
+                if (match == location) {
+                    break;
+                }
+            }
+            (void)psListGetAndDecrement(iter);
+        }
+        return (match == location);
+    }
+
+    // find the n-th match from the head
+    match = -1;
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    while ( (cursor=(psMetadataItem*)iter->cursor) != NULL) {
+        if (regexec(preg, cursor->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            match++;
+            if (match == location) {
+                break;
+            }
+        }
+        (void)psListGetAndIncrement(iter);
+    }
+    return (match == location);
+}
+
+psMetadataItem* psMetadataGetAndIncrement(psMetadataIterator* iterator)
+{
+    psMetadataItem* oldValue;
+
+    PS_PTR_CHECK_NULL(iterator,NULL);
+
+    psListIterator* iter = iterator->iter;
+    PS_PTR_CHECK_NULL(iterator->iter,NULL);
+
+    regex_t* preg = iterator->preg;
+
+    // handle trivial case where no regex subsetting is required.
+    if (preg == NULL) {
+        return (psMetadataItem*)psListGetAndIncrement(iter);
+    }
+
+    oldValue = (psMetadataItem*)iter->cursor;
+
+    while (psListGetAndIncrement(iter) != NULL) {
+        if (iter->cursor != NULL &&
+                regexec(preg, ((psMetadataItem*)iter->cursor)->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            break;
+        }
+    }
+    return oldValue;
+}
+
+psMetadataItem* psMetadataGetAndDecrement(psMetadataIterator* iterator)
+{
+    psMetadataItem* oldValue;
+
+    PS_PTR_CHECK_NULL(iterator,NULL);
+
+    psListIterator* iter = iterator->iter;
+    PS_PTR_CHECK_NULL(iterator->iter,NULL);
+
+    regex_t* preg = iterator->preg;
+
+    // handle trivial case where no regex subsetting is required.
+    if (preg == NULL) {
+        return (psMetadataItem*)psListGetAndDecrement(iter);
+    }
+
+    oldValue = (psMetadataItem*)iter->cursor;
+
+    while (psListGetAndDecrement(iter) != NULL) {
+        if (iter->cursor != NULL &&
+                regexec(preg, ((psMetadataItem*)iter->cursor)->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            break;
+        }
+    }
+    return oldValue;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadata.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadata.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadata.h	(revision 22271)
@@ -0,0 +1,479 @@
+/** @file  psMetadata.h
+*
+*  @brief Contains metadata struuctures, enumerations and functions prototypes
+*
+*  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+*  prototypes necessary creating psLib metadata APIs
+*
+*  @ingroup Metadata
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.44.4.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:56 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#ifndef PS_METADATA_H
+#define PS_METADATA_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include "psHash.h"
+#include "psList.h"
+#include "psImage.h"
+#include "psLookupTable.h"
+
+/// @addtogroup Metadata
+/// @{
+
+/** Metadata item type.
+ *
+ * Enumeration for maintaining metadata item types.
+ */
+typedef enum {
+    PS_META_S32 = PS_TYPE_S32,         ///< psS32 primitive data.
+    PS_META_F32 = PS_TYPE_F32,         ///< psF32 primitive data.
+    PS_META_F64 = PS_TYPE_F64,         ///< psF64 primitive data.
+    PS_META_BOOL = PS_TYPE_BOOL,       ///< psBool primitive data.
+    PS_META_LIST = 0x10000,            ///< List data (Stored as item.data.list).
+    PS_META_STR,                       ///< String data (Stored as item.data.V).
+    PS_META_VEC,                       ///< Vector data (Stored as item.data.V).
+    PS_META_IMG,                       ///< Image data (Stored as item.data.V).
+    PS_META_HASH,                      ///< Hash data (Stored as item.data.V).
+    PS_META_LOOKUPTABLE,               ///< Lookup table data (Stored as item.data.V).
+    PS_META_JPEG,                      ///< JPEG data (Stored as item.data.V).
+    PS_META_PNG,                       ///< PNG data (Stored as item.data.V).
+    PS_META_ASTROM,                    ///< Astrometric coefficients (Stored as item.data.V).
+    PS_META_UNKNOWN,                   ///< Other data (Stored as item.data.V).
+    PS_META_MULTI                      ///< Used internally, do not create an metadata item of this type.
+} psMetadataType;
+
+#define PS_META_IS_PRIMITIVE(TYPE) \
+(TYPE == PS_META_S32 || \
+ TYPE == PS_META_F32 || \
+ TYPE == PS_META_F64 || \
+ TYPE == PS_META_BOOL)
+
+#define PS_META_PRIMITIVE_TYPE(METATYPE) ( \
+        (METATYPE==PS_META_S32) ? PS_TYPE_S32 : \
+        (METATYPE==PS_META_F32) ? PS_TYPE_F32 : \
+        (METATYPE==PS_META_F64) ? PS_TYPE_F64 : \
+        (METATYPE==PS_META_BOOL) ? PS_TYPE_BOOL : 0 )
+
+/** Option flags for psMetadata functions
+ *
+ *  Enumeration for the modification of the behaviour in psMetadataAddItem.
+ * 
+ *  @see psMetadataAddItem
+ */
+typedef enum {
+    PS_META_DEFAULT = 0,               ///< default behaviour (duplicate entry is an error)
+    PS_META_REPLACE = 0x1000000,       ///< allow entry to be replaced
+    PS_META_DUPLICATE_OK = 0x2000000   ///< allow duplicate entries
+} psMetadataFlags;
+
+#define PS_METADATA_FLAGS_MASK 0xFF000000
+#define PS_METADATA_TYPE_MASK 0x00FFFFFF
+
+/** Metadata data structure.
+ *
+ *  Struct for holding metadata items. Metadata items are held in two
+ *  containers. The first employs a doubly-linked list to preserve the order
+ *  of the metadata. The second container employs a hash table which
+ *  allows fast lookup when given a metadata keyword.
+ */
+typedef struct psMetadata
+{
+    psList*  list;                     ///< Metadata in linked-list
+    psHash*  table;                    ///< Metadata in a hash table
+}
+psMetadata;
+
+/** Metadata iterator
+ *
+ *  Iterator for metadata.
+ */
+typedef struct
+{
+    psListIterator* iter;              ///< iterator for the psMetadata's psList
+    regex_t* preg;                     ///< the subsetting regular expression
+}
+psMetadataIterator;
+
+
+/** Metadata item data structure.
+ *
+ * Struct for maintaining metadata items of varying types. It also contains
+ * information about the item name, flags, comments, and other items with the same name.
+ */
+typedef struct psMetadataItem
+{
+    const psS32 id;                    ///< Unique ID for metadata item.
+    char *name;                        ///< Name of metadata item.
+    psMetadataType type;               ///< Type of metadata item.
+    union {
+        psBool B;                      ///< boolean data
+        psS32 S32;                     ///< Signed 32-bit integer data.
+        psF32 F32;                     ///< Single-precision float data.
+        psF64 F64;                     ///< Double-precision float data.
+        psList *list;                  ///< List data.
+        psMetadata *md;                ///< Metadata data.
+        psPtr V;                       ///< Pointer to other type of data.
+    } data;                            ///< Union for data types.
+    char *comment;                     ///< Optional comment ("", not NULL).
+}
+psMetadataItem;
+
+/** Create a metadata item.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct. The name argument specifies the name to use for this item, and
+ *  may include sprintf formatting codes. The format entry specifies both
+ *  the metadata type and optional flags and is created by bit-wise or of the
+ *  appropriate type and flag. The comment argument is a fixed string used to
+ *  comment the metadata item. The arguments to the name formatting codes and
+ *  the metadata itself are passed as arguments following the comment string.
+ *  The data must be a pointer for any of the elements stored in data.void.
+ *  The argument list must be interpreted appropriately by the va_list
+ *  operators in the function specified size and type.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAlloc(
+    const char *name,                  ///< Name of metadata item.
+    psMetadataType type,               ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+);
+
+/** Create a metadata item with specified string data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocStr(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    const char* value                  ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psF32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocF32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psF32 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psF64 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocF64(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psF64 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psS32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocS32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psS32 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psBool data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocBool(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psBool value                       ///< the value of the metadata item.
+);
+
+#ifndef SWIG
+/** Create a metadata item with va_list.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct. The name argument specifies the name to use for this item, and
+ *  may include sprintf formatting codes. The format entry specifies both
+ *  the metadata type and optional flags and is created by bit-wise or of the
+ *  appropriate type and flag. The comment argument is a fixed string used to
+ *  comment the metadata item. The arguments to the name formatting codes and
+ *  the metadata itself are passed as arguments following the comment string.
+ *  The data must be a pointer for any of the elements stored in data.void.
+ *  The argument list must be interpreted appropriately by the va_list
+ *  operators in the function specified size and type.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocV(
+    const char *name,                  ///< Name of metadata item.
+    psMetadataType type,               ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#endif
+
+/** Create a metadata collection.
+ *
+ *  Returns an empty metadata container with fully allocated internal metadata
+ *  containers.
+ *
+ *  @return psMetadata* : Pointer metadata.
+ */
+psMetadata* psMetadataAlloc(void);
+
+/** Add existing metadata item to metadata collection.
+ *
+ *  Add a metadata item that has already been created to the metadata
+ *  collection.
+ *
+ *  @return bool: True for success, false for failure.
+ */
+psBool psMetadataAddItem(
+    psMetadata*  md,                   ///< Metadata collection to insert metadat item.
+    const psMetadataItem*  item, ///< Metadata item to be added.
+    psS32 location,                    ///< Location to be added.
+    psS32 flags                        ///< Options flag mask, see psMetadataFlags enum
+);
+
+/** Create and add a metadata item to metadata collection.
+ *
+ * Creates a new metadata item add to the metadata collection.
+ *
+ * @return bool: True for success, false for failure.
+ */
+psBool psMetadataAdd(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item.
+    psS32 location,                    ///< Location to be added.
+    const char *name,                  ///< Name of metadata item.
+    int type,                          ///< Type of metadata item (psMetadataType) and options (psMetadataFlags)
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+);
+
+#ifndef SWIG
+/** Create and add a metadata item to metadata collection.
+ *
+ * Creates a new metadata item add to the metadata collection.
+ *
+ * @return bool: True for success, false for failure.
+ */
+psBool psMetadataAddV(
+    psMetadata* md,                    ///< Metadata collection to insert metadat item.
+    psS32 location,                    ///< Location to be added.
+    const char *name,                  ///< Name of metadata item.
+    int type,                          ///< Type of metadata item (psMetadataType) and options (psMetadataFlags)
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#endif
+
+psBool psMetadataAddS32(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, psS32 value);
+psBool psMetadataAddF32(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, psF32 value);
+psBool psMetadataAddF64(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, psF64 value);
+psBool psMetadataAddList(psMetadata* md, psS32 location, const char* name,
+                         const char* comment, const psList* value);
+psBool psMetadataAddStr(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, const char* value);
+psBool psMetadataAddVector(psMetadata* md, psS32 location, const char* name,
+                           const char* comment, const psVector* value);
+psBool psMetadataAddImage(psMetadata* md, psS32 location, const char* name,
+                          const char* comment, const psImage* value);
+psBool psMetadataAddHash(psMetadata* md, psS32 location, const char* name,
+                         const char* comment, const psHash* value);
+psBool psMetadataAddLookupTable(psMetadata* md, psS32 location, const char* name,
+                                const char* comment, const psLookupTable* value);
+psBool psMetadataAddUnknown(psMetadata* md, psS32 location, const char* name,
+                            const char* comment, const psPtr value);
+
+/** Remove an item from metadata collection.
+ *
+ *  Items may be removed from metadata by specifing a key or location. If the
+ *  name is null, the where argument is used instead. If name is not null,
+ *  where is set to PS_LIST_UNKNOWN. If the item is found, it is removed from
+ *  the metadata and true is returned.  If the key is not unique, then all
+ *  items corresponding to it are removed.
+ *
+ * @return bool: True for success, false for failure.
+ */
+psBool psMetadataRemove(
+    psMetadata*  md,           ///< Metadata collection to remove metadata item.
+    psS32 where,               ///< Location to be removed.
+    const char * key           ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the first item is returned. If the item is not found, null is
+ *  returned.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataLookup(
+    psMetadata * md,                   ///< Metadata collection to lookup metadata item.
+    const char * key                   ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its double precision value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psF64 : Value of metadata item.
+ */
+psF64 psMetadataLookupF64(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its single precision value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psF32 : Value of metadata item.
+ */
+psF32 psMetadataLookupF32(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psS32 : Value of metadata item.
+ */
+psS32 psMetadataLookupS32(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its boolean value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psBool : Value of metadata item.
+ */
+psBool psMetadataLookupBool(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return void* : Value of metadata item.
+ */
+psPtr psMetadataLookupPtr(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata* md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on list index.
+ *
+ *  Items may be found in the metadata by their entry position in the list
+ *  container.
+ *
+ *  @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataGet(
+    psMetadata*  md,                   ///< Metadata collection to insert metadat item.
+    psS32 location                     ///< Location to be retrieved.
+);
+
+/** Creates a psMetadataIterator to iterate over the specified psMetadata.
+ *
+ *  Supports the subsetting of the metadata via keyword using regular
+ *  expression.  If no regular expression is specified, iteration
+ *  over the entire psMetadata is performed.
+ *
+ *  @return psMetadataIterator*        a new psMetadataIterator, of NULL if error occurred
+ */
+psMetadataIterator* psMetadataIteratorAlloc(
+    psMetadata* md,                    ///< the psMetadata to iterate with
+    int location,                      ///< the initial starting point (after subsetting).
+    const char* regex
+    ///< A regular expression for subsetting the psMetadata.  If NULL, no
+    ///< subsetting is performed.
+);
+
+/** Set the iterator of the psMetadat to a given position.  If location is
+ *  invalid the iterator position is not changed.
+ *
+ *  @return psBool        TRUE if iterator successfully set, otherwise FALSE.
+*/
+psBool psMetadataIteratorSet(
+    psMetadataIterator* iterator,      ///< psMetadata iterator
+    int location                       ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Position the specified iterator to the next matching item in psMetadata,
+ *  given the regular expression of the iterator
+ *
+ *  @return psPtr       the psMetadataItem at the original iterator position
+ *                      or NULL if the iterator went past the end of the list.
+ */
+psMetadataItem* psMetadataGetAndIncrement(
+    psMetadataIterator* iterator           ///< iterator to move
+);
+
+/** Position the specified iterator to the previous matching item in psMetadata,
+ *  given the regular expression of the iterator
+ *
+ *  @return psPtr       the psMetadataItem at the original iterator position
+ *                      or NULL if the iterator went past the beginning of the
+ *                      list.
+ */
+psMetadataItem* psMetadataGetAndDecrement(
+    psMetadataIterator* iterator           ///< iterator to move
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadataIO.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadataIO.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadataIO.c	(revision 22271)
@@ -0,0 +1,1168 @@
+/** @file  psMetadataIO.c
+*
+*  @brief Contains metadata input/output functions.
+*
+*  This file defines functions to read and write metadata to/from an external file.
+*
+*  @ingroup Metadata
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-26 19:53:30 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <libxml/parser.h>
+#include <fitsio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "psAbort.h"
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psString.h"
+#include "psList.h"
+#include "psHash.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psMetadataIO.h"
+#include "psConstants.h"
+#include "psAstronomyErrors.h"
+
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Check for FITS errors */
+#define FITS_ERROR(STRING,PS_ERROR)                                                                          \
+fits_get_errstatus(status, fitsErr);                                                                         \
+psError(PS_ERR_IO,true, STRING, PS_ERROR, fitsErr);                                                          \
+status = 0;                                                                                                  \
+fits_close_file(fd, &status);                                                                                \
+if(status){                                                                                                  \
+    fits_get_errstatus(status, fitsErr);                                                                     \
+    psError(PS_ERR_IO,true, "Couldn't close FITS file. FITS error: %s", fitsErr);                            \
+}                                                                                                            \
+status = 0;                                                                                                  \
+psFree(output);                                                                                              \
+return NULL;
+
+/** Free and null temporary variables used by config file parser */
+#define CLEAR_TEMPS()                                                                                        \
+if(strName) {                                                                                                \
+    psFree(strName);                                                                                         \
+    strName = NULL;                                                                                          \
+}                                                                                                            \
+if(strType) {                                                                                                \
+    psFree(strType);                                                                                         \
+    strType = NULL;                                                                                          \
+}                                                                                                            \
+if(strValue) {                                                                                               \
+    psFree(strValue);                                                                                        \
+    strValue = NULL;                                                                                         \
+}                                                                                                            \
+if(strComment) {                                                                                             \
+    psFree(strComment);                                                                                      \
+    strComment = NULL;                                                                                       \
+}
+
+/** Maximum size of a FITS line */
+#define FITS_LINE_SIZE 80
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 256
+
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static void saxEndElement(void *ctx, const xmlChar *tagName);
+static void initVectorXml(void *ctx, char *tagName);
+static void initMetadataItemXml(void *ctx, char *tagName);
+static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts);
+
+/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+ *  must be null terminated. */
+psBool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+char *cleanString(char *inString, psS32 sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+/** Count repeat occurances of a single character within a line. The input string must be null terminated. */
+psS32 repeatedChars(char *inString, char ch)
+{
+    psS32 count = 0;
+
+
+    while(*inString!='\0') {
+        if(*inString == ch) {
+            count++;
+        }
+        inString++;
+    }
+
+    return count;
+}
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+char* getToken(char **inString, char *delimiter, psS32 *status)
+{
+    char *cleanToken = NULL;
+    psS32 sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = 1;
+    }
+
+    return cleanToken;
+}
+
+/** Returns single parsed value as a double precision number. The input string must be cleaned and null
+ * terminated. */
+double parseValue(char *inString, psS32 *status)
+{
+    char *end = NULL;
+    double value = 0.0;
+
+
+    value = strtod(inString, &end);
+    if(*end != '\0') {
+        *status = 1;
+    } else if(inString==end) {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns true or false. 'T', 't', '1', 'F', 'f', and '0' are acceptable, parsable variations. */
+psBool parseBool(char *inString, psS32 *status)
+{
+    psBool value = false;
+
+
+    if(*inString=='T' || *inString=='t' || *inString=='1') {
+        value = true;
+    } else if(*inString=='F' || *inString=='f' || *inString=='0') {
+        value = false;
+    } else {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns parsed vector filled with with data. The input string must be null terminated. */
+psVector* parseVector(char *inString, psElemType elemType, psS32 *status)
+{
+    char *end = NULL;
+    char *saveValue = NULL;
+    psS32 i = 0;
+    psS32 numValues = 0;
+    double value = 0.0;
+    psVector *vec = NULL;
+
+
+    // Cycle through string and count entries
+    saveValue = inString;
+    while(*inString!='\0') {
+        strtod(inString, &end);
+        if(inString==end) {
+            *status = 1;
+            return NULL;
+        }
+        while(*end==' ' || *end==',') { // Commas or spaces may be used as delimiters for vector values
+            end++;
+        }
+        inString=end;
+        numValues++;
+    }
+
+    // Cycle through string and convert string values to values
+    if(numValues) {
+        inString = saveValue;
+        end = NULL;
+        vec = psVectorAlloc(numValues, elemType);
+
+        while(*inString!='\0') {
+            value = strtod(inString, &end);
+            if(inString==end) {
+                *status = 1;
+                return vec;
+            }
+            switch(elemType) {
+            case PS_TYPE_U8:
+                vec->data.U8[i++] = (psU8)value;
+                break;
+            case PS_TYPE_S32:
+                vec->data.S32[i++] = (psS32)value;
+                break;
+            case PS_TYPE_F32:
+                vec->data.F32[i++] = (psF32)value;
+                break;
+            case PS_TYPE_F64:
+                vec->data.F64[i++] = (psF64)value;
+                break;
+            default:
+                *status = 1;
+                psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                        PS_ERRORTEXT_psMetadataIO_TYPE_INVALID,
+                        elemType);
+            }
+
+            while(*end==' ' || *end==',') {
+                end++;
+            }
+            inString=end;
+        }
+    }
+
+    return vec;
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+bool psMetadataItemPrint(FILE * fd, const char *format, const psMetadataItem* metadataItem)
+{
+    psMetadataType type;
+    psBool success = true;
+
+    PS_PTR_CHECK_NULL(fd, success);
+    PS_PTR_CHECK_NULL(format, success);
+    PS_PTR_CHECK_NULL(metadataItem, success);
+
+    type = metadataItem->type;
+
+    // determining the format type
+    char* fType = strchr(format,'%');
+    if (fType == NULL) {
+        // well, the format contains no reference to the metadataItem's data:
+        // that is truly trival to do!
+        fprintf(fd,format);
+        return success;
+    }
+
+    // skip over any format modifiers
+    const char* formatEnd = format+strlen(format);
+    while ( (fType < formatEnd) &&
+        (strchr(" +-01234567890.$#, hlL",*(++fType)) != NULL) ) {}
+
+    #define METADATAITEM_NUMERIC_CAST(FORMAT_TYPE) { \
+        switch(type) { \
+        case PS_META_BOOL: \
+            fprintf(fd, format, (FORMAT_TYPE) metadataItem->data.B); \
+            break; \
+        case PS_META_S32: \
+            fprintf(fd,format,(FORMAT_TYPE)  metadataItem->data.S32); \
+            break; \
+        case PS_META_F32: \
+            fprintf(fd, format,(FORMAT_TYPE)  metadataItem->data.F32); \
+            break; \
+        case PS_META_F64: \
+            fprintf(fd, format,(FORMAT_TYPE) metadataItem->data.F64); \
+            break; \
+        default: \
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true, \
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, (int)type); \
+            success = false; \
+        } \
+    }
+
+    switch(*fType) {
+    case 'd':
+    case 'i':
+    case 'c':
+        METADATAITEM_NUMERIC_CAST(int)
+        break;
+    case 'o':
+    case 'u':
+    case 'x':
+    case 'X':
+        METADATAITEM_NUMERIC_CAST(unsigned int)
+        break;
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+    case 'a':
+    case 'A':
+        METADATAITEM_NUMERIC_CAST(double)
+        break;
+    case 's':
+        if (type == PS_META_STR) {
+            fprintf(fd,format,(char*)metadataItem->data.V);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true,
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, (int)type);
+            success = false;
+        }
+        break;
+    case 'p':
+        fprintf(fd,format,metadataItem->data.V);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psMetadata_FORMAT_INVALID, *fType);
+        break;
+    }
+
+    return success;
+}
+
+
+psMetadata* psMetadataReadHeader(psMetadata* output, char *extName, psS32 extNum, char *fileName)
+{
+    psBool tempBool;
+    psBool success;
+    char keyType;
+    char keyName[FITS_LINE_SIZE];
+    char keyValue[FITS_LINE_SIZE];
+    char keyComment[FITS_LINE_SIZE];
+    char fitsErr[MAX_STRING_LENGTH];
+    psS32 i;
+    psS32 hduType = 0;
+    psS32 status = 0;
+    psS32 numKeys = 0;
+    psS32 keyNum = 0;
+    fitsfile *fd = NULL;
+
+    PS_PTR_CHECK_NULL(fileName,NULL);
+
+    fits_open_file(&fd, fileName, READONLY, &status);
+    if(fd == NULL || status != 0) {
+        FITS_ERROR("FITS error while opening file: %s %s", fileName);
+        return NULL;
+    }
+
+    if (extName == NULL && extNum < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psMetadataIO_EXTNUM_NOTPOSITIVE,
+                extNum);
+        return NULL;
+    }
+
+    // Allocate metadata if user didn't
+    if (output == NULL) {
+        output = psMetadataAlloc();
+    }
+
+    // Move to user designated HDU number or HDU name in FITS file. HDU numbers starts at one.
+    if (extName != NULL) {
+        if (fits_movnam_hdu(fd, ANY_HDU, extName, 0, &status) != 0) {
+            FITS_ERROR("FITS error while locating header %s: %s", extName);
+        }
+    } else {
+        if (fits_movabs_hdu(fd, extNum, &hduType, &status) != 0) {
+            FITS_ERROR("FITS error while locating header %d: %s", extNum);
+        }
+    }
+
+    // Get number of key names
+    if (fits_get_hdrpos(fd, &numKeys, &keyNum, &status) != 0) {
+        FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+    }
+
+    // Get each key name. Keywords start at one.
+    for (i = 1; i <= numKeys; i++) {
+        if (fits_read_keyn(fd, i, keyName, keyValue, keyComment, &status) != 0) {
+            FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+        }
+        if (fits_get_keytype(keyValue, &keyType, &status) != 0) {
+            fits_get_errstatus(status, fitsErr);
+            if (status != VALUE_UNDEFINED) {
+                FITS_ERROR("FITS error while determining key %d type: %s", keyNum);
+            } else {
+                // Some keywords are still valid if they don't have a type (like COMMENTS and HISTORY)
+                keyType = 'C';
+                status = 0;
+            }
+        }
+
+        switch (keyType) {
+        case 'I':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_S32 | PS_META_DUPLICATE_OK,
+                                    keyComment, atoi(keyValue));
+            break;
+        case 'F':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_F64 | PS_META_DUPLICATE_OK,
+                                    keyComment, atof(keyValue));
+            break;
+        case 'C':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_STR | PS_META_DUPLICATE_OK,
+                                    keyComment, keyValue);
+            break;
+        case 'L':
+            tempBool = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_BOOL | PS_META_DUPLICATE_OK,
+                                    keyComment, tempBool);
+            break;
+        case 'U':
+        case 'X':
+        default:
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FITS_METATYPE_INVALID, keyType);
+            return output;
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadataIO_ADD_FAILED, keyName);
+            return output;
+        }
+    }
+
+    return output;
+}
+
+psMetadata* psMetadataParseConfig(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
+{
+    psBool tempBool;
+    char *line = NULL;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *strComment = NULL;
+    char *linePtr = NULL;
+    psElemType vecType = 0;
+    psS32 status = 0;
+    psU32 lineCount = 0;
+    psF64 tempDbl = 0.0;
+    psS32 tempInt = 0.0;
+    psVector *tempVec = NULL;
+    FILE *fp = NULL;
+    psMetadataType mdType;
+    psMetadataFlags flags;
+    psBool addStatus;
+
+    // Check for nulls
+    PS_PTR_CHECK_NULL(fileName,NULL);
+    if((fp=fopen(fileName, "r")) == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
+        return NULL;
+    }
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        md = psMetadataAlloc();
+    }
+
+    // Create reusable line for continuous read
+    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+    // While loop to parse the file
+    while(fgets(line, MAX_STRING_LENGTH, fp) != NULL) {
+
+        // Initialize variables for new line
+        linePtr = line;
+        lineCount++;
+        CLEAR_TEMPS();
+
+        // If line is not a comment or blank, then extract data
+        if(!ignoreLine(linePtr)) {
+
+            // Check for more than one '*' or '@' in a line
+            if(repeatedChars(linePtr, '@') > 1) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
+                continue;
+            } else if(repeatedChars(linePtr, '*') > 1) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '*', lineCount, fileName);
+                continue;
+            } else if(repeatedChars(linePtr, '~') > 0) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '~', lineCount, fileName);
+                continue;
+            }
+
+            // Get metadata item name
+            strName = getToken(&linePtr, " ", &status);
+            if(strName==NULL || status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "name", lineCount, fileName);
+                continue;
+            }
+
+            flags = (overwrite) ? PS_META_REPLACE : PS_META_DEFAULT;
+
+            // Get the metadata item type
+            strType = getToken(&linePtr, " ", &status);
+            if(strType==NULL) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
+                        fileName);
+                continue;
+            } else {
+                char* tempStrType = strType;
+                if(*strType == '*') {
+                    flags = PS_META_DUPLICATE_OK;
+                    tempStrType = strType+1;
+                }
+
+                if(!strncmp(tempStrType, "STR", 3)) {
+                    mdType = PS_META_STR;
+                } else if(!strncmp(tempStrType, "BOOL", 4)) {
+                    mdType = PS_META_BOOL;
+                } else if(!strncmp(tempStrType, "S32", 3)) {
+                    mdType = PS_META_S32;
+                } else if(!strncmp(tempStrType, "F32", 3)) {
+                    mdType = PS_META_F32;
+                } else if(!strncmp(tempStrType, "F64", 3)) {
+                    mdType = PS_META_F64;
+                } else {
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineCount,
+                            fileName);
+                    continue;
+                }
+            }
+
+            if(*strName == '@') {
+                vecType = PS_META_PRIMITIVE_TYPE(mdType);
+                mdType = PS_META_VEC;
+            }
+
+            // Get the metadata item value if there is one.
+            strValue = getToken(&linePtr, "#", &status);
+            if(status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                        fileName);
+                continue;
+            }
+            if(strValue==NULL) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                        fileName);
+                continue;
+            }
+
+            // Not all lines will have comments, so NULL is ok.
+            strComment = getToken(&linePtr,"~", &status);
+            if(status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                        fileName);
+                continue;
+            }
+
+            // Create and add metadata item to metadata and parse values
+            switch (mdType) {
+            case PS_META_STR:
+                addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                          mdType | flags,
+                                          strComment, strValue);
+                break;
+            case PS_META_VEC:
+                tempVec = parseVector(strValue, vecType, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName+1,
+                                              mdType | flags,
+                                              strComment, tempVec);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                psFree(tempVec);
+                break;
+            case PS_META_BOOL:
+                tempBool = parseBool(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempBool);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                break;
+            case PS_META_S32:
+                tempInt = (psS32)parseValue(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempInt);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                break;
+            case PS_META_F32:
+            case PS_META_F64:
+                tempDbl = parseValue(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempDbl);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true,
+                            PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType, lineCount,
+                            fileName);
+                    continue;
+                }
+                break;
+            default:
+                (*nFail)++;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, mdType, lineCount,
+                        fileName);
+                continue;
+            } // switch
+            if (! addStatus) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineCount,
+                        fileName);
+            }
+
+        } // if ignoreLine
+    } // while loop
+
+    psFree(line);
+
+    return md;
+}
+
+static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts)
+{
+    psU64 i = 0;
+    char* psTagName = NULL;
+    char *psAttName = NULL;
+    char *psAttValue = NULL;
+    const xmlChar *attName = NULL;
+    const xmlChar *attValue = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+
+    // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+    psTagName = psStringCopy((const char*)tagName);
+
+    // Metadata containter for housing element attributes used by other SAX events
+    htAtts = psHashAlloc(10);
+
+
+    // Get tag name
+    if(psTagName != NULL) {
+        psHashAdd(htAtts, "tagName", psTagName);
+    } else {
+        PS_PTR_CHECK_NULL_GENERAL(psTagName, return);
+        psFree(htAtts);
+        psFree(psTagName);
+        return;
+    }
+
+    // Get all attribute names and attribute values
+    if(atts != NULL) {
+        attName = atts[i++];
+        attValue = atts[i++];
+        while(attName != NULL) {
+            if(attValue != NULL) {
+
+                // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+                psAttName = psStringCopy((const char*)attName);
+                psAttValue = psStringCopy((const char*)attValue);
+                psHashAdd(htAtts, psAttName, psAttValue);
+                psFree(psAttName);
+                psFree(psAttValue);
+            } else {
+                PS_PTR_CHECK_NULL_GENERAL(psAttValue, return);
+                psFree(htAtts);
+                psFree(psTagName);
+                return;
+            }
+            attName = atts[i++];
+            attValue = atts[i++];
+        }
+    }
+
+    // Add attributes to metadata
+
+    psMetadataAdd(md, PS_LIST_TAIL, "htAtts",
+                  PS_META_HASH | PS_META_DUPLICATE_OK,
+                  NULL, htAtts);
+
+    psFree(psTagName);
+    psFree(htAtts);
+
+    return;
+}
+
+static void initMetadataItemXml(void *ctx, char *tagName)
+{
+    psBool overwrite = false;
+    psBool tempBool = false;
+    psS32 status = 0;
+    psU32 lineNumber = 0;
+    psF64 tempDbl = 0.0;
+    psS32 tempInt = 0.0;
+    psMetadataType mdType = PS_META_UNKNOWN;
+    char *fileName = NULL;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    psMetadataItem *metadataItem = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    metadataItem = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(metadataItem, return);
+    PS_PTR_CHECK_NULL_GENERAL(metadataItem->data.list, return);
+    metadataItem = (psMetadataItem*)psListGet(metadataItem->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)metadataItem->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+    fileName = (char*)input->filename;
+    PS_PTR_CHECK_NULL_GENERAL(fileName, return);
+    lineNumber = input->line;
+
+    // Get attribute name
+    strName = psHashLookup(htAtts, "name");
+    if(strName == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_NO_NAME, lineNumber, fileName);
+        return;
+    }
+
+    // Get attribute type, if there is one
+    strType = psHashLookup(htAtts, "psType");
+    if(strType!= NULL) {
+        if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psString")) {
+            mdType = PS_META_STR;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psBool")) {
+            mdType = PS_META_BOOL;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psS32")) {
+            mdType = PS_META_S32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF32")) {
+            mdType = PS_META_F32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF64")) {
+            mdType = PS_META_F64;
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE, strType, lineNumber,
+                    fileName);
+            return;
+        }
+    }
+
+    // Get attribute value, if there is one
+    strValue = psHashLookup(htAtts, "value");
+
+    /* If metadata item is found, and is not a folder node, and overwrite is allowed, then remove
+    existing and allow switch/case below to add new item. If overwrite is false, then report error. If
+    found item is folder node, then psMetadataAdd will automatically add a new child. */
+    metadataItem = psMetadataLookup(md, "overwrite");
+    if(metadataItem != NULL) {
+        overwrite = parseBool((char*)strValue, &status);
+        if(status) {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        metadataItem = psMetadataLookup(md, strName);
+        if(metadataItem != NULL) {
+            if(metadataItem->type != PS_META_LIST) {
+                if(overwrite) {
+                    psMetadataRemove(md, INT_MIN, strName);
+                } else {
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineNumber,
+                            fileName);
+                    return;
+                }
+            }
+        }
+    }
+
+    // Create metadata item and add to metadata
+    switch(mdType) {
+    case PS_META_LIST:
+        psMetadataAdd(md, PS_LIST_TAIL, strName,
+                      mdType | PS_META_DUPLICATE_OK,
+                      NULL, NULL);
+        break;
+    case PS_META_STR:
+        psMetadataAdd(md, PS_LIST_TAIL, strName,
+                      mdType | PS_META_DUPLICATE_OK,
+                      NULL, strValue);
+        break;
+    case PS_META_BOOL:
+        tempBool = parseBool((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempBool);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    case PS_META_S32:
+        tempInt = (psS32)parseValue((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempInt);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    case PS_META_F32:
+    case PS_META_F64:
+        tempDbl = parseValue((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempDbl);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    default:
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineNumber, fileName);
+    } // End switch
+
+    return;
+}
+
+
+static void initVectorXml(void *ctx, char *tagName)
+{
+    bool overwrite = false;
+    psS32 status = 0;
+    psU32 lineNumber = 0;
+    psElemType pType = 0;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *fileName = NULL;
+    psMetadataItem *table = NULL;
+    psMetadataItem *tables = NULL;
+    psMetadataItem *metadataItem = NULL;
+    psVector *vec = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    tables = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(tables, return);
+    PS_PTR_CHECK_NULL_GENERAL(tables->data.list, return);
+    table = (psMetadataItem*)psListGet(tables->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)table->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+    fileName = (char*)input->filename;
+    PS_PTR_CHECK_NULL_GENERAL(fileName, return);
+    lineNumber = input->line;
+
+    // Get attribute name
+    strName = psHashLookup(htAtts, "name");
+    if(strName == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_NO_NAME, lineNumber, fileName);
+        return;
+    }
+
+    // Get attribute type, if there is one
+    strType = psHashLookup(htAtts, "psType");
+    if(strType!= NULL) {
+        if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psBool")) {
+            pType = PS_TYPE_U8;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psS32")) {
+            pType = PS_TYPE_S32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF32")) {
+            pType = PS_TYPE_F32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF64")) {
+            pType = PS_TYPE_F64;
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE, strName, lineNumber,
+                    fileName);
+            return;
+        }
+    }
+
+    strValue = psHashLookup(htAtts, "value");
+    PS_PTR_CHECK_NULL_GENERAL(strValue, return);
+
+
+    /* If metadata item is found, and is not a folder node, and overwrite is allowed, then remove
+    existing and allow switch/case below to add new item. If overwrite is false, then report error. If
+    found item is folder node, then psMetadataAdd will automatically add a new child. */
+    metadataItem = psMetadataLookup(md, "overwrite");
+    if(metadataItem != NULL) {
+        overwrite = parseBool((char*)strValue, &status);
+        if(status) {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    input->line, input->filename);
+        }
+        metadataItem = psMetadataLookup(md, strName);
+        if(metadataItem != NULL) {
+            if(metadataItem->type != PS_META_LIST) {
+                if(overwrite) {
+                    psMetadataRemove(md, INT_MIN, strName);
+                } else {
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineNumber,
+                            fileName);
+                    return;
+                }
+            }
+        }
+    }
+
+    // Get value
+    vec = parseVector((char*)strValue, pType, &status);
+    if(!status) {
+        psMetadataAdd(md, PS_LIST_TAIL, strName+1,
+                      PS_META_VEC | PS_META_DUPLICATE_OK,
+                      NULL, vec);
+    } else {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                lineNumber, fileName);
+    }
+    psFree(vec);
+}
+
+static void saxEndElement(void *ctx, const xmlChar *tagName)
+{
+    char *psStartTagName = NULL;
+    char *psEndTagName = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    psMetadataItem *table = NULL;
+    psMetadataItem *tables = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    tables = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(tables, return);
+    PS_PTR_CHECK_NULL_GENERAL(tables->data.list, return);
+    table = (psMetadataItem*)psListGet(tables->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)table->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+
+    // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+    psEndTagName = psStringCopy((const char*)tagName);
+
+    // Compare start and end tag names
+    psStartTagName = psHashLookup(htAtts, "tagName");
+    PS_PTR_CHECK_NULL_GENERAL(psStartTagName, return);
+    if(strcmp(psEndTagName, psStartTagName)) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TAG_MISMATCH, psStartTagName, psEndTagName);
+    }
+
+    // Initialize psLib structs
+    if(!strcmp(psEndTagName, "psMetadataItem")) {
+        initMetadataItemXml(ctx, psEndTagName);
+    } else if(!strcmp(psEndTagName, "psVector")) {
+        initVectorXml(ctx, psEndTagName);
+    } else if(strcmp(psEndTagName, "psRoot")) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TAG_UNKNOWN, psEndTagName);
+    }
+
+    // Free temporary metadata item and its hash table
+    psListRemove(tables->data.list, PS_LIST_TAIL);
+
+    psFree(psEndTagName);
+
+    return;
+}
+
+psMetadata*  psMetadataParseConfigXml(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
+{
+    xmlSAXHandler saxHandler;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(fileName, NULL);
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        md = psMetadataAlloc();
+    }
+
+    // Sax handler initializations
+    saxHandler.internalSubset           = NULL;
+    saxHandler.isStandalone             = NULL;
+    saxHandler.hasInternalSubset        = NULL;
+    saxHandler.hasExternalSubset        = NULL;
+    saxHandler.resolveEntity            = NULL;
+    saxHandler.getEntity                = NULL;
+    saxHandler.entityDecl               = NULL;
+    saxHandler.notationDecl             = NULL;
+    saxHandler.attributeDecl            = NULL;
+    saxHandler.elementDecl              = NULL;
+    saxHandler.unparsedEntityDecl       = NULL;
+    saxHandler.setDocumentLocator       = NULL;
+    saxHandler.startDocument            = NULL;
+    saxHandler.endDocument              = NULL;
+    saxHandler.startElement             = saxStartElement;
+    saxHandler.endElement               = saxEndElement;
+    saxHandler.reference                = NULL;
+    saxHandler.characters               = NULL;
+    saxHandler.ignorableWhitespace      = NULL;
+    saxHandler.processingInstruction    = NULL;
+    saxHandler.comment                  = NULL;
+    saxHandler.warning                  = xmlParserError;
+    saxHandler.error                    = xmlParserError;
+    saxHandler.fatalError               = xmlParserError;
+    saxHandler.getParameterEntity       = NULL;
+    saxHandler.cdataBlock               = NULL;
+    saxHandler.externalSubset           = NULL;
+    saxHandler.initialized              = 1;
+    saxHandler._private                 = md;
+    saxHandler.startElementNs           = NULL;
+    saxHandler.endElementNs             = NULL;
+    saxHandler.serror                   = NULL;
+
+    // Parse XML file
+    if (xmlSAXUserParseFile(&saxHandler, NULL, fileName)) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
+        return NULL;
+    }
+
+    // Parser and memory cleanups for libxml2
+    xmlCleanupParser();
+    xmlMemoryDump();
+
+    return md;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadataIO.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadataIO.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psMetadataIO.h	(revision 22271)
@@ -0,0 +1,85 @@
+/** @file  psMetadataIO.h
+ *
+ *  @brief Contains metadata input/output functions.
+ *
+ *  This file defines functions to read and write metadata to/from an external file.
+ *
+ *  @ingroup Metadata
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-07 20:58:50 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_METADATAIO_H
+#define PS_METADATAIO_H
+
+/// @addtogroup Metadata
+/// @{
+
+
+/** Print metadata item to file.
+ *
+ *  Metadata items may be printed to an open file descriptor based on a
+ *  provided format. The format is a sprintf format statement with exactly
+ *  one % formatting command. If the metadata item type is a numeric type,
+ *  this formatting command must also be numeric, and the type conversion
+ *  performed to the value to match the format type. If the metadata type is
+ *  a string, the fromatting command must also be for a string. If the
+ *  metadata type is any other data type, printing is not allowed.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+bool psMetadataItemPrint(
+    FILE * fd,                         ///< Pointer to file to write metadata item.
+    const char *format,                ///< Format to print metadata item.
+    const psMetadataItem* metadataItem ///< Metadata item to print.
+);
+
+/** Read metadata header.
+ *
+ *  Read a metadata header from file. If the file is not found, an error is
+ *  reported.
+ *
+ *  @return psMetadata* : Pointer to resulting metadata.
+ */
+psMetadata* psMetadataReadHeader(
+    psMetadata* output,                ///< Resulting metadata from read.
+    char *extName,                     ///< File name extension string.
+    psS32 extNum,                      ///< File name extension number. Starts at 1.
+    char *fileName                     ///< Name of file to read.
+);
+
+/** Read metadata configuration file.
+ *
+ *  Loads pre-defined settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+psMetadata* psMetadataParseConfig(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    psU32 *nFail,                      ///< Number of failed lines.
+    const char *fileName,              ///< Name of file to read.
+    psBool overwrite                   ///< Allow overwrite of duplicate specifications.
+);
+
+/** Read XML metadata configuration file.
+ *
+ *  Loads pre-defined XML settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+
+psMetadata*  psMetadataParseConfigXml(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    psU32 *nFail,                      ///< Number of failed lines.
+    const char *fileName,              ///< Name of file to read.
+    psBool overwrite                   ///< Allow overwrite of duplicate specifications.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psPixels.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psPixels.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psPixels.c	(revision 22271)
@@ -0,0 +1,262 @@
+/** @file  psPixels.c
+ *
+ *  @brief Contains psPixel related functions
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-23 00:10:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "psPixels.h"
+#include "psMemory.h"
+
+typedef int(*qsortCompareFcn)(const void *, const void *);
+
+static void pixelsFree(psPixels* pixels)
+{
+    if (pixels != NULL) {
+        psFree(pixels->data);
+    }
+}
+
+// for use by qsort, etc.
+static int comparePixelCoord(psPixelCoord* coord1, psPixelCoord* coord2)
+{
+    // check row first
+    if (coord1->y < coord2->y) {
+        return -1;
+    }
+
+    if (coord1->y > coord2->y) {
+        return 1;
+    }
+
+    // rows are the same, so check column
+    if (coord1->x < coord2->x) {
+        return -1;
+    }
+
+    if (coord1->x > coord2->x) {
+        return 1;
+    }
+
+    return 0;
+}
+
+psPixels* psPixelsAlloc(int size)
+{
+    psPixels* out = psAlloc(sizeof(psPixels));
+
+    if (size > 0) {
+        out->data = psAlloc(sizeof(psPixelCoord)*size);
+    } else {
+        out->data = NULL;
+    }
+    out->n = 0;
+    out->nalloc = size;
+
+    psMemSetDeallocator(out, (psFreeFcn)pixelsFree);
+
+    return NULL;
+}
+
+psPixels* psPixelsRealloc(psPixels* pixels, int size)
+{
+    if (pixels == NULL) {
+        return psPixelsAlloc(size);
+    }
+
+    pixels->data = psRealloc(pixels->data, sizeof(psPixelCoord)*size);
+
+    pixels->nalloc = size;
+    if (pixels->n > pixels->nalloc) {
+        pixels->n = pixels->nalloc;
+    }
+
+    return pixels;
+}
+
+psPixels* psPixelsCopy(psPixels* out, const psPixels* in)
+{
+    if (in == NULL) {
+        return NULL;
+    }
+
+    out = psPixelsRealloc(out, in->n);
+
+    memcpy(in->data,out->data, in->n*sizeof(psPixelCoord));
+    out->n = in->n;
+
+    return out;
+}
+
+psImage *psPixelsToMask(psImage *out, const psPixels *pixels, const psRegion *region, unsigned int maskVal)
+{
+    // check that the input pixel vector is valid
+    if (pixels == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    psPixelCoord* data = pixels->data;
+    if (data == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+
+    // check if the input region is valid
+    if (region == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    int x0 = region->x0;
+    int x1 = region->x1;
+    int y0 = region->y0;
+    int y1 = region->y1;
+
+    // determine the output image size
+    int numRows = x1-x0;
+    int numCols = y1-y0;
+    if (numRows < 1 || numCols < 1) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+
+    //  allocate the output image
+    out = psImageRecycle(out, numCols, numRows, PS_TYPE_MASK);
+    if (out == NULL) {
+        // XXX: Error message
+        return NULL;
+    }
+    *(psS32*)&out->row0 = x0;
+    *(psS32*)&out->col0 = y0;
+
+    // initialize image to all zeros
+    int columnByteSize = sizeof(PS_TYPE_MASK)*numCols;
+    for (int row = 0; row < numRows; row++) {
+        memset(out->data.U8[row],0,columnByteSize);
+    }
+
+    // determine the length of the pixel vector
+    int length = pixels->n;
+
+    // cycle through the vector of pixels and insert pixels into image
+    psMaskType** outData = out->data.PS_TYPE_MASK_DATA;
+    for (int p = 0; p < length; p++) {
+        psS32 x = data[p].x;
+        psS32 y = data[p].y;
+        // pixel in region?
+        if (x >= x0 && x < x1 && y >= y0 && y < y1) {
+            outData[x-x0][y-y0] |= maskVal;
+        }
+    }
+
+    return out;
+}
+
+psPixels *psMaskToPixels(psPixels *out, const psImage *mask, unsigned int maskVal)
+{
+    if (mask == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    if (mask->type.type != PS_TYPE_MASK) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    int numRows = mask->numRows;
+    int numCols = mask->numCols;
+
+    // assumption: number of masked pixels is relatively small compared to
+    // total pixels, so it is best to just start with a guess and resize if
+    // necessary
+    int minPixels = numRows*numCols/100; // initial guess, 1% of pixels masked
+    if (minPixels < 32) { // enforce a minimum size
+        minPixels = 32;
+    }
+
+    // check out's validity (allocated/minimum size)
+    if (out == NULL || out->data == NULL || out->nalloc < minPixels) {
+        out = psPixelsRealloc(out,minPixels);
+    }
+
+    // start with a blank list of pixels
+    out->n = 0;
+
+    // find the mask pixels in the image
+    int numPixels = 0;
+    psPixelCoord* data = out->data;
+    int nalloc = out->nalloc;
+    for (int row=0; row<numRows; row++) {
+        psMaskType* maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        for (int col=0; col<numCols; col++) {
+            if ( (maskRow[col] | maskVal) != 0 ) {
+                // check the vector sizes, and expand if necessary
+                if (nalloc >= numPixels) {
+                    out = psPixelsRealloc(out, 2*nalloc);
+                    nalloc = out->nalloc;
+                    data = out->data;
+                }
+
+                data[numPixels].x = col;
+                data[numPixels].y = row;
+                numPixels++;
+            }
+        }
+    }
+
+    return out;
+}
+
+psPixels* psPixelsConcatenate(psPixels *out,const psPixels *pixels)
+{
+    if (pixels == NULL) {
+        // XXX: Error message
+        return NULL;
+    }
+    int pixelsN = pixels->n;
+    psPixelCoord* pixelsData = pixels->data;
+
+    if (out == NULL) {
+        // simple copy pixels
+        out = psPixelsCopy(out,pixels);
+        return out;
+    }
+
+    // make sure the out is large enough to fit the result
+    int outN = out->n;
+    out = psPixelsRealloc(out,outN + pixelsN);
+    psPixelCoord* outData = out->data;
+
+    // sort the OUT array to help in searching for duplicates later
+    qsort(outData, sizeof(psPixelCoord), outN,
+          (qsortCompareFcn)comparePixelCoord);
+
+    // add non-duplicate values in pixels to out
+    psPixelCoord pCoord;
+    int end = outN;
+    for (int n = 0; n < pixelsN; n++) {
+        pCoord = pixelsData[n];
+        if (bsearch(&pCoord, outData, sizeof(psPixelCoord), outN,
+                    (qsortCompareFcn)comparePixelCoord) == NULL) {
+            // no match in OUT array of this value
+            outData[end++] = pCoord;
+        }
+    }
+    out->n = end; // set number of elements to reflect added data
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psPixels.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psPixels.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psPixels.h	(revision 22271)
@@ -0,0 +1,127 @@
+/** @file  psPixels.h
+ *
+ *  @brief Contains psPixel related functions
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-23 00:10:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_PIXELS_H
+#define PS_PIXELS_H
+
+#include "psImage.h"
+#include "psVector.h"
+
+/// @addtogroup Image
+/// @{
+
+typedef struct
+{
+    psS32 x;
+    psS32 y;
+}
+psPixelCoord;
+
+/** list of pixel coordinates
+ *
+ *  Usually an image mask is the best way to carry information about what
+ *  pixels mean what. However, in the case where the number of pixels in which
+ *  we are interested is limited, it is more efï¬cient to simply carry a list
+ *  of pixels. An example of this is in the image combination code, where we
+ *  want to perform an operation on a relatively small fraction of pixels, and
+ *  it is inefï¬cient to go through an entire mask image checking each pixel.
+ *
+ */
+typedef struct
+{
+    int n;
+    int nalloc;
+    psPixelCoord* data;
+}
+psPixels;
+
+
+/** Allocates a new psPixels structure
+ *
+ *  @return psPixels*   new psPixels
+ */
+psPixels* psPixelsAlloc(
+    int size                           ///< the size of the coordinate vectors
+);
+
+/** resizes a psPixels structure
+ *
+ *  @return psPixels*   resized psPixels
+ */
+psPixels* psPixelsRealloc(
+    psPixels* pixels,                  ///< psPixels to resize, or NULL to create new psPixels
+    int size                           ///< the size of the coordinate vectors
+);
+
+/** Copies a psPixels object
+ *
+ *  Makes a deep copy of the data in a psPixels object.  Any data in the OUT
+ *  parameter will be destroyed and OUT will be resized, if necessary.
+ *
+ *  @return psPixels*   a new psPixels that is a duplicate to IN
+ */
+psPixels* psPixelsCopy(
+    psPixels* out,                     ///< psPixels struct to recycle, or NULL
+    const psPixels* in                 ///< psPixels struct to copy
+);
+
+/** Generate a psImage from a psPixels
+ *
+ *  psPixelsToMask shall return an image of type U8 with the pixels lying
+ *  within the speciï¬ed region set to the maskVal. The out image shall be
+ *  modiï¬ed if supplied, or allocated and returned if NULL. The size of the
+ *  output image shall be region->x1 - region->x0 by region->y1 - region->y0,
+ *  with out->x0 = region->x0 and out->y0 = region->y0. In the event that
+ *  either of pixels or region are NULL, the function shall generate an
+ *  error and return NULL.
+ *
+ *  @return psImage*    generated mask image
+ */
+psImage* psPixelsToMask(
+    psImage* out,                      ///< psImage to recycle, or NULL
+    const psPixels* pixels,            ///< list of pixels to use
+    const psRegion* region,            ///< region to define the output mask image
+    unsigned int maskVal               ///< the mask bit-values to act upon
+);
+
+/** Generate a psPixels from a mask psImage
+ *
+ *  psMaskToPixels shall return a psPixels consisting of the coordinates in
+ *  the mask that match the maskVal. The out pixel list shall be modiï¬ed if
+ *  supplied, or allocated and returned if NULL. In hte event that mask is
+ *  NULL, the function shall generate an error and return NULL.
+ *
+ *  @return psPixels*   generated psPixels pixel list
+ */
+psPixels* psMaskToPixels(
+    psPixels *out,                     ///< psPixels to recycle, or NULL
+    const psImage *mask,               ///< the input mask psImage
+    unsigned int maskVal               ///< the mask bit-values to act upon
+);
+
+/** Concatenates two psPixels
+ *
+ *  psPixelsConcatenate shall concatenate pixels onto out. In the event that
+ *  out is NULL, a new psPixels shall be allocated, and the contents of
+ *  pixels simply copied in. If pixels is NULL, the function shall generate
+ *  an error and return NULL. The function shall take care to ensure that
+ *  there are no duplicate pixels in out.
+ *
+ *  @return psPixels         Concatenated psPixel list
+ */
+psPixels* psPixelsConcatenate(
+    psPixels *out,                     ///< psPixels to recycle, or NULL
+    const psPixels *pixels             ///< psPixels to append to OUT
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psScalar.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psScalar.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psScalar.c	(revision 22271)
@@ -0,0 +1,139 @@
+/** @file  psScalar.c
+ *
+ *  @brief Contains basic scalar definitions and operations
+ *
+ *  This file defines the basic type for a scalar struct and functions useful
+ *  in manupulating scalars.
+ * *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.14.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psAbort.h"
+
+#include "psCollectionsErrors.h"
+
+psScalar* psScalarAlloc(psC64 value, psElemType dataType)
+{
+    psScalar* scalar = NULL;
+
+    // Create scalar
+    scalar = (psScalar* ) psAlloc(sizeof(psScalar));
+
+    scalar->type.dimen = PS_DIMEN_SCALAR;
+    scalar->type.type = dataType;
+
+    switch (dataType) {
+    case PS_TYPE_S8:
+        scalar->data.S8 = (psS8) value;
+        break;
+    case PS_TYPE_U8:
+        scalar->data.U8 = (psU8) value;
+        break;
+    case PS_TYPE_S16:
+        scalar->data.S16 = (psS16) value;
+        break;
+    case PS_TYPE_U16:
+        scalar->data.U16 = (psU16) value;
+        break;
+    case PS_TYPE_S32:
+        scalar->data.S32 = (psS32) value;
+        break;
+    case PS_TYPE_U32:
+        scalar->data.U32 = (psU32) value;
+        break;
+    case PS_TYPE_S64:
+        scalar->data.S64 = (psS64) value;
+        break;
+    case PS_TYPE_U64:
+        scalar->data.U64 = (psU64) value;
+        break;
+    case PS_TYPE_F32:
+        scalar->data.F32 = (psF32) value;
+        break;
+    case PS_TYPE_F64:
+        scalar->data.F64 = (psF64) value;
+        break;
+    case PS_TYPE_C32:
+        scalar->data.C32 = (psC32) value;
+        break;
+    case PS_TYPE_C64:
+        scalar->data.C64 = (psC64) value;
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psScalar_UNSUPPORTED_TYPE,
+                dataType);
+        psFree(scalar);
+        return NULL;
+    }
+
+    return scalar;
+}
+
+psScalar* psScalarCopy(const psScalar *scalar)
+{
+    psElemType dataType;
+    psScalar *newScalar = NULL;
+
+    if (scalar == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psScalar_COPY_NULL);
+        return NULL;
+    }
+
+    dataType = scalar->type.type;
+    switch (dataType) {
+    case PS_TYPE_S8:
+        newScalar =  psScalarAlloc(scalar->data.S8, dataType);
+        break;
+    case PS_TYPE_U8:
+        newScalar =  psScalarAlloc(scalar->data.U8, dataType);
+        break;
+    case PS_TYPE_S16:
+        newScalar =  psScalarAlloc(scalar->data.S16, dataType);
+        break;
+    case PS_TYPE_U16:
+        newScalar =  psScalarAlloc(scalar->data.U16, dataType);
+        break;
+    case PS_TYPE_S32:
+        newScalar =  psScalarAlloc(scalar->data.S32, dataType);
+        break;
+    case PS_TYPE_U32:
+        newScalar =  psScalarAlloc(scalar->data.U32, dataType);
+        break;
+    case PS_TYPE_S64:
+        newScalar =  psScalarAlloc(scalar->data.S64, dataType);
+        break;
+    case PS_TYPE_U64:
+        newScalar =  psScalarAlloc(scalar->data.U64, dataType);
+        break;
+    case PS_TYPE_F32:
+        newScalar =  psScalarAlloc(scalar->data.F32, dataType);
+        break;
+    case PS_TYPE_F64:
+        newScalar =  psScalarAlloc(scalar->data.F64, dataType);
+        break;
+    case PS_TYPE_C32:
+        newScalar =  psScalarAlloc(scalar->data.C32, dataType);
+        break;
+    case PS_TYPE_C64:
+        newScalar =  psScalarAlloc(scalar->data.C64, dataType);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psScalar_UNSUPPORTED_TYPE,
+                dataType);
+        return NULL;
+    }
+
+    return newScalar;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psScalar.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psScalar.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psScalar.h	(revision 22271)
@@ -0,0 +1,84 @@
+
+/** @file  psScalar.h
+ *
+ *  @brief Contains basic scalar definitions and operations
+ *
+ *  This file defines the basic type for a scalar struct and functions useful
+ *  in manupulating scalars.
+ *
+ *  @ingroup Scalar
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.11.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_SCALAR_H
+#define PS_SCALAR_H
+
+#include "psType.h"
+
+/// @addtogroup Scalar
+/// @{
+
+/** Basic scalar data structure.
+ *
+ * Struct for maintaining a scalar of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psType type;                ///< Type of data.
+
+    union {
+        psU8 U8;                ///< Unsigned 8-bit integer data.
+        psU16 U16;              ///< Unsigned 16-bit integer data.
+        psU32 U32;              ///< Unsigned 32-bit integer data.
+        psU64 U64;              ///< Unsigned 64-bit integer data.
+        psS8 S8;                ///< Signed 8-bit integer data.
+        psS16 S16;              ///< Signed 16-bit integer data.
+        psS32 S32;              ///< Signed 32-bit integer data.
+        psS64 S64;              ///< Signed 64-bit integer data.
+        psF32 F32;              ///< Single-precision float data.
+        psF64 F64;              ///< Double-precision float data.
+        psC32 C32;              ///< Single-precision complex data.
+        psC64 C64;              ///< Double-precision complex data.
+    } data;                     ///< Union for data types.
+}
+psScalar;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Allocate a scalar.
+ *
+ * Uses psLib memory allocation functions to create scalar data as defined by the psType type.
+ * Accepts a complex 64 bit float for input value, as max size, but resizes according to
+ * correct type.
+ *
+ * @return psScalar*   Pointer to a new psScalar.
+ */
+psScalar* psScalarAlloc(
+    psC64 value,                       ///< Data to be put into psScalar.
+    psElemType dataType                ///< Type of data to be held by psScalar.
+);
+
+/** Copy a scalar.
+ *
+ * Uses psLib memory allocation functions to copy a scalar.
+ *
+ * @return psScalar*    A copy of the input scalar
+ */
+psScalar* psScalarCopy(
+    const psScalar *scalar  ///< Scalar to copy.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psVector.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psVector.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psVector.c	(revision 22271)
@@ -0,0 +1,539 @@
+/** @file  psVector.c
+*
+*  @brief Contains support for basic vector types
+*
+*  This file defines the basic type for a vector struct and functions useful
+*  in manupulating vectors.
+*
+*  @author Ross Harman, MHPCC
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.41 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-29 02:25:09 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <string.h>                        // for memcpy
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psVector.h"
+#include "psLogMsg.h"
+#include "psCompare.h"
+
+#include "psCollectionsErrors.h"
+
+typedef struct
+{
+    p_psVectorData data; // need this first for psVectorSortIndex to work.
+    psU32 index;
+}
+indexedVector;
+
+static void vectorFree(psVector* psVec);
+
+static void vectorFree(psVector* psVec)
+{
+    if (psVec == NULL) {
+        return;
+    }
+
+    psFree(psVec->data.U8);
+}
+
+// FUNCTION IMPLEMENTATION - PUBLIC
+
+psVector* psVectorAlloc(psU32 nalloc, psElemType elemType)
+{
+    psVector* psVec = NULL;
+    psS32 elementSize = 0;
+
+    elementSize = PSELEMTYPE_SIZEOF(elemType);
+
+    // Create vector struct
+    psVec = (psVector* ) psAlloc(sizeof(psVector));
+    psMemSetDeallocator(psVec, (psFreeFcn) vectorFree);
+
+    psVec->type.dimen = PS_DIMEN_VECTOR;
+    psVec->type.type = elemType;
+    *(int*)&psVec->nalloc = nalloc;
+    psVec->n = nalloc;
+
+    // Create vector data array
+    psVec->data.U8 = psAlloc(nalloc * elementSize);
+
+    return psVec;
+}
+
+psVector* psVectorRealloc(psVector* in, psU32 nalloc)
+{
+    psS32 elementSize = 0;
+    psElemType elemType;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_REALLOC_NULL);
+        return NULL;
+    } else if (in->nalloc != nalloc) {     // No need to realloc to same size
+        elemType = in->type.type;
+        elementSize = PSELEMTYPE_SIZEOF(elemType);
+        if (nalloc < in->n) {
+            in->n = nalloc;
+        }
+        // Realloc after decrementation to avoid accessing freed array elements
+        in->data.U8 = psRealloc(in->data.U8, nalloc * elementSize);
+        *(int*)&in->nalloc = nalloc;
+    }
+
+    return in;
+}
+
+psVector* psVectorRecycle(psVector* in, psU32 n, psElemType type)
+{
+    psS32 byteSize;
+
+    if (in == NULL) {
+        return psVectorAlloc(n, type);
+    }
+
+    if (in->type.dimen !=  PS_DIMEN_VECTOR &&
+            in->type.dimen !=  PS_DIMEN_TRANSV) {
+        psFree(in);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVector_NOT_A_VECTOR);
+        return NULL;
+    }
+
+    byteSize = n * PSELEMTYPE_SIZEOF(type);
+
+    // need to increase data buffer?
+    if (byteSize > in->nalloc*PSELEMTYPE_SIZEOF(in->type.type)) {
+        in->data.U8 = psRealloc(in->data.U8, byteSize);
+        *(int*)&in->nalloc = n;
+    }
+
+    in->type.dimen = PS_DIMEN_VECTOR;
+    in->type.type = type;
+    in->n = n;
+    return in;
+}
+
+psVector* psVectorCopy(psVector* out, const psVector* in, psElemType type)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_SORT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    psS32 nElements = in->n;
+
+    out = psVectorRecycle(out, nElements, type);
+
+    #define PSVECTOR_COPY(INTYPE,OUTTYPE) { \
+        ps##INTYPE *inVec = in->data.INTYPE; \
+        ps##OUTTYPE *outVec = out->data.OUTTYPE; \
+        for (psS32 col=0;col<nElements;col++) { \
+            *(outVec++) = *(inVec++); \
+        } \
+    }
+
+    #define PSVECTOR_COPY_CASE(OUTTYPE) \
+case PS_TYPE_##OUTTYPE: { \
+        switch (in->type.type) { \
+        case PS_TYPE_S8: \
+            PSVECTOR_COPY(S8,OUTTYPE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSVECTOR_COPY(S16,OUTTYPE); \
+            break; \
+        case PS_TYPE_S32: \
+            PSVECTOR_COPY(S32,OUTTYPE); \
+            break; \
+        case PS_TYPE_S64: \
+            PSVECTOR_COPY(S64,OUTTYPE); \
+            break; \
+        case PS_TYPE_U8: \
+            PSVECTOR_COPY(U8,OUTTYPE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSVECTOR_COPY(U16,OUTTYPE); \
+            break; \
+        case PS_TYPE_U32: \
+            PSVECTOR_COPY(U32,OUTTYPE); \
+            break; \
+        case PS_TYPE_U64: \
+            PSVECTOR_COPY(U64,OUTTYPE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSVECTOR_COPY(F32,OUTTYPE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSVECTOR_COPY(F64,OUTTYPE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSVECTOR_COPY(C32,OUTTYPE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSVECTOR_COPY(C64,OUTTYPE); \
+            break; \
+        default: { \
+                char* typeStr; \
+                PS_TYPE_NAME(typeStr,type); \
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                        PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE, \
+                        typeStr); \
+                psFree(out); \
+            } \
+        } \
+        break; \
+    }
+
+    switch (type) {
+        PSVECTOR_COPY_CASE(S8);
+        PSVECTOR_COPY_CASE(S16);
+        PSVECTOR_COPY_CASE(S32);
+        PSVECTOR_COPY_CASE(S64);
+        PSVECTOR_COPY_CASE(U8);
+        PSVECTOR_COPY_CASE(U16);
+        PSVECTOR_COPY_CASE(U32);
+        PSVECTOR_COPY_CASE(U64);
+        PSVECTOR_COPY_CASE(F32);
+        PSVECTOR_COPY_CASE(F64);
+        PSVECTOR_COPY_CASE(C32);
+        PSVECTOR_COPY_CASE(C64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE,
+                    typeStr);
+            psFree(out);
+
+            break;
+        }
+    }
+    return out;
+
+
+}
+
+psVector* psVectorSort(psVector* outVector, const psVector* inVector)
+{
+    psS32 N = 0;
+    psS32 elSize = 0;
+    psPtr inVec = NULL;
+    psPtr outVec = NULL;
+    psElemType inType = 0;
+
+    if (inVector == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_SORT_NULL);
+        psFree(outVector);
+        return NULL;
+    }
+
+    inType = inVector->type.type;
+    N = inVector->n;
+    inVec = (psPtr)inVector->data.U8;
+    elSize = PSELEMTYPE_SIZEOF(inType);
+
+    if (outVector == NULL) {
+        outVector = psVectorAlloc(N, inType);
+    }
+
+    // check to see if output vector needs to be resized/retyped
+    if ( (N > outVector->nalloc) ||
+            (inType != outVector->type.type) ) {
+        // reshape the output vector to match the input vector's size/type.
+        outVector = psVectorRecycle(outVector,N,inType);
+    }
+    outVector->n = N;
+    outVec = outVector->data.U8;
+
+    if (N == 0) {
+        // no need to sort anything, as there are no elements in input vector.
+        return outVector;
+    }
+
+    // Copy input vector values into output vector if not in-place sorting
+    if (inVector != outVector) {
+        memcpy(outVec, inVec, elSize * N);
+    }
+
+    // Sort output vector
+    switch (inType) {
+    case PS_TYPE_U8:
+        qsort(outVec, N, elSize, psCompareU8);
+        break;
+    case PS_TYPE_U16:
+        qsort(outVec, N, elSize, psCompareU16);
+        break;
+    case PS_TYPE_U32:
+        qsort(outVec, N, elSize, psCompareU32);
+        break;
+    case PS_TYPE_U64:
+        qsort(outVec, N, elSize, psCompareU64);
+        break;
+    case PS_TYPE_S8:
+        qsort(outVec, N, elSize, psCompareS8);
+        break;
+    case PS_TYPE_S16:
+        qsort(outVec, N, elSize, psCompareS16);
+        break;
+    case PS_TYPE_S32:
+        qsort(outVec, N, elSize, psCompareS32);
+        break;
+    case PS_TYPE_S64:
+        qsort(outVec, N, elSize, psCompareS64);
+        break;
+    case PS_TYPE_F32:
+        qsort(outVec, N, elSize, psCompareF32);
+        break;
+    case PS_TYPE_F64:
+        qsort(outVec, N, elSize, psCompareF64);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE,
+                inType);
+        psFree(outVector);
+        return NULL;
+    }
+
+    return outVector;
+}
+
+psVector* psVectorSortIndex(psVector* outVector, const psVector* inVector)
+{
+    psS32 N = 0;
+    psElemType inType = 0;
+
+    if (inVector == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_SORT_NULL);
+        psFree(outVector);
+        return NULL;
+    }
+
+    inType = inVector->type.type;
+    N = inVector->n;
+
+    if (N == 0) {
+        // no need to sort anything, as there are no elements in input vector.
+        return outVector;
+    }
+
+    // ok, let's create a temporary indexed vector
+    indexedVector* idxVector = psAlloc(sizeof(indexedVector)*N);
+    int elSize = PSELEMTYPE_SIZEOF(inType);
+    for (int i = 0; i < N; i++) {
+        idxVector[i].data.U8 = inVector->data.U8+i*elSize;
+        idxVector[i].index = i;
+    }
+
+    // Sort indexed vector
+    // n.b., since first element in indexedVector is a pointer to the data,
+    // we can use the 'Ptr' version of the standard compare functions
+    switch (inType) {
+    case PS_TYPE_U8:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU8Ptr);
+        break;
+    case PS_TYPE_U16:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU16Ptr);
+        break;
+    case PS_TYPE_U32:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU32Ptr);
+        break;
+    case PS_TYPE_U64:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU64Ptr);
+        break;
+    case PS_TYPE_S8:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS8Ptr);
+        break;
+    case PS_TYPE_S16:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS16Ptr);
+        break;
+    case PS_TYPE_S32:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS32Ptr);
+        break;
+    case PS_TYPE_S64:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS64Ptr);
+        break;
+    case PS_TYPE_F32:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareF32Ptr);
+        break;
+    case PS_TYPE_F64:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareF64Ptr);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE,
+                inType);
+        psFree(idxVector);
+        psFree(outVector);
+        return NULL;
+    }
+
+    // extract the indices to the output vector
+    outVector = psVectorRecycle(outVector, N, PS_TYPE_U32);
+    psU32* outData = outVector->data.U32;
+    for (int i = 0; i < N; i++) {
+        outData[i] = idxVector[i].index;
+    }
+
+    // Free temp memory
+    psFree(idxVector);
+
+    return outVector;
+}
+
+char* psVectorToString(psVector* vector, int maxLength)
+{
+
+    if (maxLength < 5) {
+        return NULL;
+    }
+
+    char* str = psAlloc(sizeof(char)*maxLength+1);
+
+    if (vector == NULL) {
+        snprintf(str,maxLength, "NULL");
+        return str;
+    }
+
+    int size = vector->n;
+
+    if (size == 0) {
+        snprintf(str,maxLength, "[]");
+        return str;
+    }
+
+    char* tempStr = psAlloc(sizeof(char)*maxLength+1);
+    *str = '\0';
+    bool full = false;
+
+    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
+case PS_TYPE_##TYPE: \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
+        strncat(str,tempStr,maxLength); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    #define APPEND_ELEMENTS_CASE_COMPLEX(TYPE,CREAL,CIMAG) \
+case PS_TYPE_##TYPE: \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s%g%+gi", prefix, \
+                 CREAL(vector->data.TYPE[lcv]), \
+                 CIMAG(vector->data.TYPE[lcv])); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    int lcv;
+    char* prefix = "[";
+    switch(vector->type.type) {
+        APPEND_ELEMENTS_CASE(S8,char,"%hd")
+        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
+        APPEND_ELEMENTS_CASE(S32,int,"%d")
+        APPEND_ELEMENTS_CASE(S64,long,"%ld")
+        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
+        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
+        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
+        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
+        APPEND_ELEMENTS_CASE(F32,double,"%g")
+        APPEND_ELEMENTS_CASE(F64,double,"%g")
+        APPEND_ELEMENTS_CASE_COMPLEX(C32,crealf,cimagf)
+        APPEND_ELEMENTS_CASE_COMPLEX(C64,creal,cimag)
+    default:
+        snprintf(str,maxLength,"[...]");
+        break;
+    }
+
+    if (full) {
+        // couldn't all fit in given string length
+
+        // remove elements until there is room for ",...]"
+        while (strlen(str) > maxLength - 5) {
+            char* lastComma = strrchr(str,',');
+            if (lastComma == NULL) { // no comma, must be first number
+                str[1] = '\0';
+            } else {
+                *lastComma = '\0';
+            }
+        }
+        strncat(str,",...]",maxLength);
+    } else {
+        strncat(str,"]",maxLength);
+    }
+
+    psFree(tempStr);
+
+    return str;
+}
+
+psF64 p_psVectorGetElementF64(psVector* vector,
+                              int position)
+{
+    if (vector == NULL) {
+        return NAN;
+    }
+    if (position < 0 || position >= vector->n) {
+        return NAN;
+    }
+
+    switch (vector->type.type) {
+    case PS_TYPE_U8:
+        return vector->data.U8[position];
+        break;
+    case PS_TYPE_U16:
+        return vector->data.U16[position];
+        break;
+    case PS_TYPE_U32:
+        return vector->data.U32[position];
+        break;
+    case PS_TYPE_U64:
+        return vector->data.U64[position];
+        break;
+    case PS_TYPE_S8:
+        return vector->data.S8[position];
+        break;
+    case PS_TYPE_S16:
+        return vector->data.S16[position];
+        break;
+    case PS_TYPE_S32:
+        return vector->data.S32[position];
+        break;
+    case PS_TYPE_S64:
+        return vector->data.S64[position];
+        break;
+    case PS_TYPE_F32:
+        return vector->data.F32[position];
+        break;
+    case PS_TYPE_F64:
+        return vector->data.F64[position];
+    default:
+        return NAN;
+    }
+}
+
+bool p_psVectorPrint (FILE *f, psVector *a, char *name)
+{
+
+    fprintf (f, "vector: %s\n", name);
+
+    for (int i = 0; i < a[0].n; i++) {
+        fprintf (f, "%f\n", p_psVectorGetElementF64(a, i));
+    }
+    fprintf (f, "\n");
+    return (true);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/collections/psVector.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/collections/psVector.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/collections/psVector.h	(revision 22271)
@@ -0,0 +1,178 @@
+/** @file  psVector.h
+ *
+ *  @brief Contains basic vector definitions and operations
+ *
+ *  This file defines the basic type for a vector struct and functions useful
+ *  in manupulating vectors.
+ *
+ *  @ingroup Vector
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-29 02:25:09 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_VECTOR_H
+#define PS_VECTOR_H
+
+#include<stdio.h>
+
+#include "psType.h"
+
+/// @addtogroup Vector
+/// @{
+
+///< Union of psVector data types.
+typedef union {
+    psU8* U8;               ///< Unsigned 8-bit integer data.
+    psU16* U16;             ///< Unsigned 16-bit integer data.
+    psU32* U32;             ///< Unsigned 32-bit integer data.
+    psU64* U64;             ///< Unsigned 64-bit integer data.
+    psS8* S8;               ///< Signed 8-bit integer data.
+    psS16* S16;             ///< Signed 16-bit integer data.
+    psS32* S32;             ///< Signed 32-bit integer data.
+    psS64* S64;             ///< Signed 64-bit integer data.
+    psF32* F32;             ///< Single-precision float data.
+    psF64* F64;             ///< Double-precision float data.
+    psC32* C32;             ///< Single-precision complex data.
+    psC64* C64;             ///< Double-precision complex data.
+} p_psVectorData;
+
+/** An vector to support primitive types.
+ *
+ * Struct for maintaining an vector of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psType type;                ///< Type of data.
+    int n;                      ///< Number of elements in use.
+    const int nalloc;           ///< Total number of elements available.
+    p_psVectorData data;        ///< Union for data types.
+}
+psVector;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+
+/** Allocate a vector.
+ *
+ *  Uses psLib memory allocation functions to create a vector collection of
+ *  data as defined by the psType type.
+ *
+ * @return psVector*    Pointer to psVector.
+ */
+psVector* psVectorAlloc(
+    psU32 nalloc,               ///< Total number of elements to make available.
+    psElemType dataType                ///< Type of data to be held by vector.
+);
+
+/** Reallocate a vector.
+ *
+ *  Uses psLib memory allocation functions to reallocate a vector collection
+ *  of data. The vector is reallocated according to the psType type member
+ *  contained within the vector.
+ *
+ *  @return psVector*      Pointer to psVector.
+ *
+ */
+psVector* psVectorRealloc(
+    psVector* psVec,                   ///< Vector to reallocate.
+    psU32 nalloc                       ///< Total number of elements to make available.
+);
+
+/** Recycle a vector.
+ *
+ *  Uses psLib memory allocation functions to reallocate a vector collection
+ *  of data. The vector is reallocated according to the psElemType type
+ *  parameter.
+ *
+ * @return psVector*       Pointer to psVector.
+ *
+ */
+psVector* psVectorRecycle(
+    psVector* psVec,
+    ///< Vector to recycle.  If NULL, a new vector is created.  No effort
+    ///< taken to preserve the values.
+
+    psU32 nalloc,                      ///< Total number of elements to make available.
+    psElemType type                    ///< the datatype of the returned vector
+);
+
+/** Copy a vector, converting types.
+ *
+ *  Performs a deep copy of the elements of one psVector to a new psVector,
+ *  converting numeric types to a specified type.
+ *
+ * @return psVector*       Pointer to resulting psVector.
+ *
+ */
+psVector* psVectorCopy(
+    psVector* out,                     ///< if non-NULL, a psVector to recycle
+    const psVector* in,                ///< the vector to copy.
+    psElemType type                    ///< the data type of the resulting psVector
+);
+
+/** Sort an array of floats.
+ *
+ *  Sorts an array of floats in ascending order.  This function is valid for
+ *  all non-complex data types.
+ *
+ *  @return  psVector*     Pointer to sorted psVector.
+ */
+psVector* psVectorSort(
+    psVector* outVector,               ///< the output vector to recycle, or NULL if new vector desired.
+    const psVector* inVector           ///< the vector to sort.
+);
+
+/** Creates an array of indices based on sort ordered of array.
+ *
+ *  Sorts a vector and creates an integer array holding indices of
+ *  sorted float values based on pre-sort index positions.
+ *
+ *  @return  psVector*     vector of the indices of sort.
+ */
+psVector* psVectorSortIndex(
+    psVector* outVector,               ///< vector to recycle
+    const psVector* inVector           ///< vector to sort
+);
+
+/** Creates a string from a psVector's values in the form "[x0,x1,x2]".
+ *
+ *  @return psPtr          a newly allocated string
+ */
+char* psVectorToString(
+    psVector* vector,                  ///< vector to create a string from
+    int maxLength                      ///< the maximum length of the resulting string
+);
+
+/** Returns an element in the vector as a psF64 value
+ *
+ *  @return psF64          the value at specified position, or NAN if position is invalid.
+ */
+psF64 p_psVectorGetElementF64(
+    psVector* vector,                  ///< vector to retrieve element
+    int position                       ///< the vector position to get
+);
+
+/** Print a vector to a stream
+ *
+ *  @return psBool          TRUE is successful, otherwise FALSE.
+ */
+bool p_psVectorPrint (
+    FILE *f,                           ///< output stream
+    psVector *a,                       ///< vector to print
+    char *name                         ///< name of vector (for title)
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/.cvsignore	(revision 22271)
@@ -0,0 +1,7 @@
+Makefile.in
+.deps
+.libs
+Makefile
+*.lo
+*.la
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/Makefile.am	(revision 22271)
@@ -0,0 +1,30 @@
+#Makefile for dataIO functions of psLib
+#
+INCLUDES = \
+	-I$(top_srcdir)/src/astronomy \
+	-I$(top_srcdir)/src/collections \
+	-I$(top_srcdir)/src/dataManip \
+	-I$(top_srcdir)/src/image \
+	-I$(top_srcdir)/src/sysUtils \
+	$(all_includes)
+
+noinst_LTLIBRARIES = libpslibdataIO.la
+
+libpslibdataIO_la_SOURCES = \
+	psLookupTable.c \
+	psFits.c \
+	psDB.c
+
+
+BUILT_SOURCES = psFileUtilsErrors.h
+EXTRA_DIST = psFileUtilsErrors.dat psFileUtilsErrors.h dataIO.i
+
+psFileUtilsErrors.h: psFileUtilsErrors.dat
+	perl $(top_srcdir)/src/parseErrorCodes.pl --data=$? $@
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psLookupTable.h \
+	psFits.h \
+	psDB.h
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/dataIO.i
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/dataIO.i	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/dataIO.i	(revision 22271)
@@ -0,0 +1,4 @@
+/* dataIO headers */
+%include "psFileUtilsErrors.h"
+%include "psFits.h"
+%include "psLookupTable.h"
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psDB.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psDB.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psDB.c	(revision 22271)
@@ -0,0 +1,1596 @@
+/** @file  psDB.c
+ *
+ * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
+ * vim: set cindent ts=8 sw=4 expandtab:
+ *
+ *  @brief database functions
+ *
+ *  This file contains functions that perform basic database operations.  MySQL
+ *  4.1.2 or newer is required.
+ *
+ *  @author Aaron Culliney
+ *  @author Joshua Hoblitt
+ *
+ *  @version $Revision: 1.16.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2005 Joshua Hoblitt, University of Hawaii
+ */
+
+#ifdef BUILD_PSDB
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <mysql.h>
+#include <mysql_com.h> // enum_field_types
+
+#include "psDB.h"
+#include "psMemory.h"
+#include "psAbort.h"
+#include "psError.h"
+#include "psString.h"
+
+
+typedef struct
+{
+    enum enum_field_types type;
+    bool            isUnsigned;
+}
+mysqlType;
+
+// database utility functions
+static bool     psDBRunQuery(psDB *dbh, const char *query);
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount);
+
+// SQL generation functions
+static char    *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, psU64 limit);
+static char    *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row);
+static char    *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values);
+static char    *psDBGenerateDeleteRowSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateWhereSQL(psMetadata *where);
+static char    *psDBGenerateSetSQL(psMetadata *set
+                                  );
+
+// lookup table functions
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags);
+static char    *psDBPTypeToSQL(psElemType pType);
+static mysqlType *psDBPTypeToMySQL(psElemType pType);
+
+static psHash  *psDBGetPTypeToSQLTable(void);
+static void     psDBPTypeToSQLTableCleanup(void);
+
+static psHash  *psDBGetSQLToPTypeTable(void);
+static void     psDBSQLToPTypeTableCleanup(void);
+
+static psHash  *psDBGetMySQLToSQLTable(void);
+static void     psDBMySQLToSQLTableCleanup(void);
+
+static psHash  *psDBGetPTypeToMySQLTable(void);
+static void     psDBPTypeToMySQLTableCleanup(void);
+
+static psPtr    psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned);
+static void     psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string);
+static void     psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value);
+
+// pType utility functions
+static psPtr    psDBGetPTypeNaN(psElemType pType);
+static bool     psDBIsPTypeNaN(psElemType pType, psPtr data);
+
+// string utility functions
+static char    *psDBIntToString(psU64 n);
+
+
+// public functions
+/*****************************************************************************/
+
+psDB *psDBInit(const char *host, const char *user, const char *passwd, const char *dbname)
+{
+    MYSQL           *mysql;
+    psDB            *dbh;
+
+    mysql = mysql_init(NULL);
+    if (!mysql) {
+        psAbort(__func__, "mysql_init(), out of memory.");
+    }
+
+    if (!mysql_real_connect(mysql, host, user, passwd, dbname, 0, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to connect to database.  Error: %s", mysql_error(mysql));
+
+        mysql_close(mysql);
+
+        return NULL;
+    }
+
+    dbh = psAlloc(sizeof(psDB));
+
+    dbh->mysql = mysql;
+
+    return dbh;
+}
+
+void psDBCleanup(psDB *dbh)
+{
+    mysql_close(dbh->mysql);
+    dbh->mysql = NULL;
+    psFree(dbh);
+
+    // ASC WARNING NOTE: the psDBSQLToPTypeTableCleanup cleanup routine
+    // needs to be called first because it refers to
+    // psDBGetPTypeToSQLTable ...
+    psDBSQLToPTypeTableCleanup();
+    psDBMySQLToSQLTableCleanup();
+    psDBPTypeToSQLTableCleanup();
+    psDBPTypeToMySQLTableCleanup();
+}
+
+bool psDBCreate(psDB *dbh, const char *dbname)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "CREATE DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_create_db() is deprecated
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create new database.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBChange(psDB *dbh, const char *dbname)
+{
+    if (mysql_select_db(dbh->mysql, dbname) != 0) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to change database.  Error: %s", mysql_error(dbh->mysql));
+
+        return false;
+    }
+
+    return true;
+}
+
+bool psDBDrop(psDB *dbh, const char *dbname)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "DROP DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_drop_db() is deprecated
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop database.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBCreateTable(psDB *dbh, const char *tableName, const psMetadata *md)
+{
+    char            *query;
+    bool            status;
+
+    query = psDBGenerateCreateTableSQL(tableName, md);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create table.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBDropTable(psDB *dbh, const char *tableName)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "DROP TABLE %s", tableName);
+
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop table.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+psArray *psDBSelectColumn(psDB *dbh, const char *tableName, const char *col, psU64 limit)
+{
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    char            *query;             // SQL query
+    my_ulonglong    rowCount;           // number of rows in db result set
+    unsigned long   dataSize;           // size of field
+    unsigned int    fieldCount;         // number of fields in db result set
+    psArray         *column = NULL;     // return array
+    psPtr           data;               // copy of result field
+
+    query = psDBGenerateSelectRowSQL(tableName, col, NULL, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+        return NULL;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+        psFree(query);
+        return NULL;
+    }
+
+    psFree(query);
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query returned no data.  If it's non-zero
+        // then something bad has happened.
+        if (fieldCount != 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+            return NULL;
+        }
+    }
+
+    rowCount = mysql_num_rows(result);
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    column = psArrayAlloc(rowCount);
+    column->n = 0;
+
+    while ((row = mysql_fetch_row(result))) {
+        // get the first element of lengths array that is part of the
+        // result set
+        dataSize = *(mysql_fetch_lengths(result));
+
+        // represent NULL as an empty string
+        if (row[0] == NULL) {
+            data = psStringCopy("");
+        } else {
+            data = psAlloc(dataSize+1);
+            memcpy(data, row[0], dataSize);
+            ((char*)data)[dataSize] = '\0';
+        }
+
+        // add field to return array
+        psArrayAdd(column, 0, data);
+        psFree(data);
+    }
+
+    mysql_free_result(result);
+
+    return column;
+}
+
+// dest = assign to, source = source string psArray, conv = conversion function,
+// type = type to cast to, pType = psElemType
+#define PS_STR_ARRAY_TO_PTYPE(dest, source, conv, type, pType) \
+{ \
+    psPtr           myNaN; \
+    int             i; \
+    \
+    for (i = 0; i < source->n; i++) { \
+        if (strlen(source->data[i])) { \
+            dest[i] = (type)conv(source->data[i]); \
+        } else { \
+            myNaN = psDBGetPTypeNaN(pType); \
+            dest[i] = *(type *)myNaN; \
+            psFree(myNaN); \
+        } \
+    } \
+}
+
+psVector *psDBSelectColumnNum(psDB *dbh, const char *tableName, const char *col, psElemType pType, const psU64 limit)
+{
+    psArray         *stringColumn;      // source psArray
+    psVector        *column;            // dest psVector
+
+    stringColumn = psDBSelectColumn(dbh, tableName, col, limit);
+    if (!stringColumn) {
+        // could be an error or the result set was just empty
+        return NULL;
+    }
+
+    column = psVectorAlloc(stringColumn->n, pType);
+
+    // conversion functions are a portability issue
+    switch (pType) {
+    case PS_TYPE_S8:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S8, stringColumn, atoi, psS8, PS_TYPE_S8);
+        break;
+    case PS_TYPE_S16:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S16, stringColumn, atoi, psS16, PS_TYPE_S16);
+        break;
+    case PS_TYPE_S32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S32, stringColumn, atoi, psS32, PS_TYPE_S32);
+        break;
+    case PS_TYPE_S64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S64, stringColumn, atoll, psS64, PS_TYPE_S64);
+        break;
+    case PS_TYPE_U8:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U8, stringColumn, atoi, psU8, PS_TYPE_U8);
+        break;
+    case PS_TYPE_U16:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U16, stringColumn, atoi, psU16, PS_TYPE_U16);
+        break;
+    case PS_TYPE_U32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U32, stringColumn, atoi, psU32, PS_TYPE_U32);
+        break;
+    case PS_TYPE_U64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U64, stringColumn, atoll, psU64, PS_TYPE_U64);
+        break;
+    case PS_TYPE_F32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.F32, stringColumn, atof, psF32, PS_TYPE_F32);
+        break;
+    case PS_TYPE_F64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.F64, stringColumn, atof, psF64, PS_TYPE_F64);
+        break;
+    case PS_TYPE_C32:
+        // this is a bogus SQL type
+        PS_STR_ARRAY_TO_PTYPE(column->data.C32, stringColumn, atof, psC32, PS_TYPE_C32);
+        break;
+    case PS_TYPE_C64:
+        // this is a bogus SQL type
+        PS_STR_ARRAY_TO_PTYPE(column->data.C64, stringColumn, atof, psC64, PS_TYPE_C64);
+        break;
+    case PS_TYPE_BOOL:
+        // valid for psVector?
+        break;
+    }
+
+    psFree(stringColumn);
+
+    return column;
+}
+
+psArray *psDBSelectRows(psDB *dbh, const char *tableName, psMetadata *where, const psU64 limit)
+{
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    MYSQL_FIELD     *field;             // field type info
+    char            *query;             // SQL query
+    my_ulonglong    rowCount;           // number of rows in db result set
+    unsigned int    fieldCount;         // number of fields in db result set
+    unsigned long   *fieldLength;       // field sizes
+    long            len;                // field length
+    psArray         *resultSet;         // return array
+    int             i;                  // field index
+    psMetadata      *md;                // a row
+    psU32           pType;              // psElemType of a field
+    psPtr           data;               // copy of result field
+
+    query = psDBGenerateSelectRowSQL(tableName, NULL, where, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+
+        psFree(query);
+
+        return NULL;
+    }
+
+    psFree(query);
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query should have returned no data.  If
+        // it's non-zero then something bad has happened.
+        if (fieldCount != 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+
+            return NULL;
+        }
+    }
+
+    rowCount = mysql_num_rows(result);
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    resultSet = psArrayAlloc(rowCount);
+    resultSet->n = 0;
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    while ((row = mysql_fetch_row(result))) {
+        // allocate new psMetadata to represent a row
+        md = psMetadataAlloc();
+
+        fieldLength = mysql_fetch_lengths(result);
+
+        for (i = 0; i < fieldCount; i++) {
+            // lookup MySQL column type
+            pType = psDBMySQLToPType(field[i].type, field[i].flags);
+
+            len = fieldLength[i];
+            if (len) {
+                data = psAlloc(len+1);
+                memcpy(data, row[i], len);
+                ((char*)data)[len] = '\0';
+            } else {
+                data = psDBGetPTypeNaN(pType);
+            }
+
+            // copy field data and convert NULLs to the appropriate NaN value
+            if (pType == PS_META_STR) {
+                psMetadataAddStr(md, 0, field[i].name, "", data);
+            } else if (pType == PS_META_S32) {
+                psMetadataAddS32(md, 0, field[i].name, "", atoll(data));
+            } else if (pType == PS_META_F32) {
+                psMetadataAddF32(md, 0, field[i].name, "", atof(data));
+            } else if (pType == PS_META_F64) {
+                psMetadataAddF64(md, 0, field[i].name, "", atof(data));
+            } else {
+                // XXX: assume binary string ...
+                psMetadataAddStr(md, 0, field[i].name, "", data);
+            }
+
+            psFree(data);
+        }
+
+        // add row to result set
+        psArrayAdd(resultSet, 0, md);
+        psFree(md);
+    }
+
+    mysql_free_result(result);
+
+    return resultSet;
+}
+
+bool psDBInsertOneRow(psDB *dbh, const char *tableName, const psMetadata *row)
+{
+    psArray         *rowSet;            // psArray of row to insert
+
+    rowSet = psArrayAlloc(1);
+    rowSet->n = 0;
+    psArrayAdd(rowSet, 0, row);
+
+    if (!psDBInsertRows(dbh, tableName, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "Insert failed.");
+
+        psFree(rowSet);
+
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool psDBInsertRows(psDB *dbh, const char *tableName, const psArray *rowSet)
+{
+    psMetadata      *row;               // row of data
+    char            *query;             // SQL query
+    MYSQL_STMT      *stmt;              // prepared db statement
+    MYSQL_BIND      *bind;              // field values to insert
+    unsigned long   paramCount;         // number of placeholders in query
+    psU64           j;                  // row index
+
+    // we are assuming that all rows in the set have an identical with reguard
+    // to field count and type
+    row = rowSet->data[0];
+
+    query = psDBGenerateInsertRowSQL(tableName, row);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    stmt = mysql_stmt_init(dbh->mysql);
+    if (!stmt) {
+        psAbort(__func__, "mysql_stmt_init(), out of memory.");
+    }
+
+    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_stmt_close(stmt);
+        psFree(query);
+
+        return false;
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    paramCount = mysql_stmt_param_count(stmt);
+
+    // structure larger enough to hold one field of data per place holder
+    bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+
+    // loop over rows
+    for (j = 0; j < rowSet->n; j++) {
+        row = rowSet->data[j];
+
+        // reset bind for each row
+        memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
+
+        if (!psDBPackRow(bind, row, paramCount)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to pack params into bind structure.");
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+        }
+
+        if (mysql_stmt_bind_param(stmt, bind)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+
+            return false;
+        }
+
+        if (mysql_stmt_execute(stmt)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s",
+                    mysql_stmt_error(stmt));
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+
+            return false;
+        }
+    } // end loop over rows
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    psFree(bind);
+    mysql_stmt_close(stmt);
+
+    return true;
+}
+
+psArray *psDBDumpRows(psDB *dbh, const char *tableName)
+{
+    return psDBSelectRows(dbh, tableName, NULL, 0);
+}
+
+psMetadata *psDBDumpCols(psDB *dbh, const char *tableName)
+{
+    MYSQL_RES       *result;
+    MYSQL_FIELD     *field;
+    unsigned int    fieldCount;
+    psMetadata      *table;
+    psU32           pType;
+    unsigned int    i;
+    psPtr           column;
+
+    // find column types
+    result = mysql_list_fields(dbh->mysql, tableName, NULL);
+    if (!result) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Failed to retrieve column types.");
+    }
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    table = psMetadataAlloc();
+
+    // fetch each column and load into psMetadata
+    for (i =0; i < fieldCount; i++) {
+        // find ptype of column
+        pType = psDBMySQLToPType(field[i].type, field[i].flags);
+        //psLogMsg( __func__, PS_LOG_INFO, "pType=[%ld]\n", pType );
+
+        // if the ptype is PS_TYPE_PTR assume that it's a string and fetch the
+        // column as an psArray of strings; otherwise fetch the column as a
+        // psVector.
+        if (pType == PS_META_STR) {
+            // PS_META_UNKNOWN -> PS_META_ARRAY ?
+            column = psDBSelectColumn(dbh, tableName, field[i].name, 0);
+            psMetadataAddStr(table, 0, field[i].name, "", column);
+            psFree(column);
+        } else {
+            column = psDBSelectColumnNum(dbh, tableName, field[i].name, pType, 0);
+            psMetadataAddVector(table, 0, field[i].name, "", column);
+            psFree(column);
+        }
+    }
+
+    return table;
+}
+
+psS64 psDBUpdateRows(psDB *dbh, const char *tableName, const psMetadata *where, const psMetadata *values)
+{
+    char            *query;
+    MYSQL_STMT      *stmt;              // prepared db statement
+    unsigned long   paramCount;         // number of placeholders in query
+    MYSQL_BIND      *bind;              // field values to insert
+    my_ulonglong    rowsAffected;       // number of rows affected by query
+
+    query = psDBGenerateUpdateRowSQL(tableName, where, values);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return -1;
+    }
+
+    stmt = mysql_stmt_init(dbh->mysql);
+    if (!stmt) {
+        psAbort(__func__, "mysql_stmt_init(), out of memory.");
+    }
+
+    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_stmt_close(stmt);
+        psFree(query);
+
+        return -1;
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    paramCount = mysql_stmt_param_count(stmt);
+
+    // structure large enough to hold one field of data per place holder
+    bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+
+    // init bind
+    memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
+
+    if (!psDBPackRow(bind, values, paramCount)) {
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    if (mysql_stmt_bind_param(stmt, bind)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    if (mysql_stmt_execute(stmt)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s",
+                mysql_stmt_error(stmt));
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    psFree(bind);
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    rowsAffected = mysql_stmt_affected_rows(stmt);
+
+    mysql_stmt_close(stmt);
+
+    return rowsAffected;
+}
+
+psS64 psDBDeleteRows(psDB *dbh, const char *tableName, const psMetadata *where)
+{
+    char            *query;
+
+    query = psDBGenerateDeleteRowSQL(tableName, where);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return -1;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Delete failed.");
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(query);
+
+        return -1;
+    }
+
+    psFree(query);
+
+    if (!where) {
+        // we truncated the table so mysql_affected_rows() won't work
+        return 1;
+    }
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    return (psS64)mysql_affected_rows(dbh->mysql);
+}
+
+// database utility functions
+/*****************************************************************************/
+
+static bool psDBRunQuery(psDB *dbh, const char *query)
+{
+    if (mysql_real_query(dbh->mysql, query, (unsigned long)strlen(query)) !=0) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to execute query.  Error: %s\n", mysql_error(dbh->mysql));
+
+        return false;
+    }
+
+    return true;
+}
+
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount)
+{
+    psListIterator  *cursor;            // row iterator
+    psMetadataItem  *item;              // field in row
+    mysqlType       *mType;             // type tmp variable
+    static bool     isNull = true;      // used in a MYSQL_BIND to indicate NULL,
+    // this will be used outside of this func
+    psU32           i;                  // field index
+
+    // check size of values == paramCount ?
+
+    cursor = psListIteratorAlloc(values->list, 0, false);
+
+    // loop over fields
+    i = 0;
+    while ((item = psListGetAndIncrement(cursor))) {
+        // lookup pType -> mysql type
+        mType = psDBPTypeToMySQL(item->type);
+
+        bind[i].buffer_type = mType->type;
+        bind[i].is_unsigned = mType->isUnsigned;
+
+        psFree(mType);
+
+        // input data length is determined by the MYSQL_TYPE_* unless it's a string
+        if (item->type == PS_TYPE_S32) {
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.S32;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S32)
+                              ? (my_bool *)&isNull
+                              : NULL;
+        } else if (item->type == PS_TYPE_F32) {
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.F32;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F32)
+                              ? (my_bool *)&isNull
+                              : NULL;
+        } else if (item->type == PS_TYPE_F64) {
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.F64;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64)
+                              ? (my_bool *)&isNull
+                              : NULL;
+            // convert NaNs to NULL and set the buffer_length for strings
+            //} else if ((item->type == PS_META_STR) && (item->pType == PS_TYPE_PTR)) {
+        } else if (item->type == PS_META_STR) {
+
+            bind[i].buffer_length = (unsigned long)strlen((char *)item->data.V);
+            bind[i].length  = &bind[i].buffer_length;
+            bind[i].buffer  = item->data.V;
+            bind[i].is_null = *(char *)item->data.V == '\0'
+                              ? (my_bool *)&isNull
+                              : NULL;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE , true,
+                    "FIXME: Only type of PS_TYPE_S32 (PS_META_S32), "
+                    "PS_TYPE_F32 (PS_META_F32), PS_TYPE_F64 (PS_META_F64), "
+                    "and PS_META_STR are supported.");
+
+            psFree(cursor);
+
+            return false;
+        }
+
+        // increment field index
+        i++;
+    }
+
+    psFree(cursor);
+
+    return true;
+}
+
+
+// SQL generation functions
+/*****************************************************************************/
+
+static char *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *table)
+{
+    char            *query = NULL;      // complete query
+    psMetadataItem  *item;              // column description
+    psListIterator  *cursor;            // column iterator
+    char            *colType;           // type lookup table
+
+    if (!table) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "table param may not be null.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "CREATE TABLE %s (", tableName);
+
+    cursor = psListIteratorAlloc(table->list, 0, false);
+
+    // find column name and type
+    while ((item = psListGetAndIncrement(cursor))) {
+        if ((item->type == PS_META_S32) || (item->type == PS_META_F32) || (item->type == PS_META_F64) ||
+                (item->type == PS_TYPE_S32) || (item->type == PS_TYPE_F32) || (item->type == PS_TYPE_F64)) {
+            // + column name + _ + column type
+            colType = psDBPTypeToSQL(item->type);
+            psStringAppend(&query, "%s %s", item->name, colType);
+            psFree(colType);
+        } else if (item->type == PS_META_STR) {
+            // + column name + _ + varchar( + length + )
+            psStringAppend(&query, "%s VARCHAR(%s)", item->name, item->data.V);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "FIXME: Only type of PS_META_S32, PS_META_F32, PS_META_F64, "
+                    "and PS_META_STR are supported, (not %d).", item->type);
+
+            psFree(query);
+            psFree(cursor);
+
+            return NULL;
+        }
+
+        // add a , after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psListIteratorSet(cursor, 0);
+
+    // find database indexes
+    while ((item = psListGetAndIncrement(cursor))) {
+        if ((strncmp(item->comment, "Primary Key", strlen("Primary Key"))) == 0) {
+            psStringAppend(&query, ", PRIMARY KEY(%s)", item->name);
+        } else if ((strncmp(item->comment, "Key", strlen("Key"))) == 0) {
+            psStringAppend(&query, ", KEY(%s)", item->name);
+        }
+    }
+
+    psFree(cursor);
+
+    // end column types + table type
+    psStringAppend(&query, ") ENGINE=innodb");
+
+    return query;
+}
+
+static char *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+    char            *limitString;
+
+    // select all columns if col is NULL
+    if (col) {
+        psStringAppend(&query, "SELECT %s FROM %s", col, tableName);
+    } else {
+        psStringAppend(&query, "SELECT * FROM %s", tableName);
+    }
+
+    // select all rows if where is NULL
+    if (where) {
+        whereSQL = psDBGenerateWhereSQL(where);
+        if (!whereSQL) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+            psFree(query);
+
+            return NULL;
+        }
+        psStringAppend(&query, " %s", whereSQL);
+        psFree(whereSQL);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        limitString = psDBIntToString(limit);
+        psStringAppend(&query, " LIMIT %s", limitString);
+        psFree(limitString);
+    }
+
+    return query;
+}
+
+static char *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    psStringAppend(&query, "INSERT INTO %s (", tableName);
+
+    cursor = psListIteratorAlloc(row->list, 0, false);
+
+    // get field names
+    while ((item = psListGetAndIncrement(cursor))) {
+        psStringAppend(&query, item->name);
+
+        // + , + _ between every field name
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    // end of field names
+    psStringAppend(&query, ") VALUES (");
+
+    psListIteratorSet(cursor, 0);
+
+    // create value place holders
+    while ((item = psListGetAndIncrement(cursor))) {
+        psStringAppend(&query, "?");
+
+        // + ", " between every place holder
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psFree(cursor);
+
+    // end of values
+    psStringAppend(&query, ")");
+
+    return query;
+}
+
+static char *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values)
+{
+    char            *query = NULL;
+    char            *setSQL;
+    char            *whereSQL;
+
+    if ((!values) || (!where)) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "values and where params may not be NULL.");
+
+        return NULL;
+    }
+
+    setSQL = psDBGenerateSetSQL(values);
+    if (!setSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    whereSQL = psDBGenerateWhereSQL(where);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "UPDATE %s %s %s", tableName, setSQL, whereSQL);
+    psFree(setSQL);
+    psFree(whereSQL);
+
+    return query;
+}
+
+static char *psDBGenerateDeleteRowSQL(const char *tableName, psMetadata *where)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+
+    // delete all rows if where is NULL
+    if (!where) {
+        psStringAppend(&query, "TRUNCATE TABLE %s", tableName);
+
+        return query;
+    }
+
+    whereSQL = psDBGenerateWhereSQL(where);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "DELETE FROM %s %s", tableName, whereSQL);
+    psFree(whereSQL);
+
+    return query;
+}
+
+static char *psDBGenerateWhereSQL(psMetadata *where)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    if (!where) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "where param may not be NULL.");
+
+        return NULL;
+    }
+
+    query = psStringCopy("WHERE ");
+
+    cursor = psListIteratorAlloc(where->list, 0, false);
+
+    // find column name and match pattern
+    while ((item = psListGetAndIncrement(cursor))) {
+        // item->data must be a string
+        if ((item->type == PS_META_S32) || (item->type == PS_TYPE_S32)) {
+            psStringAppend(&query, "%s=%d", item->name, (int)(item->data.S32));
+        } else if ((item->type == PS_META_F32) || (item->type == PS_TYPE_F32)) {
+            psStringAppend(&query, "%s=%g", item->name, (double)(item->data.F32));
+        } else if ((item->type == PS_META_F64) || (item->type == PS_TYPE_F64)) {
+            psStringAppend(&query, "%s=%g", item->name, (double)(item->data.F64));
+        } else if (item->type == PS_META_STR) {
+            // + column name + _ + like + _ + ' + value + '
+            if (*(char *)item->data.V == '\0') {
+                psStringAppend(&query, "%s IS NULL", item->name);
+            } else {
+                // XXX ASC NOTE: we should have a better match for
+                // char & varchar columns than this.  LIKE is OK for
+                // very large TEXT columns that really shouldn't be
+                // used in a where clause...
+                psStringAppend(&query, "%s LIKE '%s'", item->name, item->data.V);
+            }
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "Only types PS_META_S32, PS_META_F32, PS_META_F64, PS_META_STR is supported");
+
+            psFree(cursor);
+            psFree(query);
+
+            return NULL;
+        }
+
+        // + " and " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, " AND ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+static char *psDBGenerateSetSQL(psMetadata *set
+                               )
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    if (!set
+       ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "set param may not be NULL.");
+
+        return NULL;
+    }
+
+    query = psStringCopy("SET ");
+
+    cursor = psListIteratorAlloc(set
+                                 ->list, 0, false);
+
+    // find column name
+    while ((item = psListGetAndIncrement(cursor))) {
+        // + column name + _ + = + _ + ?
+        psStringAppend(&query, "%s = ?", item->name);
+
+        // + ", " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ",  ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+
+// lookup table functions
+/*****************************************************************************/
+
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags)
+{
+    psHash          *mysqlToSQLTable;   // type lookup table
+    psHash          *sqlToPSTable;      // type lookup table
+    char            *key;               // hash tmp value
+    char            *value;             // hash tmp value
+    char            *sqlType;           // copy of lookup table result
+    psU32           pType;              // psElemType of a field
+
+    mysqlToSQLTable = psDBGetMySQLToSQLTable();
+
+    // lookup MySQL column type
+    key     = psDBIntToString((psU64)type);
+    sqlType = psHashLookup(mysqlToSQLTable, key);
+    psFree(key);
+    psFree(mysqlToSQLTable);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+        return -1;
+    }
+
+    // MySQL column types can not be directly translated to PS
+    // types as the the mysql types do not tell you if the value is
+    // signed or unsigned.  The result is this ugly conversion from
+    // mysql -> ascii -> ptype
+    if (flags & UNSIGNED_FLAG) {
+        psStringPrepend(&sqlType, "UNSIGNED ");
+    }
+
+    //psLogMsg( __func__, PS_LOG_INFO, "sqlType=[%s]\n", sqlType );
+
+    // convert MySQL type to PS type
+    sqlToPSTable = psDBGetSQLToPTypeTable();
+    value = psHashLookup(sqlToPSTable, sqlType);
+    psFree(sqlToPSTable);
+
+    if (!value) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return -1;
+    }
+
+    pType = (psU32)atol(value);
+
+    return pType;
+}
+
+static char *psDBPTypeToSQL(psElemType pType)
+{
+    psHash          *pTypeToSQLTable;   // type lookup table
+    char            *key;               // hash tmp value
+    char            *sqlType;             // hash tmp value
+
+    pTypeToSQLTable = psDBGetPTypeToSQLTable();
+
+    key = psDBIntToString((psU64)pType);
+    sqlType = psHashLookup(pTypeToSQLTable, key);
+    psFree(key);
+    psFree(pTypeToSQLTable);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(sqlType);
+
+    return sqlType;
+}
+
+static mysqlType *psDBPTypeToMySQL(psElemType pType)
+{
+    psHash          *pTypeToMySQLTable; // type lookup table
+    char            *key;               // hash tmp value
+    mysqlType       *mType;             // mysqlType struct to return
+
+    pTypeToMySQLTable = psDBGetPTypeToMySQLTable();
+
+    key = psDBIntToString((psU64)pType);
+    mType = psHashLookup(pTypeToMySQLTable, key);
+    psFree(key);
+    psFree(pTypeToMySQLTable);
+
+    if (!mType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(mType);
+
+    return mType;
+}
+
+static psHash *psDBGetPTypeToSQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(14);
+
+        // no support for CHAR, TEXT or GLOB
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S8,  "TINYINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S16, "SMALLINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S32, "INT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S64, "BIGINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U8,  "UNSIGNED TINYINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U16, "UNSIGNED SMALLINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U32, "UNSIGNED INT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U64, "UNSIGNED BIGINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_F32, "FLOAT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_F64, "DOUBLE");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_C32, "PS_TYPE_C32 is not supported");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_C64, "PS_TYPE_C64 is not supported");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_BOOL,"BOOLEAN");
+        psDBAddToLookupTable(lookupTable, PS_META_STR, "VARCHAR");
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBPTypeToSQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetPTypeToSQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetSQLToPTypeTable(void)
+{
+    static psHash   *lookupTable = NULL;
+    psHash          *psToSQLTable;
+    psList          *list;
+    psListIterator  *cursor;
+    char            *key;
+    char            *value;
+
+    if (!lookupTable) {
+        // invert the PSToSQL table
+        psToSQLTable = psDBGetPTypeToSQLTable();
+        lookupTable = psHashAlloc(psToSQLTable->nbucket);
+
+        list = psHashKeyList(psToSQLTable);
+        cursor = psListIteratorAlloc(list, 0, false);
+
+        while ((key = psListGetAndIncrement(cursor))) {
+            value = psHashLookup(psToSQLTable, key);
+            // switch key and value
+            psHashAdd(lookupTable, value, key);
+        }
+
+        // Add BLOB & TEXT reverse mappings
+        value = psDBIntToString((psU64)PS_META_STR);
+        psHashAdd(lookupTable, "BLOB",    value);
+        psHashAdd(lookupTable, "TEXT",    value);
+        psFree(value);
+
+        // DECIMAL does not exist in the pType to SQL table
+        value = psDBIntToString(0);
+        psHashAdd(lookupTable, "DECIMAL", value);
+        psFree(value);
+
+        psFree(cursor);
+        psFree(list);
+        psFree(psToSQLTable);
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBSQLToPTypeTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetSQLToPTypeTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetMySQLToSQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(20);
+
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TINY,      "TINYINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_SHORT,     "SMALLINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_LONG,      "INT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_INT24,     "MEDIUMINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_LONGLONG,  "BIGINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DECIMAL,   "DECIMAL");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_FLOAT,     "FLOAT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DOUBLE,    "DOUBLE");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TIMESTAMP, "TIMESTAMP");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DATE,      "DATE");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TIME,      "TIME");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DATETIME,  "DATETIME");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_YEAR,      "YEAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_STRING,    "CHAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_VAR_STRING,"VARCHAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_BLOB,      "BLOB");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_SET,       "SET");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_ENUM,      "ENUM");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_NULL,      "NULL-type");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_CHAR,      "TINYINT");
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBMySQLToSQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetMySQLToSQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetPTypeToMySQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(14);
+
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_F32,    psDBMySQLTypeAlloc(MYSQL_TYPE_FLOAT,      false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_F64,    psDBMySQLTypeAlloc(MYSQL_TYPE_DOUBLE,     false));
+        // bogus type
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_C32,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        // bogus type
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_C64,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_BOOL,   psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        // XXX: removed PS_TYPE_PTR, can this be removed too?
+        // psDBAddVoidToLookupTable(lookupTable, PS_TYPE_PTR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+
+        psDBAddVoidToLookupTable(lookupTable, PS_META_STR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_VEC,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_IMG,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_HASH,   psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_LOOKUPTABLE,
+                                 psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_JPEG,   psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_PNG,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_ASTROM, psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_UNKNOWN,psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBPTypeToMySQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetPTypeToMySQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psPtr psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned)
+{
+    mysqlType       *mType;
+
+    mType = psAlloc(sizeof(mysqlType));
+    mType->type       = type;
+    mType->isUnsigned = isUnsigned;
+
+    return mType;
+}
+
+static void psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string)
+{
+    char            *key;
+    char            *value;
+
+    key = psDBIntToString((psU64)type);
+    value = psStringCopy(string);
+
+    psHashAdd(lookupTable, key, value);
+
+    psFree(key);
+    psFree(value);
+}
+
+static void psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value)
+{
+    char            *key;
+
+    key = psDBIntToString((psU64)type);
+
+    psHashAdd(lookupTable, key, value);
+
+    // destructive of value parameter
+    psFree(value);
+    psFree(key);
+}
+
+
+// pType utility functions
+/*****************************************************************************/
+
+#define PS_NAN_ALLOC(dest, type, nan) \
+dest = psAlloc(sizeof(type)); \
+*(type *)dest = nan;
+
+static psPtr psDBGetPTypeNaN(psElemType pType)
+{
+    psPtr           myNaN;
+
+    switch (pType) {
+    case PS_TYPE_S8:
+        PS_NAN_ALLOC(myNaN, psS8, PS_MAX_S8);
+        break;
+    case PS_TYPE_S16:
+        PS_NAN_ALLOC(myNaN, psS16, PS_MAX_S16);
+        break;
+    case PS_TYPE_S32:
+        PS_NAN_ALLOC(myNaN, psS32, PS_MAX_S32);
+        break;
+    case PS_TYPE_S64:
+        PS_NAN_ALLOC(myNaN, psS64, PS_MAX_S64);
+        break;
+    case PS_TYPE_U8:
+        PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
+        break;
+    case PS_TYPE_U16:
+        PS_NAN_ALLOC(myNaN, psU16, PS_MAX_U16);
+        break;
+    case PS_TYPE_U32:
+        PS_NAN_ALLOC(myNaN, psU32, PS_MAX_U32);
+        break;
+    case PS_TYPE_U64:
+        PS_NAN_ALLOC(myNaN, psU64, PS_MAX_U64);
+        break;
+    case PS_TYPE_F32:
+        PS_NAN_ALLOC(myNaN, psF32, NAN);
+        break;
+    case PS_TYPE_F64:
+        PS_NAN_ALLOC(myNaN, psF64, NAN);
+        break;
+    case PS_TYPE_C32:
+        // this is a bogus SQL type
+        PS_NAN_ALLOC(myNaN, psC32, NAN);
+        break;
+    case PS_TYPE_C64:
+        // this is a bogus SQL type
+        PS_NAN_ALLOC(myNaN, psC64, NAN);
+        break;
+    case PS_TYPE_BOOL:
+        // what is NaN for a bool?
+        break;
+    }
+
+    return myNaN;
+}
+
+#define PS_IS_NAN(type, data, nan) *(type *)data == nan
+
+static bool psDBIsPTypeNaN(psElemType pType, psPtr data)
+{
+    bool    isNaN;
+
+    switch (pType) {
+    case PS_TYPE_S8:
+        isNaN = PS_IS_NAN(psS8, data, PS_MAX_S8);
+        break;
+    case PS_TYPE_S16:
+        isNaN = PS_IS_NAN(psS16, data, PS_MAX_S16);
+        break;
+    case PS_TYPE_S32:
+        isNaN = PS_IS_NAN(psS32, data, PS_MAX_S32);
+        break;
+    case PS_TYPE_S64:
+        isNaN = PS_IS_NAN(psS64, data, PS_MAX_S64);
+        break;
+    case PS_TYPE_U8:
+        isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
+        break;
+    case PS_TYPE_U16:
+        isNaN = PS_IS_NAN(psU16, data, PS_MAX_U16);
+        break;
+    case PS_TYPE_U32:
+        isNaN = PS_IS_NAN(psU32, data, PS_MAX_U32);
+        break;
+    case PS_TYPE_U64:
+        isNaN = PS_IS_NAN(psU64, data, PS_MAX_U64);
+        break;
+    case PS_TYPE_F32:
+        isNaN = PS_IS_NAN(psF32, data, NAN);
+        break;
+    case PS_TYPE_F64:
+        isNaN = PS_IS_NAN(psF64, data, NAN);
+        break;
+    case PS_TYPE_C32:
+        // this is a bogus SQL type
+        isNaN = PS_IS_NAN(psC32, data, NAN);
+        break;
+    case PS_TYPE_C64:
+        // this is a bogus SQL type
+        isNaN = PS_IS_NAN(psC64, data, NAN);
+        break;
+    case PS_TYPE_BOOL:
+        // what is NaN for a bool?
+        break;
+    }
+
+    return isNaN;
+}
+
+
+// string utility functions
+/*****************************************************************************/
+
+static char *psDBIntToString(psU64 n)
+{
+    char            *string;
+    size_t          length;
+
+    // length of string + \0
+    // if n is 0, length is 1 char + \0
+    length = n ? (size_t)log10((double)n) + 1
+             : 2;
+    string = psAlloc(length);
+    sprintf(string, "%li", (long int)n);
+
+    return string;
+}
+
+#endif // BUILD_PSDB
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psDB.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psDB.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psDB.h	(revision 22271)
@@ -0,0 +1,267 @@
+/** @file  psDB.h
+ *
+ *  @brief database types and functions
+ *
+ *  This file defines the abstract database type and functions that
+ *  perform basic database operations.
+ *
+ *  @ingroup DataBase
+ *
+ *  @author Joshua Hoblitt
+ *
+ *  @version $Revision: 1.5.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2005 Joshua Hoblitt, University of Hawaii
+ */
+
+#ifndef PS_DB_H
+#define PS_DB_H 1
+
+#ifdef BUILD_PSDB
+
+#include "psType.h"
+#include "psMetadata.h"
+
+/// @addtogroup DataBase
+/// @{
+
+/** Database handle
+ *
+ *  An opaque object representing a database connection.
+ *
+ */
+typedef struct
+{
+    void* mysql;   ///< MySQL database handle
+}
+psDB;
+
+/** Opens a new database connection
+ *
+ *  @return A new psDB object if the database connection is successful or NULL on
+ *  failure.
+ */
+psDB *psDBInit(
+    const char *host,                   ///< Database server hostname
+    const char *user,                   ///< Database username
+    const char *passwd,                 ///< Database password
+    const char *dbname                  ///< Database namespace
+);
+
+/** Closes a database connection
+ */
+void psDBCleanup(
+    psDB *dbh                           ///< Database handle
+);
+
+/** Creates a new database namespace
+ *
+ * @return true on success
+ */
+bool psDBCreate(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< New database namespace
+);
+
+/** Changes the current database namespace
+ *
+ * @return true on success
+ */
+bool psDBChange(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< Database namespace
+);
+
+/** Drops a database namespace
+ *
+ * @return true on success
+ */
+bool psDBDrop(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< Database namespace
+);
+
+/** Creates a new database table
+ *
+ * This function generates and executes the SQL needed to create a table named
+ * "tableName", with the column names and data types as described in "md".  Each
+ * data item in the psMetadata collection represents a single table field.  The
+ * name of the field is given by the name of the psMetadataItem and the data
+ * type is give by the psMetadataItem.type and psMetadataItem.ptype entries.  A
+ * lookup table should be used to convert from PSLib types into MySQL
+ * compatible SQL data types.  For example, a PS_META_STR would map to an SQL99
+ * varchar.  If the value of type is PS_META_STR then the psMetadataItem.data
+ * element is set to a string with the length for the field written as a text
+ * string.  The value of the psMetadataItem.data element is unused for the
+ * PS_META_PRIMITIVE types.  Other psMetadata types beyond PS_META_STR and
+ * PS_META_PRIMITIVE are not allowed in a table definition.  
+ *
+ * Database indexes can be specified setting the "comment" field to "Primary
+ * Key" or "Key".  Comments are otherwise ignored.
+ *
+ * @return true on success
+ */
+bool psDBCreateTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psMetadata *md  ///< Column names, types, and indexes
+);
+
+/** Deletes a database table
+ *
+ * @return true on success
+*/
+bool psDBDropTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Selects a column from a table
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.
+ *
+ * @return A psArray of strings or NULL on failure
+ */
+psArray *psDBSelectColumn(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const char *col,                    ///< Column name
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Selects a column from a table and casts it to a given type
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.  The data in the column is cast to to "pType".
+ *
+ * @return A psVector or NULL on failure
+ */
+psVector *psDBSelectColumnNum(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const char *col,                    ///< Column name
+    psElemType pType,                   ///< Resulting psVector type
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Selects a set of rows from a table
+ *
+ * This function returns rows from the specified table which match the
+ * restrictions given by "where".  The restrictions are specified as field /
+ * value pairs.  The psMetadata collection "where" must consist of valid
+ * database fields.  The selected rows are returned as a psArray of psMetadata
+ * values, one per row.
+ *
+ * Currently, the "where" specification only supports the PS_META_STR type.
+ * The string value can be a SQL match pattern, e.g. "%foo%", or an empty
+ * string, e.g. "", to match NULL field values.
+ *
+ * @return A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBSelectRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psMetadata *where,                  ///< Row match criteria
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Insert a single row into a table
+ *
+ * This function inserts the data from "row" into "tableName".
+ *
+ * The "row" specification uses the psMetadataItem name as the column name.
+ * The field values may be specified in any order.  psMetadata types beyond
+ * PS_META_STR and PS_META_PRIMITIVE are not supported.  If fields are
+ * specified in "row" that do not exist in "tableName", the insert will fail.
+ *
+ * @return true on success
+ */
+bool psDBInsertOneRow(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psMetadata *row  ///< Row description
+);
+
+/** Insert a set of rows into a table
+ *
+ * This function inserts the data from "rowSet" into "tableName".
+ *
+ * "rowSet" is a psArray of psMetadata containing row specifications identical to
+ * those used in psDBInsertOneRow().
+ *
+ * @return true on success
+ */
+bool psDBInsertRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psArray *rowSet  ///< Set of rows to insert
+);
+
+/** Retrieves all rows from a table
+ *
+ * This function fetches all rows as an psArray of psMetadata.  The rows are in
+ * the same psMetadata format as used in psDBInsertOneRow() & psDBInsertRows().
+ *
+ * @return A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBDumpRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Retrieves all columns from a table
+ *
+ * This function fetches all columns, as either a psVector or a psArray
+ * depending on whether or not the column is numeric, and return them in a
+ * psMetadata structure where psMetadataItem.name contains the column's name.
+ *
+ * @return A psMetadata containing either a psArrays or psVector per column
+ */
+psMetadata *psDBDumpCols(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Updates the field values, as specified, in a table
+ *
+ * This function updates the fields contained in "values" in the row(s) that
+ * have a field with the value indicated by "where".  Where "where" is in the
+ * same format as used in psDBSelectRows().
+ *
+ * The "values" specification uses the same format as the row specification
+ * used in psDBInsertOneRow(), etc.
+ *
+ * @return The number of rows modified or a negative value on error
+ */
+psS64 psDBUpdateRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psMetadata *where,  ///< Row match criteria
+    const psMetadata *values  ///< new field values
+);
+
+/** Deletes rows, as specified, in a table
+ *
+ * Delete the rows that are matched by "where" using the same semantics for
+ * "where" as in psDBUpdateRow().
+ *
+ * If "where" is NULL, all rows in the table will be removed and regardless of
+ * the number of rows that were dropped, only 1 will be returned on success.
+ *
+ * @return The number of rows removed or a negative value on error
+ */
+psS64 psDBDeleteRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *where  ///< Row match criteria
+);
+
+/// @}
+
+#endif // BUILD_PSDB
+
+#endif // PS_DB_H
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFileUtilsErrors.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFileUtilsErrors.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFileUtilsErrors.dat	(revision 22271)
@@ -0,0 +1,58 @@
+#
+#  This file is used to generate psFileUtilsErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERROR_TEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+psLookupTable_FILE_NOT_FOUND           Failed to open file %s.
+psLookupTable_PARSE_VALUE              Unable to parse string, %s on line %lld.
+psLookupTable_PARSE_TYPE               Unable to parse type, %s on line %lld.
+psLookupTable_PARSE_GENERAL            Unable to read lookup table item, %s on line %lld
+psLookupTable_INTERPOLATE_HIGH         High index too big, %d.
+psLookupTable_INTERPOLATE_LOW          Low index too small, %d.
+psLookupTable_DIVIDE_BY_ZERO           Divide by zero error during interpolation.
+psLookupTable_INVALID_TYPE             Invalid psLookupType, %d;
+psLookupTable_TABLE_INVALID            Lookup table is invalid.
+#
+psFits_NULL                            The input psFits object can not NULL.
+psFits_FILENAME_INVALID                Could not open file,'%s'.\nCFITSIO Error: %s
+psFits_FILENAME_NULL                   Specified filename can not be NULL.
+psFits_EXTNAME_NULL                    Specified extension name can not be NULL.
+psFits_EXTNAME_INVALID                 Could not find HDU '%s' in file %s.\nCFITSIO Error: %s
+psFits_EXTNUM_ABS_MOVE_FAILED          Could not move to specified HDU #%d in file %s.\nCFITSIO Error: %s
+psFits_EXTNUM_REL_MOVE_FAILED          Could not move %d HDUs from current position in file %s.\nCFITSIO Error: %s
+psFits_GET_EXTNUM_FAILED               Failed to determine the current HDU number in file %s.\nCFITSIO Error: %s
+psFits_GETNUMHDUS_FAILED               Failed to determine the number of HDUs in file %s.\nCFITSIO Error: %s
+psFits_GETHDUTYPE_FAILED               Failed to determine an HDU type in file %s.\nCFITSIO Error: %s
+psFits_GETNUMKEYS_FAILED               Failed to determine the number of header keys in file %s.\nCFITSIO Error: %s
+psFits_GET_TABLE_SIZE_FAILED           Failed to determine the size of the current HDU table.\nCFITSIO Error: %s
+psFits_FILENAME_CREATE_FAILED          Could not create file,'%s'.\nCFITSIO Error: %s
+psFits_TYPE_UNSUPPORTED                Specified type, %s, is not supported.
+psFits_CREATE_HDU_FAILED               Could not create new image HDU in file,'%s'.\nCFITSIO Error: %s
+psFits_GET_HDU_TYPE_FAILED             Could not determine the HDU type.\nCFITSIO Error: %s
+psFits_NOT_IMAGE_TYPE                  Current FITS HDU type must be an image.
+psFits_NOT_TABLE_TYPE                  Current FITS HDU type must be a table.
+psFits_TABLE_EMPTY                     Can't create a table without any rows.
+psFits_CFITSIO_ERROR                   CFITSIO error: %s
+psFits_METATYPE_INVALID                Specified FITS metadata type, %c, is not supported.
+psFits_METADATA_ADD_FAILED             Failed to add metadata item, %s.
+psFits_WRITE_FAILED                    Could not write data to file,'%s'.\nCFITSIO Error: %s
+psFits_IMAGE_NULL                      The input psImage was NULL.  Need a non-NULL psImage for operation to be performed.
+psFits_METADATA_NULL                   The input psMetadata was NULL.  Need a non-NULL psMetadata for operation to be performed.
+psFits_METADATA_PTYPE_UNSUPPORTED      A metadata item's primative type, %d, is not supported.
+psFits_ROW_INVALID                     Specified row, %d, is not valid for current table of %d rows.
+psFits_GET_TABLE_ELEMENT               Failed to retrieve table element (%d,%d).\nCFITSIO Error: %s
+psFits_FIND_COLUMN                     Specified column, %s, was not found.\nCFITSIO Error: %s
+psFits_GET_COLTYPE                     Could not determine the datatype of the table column.\nCFITSIO Error: %s
+psFits_TABLE_READ_COL                  Failed to read table column.\nCFITSIO Error: %s
+psFits_DATATYPE_UNKNOWN                Could not determine image data type.\nCFITSIO Error: %s
+psFits_IMAGE_DIM_UNKNOWN               Could not determine image dimensions.\nCFITSIO Error: %s
+psFits_IMAGE_DIMENSION_UNSUPPORTED     Image number of dimensions, %d, is not valid.  Only two or three dimensions supported for FITS I/O.
+psFits_IMAGE_SIZE_UNKNOWN              Could not determine image size.\nCFITSIO Error: %s
+psFits_FITS_TYPE_UNSUPPORTED           FITS image type, BITPIX=%d, is not supported.
+psFits_READ_FAILED                     Reading FITS file failed.\nCFITSIO Error: %s
+psFits_IMAGE_UPDATE_TYPE_MISMATCH      Can not update a %s image given a %s image.
+psFits_FITS_Z_SMALL                    Current FITS HDU has %d z-planes, but z-plane %d was specified.
+#
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFileUtilsErrors.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFileUtilsErrors.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFileUtilsErrors.h	(revision 22271)
@@ -0,0 +1,80 @@
+/** @file  psFileUtilsErrors.h
+ *
+ *  @brief Contains the error text for the dataIO functions
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-08 17:58:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FILEUTIL_ERRORS_H
+#define PS_FILEUTIL_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psAstronomyErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psAstronomyErrors.dat lines)
+ *     $2  The error text (rest of the line in psAstronomyErrors.dat)
+ *     $n  The order of the source line in psAstronomyErrors.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_psLookupTable_FILE_NOT_FOUND "Failed to open file %s."
+#define PS_ERRORTEXT_psLookupTable_PARSE_VALUE "Unable to parse string, %s on line %lld."
+#define PS_ERRORTEXT_psLookupTable_PARSE_TYPE "Unable to parse type, %s on line %lld."
+#define PS_ERRORTEXT_psLookupTable_PARSE_GENERAL "Unable to read lookup table item, %s on line %lld"
+#define PS_ERRORTEXT_psLookupTable_INTERPOLATE_HIGH "High index too big, %d."
+#define PS_ERRORTEXT_psLookupTable_INTERPOLATE_LOW "Low index too small, %d."
+#define PS_ERRORTEXT_psLookupTable_DIVIDE_BY_ZERO "Divide by zero error during interpolation."
+#define PS_ERRORTEXT_psLookupTable_INVALID_TYPE "Invalid psLookupType, %d;"
+#define PS_ERRORTEXT_psLookupTable_TABLE_INVALID "Lookup table is invalid."
+#define PS_ERRORTEXT_psFits_NULL "The input psFits object can not NULL."
+#define PS_ERRORTEXT_psFits_FILENAME_INVALID "Could not open file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FILENAME_NULL "Specified filename can not be NULL."
+#define PS_ERRORTEXT_psFits_EXTNAME_NULL "Specified extension name can not be NULL."
+#define PS_ERRORTEXT_psFits_EXTNAME_INVALID "Could not find HDU '%s' in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_EXTNUM_ABS_MOVE_FAILED "Could not move to specified HDU #%d in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_EXTNUM_REL_MOVE_FAILED "Could not move %d HDUs from current position in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GET_EXTNUM_FAILED "Failed to determine the current HDU number in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED "Failed to determine the number of HDUs in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED "Failed to determine an HDU type in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETNUMKEYS_FAILED "Failed to determine the number of header keys in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GET_TABLE_SIZE_FAILED "Failed to determine the size of the current HDU table.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED "Could not create file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED "Specified type, %s, is not supported."
+#define PS_ERRORTEXT_psFits_CREATE_HDU_FAILED "Could not create new image HDU in file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED "Could not determine the HDU type.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE "Current FITS HDU type must be an image."
+#define PS_ERRORTEXT_psFits_NOT_TABLE_TYPE "Current FITS HDU type must be a table."
+#define PS_ERRORTEXT_psFits_TABLE_EMPTY "Can't create a table without any rows."
+#define PS_ERRORTEXT_psFits_CFITSIO_ERROR "CFITSIO error: %s"
+#define PS_ERRORTEXT_psFits_METATYPE_INVALID "Specified FITS metadata type, %c, is not supported."
+#define PS_ERRORTEXT_psFits_METADATA_ADD_FAILED "Failed to add metadata item, %s."
+#define PS_ERRORTEXT_psFits_WRITE_FAILED "Could not write data to file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_IMAGE_NULL "The input psImage was NULL.  Need a non-NULL psImage for operation to be performed."
+#define PS_ERRORTEXT_psFits_METADATA_NULL "The input psMetadata was NULL.  Need a non-NULL psMetadata for operation to be performed."
+#define PS_ERRORTEXT_psFits_METADATA_PTYPE_UNSUPPORTED "A metadata item's primative type, %d, is not supported."
+#define PS_ERRORTEXT_psFits_ROW_INVALID "Specified row, %d, is not valid for current table of %d rows."
+#define PS_ERRORTEXT_psFits_GET_TABLE_ELEMENT "Failed to retrieve table element (%d,%d).\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FIND_COLUMN "Specified column, %s, was not found.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GET_COLTYPE "Could not determine the datatype of the table column.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_TABLE_READ_COL "Failed to read table column.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_DATATYPE_UNKNOWN "Could not determine image data type.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_IMAGE_DIM_UNKNOWN "Could not determine image dimensions.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_IMAGE_DIMENSION_UNSUPPORTED "Image number of dimensions, %d, is not valid.  Only two or three dimensions supported for FITS I/O."
+#define PS_ERRORTEXT_psFits_IMAGE_SIZE_UNKNOWN "Could not determine image size.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FITS_TYPE_UNSUPPORTED "FITS image type, BITPIX=%d, is not supported."
+#define PS_ERRORTEXT_psFits_READ_FAILED "Reading FITS file failed.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_IMAGE_UPDATE_TYPE_MISMATCH "Can not update a %s image given a %s image."
+#define PS_ERRORTEXT_psFits_FITS_Z_SMALL "Current FITS HDU has %d z-planes, but z-plane %d was specified."
+//~End
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFits.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFits.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFits.c	(revision 22271)
@@ -0,0 +1,1702 @@
+/** @file  psFits.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.29.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+#include "psFileUtilsErrors.h"
+#include "psImageExtraction.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+// list of FITS header keys to ignore.
+static char* standardFitsKeys[] = {
+                                      NULL
+                                  };
+
+static psElemType convertFitsToPsType(int datatype)
+{
+    switch (datatype) {
+    case TBYTE:
+        return PS_TYPE_U8;
+    case TSBYTE:
+        return PS_TYPE_S8;
+    case TSHORT:
+        return PS_TYPE_S16;
+    case TUSHORT:
+        return PS_TYPE_U16;
+    case TLONG:
+        if (sizeof(long) == 8) {
+            return PS_TYPE_S64;
+        }
+        // no break
+    case TINT:
+        return PS_TYPE_S32;
+    case TULONG:
+        if (sizeof(unsigned long) == 8) {
+            return PS_TYPE_U64;
+        }
+        // no break
+    case TUINT:
+        return PS_TYPE_U32;
+    case TLONGLONG:
+        return PS_TYPE_S64;
+    case TFLOAT:
+        return PS_TYPE_F32;
+    case TDOUBLE:
+        return PS_TYPE_F64;
+    case TCOMPLEX:
+        return PS_TYPE_C32;
+    case TDBLCOMPLEX:
+        return PS_TYPE_C64;
+    case TLOGICAL:
+        return PS_TYPE_BOOL;
+    default:
+        psError(PS_ERR_IO, true,
+                "Unknown FITS datatype, %d.",
+                datatype);
+        return 0;
+    }
+}
+
+static bool convertPsTypeToFits(psElemType type, int* bitPix, double* bZero, int* dataType)
+{
+
+    int bitpix;
+    int datatype;
+    double bzero = 0.0;
+
+    switch (type) {
+
+    case PS_TYPE_U8:
+        bitpix = BYTE_IMG;
+        datatype = TBYTE;
+        break;
+
+    case PS_TYPE_BOOL:
+    case PS_TYPE_S8:
+        bitpix = BYTE_IMG;
+        bzero = INT8_MIN;
+        datatype = TSBYTE;
+        break;
+
+    case PS_TYPE_U16:
+        bitpix = SHORT_IMG;
+        bzero = -1.0 * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+
+    case PS_TYPE_S16:
+        bitpix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+
+    case PS_TYPE_U32:
+        bitpix = LONG_IMG;
+        bzero = -1.0 * INT32_MIN;
+        datatype = TUINT;
+        break;
+
+    case PS_TYPE_S32:
+        bitpix = LONG_IMG;
+        datatype = TINT;
+        break;
+
+    case PS_TYPE_F32:
+        bitpix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+
+    case PS_TYPE_F64:
+        bitpix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED,
+                    typeStr);
+            return false;
+        }
+    }
+
+    // pass the requested parameters  (NULL parameters are not set, of course).
+    if (bitPix != NULL) {
+        *bitPix = bitpix;
+    }
+
+    if (dataType != NULL) {
+        *dataType = datatype;
+    }
+
+    if (bZero != NULL) {
+        *bZero = bzero;
+    }
+
+    return true;
+}
+
+static bool convertMetadataTypeToBinaryTForm(psMetadataType type, char** fitsType)
+{
+    switch (type) {
+    case PS_META_BOOL:
+        *fitsType = psStringCopy("1L");
+        break;
+    case PS_META_S32:
+        *fitsType = psStringCopy("1J");
+        break;
+    case PS_META_F32:
+        *fitsType = psStringCopy("1E");
+        break;
+    case PS_META_F64:
+        *fitsType = psStringCopy("1D");
+        break;
+        // XXX: Handle other types, e.g., Vectors, etc.
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool isHDUEmpty(const psFits* fits)
+{
+    /* check for keys - no keys means this is really an empty HDU */
+    int keysexist = -1;
+    int morekeys;
+    int status = 0;
+
+    fits_get_hdrspace(fits->p_fd, &keysexist, &morekeys, &status);
+
+    // if no keys exist and not primary HDU, this really is an empty HDU
+    if (keysexist == 0) {
+        return true;
+    }
+
+    return false;
+
+}
+
+static void fitsFree(psFits* fits)
+{
+    int status = 0;
+
+    if (fits != NULL) {
+        (void)fits_close_file(fits->p_fd, &status);
+        psFree((void*)fits->filename);
+    }
+}
+
+psFits* psFitsAlloc(const char* name)
+{
+    int status = 0;
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_FILENAME_NULL);
+        return NULL;
+    }
+
+    /* Open/Create the FITS file */
+    if (access(name, F_OK) == 0) {     // file exists
+        (void)fits_open_file(&fptr, name, READWRITE, &status);
+        if (fptr == NULL) { // if failed, try openning as just read-only
+            status = 0;
+            (void)fits_open_file(&fptr, name, READONLY, &status);
+        }
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psFits_FILENAME_INVALID,
+                    name, fitsErr);
+            return NULL;
+        }
+    } else {  // file does not exist, so create.
+        (void)fits_create_file(&fptr, name, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED,
+                    name, fitsErr);
+            return NULL;
+        }
+    }
+
+    psFits* fits = psAlloc(sizeof(psFits));
+    fits->filename = psAlloc(strlen(name)+1);
+    fits->p_fd = fptr;
+    strcpy((char*)fits->filename,name);
+    psMemSetDeallocator(fits,(psFreeFcn)fitsFree);
+
+    return fits;
+}
+
+bool psFitsMoveExtName(const psFits* fits,
+                       const char* extname)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (extname == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_EXTNAME_NULL);
+        return false;
+    }
+
+
+    if (fits_movnam_hdu(fits->p_fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNAME_INVALID,
+                extname, fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsMoveExtNum(const psFits* fits,
+                      int extnum,
+                      bool relative)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    int status = 0;
+    int hdutype = 0;
+
+    if (relative) {
+        fits_movrel_hdu(fits->p_fd, extnum, &hdutype, &status);
+        if (status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_LOCATION_INVALID, true,
+                    PS_ERRORTEXT_psFits_EXTNUM_REL_MOVE_FAILED,
+                    extnum, fits->filename, fitsErr);
+            return false;
+        }
+    } else {
+        fits_movabs_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+        if (status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_LOCATION_INVALID, true,
+                    PS_ERRORTEXT_psFits_EXTNUM_ABS_MOVE_FAILED,
+                    extnum, fits->filename, fitsErr);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int psFitsGetExtNum(const psFits* fits)
+{
+    int hdunum;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+
+    return fits_get_hdu_num(fits->p_fd,&hdunum) - 1;
+}
+
+char* psFitsGetExtName(const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    int status = 0;
+    char name[MAX_STRING_LENGTH];
+
+    if (fits_read_key_str(fits->p_fd, "EXTNAME", name, NULL, &status) != 0) {
+        status = 0;
+        if (fits_read_key_str(fits->p_fd, "HDUNAME", name, NULL, &status) != 0) {
+            int num = psFitsGetExtNum(fits);
+            snprintf(name, MAX_STRING_LENGTH, "EXT-%3d",num);
+        }
+    }
+    return psStringCopy(name);
+}
+
+bool psFitsSetExtName(const psFits* fits, const char* name)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_EXTNAME_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    if (fits_update_key_str(fits->p_fd, "EXTNAME", (char*)name, NULL, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+int psFitsGetSize(const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return 0;
+    }
+
+    int num = 0;
+    int status = 0;
+
+    if (fits_get_num_hdus(fits->p_fd, &num, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED,
+                fits->filename, fitsErr);
+        return 0;
+    }
+
+    return num;
+}
+
+psFitsType psFitsGetExtType(const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = PS_FITS_TYPE_NONE;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED,
+                fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    if (hdutype == PS_FITS_TYPE_IMAGE &&
+            psFitsGetExtNum(fits) > 0 &&
+            isHDUEmpty(fits)) {
+        return PS_FITS_TYPE_ANY;
+    }
+
+    return hdutype;
+}
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psMetadataAlloc();
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psMetadata container.");
+            return NULL;
+        }
+    }
+
+    // Get number of key names
+    int numKeys = 0;
+    int keyNum = 0;
+    int status = 0;
+    fits_get_hdrpos(fits->p_fd, &numKeys, &keyNum, &status);
+
+    // Get each key name. Keywords start at one.
+    char keyType;
+    char keyName[MAX_STRING_LENGTH];
+    char keyValue[MAX_STRING_LENGTH];
+    char keyComment[MAX_STRING_LENGTH];
+    psBool tempBool;
+    psBool success;
+    psBool stdKey;
+    for (int i = 1; i <= numKeys; i++) {
+
+        fits_read_keyn(fits->p_fd, i, keyName, keyValue, keyComment, &status);
+
+        stdKey = false;
+
+        int stdKeyIdx = 0;
+        while (standardFitsKeys[stdKeyIdx] != NULL && ! stdKey) {
+            if (strcmp(keyName,standardFitsKeys[stdKeyIdx++]) == 0) {
+                stdKey = true;
+            }
+        }
+
+        if (keyValue[0] != 0) { // blank values are not handled by fits_get_keytype
+            fits_get_keytype(keyValue, &keyType, &status);
+        } else {
+            keyType = 'C';
+        }
+        if (status != 0) {
+            break;
+        }
+
+        if (! stdKey) {
+            switch (keyType) {
+            case 'X': // bit
+            case 'I': // short int.
+            case 'J': // int.
+            case 'B': // byte
+                success = psMetadataAdd(out,
+                                        PS_LIST_TAIL,
+                                        keyName,
+                                        PS_META_S32 | PS_META_DUPLICATE_OK,
+                                        keyComment,
+                                        atoi(keyValue));
+                break;
+            case 'U': // unsigned int. may not fit in a psS32
+            case 'K': // long int. can't all fit in a psS32
+            case 'F':
+                success = psMetadataAdd(out,
+                                        PS_LIST_TAIL,
+                                        keyName,
+                                        PS_META_F64 | PS_META_DUPLICATE_OK,
+                                        keyComment,
+                                        atof(keyValue));
+                break;
+            case 'C':
+                // remove the single-quotes at front/end
+                if (keyValue[0] == '\'' && keyValue[strlen(keyValue)-1] == '\'') {
+                    keyValue[strlen(keyValue)-1] = '\0';
+                    success = psMetadataAdd(out,
+                                            PS_LIST_TAIL,
+                                            keyName,
+                                            PS_META_STR | PS_META_DUPLICATE_OK,
+                                            keyComment,
+                                            keyValue+1);
+                } else {
+                    success = psMetadataAdd(out,
+                                            PS_LIST_TAIL,
+                                            keyName,
+                                            PS_META_STR | PS_META_DUPLICATE_OK,
+                                            keyComment,
+                                            keyValue);
+                }
+                break;
+            case 'L':
+                tempBool = (keyValue[0] == 'T') ? 1 : 0;
+                success = psMetadataAdd(out,
+                                        PS_LIST_TAIL,
+                                        keyName,
+                                        PS_META_BOOL | PS_META_DUPLICATE_OK,
+                                        keyComment,
+                                        tempBool);
+                break;
+            default:
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psFits_METATYPE_INVALID,
+                        keyType);
+                return out;
+            }
+
+            if (!success) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                        keyName);
+                return out;
+            }
+        }
+
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                fitsErr);
+        return false;
+    }
+
+    return out;
+}
+
+psHash* psFitsReadHeaderSet(psHash* out,
+                            const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psHashAlloc(10);
+        // XXX: what is the appropriate number of buckets? Got 10 from psMetadataAlloc.
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psHash container.");
+            return NULL;
+        }
+    }
+
+    int size = psFitsGetSize(fits);
+
+    int origPosition = psFitsGetExtNum(fits);
+
+    for (int lcv=0; lcv < size; lcv++) {
+        psFitsMoveExtNum(fits, lcv, false);
+
+        char* name = NULL;
+        if (lcv == 0) {
+            name = psStringCopy("PHU");
+        } else {
+            name = psFitsGetExtName(fits);
+        }
+
+        psMetadata* header = psFitsReadHeader(NULL, fits);
+        if (name != NULL && header != NULL) {
+            psHashAdd(out, name, header);
+        } else { // XXX: is this a warning or error?
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "Failed to read HDU#%d header data.",
+                     lcv);
+        }
+
+        psFree(name);
+        psFree(header);
+    }
+
+    // reposition to the original position
+    psFitsMoveExtNum(fits, origPosition, false);
+
+    return out;
+}
+
+psImage* psFitsReadImage(psImage* output, // a psImage to recycle.
+                         const psFits* fits,    // the psFits object
+                         psRegion region, // the region in the FITS image to read
+                         int z)           // the z-plane in the FITS image cube to read
+{
+    psS32 status = 0;           /* CFITSIO file vars */
+    psS32 nAxis = 0;
+    psS32 anynull = 0;
+    psS32 bitPix = 0;           /* Pixel type */
+    long nAxes[3];
+    long firstPixel[3];         /* lower-left corner of image subset */
+    long lastPixel[3];          /* upper-right corner of image subset */
+    long increment[3];          /* increment for image subset */
+    char fitsErr[80] = "";      /* CFITSIO error message string */
+    psS32 fitsDatatype = 0;
+    psS32 datatype = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        psFree(output);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on an image HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != IMAGE_HDU) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return NULL;
+    }
+
+    /* Get the data type 'bitPix' from the FITS image */
+    if (fits_get_img_equivtype(fits->p_fd, &bitPix, &status) != 0) {
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_DATATYPE_UNKNOWN,
+                fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Get the dimensions 'nAxis' from the FITS image */
+    if (fits_get_img_dim(fits->p_fd, &nAxis, &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_DIM_UNKNOWN,
+                fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Validate the number of axis */
+    if ((nAxis < 2) || (nAxis > 3)) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_DIMENSION_UNSUPPORTED,
+                nAxis);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Get the Image size from the FITS file */
+    if (fits_get_img_size(fits->p_fd, nAxis, nAxes, &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_SIZE_UNKNOWN,
+                fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    firstPixel[0] = region.x0 + 1;
+    firstPixel[1] = region.y0 + 1;
+    firstPixel[2] = z + 1;
+
+    if (region.x1 > 0) {
+        lastPixel[0] = region.x1;
+    } else {
+        lastPixel[0] = nAxes[0] + region.x1; // n.b., region.x1 < 0
+    }
+    if (region.y1 > 0) {
+        lastPixel[1] = region.y1;
+    } else {
+        lastPixel[1] = nAxes[1] + region.y1; // n.b., region.y1 < 0
+    }
+    lastPixel[2] = z + 1;
+
+    increment[0] = 1;
+    increment[1] = 1;
+    increment[2] = 1;
+
+    switch (bitPix) {
+    case BYTE_IMG:
+        datatype = PS_TYPE_U8;
+        fitsDatatype = TBYTE;
+        break;
+    case SBYTE_IMG:
+        datatype = PS_TYPE_S8;
+        fitsDatatype = TSBYTE;
+        break;
+    case USHORT_IMG:
+        datatype = PS_TYPE_U16;
+        fitsDatatype = TUSHORT;
+        break;
+    case SHORT_IMG:
+        datatype = PS_TYPE_S16;
+        fitsDatatype = TSHORT;
+        break;
+    case ULONG_IMG:
+        datatype = PS_TYPE_U32;
+        fitsDatatype = TUINT;
+        break;
+    case LONG_IMG:
+        datatype = PS_TYPE_S32;
+        fitsDatatype = TINT;
+        break;
+    case LONGLONG_IMG:
+        datatype = PS_TYPE_S64;
+        fitsDatatype = TLONGLONG;
+        break;
+    case FLOAT_IMG:
+        datatype = PS_TYPE_F32;
+        fitsDatatype = TFLOAT;
+        break;
+    case DOUBLE_IMG:
+        datatype = PS_TYPE_F64;
+        fitsDatatype = TDOUBLE;
+        break;
+    default:
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_FITS_TYPE_UNSUPPORTED,
+                bitPix);
+        psFree(output);
+        return NULL;
+    }
+
+    output = psImageRecycle(output,
+                            lastPixel[0]-firstPixel[0]+1,
+                            lastPixel[1]-firstPixel[1]+1,
+                            datatype);
+
+    if (output == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Failed to allocate a properly sized image.");
+        return false;
+    }
+
+    // n.b., this assumes contiguous image buffer
+    if (fits_read_subset(fits->p_fd, fitsDatatype, firstPixel, lastPixel, increment,
+                         NULL, output->data.V[0], &anynull, &status) != 0) {
+        psFree(output);
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_READ_FAILED,
+                fitsErr);
+        return NULL;
+    }
+
+    return output;
+
+}
+
+bool psFitsWriteImage(const psFits* fits,
+                      const psMetadata* header,
+                      const psImage* input,
+                      int numZPlanes,
+                      char* extname)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    int status = 0;
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    int naxis = 3;
+    long naxes[3];
+
+    naxes[0] = numCols;
+    naxes[1] = numRows;
+    naxes[2] = numZPlanes;
+
+    if (numZPlanes < 2) {
+        naxis = 2;
+    }
+
+    fits_create_img(fits->p_fd, bitPix, naxis, naxes, &status);
+
+    if (extname != NULL) {
+        fits_update_key_str(fits->p_fd, "EXTNAME", (char*)extname, NULL, &status);
+    }
+
+    if (bZero != 0) {        // set the bscale/bzero
+        fits_write_key_dbl(fits->p_fd, "BZERO", bZero, 12, "Pixel Value Offset", &status);
+        fits_write_key_dbl(fits->p_fd, "BSCALE", 1.0, 12, "Pixel Value Scale", &status);
+        fits_set_bscale(fits->p_fd, 1.0, bZero, &status);
+    }
+
+    // write the header, if any.
+    if (header != NULL) {
+        psFitsWriteHeader(header, fits);
+    }
+
+    if (input->parent == NULL) { // if no parent, assume that the image data is contiguous
+        fits_write_img(fits->p_fd,
+                       dataType,              // datatype
+                       1,                     // writing to the first z-plane
+                       numCols*numRows,       // number of elements to write, i.e., the whole image
+                       input->data.V[0],      // the data
+                       &status);
+    } else { // image data may not be contiguous; write one row at a time
+        int firstPixel = 1;
+        for (int row = 0; row < numRows; row++) {
+            fits_write_img(fits->p_fd,
+                           dataType,          // datatype
+                           firstPixel,
+                           numCols,           // number of elements to write, i.e., one row's worth
+                           input->data.V[row],// the raw row data
+                           &status);
+            firstPixel += numCols;  // move to next row
+        }
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+
+}
+
+bool psFitsUpdateImage(const psFits* fits,
+                       const psImage* input,
+                       psRegion region,
+                       int z)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+
+    // check to see if we are positioned on an image HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != IMAGE_HDU) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return NULL;
+    }
+
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    //check to see if the HDU has the same datatype
+    int fileBitpix;
+    int naxis;
+    long nAxes[3];
+    nAxes[2] = 1;
+    fits_get_img_param(fits->p_fd, 3, &fileBitpix, &naxis, nAxes, &status);
+
+    //check to see if the HDU has the same datatype
+    if (bitPix != fileBitpix) {
+        char* fitsTypeStr;
+        char* imageTypeStr;
+        PS_TYPE_NAME(fitsTypeStr,fileBitpix);
+        PS_TYPE_NAME(imageTypeStr,input->type.type);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_UPDATE_TYPE_MISMATCH,
+                fitsTypeStr, imageTypeStr);
+        return false;
+    }
+
+    //check if the HDU has the z-plane requested
+    if (z >= nAxes[2]) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFits_FITS_Z_SMALL,
+                nAxes[2],z);
+        return false;
+    }
+
+    // determine the region in the FITS file domain
+    long firstPixel[3];
+    long lastPixel[3];
+
+    firstPixel[0] = region.x0 + 1;
+    firstPixel[1] = region.y0 + 1;
+    firstPixel[2] = z + 1;
+
+    if (region.x1 > 0) {
+        lastPixel[0] = region.x1;
+    } else {
+        lastPixel[0] = nAxes[0] + region.x1; // n.b., region.x1 < 0
+    }
+    if (region.y1 > 0) {
+        lastPixel[1] = region.y1;
+    } else {
+        lastPixel[1] = nAxes[1] + region.y1; // n.b., region.y1 < 0
+    }
+    lastPixel[2] = z + 1;
+
+    if (firstPixel[0] < 1 || firstPixel[0] > nAxes[0] ||
+            firstPixel[1] < 1 || firstPixel[1] > nAxes[1] ||
+            lastPixel[0] < 1 || lastPixel[0] > nAxes[0] ||
+            lastPixel[1] < 1 || lastPixel[1] > nAxes[1]) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified region [%d:%d,%d:%d], is not valid given the %dx%d FITS image.",
+                region.y0,region.y1-1,region.x0,region.x1-1);
+        return false;
+
+    }
+
+    int dx = lastPixel[0] - firstPixel[0];
+    int dy = lastPixel[1] - firstPixel[1];
+    if (dx > numCols ||
+            dy > numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "The region [%d:%d,%d:%d], is not valid given the input %dx%d image.",
+                firstPixel[1]-1,lastPixel[1]-1,
+                firstPixel[0]-1,lastPixel[0]-1,
+                numCols, numRows);
+        return false;
+    }
+
+    psImage* subset;
+    if (dx != numCols || dy != numRows) {
+        // the input image needs to be subsetted
+        subset = psImageSubset((psImage*)input,0,0,dx+1,dy+1);
+    } else {
+        subset = psMemIncrRefCounter((psImage*)input);
+    }
+
+    fits_write_subset(fits->p_fd, dataType, firstPixel, lastPixel, subset->data.V[0], &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsWriteHeader(const psMetadata* header,
+                       const psFits* fits)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (header == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_METADATA_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    //transverse the metadata list and add each key.
+
+    psListIterator* iter = psListIteratorAlloc(header->list,PS_LIST_HEAD,true);
+    psMetadataItem* item;
+    while ( (item=psListGetAndIncrement(iter)) != NULL ) {
+        switch (item->type) {
+        case PS_META_BOOL: {
+                int value = item->data.B;
+                fits_update_key(fits->p_fd,
+                                TLOGICAL,
+                                item->name,
+                                &value,
+                                item->comment,
+                                &status);
+                break;
+            }
+        case PS_META_S32:
+            fits_update_key(fits->p_fd,
+                            TINT,
+                            item->name,
+                            &item->data.S32,
+                            item->comment,
+                            &status);
+            break;
+        case PS_META_F32:
+            fits_update_key(fits->p_fd,
+                            TFLOAT,
+                            item->name,
+                            &item->data.F32,
+                            item->comment,
+                            &status);
+            break;
+        case PS_META_F64:
+            fits_update_key(fits->p_fd,
+                            TDOUBLE,
+                            item->name,
+                            &item->data.F64,
+                            item->comment,
+                            &status);
+            break;
+        case PS_META_STR:
+            fits_update_key(fits->p_fd,
+                            TSTRING,
+                            item->name,
+                            item->data.V,
+                            item->comment,
+                            &status);
+            break;
+        default:  // all other META types are ignored
+            break;
+        }
+
+        if ( status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            (void)fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_WRITE_FAILED,
+                    fits->filename, fitsErr);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+psMetadata* psFitsReadTableRow(const psFits* fits,
+                               int row)
+{
+    long numRows;
+    int numCols;
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    fits_get_hdu_type(fits->p_fd,&hdutype, &status);
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // get the size of the FITS table
+    fits_get_num_rows(fits->p_fd, &numRows, &status);
+    fits_get_num_cols(fits->p_fd, &numCols, &status);
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_TABLE_SIZE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+
+    psTrace(".psFits.psFitsReadTableRow",5,"Table size is %ix%i\n",numCols, numRows);
+    // the row parameter in the proper range?
+    if (row < 0 || row >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psFits_ROW_INVALID,
+                row,numRows);
+        return NULL;
+    }
+
+    psMetadata* data = psMetadataAlloc();
+
+    int typecode;
+    long repeat;
+    long width;
+    char name[60];
+    for (int col = 1; col <= numCols; col++) {
+        // get the column name
+        if (hdutype == BINARY_TBL) {
+            fits_get_bcolparms(fits->p_fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        } else {
+            fits_get_acolparms(fits->p_fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        }
+        // get the column type
+        fits_get_coltype(fits->p_fd, col, &typecode, &repeat, &width, &status);
+
+        if (status == 0) {
+
+            #define READ_TABLE_ROW_CASE(FITSTYPE, NATIVETYPE, TYPE) \
+        case FITSTYPE: { \
+                NATIVETYPE value = 0; \
+                int anynul = 0; \
+                fits_read_col(fits->p_fd, FITSTYPE, col,row+1, \
+                              1, 1, NULL, &value, &anynul, &status); \
+                psTrace(".psFits.psFitsReadTableRow",5,"Column #%i, '%s', is type %i, repeat %i, Value = %g\n", \
+                        col, name, typecode, repeat, (double)value); \
+                psMetadataAdd(data,PS_LIST_TAIL, name, \
+                              PS_META_##TYPE, \
+                              "", (ps##TYPE)value); \
+                break; \
+            }
+
+            switch (typecode) {
+            case TBYTE:
+            case TSHORT:
+            case TLONGLONG:
+                READ_TABLE_ROW_CASE(TLONG, long, S32)
+                READ_TABLE_ROW_CASE(TFLOAT, float, F32)
+                READ_TABLE_ROW_CASE(TDOUBLE, double, F64)
+                READ_TABLE_ROW_CASE(TLOGICAL, bool, BOOL);
+            case TSTRING: {
+                    char* value = psAlloc(repeat+1);
+                    int anynul = 0;
+                    fits_read_col(fits->p_fd, TSTRING, col,row+1,
+                                  1, 1, NULL, &value, &anynul, &status);
+                    psTrace(".psFits.psFitsReadTableRow",5,"Column #%i, '%s', is type %i, repeat %i, value = %s\n",
+                            col, name, typecode, repeat, value);
+                    if (anynul == 0) {
+                        psMetadataAdd(data,PS_LIST_TAIL, name,
+                                      PS_META_STR,
+                                      "", value);
+                    }
+                    psFree(value);
+                    break;
+                }
+            default:
+                psTrace("psFits.psFitsReadTableRow", 2,
+                        "Column %d or row %d was of a non primitive type, %d",
+                        col, row, typecode);
+            }
+        }
+
+        if ( status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            (void)fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_GET_TABLE_ELEMENT,
+                    col,row,fitsErr);
+            psFree(data);
+            return NULL;
+        }
+
+    }
+
+    return data;
+}
+
+psArray* psFitsReadTableColumn(const psFits* fits,
+                               const char* colname)
+{
+    int colnum = 0;
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // find the column by name
+    if ( fits_get_colnum(fits->p_fd, CASESEN, (char*)colname, &colnum, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_FIND_COLUMN,
+                colname, fitsErr);
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = 0;
+    fits_get_num_rows(fits->p_fd, &numRows, &status);
+
+    // get the column length.
+    int width;
+    if ( fits_get_col_display_width(fits->p_fd, colnum, &width, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_COLTYPE,
+                fitsErr);
+        return NULL;
+    }
+
+    // allocate the buffers
+    psArray* result = psArrayAlloc(numRows);
+    for (int row = 0; row < numRows; row++) {
+        result->data[row] = psAlloc((width+1)*sizeof(char));
+    }
+    result->n = numRows;
+
+    fits_read_col_str(fits->p_fd,
+                      colnum,
+                      1, // firstrow
+                      1, // firestelem
+                      numRows,
+                      "", // nulstr
+                      (char**)result->data,
+                      NULL,
+                      &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_TABLE_READ_COL,
+                fitsErr);
+        return NULL;
+    }
+
+    return result;
+}
+
+psVector* psFitsReadTableColumnNum(const psFits* fits,
+                                   const char* colname)
+{
+    int status = 0;
+    int colnum = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // find the column by name
+    if ( fits_get_colnum(fits->p_fd, CASESEN, (char*)colname, &colnum, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_FIND_COLUMN,
+                colname, fitsErr);
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = 0;
+    fits_get_num_rows(fits->p_fd,
+                      &numRows,
+                      &status);
+
+    // get the column datatype.
+    int typecode;
+    long repeat;
+    long width;
+    if ( fits_get_eqcoltype(fits->p_fd, colnum, &typecode, &repeat, &width, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_COLTYPE,
+                fitsErr);
+        return NULL;
+    }
+
+    psVector* result = psVectorAlloc(numRows, convertFitsToPsType(typecode));
+
+    fits_read_col(fits->p_fd,
+                  typecode,
+                  colnum,
+                  1 /* firstrow */,
+                  1 /* firstelem */,
+                  numRows,
+                  NULL,
+                  (psPtr)(result->data.U8),
+                  NULL,
+                  &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_TABLE_READ_COL,
+                fitsErr);
+        return NULL;
+    }
+
+    return result;
+}
+
+
+psArray* psFitsReadTable(const psFits* fits)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // get the size of the FITS table
+    long numRows = 0;
+    fits_get_num_rows(fits->p_fd, &numRows, &status);
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_TABLE_SIZE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+
+
+    psArray* table = psArrayAlloc(numRows);
+
+    for (int row = 0; row < numRows; row++) {
+        psTrace(".psFits.psFitsReadTable",5,"Reading row %i of %i\n",row, numRows);
+        table->data[row] = psFitsReadTableRow(fits,row);
+    }
+
+    return table;
+}
+
+bool psFitsWriteTable(const psFits* fits,
+                      const psMetadata* header,
+                      const psArray* table,
+                      char* extname)
+{
+    int status = 0;
+    psMetadataItem* item;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+
+    int rows = table->n;
+    if (rows < 1) {
+        // no table data, what can I do?
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFits_TABLE_EMPTY);
+        return false;
+    }
+
+    // find all the columns needed
+    psArray* columns = psArrayAlloc(((psMetadata*)table->data[0])->list->size);
+    columns->n=0;
+
+    // find the unique items in the array of metadata 'rows'
+    for (int row=0; row < rows; row++) {
+        psMetadata* rowMeta = table->data[row];
+        if (rowMeta != NULL) {
+            psListIterator* iter = psListIteratorAlloc(rowMeta->list,
+                                   PS_LIST_HEAD,true);
+            while ( (item=psListGetAndIncrement(iter)) != NULL) {
+                if (PS_META_IS_PRIMITIVE(item->type)) {
+                    bool found = false;
+                    for (int n=0; n < columns->n && ! found; n++) {
+                        if (strcmp(item->name,
+                                   ((psMetadataItem*)(columns->data[n]))->name) == 0) {
+                            found = true;
+                        }
+                    }
+                    if (! found) {
+                        psArrayAdd(columns, columns->nalloc, item);
+                    }
+                }
+            }
+            psFree(iter);
+        }
+    }
+
+    if (columns->n == 0) { // no table columns found
+        // XXX: Error?
+        return false;
+    }
+
+    //create list of column names and types.
+    psArray* columnNames = psArrayAlloc(columns->n);
+    psArray* columnTypes = psArrayAlloc(columns->n);
+    for (int n=0; n < columns->n; n++) {
+        char* fitsType;
+        columnNames->data[n] = psMemIncrRefCounter(((psMetadataItem*)columns->data[n])->name);
+        if ( ! convertMetadataTypeToBinaryTForm(((psMetadataItem*)columns->data[n])->type,
+                                                &fitsType)) {
+            // XXX: error message
+            return false;
+        }
+        columnTypes->data[n] = fitsType;
+    }
+
+    fits_create_tbl(fits->p_fd,
+                    BINARY_TBL,
+                    table->n, // number of rows in table
+                    columns->n, // number of columns in table
+                    (char**)columnNames->data, // names of the columns
+                    (char**)columnTypes->data, // format of the columns
+                    NULL, // physical unit of columns
+                    extname, // extension name
+                    &status);
+
+    psFree(columnNames);
+    psFree(columnTypes);
+
+    // fill in the table elements with data
+    for (int n = 0; n < columns->n; n++) {
+        int row;
+        item = columns->data[n];
+        if (PS_META_IS_PRIMITIVE(item->type)) {
+            psVector* col = NULL;
+            switch (item->type) {
+            case PS_META_S32:
+                col = psVectorAlloc(table->n, PS_TYPE_S32);
+                for (row = 0; row < table->n; row++) {
+                    col->data.S32[row] = psMetadataLookupS32(NULL,
+                                         table->data[row],
+                                         item->name);
+                }
+                fits_write_col_int(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   col->data.S32,
+                                   &status);
+                break;
+            case PS_META_F32:
+                col = psVectorAlloc(table->n, PS_TYPE_F32);
+                for (row = 0; row < table->n; row++) {
+                    col->data.F32[row] = psMetadataLookupF32(NULL,
+                                         table->data[row],
+                                         item->name);
+                }
+                fits_write_col_flt(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   col->data.F32,
+                                   &status);
+                break;
+            case PS_META_F64:
+                col = psVectorAlloc(table->n, PS_TYPE_F64);
+                for (row = 0; row < table->n; row++) {
+                    col->data.F64[row] = psMetadataLookupF64(NULL,
+                                         table->data[row],
+                                         item->name);
+                }
+                fits_write_col_dbl(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   col->data.F64,
+                                   &status);
+                break;
+            case PS_META_BOOL:
+                col = psVectorAlloc(table->n, PS_TYPE_BOOL);
+                for (row = 0; row < table->n; row++) {
+                    col->data.S8[row] = psMetadataLookupBool(NULL,
+                                        table->data[row],
+                                        item->name);
+                }
+                fits_write_col_log(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   (char*)col->data.S8,
+                                   &status);
+                break;
+            default:
+                // XXX: error message?
+                break;
+            }
+            psFree(col);
+        } else if (item->type == PS_META_STR) {
+            psArray* col = psArrayAlloc(table->n);
+            for (row = 0; row < table->n; row++) {
+                col->data[row] = item->data.V;
+            }
+            fits_write_col_str(fits->p_fd,
+                               n, // column number
+                               1, // firstrow
+                               1, // firstelem
+                               table->n, // nelements
+                               (char**)col->data,
+                               &status);
+            psFree(col);
+        }
+    }
+
+    psFree(columns);
+
+    return true;
+}
+
+bool psFitsUpdateTable(const psFits* fits,
+                       psMetadata* data,
+                       int row)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return false;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return false;
+    }
+
+    psMetadataIterator* iter = psMetadataIteratorAlloc(data,PS_LIST_HEAD,NULL);
+
+    psMetadataItem* item;
+
+    while ( (item=psMetadataGetAndIncrement(iter)) != NULL) {
+        if (PS_META_IS_PRIMITIVE(item->type)) {
+            // operating on primitive data type, i.e., not a complex object
+            int colnum = 0;
+
+            if ( fits_get_colnum(fits->p_fd, CASESEN, item->name, &colnum, &status) == 0) {
+                // cooresponding column found in table
+                int dataType = 0;
+                convertPsTypeToFits(item->type, NULL, NULL, &dataType);
+
+                if (fits_write_col(fits->p_fd, dataType, colnum, row+1, 1, 1, &item->data,&status) != 0) {
+                    char fitsErr[MAX_STRING_LENGTH];
+                    (void)fits_get_errstatus(status, fitsErr);
+                    psError(PS_ERR_IO, true,
+                            PS_ERRORTEXT_psFits_WRITE_FAILED,
+                            fits->filename, fitsErr);
+                    psFree(iter);
+                    return false;
+                }
+            } else {
+                // the column was not found.
+                psWarning("No column with the name '%s' exists in the table.",
+                          item->name);
+            }
+        }
+    }
+
+    psFree(iter);
+
+    return true;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFits.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFits.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psFits.h	(revision 22271)
@@ -0,0 +1,268 @@
+/** @file  psFits.h
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.10.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITS_H
+#define PS_FITS_H
+
+#include<fitsio.h>
+
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psHash.h"
+#include "psImage.h"
+
+/// @addtogroup FileIO
+/// @{
+
+typedef enum {
+    PS_FITS_TYPE_NONE = -1,
+    PS_FITS_TYPE_IMAGE = IMAGE_HDU,
+    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL,
+    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,
+    PS_FITS_TYPE_ANY = ANY_HDU
+} psFitsType;
+
+/** FITS file object.
+ *
+ *  This object should be considered opaque to the user; no item in this
+ *  struct should be accessed directly.
+ *
+ */
+typedef struct
+{
+    fitsfile* p_fd;                    ///< the CFITSIO fits files handle.
+    const char* filename;              ///< the filename of the fits file
+}
+psFits;
+
+/** Opens a FITS file and allocates the associated psFits object.
+ *
+ *  @return psFits*    new psFits object for the FITS files specified or
+ *                     NULL if the open of the FITS file failed
+ */
+psFits* psFitsAlloc(
+    const char* name                   ///< the FITS file name
+);
+
+/** Moves the FITS HDU to the specified extension name.
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+bool psFitsMoveExtName(
+    const psFits* fits,                ///< the psFits object to move
+    const char* extname                ///< the extension name
+);
+
+/** Moves the FITS HDU to the specified extension number
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+bool psFitsMoveExtNum(
+    const psFits* fits,                ///< the psFits object to move
+    int extnum,                        ///< the extension number to move to (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Get the current extension number, where 0 is the primary HDU.
+ *
+ *  @return int        Current HDU number of the psFits file or < 0 if an error
+ *                     occurred.
+ */
+int psFitsGetExtNum(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Get the current extension name.
+ *
+ *  @return int        Current HDU name of the psFits file or NULL if an
+ *                     error occurred.
+ */
+char* psFitsGetExtName(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Set the current extension's name
+ *
+ *  @return bool       TRUE if the extension was successfully set, otherwise FALSE.
+ */
+bool psFitsSetExtName(
+    const psFits* fits,                ///< the psFits object
+    const char* name                   ///< the extension name
+);
+
+/** Get the total number of HDUs in the FITS file.
+ *
+ *  @return int        The total number of HDUs in the FITS file or < 0 if an
+ *                     error occurred.
+ */
+int psFitsGetSize(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Get the extension type of the current HDU.
+ *
+ *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN,
+ *                     the type could not be determined.
+ */
+psFitsType psFitsGetExtType(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of the current HDU.
+ *
+ *  @return psMetadata*   the header data
+ */
+psMetadata* psFitsReadHeader(
+    psMetadata* out,
+    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.
+
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of all HDUs.  The current HDU is not changed.
+ *
+ *  @return psHash*      the header data
+ */
+psHash* psFitsReadHeaderSet(
+    psHash* out,
+    ///< The psHash to add the header data via psMetadata items.  If null, a
+    ///< new psHash is created.  The keys of the psHash are the extension names
+    ///< of the cooresponding HDUs.
+
+    const psFits* fits                       ///< the psFits object
+);
+
+/** Writes the values of the metadata to the current HDU header.
+ *
+ *  @return bool        if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteHeader(
+    const psMetadata* header,          ///< the psMetadata data in which to write
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads an image, given the desired region and z-plane.
+ *
+ *  @return psImage*     the read image or NULL if there was an error.
+ */
+psImage* psFitsReadImage(
+    psImage* out,                      ///< a psImage to recycle.
+    const psFits* fits,                ///< the psFits object
+    psRegion region,                   ///< the region in the FITS image to read
+    int z                              ///< the z-plane in the FITS image cube to read
+);
+
+/** Writes an image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImage(
+    const psFits* fits,                ///< the psFits object
+    const psMetadata* header,          ///< header items for the new HDU.  Can be NULL.
+    const psImage* input,              ///< the image to output
+    int depth,                         ///< the number of z-planes of the FITS image data cube
+    char* extname                      ///< extension name
+);
+
+/** Updates the FITS file image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsUpdateImage(
+    const psFits* fits,                ///< the psFits object
+    const psImage* input,              ///< the image to output
+    psRegion region,                   ///< the region in the FITS image to write
+    int z                              ///< the z-planes of the FITS image data cube to write
+);
+
+/** Reads a table row.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psMetadata*    The table row's data.  The keys are the column names.
+ */
+psMetadata* psFitsReadTableRow(
+    const psFits* fits,                ///< the psFits object
+    int row                            ///< row number to read
+);
+
+/** Reads a table column.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*    Array of data items for the specified column or NULL
+ *                      if an error occurred.
+ */
+psArray* psFitsReadTableColumn(
+    const psFits* fits,                ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+/** Reads a table column of numbers.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psVector*    Vector of data for the specified column or NULL
+ *                       if an error occurred.
+ */
+psVector* psFitsReadTableColumnNum(
+    const psFits* fits,                ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+
+/** Reads a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*     Array of psMetadata items, which contains the output
+ *                       data items of each row.
+ *
+ *  @see psFitsReadTableRow
+ */
+psArray* psFitsReadTable(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Writes a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *
+ *  @see psFitsReadTableRow
+ */
+bool psFitsWriteTable(
+    const psFits* fits,                ///< the psFits object
+    const psMetadata* header,  ///< header items for the new HDU.  Can be NULL.
+    const psArray* table,
+    ///< Array of psMetadata items, which contains the output data items of each row.
+    char* extname                      ///< extension name
+);
+
+
+/** Updates a FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *
+ *  @see psFitsWriteTable
+ */
+bool psFitsUpdateTable(
+    const psFits* fits,                ///< the psFits object
+    psMetadata* data,
+    ///< Array of psMetadata items, which contains the output data items of each row.
+    int row                            ///< the row number to update.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psLookupTable.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psLookupTable.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psLookupTable.c	(revision 22271)
@@ -0,0 +1,959 @@
+/** @file  psLookupTable.c
+*
+*  @brief This file defines the structure and functions for table lookups.
+*
+*  @ingroup dataIO
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-08 17:58:57 $
+*
+*  Copyright 2004-5 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+//#ifdef DARWIN
+#undef __STRICT_ANSI__
+//#endif
+#include <stdlib.h>
+//#ifdef DARWIN
+#define __STRICT_ANSI__
+//#endif
+#include <math.h>
+#include <stdlib.h>
+
+#include "psMemory.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLookupTable.h"
+#include "psFileUtilsErrors.h"
+#include "psConstants.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 256
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static bool ignoreLine(char *inString);
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+static psU8 parseU8(char *inString, psParseErrorType *status);
+static psS8 parseS8(char *inString, psParseErrorType *status);
+static psU16 parseU16(char *inString, psParseErrorType *status);
+static psS16 parseS16(char *inString, psParseErrorType *status);
+static psU32 parseU32(char *inString, psParseErrorType *status);
+static psS32 parseS32(char *inString, psParseErrorType *status);
+static psU64 parseU64(char *inString, psParseErrorType *status);
+static psS64 parseS64(char *inString, psParseErrorType *status);
+static psF32 parseF32(char *inString, psParseErrorType *status);
+static psF64 parseF64(char *inString, psParseErrorType *status);
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status);
+static void lookupTableFree(psLookupTable* table);
+
+/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+ *  must be null terminated. */
+static bool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+static char *cleanString(char *inString, int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = PS_PARSE_ERROR_GENERAL;
+    }
+
+    return cleanToken;
+}
+
+/** Returns single parsed value as a psU8. The input string must be cleaned and null terminated. */
+static psU8 parseU8(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU8 value = 0.0;
+
+
+    value = (psU8)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS8. The input string must be cleaned and null terminated. */
+static psS8 parseS8(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS8 value = 0.0;
+
+
+    value = (psS8)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU16. The input string must be cleaned and null terminated. */
+static psU16 parseU16(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU16 value = 0.0;
+
+
+    value = (psU16)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS16. The input string must be cleaned and null terminated. */
+static psS16 parseS16(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS16 value = 0.0;
+
+
+    value = (psS16)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU32. The input string must be cleaned and null terminated. */
+static psU32 parseU32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU32 value = 0.0;
+
+
+    value = (psU32)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS32. The input string must be cleaned and null terminated. */
+static psS32 parseS32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS32 value = 0.0;
+
+
+    value = (psS32)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU64. The input string must be cleaned and null terminated. */
+static psU64 parseU64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU64 value = 0.0;
+
+
+    value = (psU64)strtoull(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS64. The input string must be cleaned and null terminated. */
+static psS64 parseS64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS64 value = 0.0;
+
+
+    value = (psS64)strtoll(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psF32. The input string must be cleaned and null terminated. */
+static psF32 parseF32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psF32 value = 0.0;
+
+
+    value = (psF32)strtof(inString, &end);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psF64. The input string must be cleaned and null terminated. */
+static psF64 parseF64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psF64 value = 0.0;
+
+
+    value = (psF64)strtod(inString, &end);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a double precision number. The input string must be cleaned and null
+ * terminated. */
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status)
+{
+    psElemType type;
+
+
+    if(vec == NULL) {
+        *status = 1;
+        return;
+    }
+
+    type = vec->type.type;
+
+    switch(type) {
+    case PS_TYPE_U8:
+        vec->data.U8[index] = parseU8(strValue, status);
+        break;
+    case PS_TYPE_S8:
+        vec->data.S8[index] = parseS8(strValue, status);
+        break;
+    case PS_TYPE_U16:
+        vec->data.U16[index] = parseU16(strValue, status);
+        break;
+    case PS_TYPE_S16:
+        vec->data.S16[index] = parseS16(strValue, status);
+        break;
+    case PS_TYPE_U32:
+        vec->data.U32[index] = parseU32(strValue, status);
+        break;
+    case PS_TYPE_S32:
+        vec->data.S32[index] = parseS32(strValue, status);
+        break;
+    case PS_TYPE_U64:
+        vec->data.U64[index] = parseU64(strValue, status);
+        break;
+    case PS_TYPE_S64:
+        vec->data.S64[index] = parseS64(strValue, status);
+        break;
+    case PS_TYPE_F32:
+        vec->data.F32[index] = parseF32(strValue, status);
+        break;
+    case PS_TYPE_F64:
+        vec->data.F64[index] = parseF64(strValue, status);
+        break;
+    default:
+        *status = PS_PARSE_ERROR_TYPE;
+    }
+
+    return;
+}
+
+static psParseErrorType printError(psU64 lineCount, char* badText, psParseErrorType status)
+{
+    switch(status) {
+    case PS_PARSE_ERROR_VALUE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_VALUE, badText, lineCount);
+        break;
+    case PS_PARSE_ERROR_TYPE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_TYPE, badText, lineCount);
+        break;
+    case PS_PARSE_ERROR_GENERAL:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_GENERAL, badText, lineCount);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_INVALID_TYPE, badText, lineCount);
+    }
+
+    return PS_LOOKUP_SUCCESS;
+}
+
+static void lookupTableFree(psLookupTable* table)
+{
+    if (table == NULL) {
+        return;
+    }
+
+    psFree(table->values);
+    psFree(table->index);
+    psFree((char*)table->fileName);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psLookupTable* psLookupTableAlloc(const char *fileName, psF64 validFrom, psF64 validTo)
+{
+    psLookupTable *outTable = NULL;
+
+
+    // Can't read table if you don't know its name
+    PS_PTR_CHECK_NULL(fileName,NULL);
+
+    // Allocate lookup table
+    outTable = (psLookupTable*)psAlloc(sizeof(psLookupTable));
+
+    // Set deallocator
+    psMemSetDeallocator(outTable, (psFreeFcn)lookupTableFree);
+
+    // Allocate and set metadata item comment
+    outTable->fileName = psStringCopy(fileName);
+
+    // Number of table rows and columns. Automatically resized by table read.
+    outTable->numRows = 0;
+    outTable->numCols = 0;
+
+    // Valid ranges. Automatically set by table read if both zero.
+    outTable->validFrom = validFrom;
+    outTable->validTo = validTo;
+
+    // Vector of independent index values. Filled by table read.
+    outTable->index = NULL;
+
+    // Array of dependent table values corresponding to index values. Filled by table read.
+    outTable->values = NULL;
+
+    return outTable;
+}
+
+#define UPDATE_VALID_TO_FROM(TABLE)                                             \
+switch (TABLE->index->type.type) {                                              \
+case PS_TYPE_U8:                                                                \
+    TABLE->validFrom = (psF64)TABLE->index->data.U8[0];                         \
+    TABLE->validTo   = (psF64)TABLE->index->data.U8[TABLE->index->nalloc-1];    \
+    break;                                                                      \
+case PS_TYPE_S8:                                                                \
+    TABLE->validFrom = (psF64)TABLE->index->data.S8[0];                         \
+    TABLE->validTo   = (psF64)TABLE->index->data.S8[TABLE->index->nalloc-1];    \
+    break;                                                                      \
+case PS_TYPE_U16:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.U16[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.U16[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_S16:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.S16[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.S16[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_U32:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.U32[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.U32[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_S32:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.S32[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.S32[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_U64:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.U64[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.U64[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_S64:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.S64[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.S64[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_F32:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.F32[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.F32[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_F64:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.F64[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.F64[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+default:                                                                        \
+    TABLE->validFrom = (psF64)0;                                                \
+    TABLE->validTo   = (psF64)0;                                                \
+    break;                                                                      \
+}
+
+#define COPY_VECTOR_VALUES(VEC_OUT,INDEX_OUT,VEC_IN,INDEX_IN)                              \
+switch(((psVector*)(VEC_IN))->type.type) {                                                 \
+case PS_TYPE_U8:                                                                           \
+    ((psVector*)VEC_OUT)->data.U8[INDEX_OUT] = ((psVector*)VEC_IN)->data.U8[INDEX_IN];     \
+    break;                                                                                 \
+case PS_TYPE_U16:                                                                          \
+    ((psVector*)VEC_OUT)->data.U16[INDEX_OUT] = ((psVector*)VEC_IN)->data.U16[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_U32:                                                                          \
+    ((psVector*)VEC_OUT)->data.U32[INDEX_OUT] = ((psVector*)VEC_IN)->data.U32[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_U64:                                                                          \
+    ((psVector*)VEC_OUT)->data.U64[INDEX_OUT] = ((psVector*)VEC_IN)->data.U64[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_S8:                                                                           \
+    ((psVector*)VEC_OUT)->data.S8[INDEX_OUT] = ((psVector*)VEC_IN)->data.S8[INDEX_IN];     \
+    break;                                                                                 \
+case PS_TYPE_S16:                                                                          \
+    ((psVector*)VEC_OUT)->data.S16[INDEX_OUT] = ((psVector*)VEC_IN)->data.S16[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_S32:                                                                          \
+    ((psVector*)VEC_OUT)->data.S32[INDEX_OUT] = ((psVector*)VEC_IN)->data.S32[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_S64:                                                                          \
+    ((psVector*)VEC_OUT)->data.S64[INDEX_OUT] = ((psVector*)VEC_IN)->data.S64[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_F32:                                                                          \
+    ((psVector*)VEC_OUT)->data.F32[INDEX_OUT] = ((psVector*)VEC_IN)->data.F32[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_F64:                                                                          \
+    ((psVector*)VEC_OUT)->data.F64[INDEX_OUT] = ((psVector*)VEC_IN)->data.F64[INDEX_IN];   \
+    break;                                                                                 \
+default:                                                                                   \
+    break;                                                                                 \
+}
+
+
+psLookupTable* psLookupTableRead(psLookupTable *table)
+{
+    bool typeLine = true;
+    char *line = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *linePtr = NULL;
+    psParseErrorType status = PS_PARSE_SUCCESS;
+    psParseErrorType lineStatus = PS_PARSE_SUCCESS;
+    psU64 lineCount = 0;
+    psU64 numRows = 0;
+    psU64 numCols = 0;
+    psU64 failedLines = 0;
+    FILE *fp = NULL;
+    psElemType elemType;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+    psBool sortIndex = false;
+
+    // Check for NULL input table
+    PS_PTR_CHECK_NULL(table,NULL);
+
+    // Check for input table with NULL file name
+    PS_PTR_CHECK_NULL(table->fileName,NULL);
+
+    // Open table file specified by table->fileName
+    if((fp=fopen(table->fileName, "r")) == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_FILE_NOT_FOUND,
+                table->fileName);
+        return table;
+    }
+
+    // Initialize vector pointers
+    indexVec = table->index;
+    values = table->values = psArrayAlloc(10);
+    values->n = 0;
+
+    // Create reusable line for continuous read
+    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+    // Loop through file to get numRows, numCols, and column data types
+    while((fgets(line, MAX_STRING_LENGTH, fp) != NULL) && (lineStatus == PS_PARSE_SUCCESS)) {
+
+        // Initialize variables for new line
+        linePtr = line;
+        lineCount++;
+
+        // If line is not a comment or blank, then extract data
+        if(!ignoreLine(linePtr)) {
+
+            if(typeLine == true) {
+
+                // Determine column types from first line in data file after comments
+                while((strType=getToken(&linePtr," ",&status))) {
+                    numCols++;
+                    typeLine = false;
+                    if(!strncmp(strType, "psU8", 4)) {
+                        elemType = PS_TYPE_U8;
+                    } else if(!strncmp(strType, "psS8", 4)) {
+                        elemType = PS_TYPE_S8;
+                    } else if(!strncmp(strType, "psU16", 5)) {
+                        elemType = PS_TYPE_U16;
+                    } else if(!strncmp(strType, "psS16", 5)) {
+                        elemType = PS_TYPE_S16;
+                    } else if(!strncmp(strType, "psU32", 5)) {
+                        elemType = PS_TYPE_U32;
+                    } else if(!strncmp(strType, "psS32", 5)) {
+                        elemType = PS_TYPE_S32;
+                    } else if(!strncmp(strType, "psU64", 5)) {
+                        elemType = PS_TYPE_U64;
+                    } else if(!strncmp(strType, "psS64", 5)) {
+                        elemType = PS_TYPE_S64;
+                    } else if(!strncmp(strType, "psF32", 5)) {
+                        elemType = PS_TYPE_F32;
+                    } else if(!strncmp(strType, "psF64", 5)) {
+                        elemType = PS_TYPE_F64;
+                    } else {
+                        status = PS_PARSE_ERROR_TYPE;
+                    }
+
+                    // Realloc number of columns as you go
+                    psVector* vec = psVectorAlloc(0, elemType);
+                    if(numCols == 1) {
+                        indexVec = vec;
+                    } else {
+                        psArrayAdd(values,0,vec);
+                        psFree(vec);
+                    }
+
+                    if(status) {
+                        printError(lineCount, strValue, status);
+                        failedLines++;
+                        lineStatus = status;
+                        status = PS_PARSE_SUCCESS;
+                    }
+                    psFree(strType);
+                }
+            } else {
+                // Parse and add values to all columns
+                numRows++;
+                numCols = 0;
+                while((strValue=getToken(&linePtr," ",&status))) {
+                    numCols++;
+
+                    // Realloc number of rows as you go
+                    if(numCols == 1) {
+                        sortIndex = false;
+                        indexVec = psVectorRecycle(indexVec, numRows, indexVec->type.type);
+                        parseValue(indexVec, numRows-1, strValue, &status);
+                    } else {
+                        valuesVec = values->data[numCols-2];
+                        valuesVec = psVectorRecycle(valuesVec, numRows, valuesVec->type.type);
+                        parseValue(valuesVec, numRows-1, strValue, &status);
+                    }
+
+                    if(status) {
+                        printError(lineCount, strValue, status);
+                        failedLines++;
+                        lineStatus = status;
+                        status = PS_PARSE_SUCCESS;
+                    }
+                    psFree(strValue);
+                } // end while
+            } // end else
+        } // if ignoreLine
+    } // end while
+
+    psFree(line);
+
+    // Set table for return
+    table->numRows = numRows;
+    table->numCols = numCols-1;
+    table->index = indexVec;
+    table->values = values;
+
+    // Set table values to indicate error detected during the read
+    if(lineStatus) {
+        table->numRows = 0;
+        table->numCols = 0;
+        table->validFrom = 0;
+        table->validTo = 0;
+        psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psLookupTable_TABLE_INVALID);
+    } else {
+        // Check if index vector needs to be sorted
+        psVector* sortedIndex = psVectorAlloc(table->numRows,PS_TYPE_U32);
+        sortedIndex = psVectorSortIndex(sortedIndex,table->index);
+        for(psS32 i = 0; i < numRows; i++ ) {
+            if(sortedIndex->data.U32[i] != i) {
+                sortIndex = true;
+                break;
+            }
+        }
+        // Check if it is necessary to sort value vectors
+        if(sortIndex) {
+            // Allocate new index vector
+            psVector* newIndexVector = psVectorAlloc(numRows,indexVec->type.type);
+            // Allocate new value vectors
+            psArray* newValueArray = psArrayAlloc(numCols-1);
+            for(psS32 j = 0; j < numCols-1; j++) {
+                psS32 type = ((psVector*)(table->values->data[j]))->type.type;
+                newValueArray->data[j] = psVectorAlloc(numRows,type);
+            }
+            for(psS32 i = 0; i < numRows; i++) {
+                // Populate new index vector
+                psU32 sortIndex = sortedIndex->data.U32[i];
+                COPY_VECTOR_VALUES(newIndexVector,i, indexVec,sortIndex)
+                // For every column populate new value vectors
+                for(psS32 j=0; j < numCols-1; j++) {
+                    COPY_VECTOR_VALUES(newValueArray->data[j],i,table->values->data[j], sortIndex)
+                }
+            }
+            // Free old index vector
+            psFree(table->index);
+            // Free old value vectors
+            psFree(table->values);
+            // Assign new vector value to table
+            table->index = newIndexVector;
+            // Assign new value vectors to table array
+            table->values = newValueArray;
+        }
+        psFree(sortedIndex);
+        // Update the validTo and validFrom
+        UPDATE_VALID_TO_FROM(table)
+    }
+
+    fclose(fp);
+
+    return table;
+}
+
+#define CONVERT_VALUE_TO_F64(VECTOR,INDEX,RESULT)     \
+switch(VECTOR->type.type) {                           \
+case PS_TYPE_U8:                                      \
+    RESULT = (psF64)VECTOR->data.U8[INDEX];           \
+    break;                                            \
+case PS_TYPE_U16:                                     \
+    RESULT = (psF64)VECTOR->data.U16[INDEX];          \
+    break;                                            \
+case PS_TYPE_U32:                                     \
+    RESULT = (psF64)VECTOR->data.U32[INDEX];          \
+    break;                                            \
+case PS_TYPE_U64:                                     \
+    RESULT = (psF64)VECTOR->data.U64[INDEX];          \
+    break;                                            \
+case PS_TYPE_S8:                                      \
+    RESULT = (psF64)VECTOR->data.S8[INDEX];           \
+    break;                                            \
+case PS_TYPE_S16:                                     \
+    RESULT = (psF64)VECTOR->data.S16[INDEX];          \
+    break;                                            \
+case PS_TYPE_S32:                                     \
+    RESULT = (psF64)VECTOR->data.S32[INDEX];          \
+    break;                                            \
+case PS_TYPE_S64:                                     \
+    RESULT = (psF64)VECTOR->data.S64[INDEX];          \
+    break;                                            \
+case PS_TYPE_F32:                                     \
+    RESULT = (psF64)VECTOR->data.F32[INDEX];          \
+    break;                                            \
+case PS_TYPE_F64:                                     \
+    RESULT = VECTOR->data.F64[INDEX];                 \
+    break;                                            \
+default:                                              \
+    RESULT = NAN;                                     \
+    break;                                            \
+}
+
+
+#define CHECK_LOWER_UPPER_BOUND(TABLE,INDEX,COLUMN)                                \
+switch (TABLE->index->type.type) {                                                 \
+case PS_TYPE_U8:                                                                   \
+    if( (psU8)index < TABLE->index->data.U8[0] ) {                                 \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psU8)index > TABLE->index->data.U8[numRows-1]) {                          \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_U16:                                                                  \
+    if( (psU16)index < TABLE->index->data.U16[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psU16)index > TABLE->index->data.U16[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_U32:                                                                  \
+    if( (psU32)index < TABLE->index->data.U32[0]) {                                \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if ( (psU32)index > TABLE->index->data.U32[numRows-1] ) {                      \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_U64:                                                                  \
+    if( (psU64)index < TABLE->index->data.U64[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psU64)index > TABLE->index->data.U64[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S8:                                                                   \
+    if( (psS8)index < TABLE->index->data.S8[0] ) {                                 \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS8)index > TABLE->index->data.S8[numRows-1] ) {                         \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S16:                                                                  \
+    if( (psS16)index < TABLE->index->data.S16[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS16)index > TABLE->index->data.S16[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S32:                                                                  \
+    if( (psS32)index < TABLE->index->data.S32[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS32)index > TABLE->index->data.S32[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S64:                                                                  \
+    if( (psS64)index < TABLE->index->data.S64[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS64)index > TABLE->index->data.S64[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_F32:                                                                  \
+    if( (psF32)index < TABLE->index->data.F32[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psF32)index > TABLE->index->data.F32[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_F64:                                                                  \
+    if( index < TABLE->index->data.F64[0] ) {                                      \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( index > TABLE->index->data.F64[numRows-1] ) {                              \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+default:                                                                           \
+    *status = PS_LOOKUP_ERROR;                                                     \
+    return NAN;                                                                    \
+    break;                                                                         \
+}                                                                                  \
+if(*status == PS_LOOKUP_PAST_TOP) {                                                \
+    CONVERT_VALUE_TO_F64(((psVector*)(TABLE->values->data[COLUMN])),0,out)         \
+    return out;                                                                    \
+} else if (*status == PS_LOOKUP_PAST_BOTTOM) {                                     \
+    CONVERT_VALUE_TO_F64(((psVector*)(TABLE->values->data[COLUMN])),numRows-1,out) \
+    return out;                                                                    \
+}
+
+
+psF64 psLookupTableInterpolate(psLookupTable *table, psF64 index, psU64 column, psLookupStatusType *status)
+{
+    psU64 hiIdx = 0;
+    psU64 loIdx = 0;
+    psU64 numRows = 0;
+    psU64 numCols = 0;
+    psF64 out = 0.0;
+    psF64 denom = 0.0;
+    psF64 convertVal = 0.0;
+    psF64 tempVal = 0.0;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+
+    // Error checks
+    // Set status to error prior to check since if checks fails it will immediately return
+    PS_PTR_CHECK_NULL(status,NAN);
+    *status = PS_LOOKUP_ERROR;
+    PS_PTR_CHECK_NULL(table,NAN);
+    indexVec = table->index;
+    values = table->values;
+    numRows = table->numRows;
+    numCols = table->numCols;
+    PS_PTR_CHECK_NULL(indexVec,NAN);
+    PS_PTR_CHECK_NULL(values,NAN);
+    PS_INT_CHECK_EQUALS(numRows, 0,NAN);
+    PS_INT_CHECK_EQUALS(numCols, 0,NAN);
+    PS_INT_CHECK_RANGE(column, 0, (int)(numCols-1), NAN);
+
+    valuesVec = (psVector*)values->data[column];
+    PS_PTR_CHECK_NULL(indexVec,NAN);
+
+    // Set status to success since it passed all parameter checks
+    *status = PS_LOOKUP_SUCCESS;
+
+    // Verify the index is within the bounds of the table
+    CHECK_LOWER_UPPER_BOUND(table,index,column)
+
+    // Find location in table where specified index is between to entries
+    CONVERT_VALUE_TO_F64(indexVec, 0, convertVal)
+    while(index > convertVal ) {
+        hiIdx++;
+        if(hiIdx >= numRows) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psLookupTable_INTERPOLATE_HIGH, hiIdx);
+            *status = PS_LOOKUP_ERROR;
+            return NAN;
+        }
+        CONVERT_VALUE_TO_F64(indexVec, hiIdx, convertVal)
+    }
+
+    // Check for negative low index and generate error
+    loIdx = hiIdx--;
+    if(loIdx < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psLookupTable_INTERPOLATE_LOW, loIdx);
+        *status = PS_LOOKUP_ERROR;
+        return NAN;
+    }
+
+    // Perform linear interpolation to calculate return value
+    CONVERT_VALUE_TO_F64(indexVec, hiIdx, denom)
+    CONVERT_VALUE_TO_F64(indexVec, loIdx, convertVal);
+    denom -= convertVal;
+    if(fabs(denom) < FLT_EPSILON) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_DIVIDE_BY_ZERO);
+        *status = PS_LOOKUP_ERROR;
+        return NAN;
+    } else {
+        CONVERT_VALUE_TO_F64(valuesVec,hiIdx,tempVal)
+        CONVERT_VALUE_TO_F64(valuesVec,loIdx,convertVal)
+        tempVal -= convertVal;
+        CONVERT_VALUE_TO_F64(indexVec,loIdx,convertVal)
+        out = tempVal*(index-convertVal)/denom;
+        CONVERT_VALUE_TO_F64(valuesVec,loIdx,convertVal)
+        out += convertVal;
+    }
+
+    return out;
+}
+
+psVector* psLookupTableInterpolateAll(psLookupTable *table, psF64 index, psVector *stats)
+{
+    psU64 i = 0;
+    psU64 numCols = 0;
+    psVector *outVector = NULL;
+    psLookupStatusType status = PS_LOOKUP_SUCCESS;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(table,NULL);
+    PS_PTR_CHECK_NULL(stats,NULL);
+    numCols = table->numCols;
+    PS_INT_CHECK_EQUALS(numCols, 0,NULL);
+
+    outVector = psVectorAlloc(numCols+1, PS_TYPE_F64);
+
+    // Fill vectors with results and status of results
+    for(i=0; i<numCols; i++) {
+        outVector->data.F64[i] = psLookupTableInterpolate(table, index, i, &status);
+        stats->data.U32[i] = status;
+    }
+
+    return outVector;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psLookupTable.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psLookupTable.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataIO/psLookupTable.h	(revision 22271)
@@ -0,0 +1,112 @@
+/** @file  psLookupTable.h
+*
+*  @brief This file defines the structure and functions for table lookups.
+*
+*  @ingroup dataIO
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-08 17:58:57 $
+*
+*  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LOOKUPTABLE_H
+#define PS_LOOKUPTABLE_H
+
+#include "psType.h"
+#include "psVector.h"
+#include "psArray.h"
+
+
+/** Lookup table structure
+ *
+ *  Holds table data read from external data files.
+ *
+ */
+typedef struct
+{
+    const char *fileName;              ///< Name of file with table
+    psU64 numRows;                     ///< Number of table rows
+    psU64 numCols;                     ///< Number of table columns
+    psF64 validFrom;                   ///< Lower bound for rable read
+    psF64 validTo;                     ///< Upper bound for table read
+    psVector *index;                   ///< Vector of independent index values
+    psArray *values;                   ///< Array of dependent table values corresponding to index values
+}
+psLookupTable;
+
+
+/** Lookup table lookup status and error conditions
+ *
+ *  Success, failure, and status conditions for table lookups.
+ */
+typedef enum {
+    PS_LOOKUP_SUCCESS             = 0x0000,        ///< Table lookup succeeded
+    PS_LOOKUP_PAST_TOP            = 0x0101,        ///< Lookup off top of table
+    PS_LOOKUP_PAST_BOTTOM         = 0x0102,        ///< Lookup off bottom of table
+    PS_LOOKUP_ERROR               = 0x0104         ///< Any other type of lookup error
+} psLookupStatusType;
+
+/** Lookup table parse status and error conditions
+ *
+ *  Success, failure, and status conditions for table parsing.
+ */
+typedef enum {
+    PS_PARSE_SUCCESS              = 0x0000,        ///< Table lookup succeeded
+    PS_PARSE_ERROR_TYPE           = 0x0101,        ///< Error parsing type
+    PS_PARSE_ERROR_VALUE          = 0x0102,        ///< Error parsing numerical value
+    PS_PARSE_ERROR_GENERAL        = 0x0104         ///< Any other type of lookup error
+}psParseErrorType;
+
+/** Allocator for psLookupTable struct
+ *
+ *  Allocates a new psLookupTable struct.
+ *
+ *  @return psLookupTable*     New psLookupTable struct.
+ */
+psLookupTable* psLookupTableAlloc(
+    const char *fileName,           ///< Name of file to read
+    psF64 validFrom,                ///< Lower bound for rable read
+    psF64 validTo                   ///< Upper bound for table read
+);
+
+/** Read lookup table
+ *
+ *  Reads a lookup table and fills corresponding psLookupTable struct.
+ *
+ *  @return psLookupTable*     New psLookupTable struct.
+ */
+psLookupTable* psLookupTableRead(
+    psLookupTable *table            ///< Table to read
+);
+
+/** Lookup and interpolate value from table.
+ *
+ *  Interpolates value from table. Sets status bit for success or one of several possible failure
+ *  conditions.
+ *
+ *  @return psLookupTable*     New psLookupTable struct
+ */
+psF64 psLookupTableInterpolate(
+    psLookupTable *table,           ///< Table with data
+    psF64 index,                    ///< Value to be interpolated
+    psU64 column,                   ///< Column in table to be interpolated
+    psLookupStatusType *status      ///< Status of lookup
+);
+
+/** Lookup and interpolate all values from table.
+ *
+ *  Interpolates all values from table. Sets status bit for success or one of several possible failure
+ *  conditions.
+ *
+ *  @return psLookupTable*     New psLookupTable struct
+ */
+psVector* psLookupTableInterpolateAll(
+    psLookupTable *table,           ///< Table with data
+    psF64 index,                    ///< Value to be interpolated
+    psVector *stats                 ///< Vector of status for each lookup
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/.cvsignore	(revision 22271)
@@ -0,0 +1,7 @@
+Makefile.in
+.deps
+.libs
+Makefile
+*.lo
+*.la
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/Makefile.am	(revision 22271)
@@ -0,0 +1,33 @@
+#Makefile for dataManip functions of psLib
+#
+INCLUDES = \
+	-I$(top_srcdir)/src/astronomy \
+	-I$(top_srcdir)/src/collections \
+	-I$(top_srcdir)/src/dataIO \
+	-I$(top_srcdir)/src/image \
+	-I$(top_srcdir)/src/sysUtils \
+	$(all_includes)
+
+noinst_LTLIBRARIES = libpslibdataManip.la
+
+libpslibdataManip_la_SOURCES = psUnaryOp.c psBinaryOp.c psStats.c \
+		psFunctions.c psMatrix.c psVectorFFT.c psMinimize.c psRandom.c
+	   
+BUILT_SOURCES = psDataManipErrors.h
+EXTRA_DIST = psDataManipErrors.dat psDataManipErrors.h dataManip.i
+
+psDataManipErrors.h: psDataManipErrors.dat
+	perl $(top_srcdir)/src/parseErrorCodes.pl --data=$? $@
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psConstants.h \
+	psStats.h  \
+	psFunctions.h \
+	psMatrix.h \
+    psBinaryOp.h \
+    psUnaryOp.h \
+	psVectorFFT.h \
+	psMinimize.h \
+	psRandom.h
+ 
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/dataManip.i
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/dataManip.i	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/dataManip.i	(revision 22271)
@@ -0,0 +1,11 @@
+/* dataManip headers */
+%include "psConstants.h"
+%include "psDataManipErrors.h"
+%include "psFunctions.h"
+%include "psMatrix.h"
+%include "psBinaryOp.h"
+%include "psUnaryOp.h"
+%include "psMinimize.h"
+%include "psRandom.h"
+%include "psStats.h"
+%include "psVectorFFT.h"
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psBinaryOp.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psBinaryOp.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psBinaryOp.c	(revision 22271)
@@ -0,0 +1,582 @@
+/** @file  psBinaryOp.c
+ *
+ *  @brief Provides binary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************
+ *  INCLUDE FILES                                                             *
+ ******************************************************************************/
+#include <string.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+/*****************************************************************************
+ *  FUNCTION IMPLEMENTATION - LOCAL                                          *
+ *****************************************************************************/
+
+// Conversion for degrees to radians
+#define D2R 0.01745329252111111  /* PI/180 */
+
+// Conversion for radians to degrees
+#define R2D 57.29577950924861   /* 180.0/PI */
+
+// Binary SCALAR_XXXX operations
+#define SCALAR_SCALAR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    o  = &((psScalar* )OUT)->data.TYPE;                                                                      \
+    i1 = &((psScalar* )IN1)->data.TYPE;                                                                      \
+    i2 = &((psScalar* )IN2)->data.TYPE;                                                                      \
+    *o = OP;                                                                                                 \
+}
+
+#define SCALAR_VECTOR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 npt = 0;                                                                                             \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    npt  = ((psVector* )IN2)->n;                                                                             \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = &((psScalar* )IN1)->data.TYPE;                                                                      \
+    i2 = ((psVector* )IN2)->data.TYPE;                                                                       \
+    for (i=0; i < npt; i++, o++, i2++) {                                                                     \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+#define SCALAR_IMAGE(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 numRows = 0;                                                                                         \
+    psS32 numCols = 0;                                                                                         \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    numRows = ((psImage* )IN2)->numRows;                                                                     \
+    numCols = ((psImage* )IN2)->numCols;                                                                     \
+    for(j = 0; j < numCols; j++) {                                                                           \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = &((psScalar* )IN1)->data.TYPE;                                                                  \
+        i2 = ((psImage* )IN2)->data.TYPE[j];                                                                 \
+        for(i = 0; i < numRows; i++, o++, i2++) {                                                            \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Binary VECTOR_XXXX operations
+#define VECTOR_SCALAR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 n1 = 0;                                                                                              \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n1  = ((psVector* )IN1)->n;                                                                              \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = ((psVector* )IN1)->data.TYPE;                                                                       \
+    i2 = &((psScalar* )IN2)->data.TYPE;                                                                      \
+    for (i=0; i < n1; i++, o++, i1++) {                                                                      \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+#define VECTOR_VECTOR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 n1 = 0;                                                                                            \
+    psS32 n2 = 0;                                                                                            \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n1  = ((psVector* )IN1)->n;                                                                              \
+    n2  = ((psVector* )IN2)->n;                                                                              \
+    if(n1 != n2) {                                                                                           \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                         \
+                n1, n2);                                                                                     \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = ((psVector* )IN1)->data.TYPE;                                                                       \
+    i2 = ((psVector* )IN2)->data.TYPE;                                                                       \
+    for (i=0; i < n1; i++, o++, i1++, i2++) {                                                                \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+#define VECTOR_IMAGE(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 j = 0;                                                                                             \
+    psS32 n1 = 0;                                                                                            \
+    psS32 numRows2 = 0;                                                                                      \
+    psS32 numCols2 = 0;                                                                                      \
+    psDimen dim1 = 0;                                                                                        \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n1  = ((psVector* )IN1)->n;                                                                              \
+    dim1 = ((psVector* )IN1)->type.dimen;                                                                    \
+    numRows2 = ((psImage* )IN2)->numRows;                                                                    \
+    numCols2 = ((psImage* )IN2)->numCols;                                                                    \
+    \
+    if(dim1 == PS_DIMEN_VECTOR) { /* Regular vectors */                                             \
+        if(n1!=numRows2) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                         \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                           \
+                    n1, numRows2);                                                                           \
+            if (OUT != IN1 && OUT != IN2) {                                                                  \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        i1 = ((psVector* )IN1)->data.TYPE;                                                                   \
+        for(j = 0; j < numRows2; j++, i1++) {                                                                \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i2 = ((psImage* )IN2)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols2; i++, o++, i2++) {                                                       \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    } else {  /* Transposed vectors */                                                                       \
+        if(n1!=numCols2) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                 \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                     \
+                    n1, numCols2);                       \
+            if (OUT != IN1 && OUT != IN2) {                                                                  \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        for(j = 0; j < numRows2; j++) {                                                                      \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i1 = ((psVector* )IN1)->data.TYPE;                                                               \
+            i2 = ((psImage* )IN2)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols2; i++, o++, i1++, i2++) {                                                 \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Binary IMAGE_XXXX operations
+#define IMAGE_SCALAR(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 numRows = 0;                                                                                         \
+    psS32 numCols = 0;                                                                                         \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    numRows = ((psImage* )IN1)->numRows;                                                                     \
+    numCols = ((psImage* )IN1)->numCols;                                                                     \
+    for(j = 0; j < numRows; j++) {                                                                           \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = ((psImage* )IN1)->data.TYPE[j];                                                                 \
+        i2 = &((psScalar* )IN2)->data.TYPE;                                                                  \
+        for(i = 0; i < numCols; i++, o++, i1++) {                                                            \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+#define IMAGE_VECTOR(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 n2 = 0;                                                                                              \
+    psS32 numRows1 = 0;                                                                                        \
+    psS32 numCols1 = 0;                                                                                        \
+    psDimen dim2 = 0;                                                                                        \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n2  = ((psVector* )IN2)->n;                                                                              \
+    dim2 = ((psVector* )IN2)->type.dimen;                                                                    \
+    numRows1 = ((psImage* )IN1)->numRows;                                                                    \
+    numCols1 = ((psImage* )IN1)->numCols;                                                                    \
+    \
+    if(dim2 == PS_DIMEN_VECTOR) { /* Regular vectors */                                             \
+        if(n2!=numRows1) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                         \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                     \
+                    n2, numRows1);                                                                           \
+            if (OUT != IN1 && OUT != IN2) {                                                                  \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        i2 = ((psVector* )IN2)->data.TYPE;                                                                   \
+        for(j = 0; j < numRows1; j++, i1++) {                                                                \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i1 = ((psImage* )IN1)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols1; i++, o++, i1++) {                                                       \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    } else {  /* Transposed vectors */                                                                       \
+        if(n2!=numCols1) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                         \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                     \
+                    n2, numCols1);                                                                           \
+            if (OUT != IN1) {                                                                                \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        for(j = 0; j < numRows1; j++) {                                                                      \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i1 = ((psVector* )IN2)->data.TYPE;                                                               \
+            i2 = ((psImage* )IN1)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols1; i++, o++, i2++, i1++) {                                                 \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+#define IMAGE_IMAGE(OUT,IN1,OP,IN2,TYPE)                                                                     \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 numRows1 = 0;                                                                                        \
+    psS32 numCols1 = 0;                                                                                        \
+    psS32 numRows2 = 0;                                                                                        \
+    psS32 numCols2 = 0;                                                                                        \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    numRows1 = ((psImage* )IN1)->numRows;                                                                    \
+    numCols1 = ((psImage* )IN1)->numCols;                                                                    \
+    numRows2 = ((psImage* )IN2)->numRows;                                                                    \
+    numCols2 = ((psImage* )IN2)->numCols;                                                                    \
+    if(numRows1!=numRows2 || numCols1!=numCols2) {                                                           \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_IMAGE_SIZE_DIFFERS,                                                     \
+                numCols1, numRows1, numCols2, numRows2);                                                     \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    for(j = 0; j < numRows1; j++) {                                                                          \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = ((psImage* )IN1)->data.TYPE[j];                                                                 \
+        i2 = ((psImage* )IN2)->data.TYPE[j];                                                                 \
+        for(i = 0; i < numCols1; i++, o++, i1++, i2++) {                                                     \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Preprocessor macro function to create arithmetic function based on input type
+#define BINARY_TYPE(DIM1,DIM2,OUT,IN1,OP,IN2)                                                                \
+switch (IN1->type) {                                                                                         \
+case PS_TYPE_U8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_U16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_S16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_F32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,F32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_F64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,F64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_C32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,C32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_C64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,C64);                                                                       \
+    break;                                                                                                   \
+default:                                                                                                     \
+    /* char* strType; \
+    PS_TYPE_NAME(strType,IN1->type);                                                                         \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                                 \
+            PS_ERRORTEXT_psMatrix_TYPE_MISMATCH,                                                             \
+            strType);  */                                                                                      \
+    if (OUT != IN1 && OUT != IN2) {                                                                          \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+// Preprocessor macro function to create arithmetic function operation name
+#define BINARY_OP(DIM1,DIM2,OUT,IN1,OP,IN2)                                                                  \
+if(!strncmp(OP, "=", 1)) {                                                                                   \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i2,IN2);                                                                  \
+} else if(!strncmp(OP, "+", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 + *i2,IN2);                                                            \
+} else if(!strncmp(OP, "-", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 - *i2,IN2);                                                            \
+} else if(!strncmp(OP, "*", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 * *i2,IN2);                                                            \
+} else if(!strncmp(OP, "/", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 / *i2,IN2);                                                            \
+} else if(!strncmp(OP, "^", 1)) {                                                                            \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN1->type)) {                                                                \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,cpow(*i1,*i2),IN2);                                                    \
+    } else {                                                                                                 \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,pow(*i1,*i2),IN2);                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "min", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN1->type)) {                                                                \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                            \
+                PS_ERRORTEXT_psMatrix_MIN_COMPLEX_SUPPORT);                                                  \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    } else {                                                                                                 \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,fmin(*i1,*i2),IN2);                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "max", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN1->type)) {                                                                \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                            \
+                PS_ERRORTEXT_psMatrix_MAX_COMPLEX_SUPPORT);                                                  \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    } else {                                                                                                 \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,fmax(*i1,*i2),IN2);                                                    \
+    }                                                                                                        \
+} else {                                                                                                     \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                                \
+            PS_ERRORTEXT_psMatrix_OPERATION_UNSUPPORTED,                                                     \
+            OP);                                                                                             \
+    if (OUT != IN1 && OUT != IN2) {                                                                          \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+psPtr psBinaryOp(psPtr out, const psPtr in1, const char *op, const psPtr in2)
+{
+
+    psVector* input1 = (psVector* ) in1;
+    psVector* input2 = (psVector* ) in2;
+
+    #define psBinaryOp_EXIT { \
+                              if (out != in1 && out != in2) { \
+                              psFree(out); \
+                              } \
+                              return NULL; \
+                            }
+
+    PS_PTR_CHECK_NULL_GENERAL(input1, psBinaryOp_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(input2, psBinaryOp_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(op, psBinaryOp_EXIT);
+
+    PS_PTR_CHECK_TYPE_EQUAL_GENERAL(input1,input2, psBinaryOp_EXIT);
+
+    PS_PTR_CHECK_DIMEN_GENERAL_NOT(input1, PS_DIMEN_OTHER, psBinaryOp_EXIT);
+    PS_PTR_CHECK_DIMEN_GENERAL_NOT(input2, PS_DIMEN_OTHER, psBinaryOp_EXIT);
+
+    psType* psType1 = (psType*)in1;
+    psType* psType2 = (psType*)in2;
+    psDimen dim1 = psType1->dimen;
+    psDimen dim2 = psType2->dimen;
+    psElemType elType1 = psType1->type;
+    psElemType elType2 = psType2->type;
+
+    if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+        if (((psVector* ) in1)->n == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Vector contains zero elements");
+        }
+    } else if (dim1 == PS_DIMEN_IMAGE) {
+        if (((psImage* ) in1)->numCols == 0 || ((psImage* ) in1)->numRows == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image contains zero length row or cols");
+        }
+    }
+
+    if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+        if (((psVector* ) in2)->n == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Vector contains zero elements");
+        }
+    } else if (dim2 == PS_DIMEN_IMAGE) {
+        if (((psImage* ) in2)->numCols == 0 || ((psImage* ) in2)->numRows == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image contains zero length row or cols");
+        }
+    }
+
+    if (dim1 == PS_DIMEN_SCALAR) {
+        if ( out != NULL && ((psType*)out)->dimen != dim2) {
+            if (out != in1 && out != in2) {
+                psFree(out);
+            }
+            out = NULL;
+        }
+        if (dim2 == PS_DIMEN_SCALAR) {
+            if (out == NULL || ((psScalar*)out)->type.type != elType1) {
+                if (out != in1 && out != in2) {
+                    psFree(out);
+                }
+                out = psScalarAlloc(0.0,elType1);
+            }
+            BINARY_OP(SCALAR, SCALAR, out, psType1, op, psType2);       // scalar op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            out = psVectorRecycle(out,((psVector*)in2)->n,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(SCALAR, VECTOR, out, psType1, op, psType2);       // scalar op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            out = psImageRecycle(out, ((psImage* ) in2)->numCols, ((psImage* ) in2)->numRows,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(SCALAR, IMAGE, out, psType1, op, psType2);        // scalar op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+        if (dim2 == PS_DIMEN_SCALAR) {
+            out = psVectorRecycle(out,((psVector*)in1)->n,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(VECTOR, SCALAR, out, psType1, op, psType2);       // vector op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            out = psVectorRecycle(out,((psVector*)in2)->n,elType2);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(VECTOR, VECTOR, out, psType1, op, psType2);       // vector op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            out = psImageRecycle(out, ((psImage* ) in2)->numCols, ((psImage* ) in2)->numRows, elType2);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(VECTOR, IMAGE, out, psType1, op, psType2);        // vector op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else if (dim1 == PS_DIMEN_IMAGE) {
+        out = psImageRecycle(out, ((psImage*)in1)->numCols, ((psImage*)in1)->numRows, elType1);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+            return NULL;
+        }
+        if (dim2 == PS_DIMEN_SCALAR) {
+            BINARY_OP(IMAGE, SCALAR, out, psType1, op, psType2);        // image op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            BINARY_OP(IMAGE, VECTOR, out, psType1, op, psType2);        // image op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            BINARY_OP(IMAGE, IMAGE, out, psType1, op, psType2); // image op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                "in1",dim1);
+        psBinaryOp_EXIT;
+    }
+
+    // Automtically free psScalar types, since they are usually allocated in the argument list when this
+    // function is called, provided that the input is not the output.
+    if(psType1->dimen==PS_DIMEN_SCALAR && in1!=out) {
+        psFree(in1);
+    }
+
+    if(psType2->dimen==PS_DIMEN_SCALAR && in2!=out) {
+        psFree(in2);
+    }
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psBinaryOp.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psBinaryOp.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psBinaryOp.h	(revision 22271)
@@ -0,0 +1,67 @@
+
+/** @file  psBinaryOp.h
+ *
+ *  @brief Provides binary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSBINARY_OP_H
+#define PSBINARY_OP_H
+
+/// @addtogroup MatrixArithmetic
+/// @{
+
+/** Perform simple binary arithmetic with images or vectors
+ *
+ *  Performs addition, subtraction, multiplication, division, power, minumum, and maximum arithmetic
+ *  operations with images and vectors. Uses the form:
+ *
+ *      out = in1 op in2,
+ *
+ *      Where op is: "=", "+", "-", "*", "/", "^", "min", or "max"
+ *
+ *  This function only supports vector-vector or image-image operations.
+ *
+ *  @return  psType* : Pointer to either psImage or psVector.
+ */
+psType* psBinaryOp(
+    psPtr out,                         ///< Output type, either psImage or psVector.
+    const psPtr in1,   ///< First input, either psImage or psVector.
+    const char *op,   ///< Operator.
+    const psPtr in2   ///< Second input, either psImage or psVector.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psConstants.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psConstants.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psConstants.h	(revision 22271)
@@ -0,0 +1,641 @@
+/** @file  psConstants.h
+ *
+ *  This file will hold definitions of various constants as well as common
+ *  macros used throughout psLib.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.66 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: Add parenthesis around all arguments so that these macros can be
+ *       called with complex expressions.
+ *
+ *  XXX: All functions which use the PS_CHECK macros must be scrutinized so
+ *  that we ensure that an argument which is expected to be output is
+ *  psFree'ed before reurning NULL.
+ *
+ */
+
+#include<math.h> // for M_PI
+
+/*****************************************************************************
+These constants are used by various functions in the psLib.
+ *****************************************************************************/
+#define PS_DETERMINE_BRACKET_STEP_SIZE 0.10
+#define PS_MAX_LMM_ITERATIONS 100
+#define PS_MAX_MINIMIZE_ITERATIONS 100
+#define PS_LEFT_SPLINE_DERIV 0.0
+#define PS_RIGHT_SPLINE_DERIV 0.0
+/*****************************************************************************
+These are common mathimatical constants used by various functions in the psLib.
+ *****************************************************************************/
+#ifndef M_PI
+#define M_PI   3.1415926535897932384626433832795029  /* pi */
+#define M_PI_2 1.5707963267948966192313216916397514  /* pi/2 */
+#define M_PI_4 0.7853981633974483096156608458198757  /* pi/4 */
+#define M_1_PI 0.3183098861837906715377675267450287  /* 1/pi */
+#define M_2_PI 0.6366197723675813430755350534900574  /* 2/pi */
+#endif
+
+#define DEG_TO_RAD(DEGREES) ((DEGREES) * M_PI / 180.0)
+#define MIN_TO_RAD(MINUTES) ((MINUTES) * M_PI / (180.0 * 60.0))
+#define SEC_TO_RAD(SECONDS) ((SECONDS) * M_PI / (180.0 * 60.0 * 60.0))
+#define RAD_TO_DEG(RADIANS) ((RADIANS) * 180.0 / M_PI)
+#define RAD_TO_MIN(RADIANS) ((RADIANS) * 180.0 * 60.0 / M_PI)
+#define RAD_TO_SEC(RADIANS) ((RADIANS) * 180.0 * 60.0 * 60.0 / M_PI)
+
+/*****************************************************************************
+ 
+*****************************************************************************/
+
+#define PS_INT_CHECK_EQUALS(NAME1, NAME2, RVAL) \
+if (NAME1 == NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_NON_EQUALS(NAME1, NAME2, RVAL) \
+if (NAME1 != NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are not equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_NON_NEGATIVE(NAME, RVAL) \
+if (NAME < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is less than 0.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_POSITIVE(NAME, RVAL) \
+if (NAME < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0 or less.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_ZERO(NAME, RVAL) \
+if (NAME < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((int)NAME < LOWER || (int)NAME > UPPER) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %d, is out of range.  Must be between %d and %d.", \
+            #NAME,(int)NAME,LOWER,UPPER); \
+    return RVAL; \
+}
+
+// Produce an error if (NAME1 > NAME2)
+#define PS_INT_COMPARE(NAME1, NAME2, RVAL) \
+if (NAME1 > NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: (%s > %s) (%d %d).", \
+            #NAME1, #NAME2, NAME1, NAME2); \
+    return(RVAL); \
+}
+
+
+// Produce an error if ((NAME1 > NAME2)
+#define PS_FLOAT_COMPARE(NAME1, NAME2, RVAL) \
+if (NAME1 > NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: (%s > %s) (%f %f)", \
+            #NAME1, #NAME2, NAME1, NAME2); \
+    return(RVAL); \
+}
+
+#define PS_FLOAT_CHECK_NON_EQUAL(NAME1, NAME2, RVAL) \
+if (fabs(NAME2 - NAME1) < FLT_EPSILON) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_FLOAT_CHECK_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((NAME) < (LOWER) || (NAME) > (UPPER)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %f, is out of range.  Must be between %f and %f.", \
+            #NAME, NAME, LOWER, UPPER); \
+    return RVAL; \
+}
+
+/*****************************************************************************
+Macros which take a generic psLib type and determine if it is NULL, or has
+the wrong type.
+*****************************************************************************/
+#define PS_PTR_CHECK_NULL(NAME, RVAL) PS_PTR_CHECK_NULL_GENERAL(NAME, return RVAL)
+#define PS_PTR_CHECK_NULL_GENERAL(NAME, CLEANUP) \
+if (NAME == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: %s is NULL.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_PTR_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_PTR_CHECK_DIMEN(NAME, DIMEN, RVAL) PS_PTR_CHECK_DIMEN_GENERAL(NAME, DIMEN, return RVAL)
+#define PS_PTR_CHECK_DIMEN_GENERAL(NAME, DIMEN, CLEANUP) \
+if (NAME->type.dimen != DIMEN) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect dimensionality.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_PTR_CHECK_DIMEN_GENERAL_NOT(NAME, DIMEN, CLEANUP) \
+if (NAME->type.dimen == DIMEN) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect dimensionality.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+
+#define PS_PTR_CHECK_SIZE_EQUAL(PTR1, PTR2, RVAL) \
+if (PTR1->n != PTR2->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "ptr %s has size %d, ptr %s has size %d.", \
+            #PTR1, PTR1->n, #PTR2, PTR2->n); \
+    return(RVAL); \
+}
+
+#define PS_PTR_CHECK_TYPE_EQUAL(PTR1, PTR2, RVAL) PS_PTR_CHECK_TYPE_EQUAL_GENERAL(PTR1, PTR2, return RVAL)
+
+#define PS_PTR_CHECK_TYPE_EQUAL_GENERAL(PTR1, PTR2, CLEANUP) \
+if (PTR1->type.type != PTR2->type.type) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "ptr %s has type %d, ptr %s has type %d.", \
+            #PTR1, PTR1->type.type, #PTR2, PTR2->type.type); \
+    CLEANUP; \
+}
+
+
+/*****************************************************************************
+    PS_VECTOR macros:
+ *****************************************************************************/
+#define PS_VECTOR_CHECK_NULL(NAME, RVAL) PS_VECTOR_CHECK_NULL_GENERAL(NAME, return RVAL)
+#define PS_VECTOR_CHECK_NULL_GENERAL(NAME, CLEANUP) \
+if (NAME == NULL || NAME->data.U8 == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psVector %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_VECTOR_CHECK_EMPTY(NAME, RVAL) PS_VECTOR_CHECK_EMPTY_GENERAL(NAME, return RVAL)
+#define PS_VECTOR_CHECK_EMPTY_GENERAL(NAME, CLEANUP) \
+if (NAME->n < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psVector %s has no elements.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_VECTOR_CHECK_TYPE_F32_OR_F64(NAME, RVAL) \
+if ((NAME->type.type != PS_TYPE_F32) && (NAME->type.type != PS_TYPE_F64)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "psVector %s: bad type(%d)", \
+            #NAME, NAME->type.type); \
+    return(RVAL); \
+} \
+
+#define PS_VECTOR_CHECK_TYPE_S16_S32_F32(NAME, RVAL) \
+if ((NAME->type.type != PS_TYPE_S16) && (NAME->type.type != PS_TYPE_S32) && (NAME->type.type != PS_TYPE_F32)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "psVector %s: bad type(%d)", \
+            #NAME, NAME->type.type); \
+    return(RVAL); \
+} \
+
+#define PS_VECTOR_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psVector %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_CHECK_SIZE_EQUAL(VEC1, VEC2, RVAL) \
+if (VEC1->n != VEC2->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %d, psVector %s has size %d.", \
+            #VEC1, VEC1->n, #VEC2, VEC2->n); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_CHECK_TYPE_EQUAL(VEC1, VEC2, RVAL) \
+if (VEC1->type.type != VEC2->type.type) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %d, psVector %s has size %d.", \
+            #VEC1, VEC1->type.type, #VEC2, VEC2->type.type); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_F64_TO_F32(X64, X32) \
+psVector *X32 = psVectorAlloc(X64->n, PS_TYPE_F32); \
+for (int i=0;i<X64->n;i++) { \
+    X32->data.F32[i] = (float) X64->data.F64[i]; \
+} \
+
+#define PS_VECTOR_F32_TO_F64(X32, X64) \
+psVector *X64 = psVectorAlloc(X32->n, PS_TYPE_F64); \
+for (int i=0;i<X32->n;i++) { \
+    X64->data.F64[i] = (float) X32->data.F32[i]; \
+} \
+
+#define PS_VECTOR_PRINT_F32(NAME) \
+for (int my_i=0;my_i<NAME->n;my_i++) { \
+    printf("%s->data.F32[%d] is %f\n", #NAME, my_i, NAME->data.F32[my_i]); \
+} \
+printf("\n"); \
+
+
+#define PS_VECTOR_CONVERT_F64_TO_F32_STATIC(OLD, NEW_PTR32, NEW_STATIC32) \
+if (OLD->type.type == PS_TYPE_F32) { \
+    NEW_PTR32 = (psVector *) OLD; \
+} else if (OLD->type.type == PS_TYPE_F64) { \
+    NEW_STATIC32 = psVectorRecycle(NEW_STATIC32, OLD->n, PS_TYPE_F32); \
+    p_psMemSetPersistent(NEW_STATIC32, true); \
+    p_psMemSetPersistent(NEW_STATIC32->data.U8, true); \
+    for (i=0; i < OLD->n ; i++) { \
+        NEW_STATIC32->data.F32[i] = (float) OLD->data.F64[i]; \
+    } \
+    NEW_PTR32 = NEW_STATIC32; \
+} \
+
+#define PS_VECTOR_CONVERT_F32_TO_F64_STATIC(OLD, NEW_PTR64, NEW_STATIC64) \
+if (OLD->type.type == PS_TYPE_F64) { \
+    NEW_PTR64 = (psVector *) OLD; \
+} else if (OLD->type.type == PS_TYPE_F32) { \
+    NEW_STATIC64 = psVectorRecycle(NEW_STATIC64, OLD->n, PS_TYPE_F64); \
+    p_psMemSetPersistent(NEW_STATIC64, true); \
+    p_psMemSetPersistent(NEW_STATIC64->data.U8, true); \
+    for (i=0; i < OLD->n ; i++) { \
+        NEW_STATIC64->data.F64[i] = (double) OLD->data.F32[i]; \
+    } \
+    NEW_PTR64 = NEW_STATIC64; \
+} \
+
+#define PS_VECTOR_GEN_YERR_STATIC_F32(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F32); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F32[i] = 1.0; \
+} \
+
+#define PS_VECTOR_GEN_YERR_STATIC_F64(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F64); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F64[i] = 1.0; \
+} \
+
+#define PS_VECTOR_GEN_X_INDEX_STATIC_F32(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F32); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F32[i] = (float) i; \
+} \
+
+#define PS_VECTOR_GEN_X_INDEX_STATIC_F64(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F64); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F64[i] = (float) i; \
+} \
+
+#define PS_VECTOR_GEN_STATIC_RECYCLED(NAME, SIZE, TYPE) \
+static psVector *NAME = NULL; \
+NAME = psVectorRecycle(NAME, SIZE, TYPE); \
+p_psMemSetPersistent(NAME, true); \
+p_psMemSetPersistent(NAME->data.U8, true); \
+
+#define PS_VECTOR_DECLARE_ALLOC_STATIC(NAME, SIZE, TYPE) \
+static psVector *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psVectorAlloc(SIZE, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_VECTOR_SET_U8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U8[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_U16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U16[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_U32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U32[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_U64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U64[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S8[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S16[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S32[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S64[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_F64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.F64[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_F32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.F32[i] = VALUE; \
+}\
+
+
+/*****************************************************************************
+    PS_POLY macros:
+*****************************************************************************/
+#define PS_POLY_CHECK_NULL(NAME, RVAL) \
+if (NAME == NULL || NAME->coeff == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: polynomial %s or its coeffs is NULL.", \
+            #NAME); \
+    return(RVAL); \
+} \
+
+#define PS_POLY_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: polynomial %s has wrong type.", #NAME); \
+    return(RVAL); \
+} \
+
+// The following macros declare and allocate a static polynomial of the
+// specified order and type.
+
+#define PS_POLY_1D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial1D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial1DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+    p_psMemSetPersistent(NAME->coeff, true); \
+    p_psMemSetPersistent(NAME->coeffErr, true); \
+    p_psMemSetPersistent(NAME->mask, true); \
+} \
+
+#define PS_POLY_2D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial2D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial2DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_3D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial3D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial3DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_4D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial4D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial4DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_1D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial1D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial1DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_2D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial2D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial2DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_3D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial3D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial3DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_4D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial4D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial4DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+/*****************************************************************************
+    PS_IMAGE macros:
+*****************************************************************************/
+#define PS_IMAGE_CHECK_NULL(NAME, RVAL) PS_IMAGE_CHECK_NULL_GENERAL(NAME, return RVAL)
+#define PS_IMAGE_CHECK_NULL_GENERAL(NAME, CLEANUP) \
+if (NAME == NULL || NAME->data.V == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psImage %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_IMAGE_CHECK_EMPTY(NAME, RVAL) PS_IMAGE_CHECK_EMPTY_GENERAL(NAME, return RVAL)
+#define PS_IMAGE_CHECK_EMPTY_GENERAL(NAME, CLEANUP) \
+if (NAME->numCols < 1 || NAME->numRows < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImage %s has zero rows or columns (%dx%d).", \
+            #NAME, NAME->numCols, NAME->numRows); \
+    CLEANUP; \
+}
+
+#define PS_IMAGE_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psImage %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_CHECK_SIZE_EQUAL(NAME1, NAME2, RVAL) \
+if ((NAME1->numCols != NAME2->numCols) || \
+        (NAME1->numRows != NAME2->numRows)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImages %s and %s are not the same size.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_CHECK_SIZE(NAME1, NUM_COLS, NUM_ROWS, RVAL) \
+if ((NAME1->numCols != NUM_COLS) || \
+        (NAME1->numRows != NUM_ROWS)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImages %s is not the correct size.", \
+            #NAME1); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_PRINT_F32(NAME) \
+printf("======== printing %s ========\n", #NAME); \
+for (int i = 0 ; i < NAME->numRows ; i++) { \
+    for (int j = 0 ; j < NAME->numCols ; j++) { \
+        printf("%.2f ", NAME->data.F32[i][j]); \
+    } \
+    printf("\n"); \
+}\
+
+#define PS_IMAGE_SET_U8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U8[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_U16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U16[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_U32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U32[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_U64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U64[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S8[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S16[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S32[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S64[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_F32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.F32[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_F64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.F64[i][j] = (VALUE); \
+    } \
+}\
+
+/*****************************************************************************
+    PS_READOUT macros:
+*****************************************************************************/
+#define PS_READOUT_CHECK_NULL(NAME, RVAL) \
+if (NAME == NULL || NAME->image == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psReadout %s or its data is NULL.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_READOUT_CHECK_EMPTY(NAME, RVAL) \
+if (NAME->image->numCols < 1 || NAME->image->numRows < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psReadout %s or its data is NULL.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_READOUT_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->image->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psImage %s has incorrect type.", #NAME); \
+    return(RVAL); \
+}
+
+/*****************************************************************************
+    Misc. macros:
+ *****************************************************************************/
+#define PS_MAX(A, B) \
+(((A) > (B)) ? (A) : (B)) \
+
+#define PS_MIN(A, B) \
+(((A) < (B)) ? (A) : (B)) \
+
+#define PS_SQR(A) \
+((A) * (A)) \
+
+#ifdef DARWIN
+#define PS_SQRT_F32(A) ((float) sqrt(A))
+#else
+#define PS_SQRT_F32(A) (sqrtf(A))
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psDataManipErrors.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psDataManipErrors.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psDataManipErrors.dat	(revision 22271)
@@ -0,0 +1,55 @@
+#
+#  This file is used to generate psDataManipErrors.h content
+#
+#
+#  Format is:
+#  ERRORNAME(one word)    ERROR_TEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+#
+psVectorFFT_TYPE_NOT_F32_C32           Input psVector type, %s, is not supported. Valid data types are psF32 and psC32.
+psVectorFFT_REVERSE_NOT_COMPLEX        Input psVector (%s) is not complex.  Reverse FFT operation requires a complex input.
+psVectorFFT_FORWARD_NOT_REAL           Input psVector (%s) is not real.  Forward FFT operation requires a real input.
+psVectorFFT_FFTW_PLAN_NULL             Could not create a valid FFT plan to perform the transform.
+psVectorFFT_TYPE_UNSUPPORTED           Specified psVector type, %s, is not supported.
+psVectorFFT_REAL_IMAG_TYPE_MISMATCH    Real psVector type, %s, and imaginary psVector type, %s, must be the same.
+psVectorFFT_REAL_IMAG_SIZE_MISMATCH    Real psVector size, %d, and imaginary psVector size, %d, must be the same.
+psVectorFFT_NONREAL_NOTSUPPORTED       Input psVector type, %s, is required to be either psF32 or psF64.
+psVectorFFT_NONCOMPLEX_NOTSUPPORTED    Input psVector type, %s, is required to be either psC32 or psC64.
+psVectorFFT_DIRECTION_NOTSET           Must specify the direction as either PS_FFT_FORWARD or PS_FFT_REVERSE.
+#
+psStats_NOT_F32_F64                    Invalid data type, %s.  Only psF32 and psF64 data types are supported.
+psStats_VECTOR_TYPE_UNSUPPORTED        Input psVector type, %s, is not supported.
+psStats_YVAL_OUT_OF_RANGE              Specified yVal, %g, is not within y-range, %g to %g.
+psStats_ROBUST_QUARTILE_BINS_FAILED    Could not determine the robust lower/upper quartile bin numbers.
+psStats_STATS_FAILED                   Failed to calculate the specified statistic.
+psStats_STATS_SAMPLE_MEDIAN_SORT_PROBLEM            Failed to sort input data.
+psStats_STATS_VECTOR_BIN_DISECT_PROBLEM		Failed to determine the bin number of a data element.
+psStats_STATS_FIT_QUADRATIC_POLYNOMIAL_1D_FIT Failed to fit a 1-dimensional polynomial to the three specified data points.  Returning NAN.
+psStats_STATS_FIT_QUADRATIC_POLY_MEDIAN Failed to determine the median of the fitted polynomial.  Returning NAN.
+psStats_ROBUST_STATS_CLIPPED_STATS	Failed to determine clipped statistics.
+
+
+psStats_STATS_POLY_MEDIAN_OUT_OF_RANGE The requested y-value does not fall with the specified range of x-values.  Returning NAN.
+#
+psFunctions_INVALID_POLYNOMIAL_TYPE    Unknown polynomial type 0x%x found.  Evaluation failed.
+psFunctions_TYPE_NOT_SUPPORTED         Input psVector type, %s, is not supported.
+psFunctions_NOT_ENOUGH_DATAPOINTS      Given vector does not have enough data points for %d-order interpolation.
+#
+psRandom_UNKNOWN_RANDFOM_NUMBER_GENERATOR_TYPE Unknown Random Number Generator Type
+psRandom_NULL_RANDOM_VAR               Random variable is NULL.
+#
+psMatrix_COUNT_DIFFERS                 Number of elements inconsistent, %d vs %d.  Number of elements must match.
+psMatrix_IMAGE_SIZE_DIFFERS            Specified psImage dimensions differed, %dx%d vs %dx%d.
+psMatrix_TYPE_MISMATCH                 Specified data type, %s, is not supported.
+psMatrix_MIN_COMPLEX_SUPPORT           The minimum operation is not supported with complex data.
+psMatrix_MAX_COMPLEX_SUPPORT           The maximum operation is not supported with complex data.
+psMatrix_OPERATION_UNSUPPORTED         Specified operation, %s, is not supported.
+psMatrix_DIMEN_OTHER_FOUND             %s's dimensionality is PS_DIMEN_OTHER, which is  not allowed.
+psMatrix_OUTPUT_VECTOR_NOT_CREATED     Couldn't create a proper output psVector.
+psMatrix_OUTPUT_IMAGE_NOT_CREATED      Couldn't create a proper output psImage.
+psMatrix_DIMEN_INVALID                 Specified parameter, %s, has invalid dimensionality, %d.
+psMatrix_VECTOR_EMPTY                  Input psVector contains no elements.  No data to perform operation with.
+psMatrix_IMAGE_EMPTY                   Input psImage contains no pixels.  No data to perform operation with.
+psMatrix_TRANSPOSE_MISMATCH            Number of rows do not match number of columns.
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psDataManipErrors.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psDataManipErrors.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psDataManipErrors.h	(revision 22271)
@@ -0,0 +1,71 @@
+/** @file  psDataManipErrors.h
+ *
+ *  @brief Contains the error text for the data manipulation functions
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_DATAMANIP_ERRORS_H
+#define PS_DATAMANIP_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psDataManipErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psDataManipErrors.dat lines)
+ *     $2  The error text (rest of the line in psDataManipErrors.dat)
+ *     $n  The order of the source line in psDataManipErrors.dat (comments excluded)
+ *
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_psVectorFFT_TYPE_NOT_F32_C32 "Input psVector type, %s, is not supported. Valid data types are psF32 and psC32."
+#define PS_ERRORTEXT_psVectorFFT_REVERSE_NOT_COMPLEX "Input psVector (%s) is not complex.  Reverse FFT operation requires a complex input."
+#define PS_ERRORTEXT_psVectorFFT_FORWARD_NOT_REAL "Input psVector (%s) is not real.  Forward FFT operation requires a real input."
+#define PS_ERRORTEXT_psVectorFFT_FFTW_PLAN_NULL "Could not create a valid FFT plan to perform the transform."
+#define PS_ERRORTEXT_psVectorFFT_TYPE_UNSUPPORTED "Specified psVector type, %s, is not supported."
+#define PS_ERRORTEXT_psVectorFFT_REAL_IMAG_TYPE_MISMATCH "Real psVector type, %s, and imaginary psVector type, %s, must be the same."
+#define PS_ERRORTEXT_psVectorFFT_REAL_IMAG_SIZE_MISMATCH "Real psVector size, %d, and imaginary psVector size, %d, must be the same."
+#define PS_ERRORTEXT_psVectorFFT_NONREAL_NOTSUPPORTED "Input psVector type, %s, is required to be either psF32 or psF64."
+#define PS_ERRORTEXT_psVectorFFT_NONCOMPLEX_NOTSUPPORTED "Input psVector type, %s, is required to be either psC32 or psC64."
+#define PS_ERRORTEXT_psVectorFFT_DIRECTION_NOTSET "Must specify the direction as either PS_FFT_FORWARD or PS_FFT_REVERSE."
+#define PS_ERRORTEXT_psStats_NOT_F32_F64 "Invalid data type, %s.  Only psF32 and psF64 data types are supported."
+#define PS_ERRORTEXT_psStats_VECTOR_TYPE_UNSUPPORTED "Input psVector type, %s, is not supported."
+#define PS_ERRORTEXT_psStats_YVAL_OUT_OF_RANGE "Specified yVal, %g, is not within y-range, %g to %g."
+#define PS_ERRORTEXT_psStats_ROBUST_QUARTILE_BINS_FAILED "Could not determine the robust lower/upper quartile bin numbers."
+#define PS_ERRORTEXT_psStats_STATS_FAILED "Failed to calculate the specified statistic."
+#define PS_ERRORTEXT_psStats_STATS_SAMPLE_MEDIAN_SORT_PROBLEM "Failed to sort input data."
+#define PS_ERRORTEXT_psStats_STATS_VECTOR_BIN_DISECT_PROBLEM "Failed to determine the bin number of a data element."
+#define PS_ERRORTEXT_psStats_STATS_FIT_QUADRATIC_POLYNOMIAL_1D_FIT "Failed to fit a 1-dimensional polynomial to the three specified data points.  Returning NAN."
+#define PS_ERRORTEXT_psStats_STATS_FIT_QUADRATIC_POLY_MEDIAN "Failed to determine the median of the fitted polynomial.  Returning NAN."
+#define PS_ERRORTEXT_psStats_ROBUST_STATS_CLIPPED_STATS "Failed to determine clipped statistics."
+#define PS_ERRORTEXT_psStats_STATS_POLY_MEDIAN_OUT_OF_RANGE "The requested y-value does not fall with the specified range of x-values.  Returning NAN."
+#define PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE "Unknown polynomial type 0x%x found.  Evaluation failed."
+#define PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED "Input psVector type, %s, is not supported."
+#define PS_ERRORTEXT_psFunctions_NOT_ENOUGH_DATAPOINTS "Given vector does not have enough data points for %d-order interpolation."
+#define PS_ERRORTEXT_psRandom_UNKNOWN_RANDFOM_NUMBER_GENERATOR_TYPE "Unknown Random Number Generator Type"
+#define PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR "Random variable is NULL."
+#define PS_ERRORTEXT_psMatrix_COUNT_DIFFERS "Number of elements inconsistent, %d vs %d.  Number of elements must match."
+#define PS_ERRORTEXT_psMatrix_IMAGE_SIZE_DIFFERS "Specified psImage dimensions differed, %dx%d vs %dx%d."
+#define PS_ERRORTEXT_psMatrix_TYPE_MISMATCH "Specified data type, %s, is not supported."
+#define PS_ERRORTEXT_psMatrix_MIN_COMPLEX_SUPPORT "The minimum operation is not supported with complex data."
+#define PS_ERRORTEXT_psMatrix_MAX_COMPLEX_SUPPORT "The maximum operation is not supported with complex data."
+#define PS_ERRORTEXT_psMatrix_OPERATION_UNSUPPORTED "Specified operation, %s, is not supported."
+#define PS_ERRORTEXT_psMatrix_DIMEN_OTHER_FOUND "%s's dimensionality is PS_DIMEN_OTHER, which is  not allowed."
+#define PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED "Couldn't create a proper output psVector."
+#define PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED "Couldn't create a proper output psImage."
+#define PS_ERRORTEXT_psMatrix_DIMEN_INVALID "Specified parameter, %s, has invalid dimensionality, %d."
+#define PS_ERRORTEXT_psMatrix_VECTOR_EMPTY "Input psVector contains no elements.  No data to perform operation with."
+#define PS_ERRORTEXT_psMatrix_IMAGE_EMPTY "Input psImage contains no pixels.  No data to perform operation with."
+#define PS_ERRORTEXT_psMatrix_TRANSPOSE_MISMATCH "Number of rows do not match number of columns."
+//~End
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psFunctions.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psFunctions.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psFunctions.c	(revision 22271)
@@ -0,0 +1,2179 @@
+/** @file  psFunctions.c
+ *
+ *  @brief Contains basic function allocation, deallocation, and evaluation
+ *         routines.
+ *
+ *  This file will hold the functions for allocated, freeing, and evaluating
+ *  polynomials.  It also contains a Gaussian functions.
+ *
+ *  @version $Revision: 1.101 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: What happens if the polyEval functions are called with data of the wrong
+ *       type?
+ *  XXX: Should the "coeffErr[]" be used as well?  Bug ???.  Ignore coeffErr
+ *
+ *  XXX: In the various polyAlloc(n) functions, n is really the order of the
+ *  polynomial plus 1.  To create a 2nd-order polynomial, n == 3.
+ *
+ *  XXX: potential bug: for a multi-dimensional polynomial with order (m, n)
+ *  the functions in this file currently do not ignore many of the
+ *  coefficients in the coeff matrix that ...
+ *
+ */
+/*****************************************************************************/
+/*  INCLUDE FILES                                                            */
+/*****************************************************************************/
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psFunctions.h"
+#include "psConstants.h"
+
+#include "psDataManipErrors.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+static void polynomial1DFree(psPolynomial1D* myPoly);
+static void polynomial2DFree(psPolynomial2D* myPoly);
+static void polynomial3DFree(psPolynomial3D* myPoly);
+static void polynomial4DFree(psPolynomial4D* myPoly);
+static void dPolynomial1DFree(psDPolynomial1D* myPoly);
+static void dPolynomial2DFree(psDPolynomial2D* myPoly);
+static void dPolynomial3DFree(psDPolynomial3D* myPoly);
+static void dPolynomial4DFree(psDPolynomial4D* myPoly);
+static void spline1DFree(psSpline1D *tmpSpline);
+static psS32 vectorBinDisectF32(psF32 *bins,psS32 numBins,psF32 x);
+static psS32 vectorBinDisectS32(psS32 *bins,psS32 numBins,psS32 x);
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+static void spline1DFree(psSpline1D *tmpSpline)
+{
+    psS32 i;
+
+    if (tmpSpline == NULL) {
+        return;
+    }
+
+    if (tmpSpline->spline != NULL) {
+        for (i=0;i<tmpSpline->n;i++) {
+            psFree((tmpSpline->spline)[i]);
+        }
+        psFree(tmpSpline->spline);
+    }
+
+    if (tmpSpline->p_psDeriv2 != NULL) {
+        psFree(tmpSpline->p_psDeriv2);
+    }
+    psFree(tmpSpline->knots);
+
+    return;
+}
+
+static void polynomial1DFree(psPolynomial1D* myPoly)
+{
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial2DFree(psPolynomial2D* myPoly)
+{
+    psS32 x = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial3DFree(psPolynomial3D* myPoly)
+{
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        for (y = 0; y < myPoly->nY; y++) {
+            psFree(myPoly->coeff[x][y]);
+            psFree(myPoly->coeffErr[x][y]);
+            psFree(myPoly->mask[x][y]);
+        }
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial4DFree(psPolynomial4D* myPoly)
+{
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (w = 0; w < myPoly->nW; w++) {
+        for (x = 0; x < myPoly->nX; x++) {
+            for (y = 0; y < myPoly->nY; y++) {
+                psFree(myPoly->coeff[w][x][y]);
+                psFree(myPoly->coeffErr[w][x][y]);
+                psFree(myPoly->mask[w][x][y]);
+            }
+            psFree(myPoly->coeff[w][x]);
+            psFree(myPoly->coeffErr[w][x]);
+            psFree(myPoly->mask[w][x]);
+        }
+        psFree(myPoly->coeff[w]);
+        psFree(myPoly->coeffErr[w]);
+        psFree(myPoly->mask[w]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial1DFree(psDPolynomial1D* myPoly)
+{
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial2DFree(psDPolynomial2D* myPoly)
+{
+    //printf("dPolynomial2DFree(): HMMM: myPoly->nX is %d\n", myPoly->nX);
+    //printf("dPolynomial2DFree(): HMMM: myPoly->nY is %d\n", myPoly->nY);
+    for (psS32 x = 0; x < myPoly->nX; x++) {
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial3DFree(psDPolynomial3D* myPoly)
+{
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        for (y = 0; y < myPoly->nY; y++) {
+            psFree(myPoly->coeff[x][y]);
+            psFree(myPoly->coeffErr[x][y]);
+            psFree(myPoly->mask[x][y]);
+        }
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial4DFree(psDPolynomial4D* myPoly)
+{
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (w = 0; w < myPoly->nW; w++) {
+        for (x = 0; x < myPoly->nX; x++) {
+            for (y = 0; y < myPoly->nY; y++) {
+                psFree(myPoly->coeff[w][x][y]);
+                psFree(myPoly->coeffErr[w][x][y]);
+                psFree(myPoly->mask[w][x][y]);
+            }
+            psFree(myPoly->coeff[w][x]);
+            psFree(myPoly->coeffErr[w][x]);
+            psFree(myPoly->mask[w][x]);
+        }
+        psFree(myPoly->coeff[w]);
+        psFree(myPoly->coeffErr[w]);
+        psFree(myPoly->mask[w]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+/*****************************************************************************
+createChebyshevPolys(n): this routine takes as input the required order n,
+and returns as output as a pointer to an array of n psPolynomial1D
+structures, corresponding to the first n Chebyshev polynomials.
+ 
+XXX: The output should be static since the Chebyshev polynomials might be
+used frequently and the data structure created here does not contain the
+outer coefficients of the Chebyshev polynomials.
+ *****************************************************************************/
+static psPolynomial1D **createChebyshevPolys(psS32 maxChebyPoly)
+{
+    PS_INT_CHECK_NON_NEGATIVE(maxChebyPoly, NULL);
+
+    psPolynomial1D **chebPolys = NULL;
+
+    chebPolys = (psPolynomial1D **) psAlloc(maxChebyPoly * sizeof(psPolynomial1D *));
+    for (psS32 i = 0; i < maxChebyPoly; i++) {
+        chebPolys[i] = psPolynomial1DAlloc(i + 1, PS_POLYNOMIAL_ORD);
+    }
+
+    // Create the Chebyshev polynomials.
+    // Polynomial i has i-th order.
+    chebPolys[0]->coeff[0] = 1;
+
+    // XXX: Bug 296
+    if (maxChebyPoly > 1) {
+        chebPolys[1]->coeff[1] = 1;
+    }
+    for (psS32 i = 2; i < maxChebyPoly; i++) {
+        for (psS32 j = 0; j < chebPolys[i - 1]->n; j++) {
+            chebPolys[i]->coeff[j + 1] = 2 * chebPolys[i - 1]->coeff[j];
+        }
+        for (psS32 j = 0; j < chebPolys[i - 2]->n; j++) {
+            chebPolys[i]->coeff[j] -= chebPolys[i - 2]->coeff[j];
+        }
+    }
+
+    return (chebPolys);
+}
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF32 ordPolynomial1DEval(psF32 x, const psPolynomial1D* myPoly)
+{
+    psS32 loop_x = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+
+    psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+            "---- Calling ordPolynomial1DEval(%f)\n", x);
+    psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+            "Polynomial order is %d\n", myPoly->n);
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+                "Polynomial coeff[%d] is %f\n", loop_x, myPoly->coeff[loop_x]);
+    }
+
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        if (myPoly->mask[loop_x] == 0) {
+            psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 10,
+                    "polysum+= sum*coeff [%f+= (%f * %f)\n", polySum, xSum, myPoly->coeff[loop_x]);
+            polySum += xSum * myPoly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+// XXX: We assume that x is scaled between -1.0 and 1.0;
+static psF32 chebPolynomial1DEval(psF32 x, const psPolynomial1D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    psVector *d;
+    psS32 n;
+    psS32 i;
+    psF32 tmp;
+
+    n = myPoly->n;
+    d = psVectorAlloc(n, PS_TYPE_F32);
+    if(myPoly->mask[n-1] == 0) {
+        d->data.F32[n-1] = myPoly->coeff[n-1];
+    } else {
+        d->data.F32[n-1] = 0.0;
+    }
+    d->data.F32[n-2] = (2.0 * x * d->data.F32[n-1]);
+    if(myPoly->mask[n-2] == 0) {
+        d->data.F32[n-2] += myPoly->coeff[n-2];
+    }
+    for (i=n-3;i>=1;i--) {
+        d->data.F32[i] = (2.0 * x * d->data.F32[i+1]) -
+                         (d->data.F32[i+2]);
+        if(myPoly->mask[i] == 0) {
+            d->data.F32[i] += myPoly->coeff[i];
+        }
+    }
+
+    tmp = (x * d->data.F32[1]) -
+          (d->data.F32[2]);
+    if(myPoly->mask[0] == 0) {
+        tmp += (0.5 * myPoly->coeff[0]);
+    }
+    psFree(d);
+    return(tmp);
+
+    /*
+
+    psS32 n;
+    psS32 i;
+    psF32 tmp;
+    psPolynomial1D **chebPolys = NULL;
+
+    n = myPoly->n;
+    chebPolys = createChebyshevPolys(n);
+
+    tmp = 0.0;
+    for (i=0;i<myPoly->n;i++) {
+        tmp+= (myPoly->coeff[i] * psPolynomial1DEval(x, chebPolys[i]));
+    }
+    tmp-= (myPoly->coeff[0]/2.0);
+
+
+    return(tmp);
+    */
+}
+
+static psF32 ordPolynomial2DEval(psF32 x,
+                                 psF32 y,
+                                 const psPolynomial2D* myPoly)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += ySum * myPoly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial2DEval(psF32 x, psF32 y, const psPolynomial2D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += myPoly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF32 ordPolynomial3DEval(psF32 x, psF32 y, psF32 z, const psPolynomial3D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+    psF32 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += zSum * myPoly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial3DEval(psF32 x, psF32 y, psF32 z, const psPolynomial3D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += myPoly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF32 ordPolynomial4DEval(psF32 w, psF32 x, psF32 y, psF32 z, const psPolynomial4D* myPoly)
+{
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF32 polySum = 0.0;
+    psF32 wSum = 1.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+    psF32 zSum = 1.0;
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        xSum = wSum;
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            ySum = xSum;
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                zSum = ySum;
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += zSum * myPoly->coeff[loop_w][loop_x][loop_y][loop_z];
+                    }
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+        wSum *= w;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial4DEval(psF32 w, psF32 x, psF32 y, psF32 z, const psPolynomial4D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(w, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nW;
+    if (myPoly->nX > maxChebyPoly) {
+        maxChebyPoly = myPoly->nX;
+    }
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += myPoly->coeff[loop_w][loop_x][loop_y][loop_z] *
+                                   psPolynomial1DEval(chebPolys[loop_w], w) *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF64 dOrdPolynomial1DEval(psF64 x, const psDPolynomial1D* myPoly)
+{
+    psS32 loop_x = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        if (myPoly->mask[loop_x] == 0) {
+            polySum += xSum * myPoly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+static psF64 dChebPolynomial1DEval(psF64 x, const psDPolynomial1D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    psVector *d;
+    psS32 n;
+    psS32 i;
+    psF64 tmp;
+
+    n = myPoly->n;
+    d = psVectorAlloc(n, PS_TYPE_F64);
+    if(myPoly->mask[n-1] == 0) {
+        d->data.F64[n-1] = myPoly->coeff[n-1];
+    } else {
+        d->data.F64[n-1] = 0.0;
+    }
+    d->data.F64[n-2] = (2.0 * x * d->data.F64[n-1]);
+    if(myPoly->mask[n-2] == 0) {
+        d->data.F64[n-2] += myPoly->coeff[n-2];
+    }
+    for (i=n-3;i>=1;i--) {
+        d->data.F64[i] = (2.0 * x * d->data.F64[i+1]) -
+                         (d->data.F64[i+2]);
+        if(myPoly->mask[i] == 0) {
+            d->data.F64[i] += myPoly->coeff[i];
+        }
+    }
+
+    tmp = (x * d->data.F64[1]) -
+          (d->data.F64[2]);
+    if(myPoly->mask[0] == 0) {
+        tmp += (0.5 * myPoly->coeff[0]);
+    }
+
+    psFree(d);
+    return(tmp);
+}
+
+static psF64 dOrdPolynomial2DEval(psF64 x, psF64 y, const psDPolynomial2D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += ySum * myPoly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial2DEval(psF64 x, psF64 y, const psDPolynomial2D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += myPoly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 dOrdPolynomial3DEval(psF64 x, psF64 y, psF64 z, const psDPolynomial3D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += zSum * myPoly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial3DEval(psF64 x, psF64 y, psF64 z, const psDPolynomial3D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += myPoly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 dOrdPolynomial4DEval(psF64 w, psF64 x, psF64 y, psF64 z, const psDPolynomial4D* myPoly)
+{
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 wSum = 1.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        xSum = wSum;
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            ySum = xSum;
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                zSum = ySum;
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += zSum * myPoly->coeff[loop_w][loop_x][loop_y][loop_z];
+                    }
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+        wSum *= w;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial4DEval(psF64 w, psF64 x, psF64 y, psF64 z, const psDPolynomial4D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(w, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nW;
+    if (myPoly->nX > maxChebyPoly) {
+        maxChebyPoly = myPoly->nX;
+    }
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += myPoly->coeff[loop_w][loop_x][loop_y][loop_z] *
+                                   psPolynomial1DEval(chebPolys[loop_w], w) *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+
+/*****************************************************************************
+fullInterpolate1DF32(): This routine will take as input n-element floating
+point arrays domain and range, and the x value, assumed to lie with the
+domain vector.  It produces as output the (n-1)-order LaGrange interpolated
+value of x.
+ 
+XXX: do we error check for non-distinct domain values?
+ *****************************************************************************/
+#define FUNC_MACRO_FULL_INTERPOLATE_1D(TYPE) \
+static psF32 fullInterpolate1D##TYPE(ps##TYPE *domain, \
+                                     ps##TYPE *range, \
+                                     psS32 n, \
+                                     ps##TYPE x) \
+{ \
+    \
+    psS32 i; \
+    psS32 m; \
+    static psVector *p = NULL; \
+    p = psVectorRecycle(p, n, PS_TYPE_##TYPE); \
+    p_psMemSetPersistent(p, true); \
+    p_psMemSetPersistent(p->data.TYPE, true); \
+    \
+    psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 4, \
+            "---- fullInterpolate1D##TYPE() begin (%d-order at x=%f) (%d data points)----\n", n-1, x, n); \
+    \
+    for (i=0;i<n;i++) { \
+        psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                "domain/range is (%f %f)\n", domain[i], range[i]); \
+    } \
+    \
+    for (i=0;i<n;i++) { \
+        p->data.TYPE[i] = range[i]; \
+        psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                "p->data.TYPE[%d] is %f\n", i, p->data.TYPE[i]); \
+        \
+    } \
+    \
+    /* From NR, during each iteration of the m loop, we are computing the \
+       p_{i ... i+m} terms. \
+    */ \
+    for (m=1;m<n;m++) { \
+        for (i=0;i<n-m;i++) { \
+            /* From NR: we are computing P_{i ... i+m} \
+             */ \
+            p->data.TYPE[i] = (((x-domain[i+m]) * p->data.TYPE[i]) + \
+                               ((domain[i]-x) * p->data.TYPE[i+1])) / \
+                              (domain[i] - domain[i+m]); \
+            /*printf("((%f-%f * %f) + (%f-%f * %f)) / (%f - %f)\n", x, domain[i+m], p->data.TYPE[i], domain[i], x, p->data.TYPE[i+1], domain[i], domain[i+m]); \
+             */ \
+            psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                    "p->data.TYPE[%d] is %f\n", i, p->data.TYPE[i]); \
+        } \
+    } \
+    psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 4, \
+            "---- fullInterpolate1D##TYPE() end ----\n"); \
+    \
+    return(p->data.TYPE[0]); \
+} \
+
+/*
+FUNC_MACRO_FULL_INTERPOLATE_1D(U8)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U16)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U32)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U64)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S8)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S16)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S32)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S64)
+FUNC_MACRO_FULL_INTERPOLATE_1D(F64)
+*/
+FUNC_MACRO_FULL_INTERPOLATE_1D(F32)
+
+
+/*****************************************************************************
+interpolate1DF32(): this is the base 1-D flat memory routine to perform
+LaGrange interpolation.
+ *****************************************************************************/
+static psF32 interpolate1DF32(psF32 *domain,
+                              psF32 *range,
+                              psS32 n,
+                              psS32 order,
+                              psF32 x)
+{
+    PS_PTR_CHECK_NULL(domain, NAN)
+    PS_PTR_CHECK_NULL(range, NAN)
+    // XXX: Check valid values for n, order, and x?
+
+    psS32 binNum;
+    psS32 numIntPoints = order+1;
+    psS32 origin;
+
+    psTrace(".psLib.dataManip.psFunctions.interpolate1DF32", 4,
+            "---- interpolate1DF32() begin ----\n");
+
+    binNum = vectorBinDisectF32(domain, n, x);
+
+    if (0 == numIntPoints%2) {
+        origin = binNum - ((numIntPoints/2) - 1);
+    } else {
+        origin = binNum - (numIntPoints/2);
+        if ((x-domain[binNum]) > (domain[binNum+1]-x)) {
+            // x is closer to binNum+1.
+            origin = 1 + (binNum - (numIntPoints/2));
+        }
+    }
+    if (origin < 0) {
+        origin = 0;
+    }
+    if ((origin + numIntPoints) > n) {
+        origin = n - numIntPoints;
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.interpolate1DF32", 4,
+            "---- interpolate1DF32() end ----\n");
+    return(fullInterpolate1DF32(&domain[origin], &range[origin], order+1, x));
+}
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - PUBLIC                                         */
+/*****************************************************************************/
+
+/*****************************************************************************
+    Evaluate a non-normalized Gaussian with the given mean and sigma at the
+    given coordianate.  Note that this is not a Gaussian deviate.  The
+    evaluated Gaussian is: \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f]
+ *****************************************************************************/
+psF32 psGaussian(psF32 x, psF32 mean, psF32 sigma, psBool normal)
+{
+    psF32 tmp = 1.0;
+
+    psTrace(".psLib.dataManip.psFunctions.psGaussian", 4,
+            "---- psGaussian() begin ----\n");
+
+    if (normal == true) {
+        tmp = 1.0 / PS_SQRT_F32(2.0 * M_PI * (sigma * sigma));
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.psGaussian", 4,
+            "---- psGaussian() end ----\n");
+    return(tmp * exp(-((x - mean) * (x - mean)) / (2.0 * sigma * sigma)));
+}
+
+/*****************************************************************************
+    p_psGaussianDev()
+ This private routine (formerly a psLib API routine) creates a psVector of the
+ specified size and type F32 and fills it with a random Gaussian distribution
+ of numbers with the specified mean and sigma.  This routine makes use of the
+ GSL routines for generating both uniformly distributed numbers and the
+ Gaussian distribution as well.
+ 
+XXX: There is no way to seed the random generator.
+ *****************************************************************************/
+psVector* p_psGaussianDev(psF32 mean, psF32 sigma, psS32 Npts)
+{
+    PS_INT_CHECK_NON_NEGATIVE(Npts, NULL);
+
+    psVector* gauss = NULL;
+    const gsl_rng_type *T = NULL;
+    gsl_rng *r = NULL;
+    psS32 i = 0;
+
+
+    gauss = psVectorAlloc(Npts, PS_TYPE_F32);
+    gauss->n = Npts;
+    gsl_rng_env_setup();
+    T = gsl_rng_default;
+    r = gsl_rng_alloc(T);
+
+    for (i = 0; i < Npts; i++) {
+        gauss->data.F32[i] = mean + gsl_ran_gaussian(r, sigma);
+    }
+
+    // XXX: Should I free r, T as well?  This is a memory leak.
+    return(gauss);
+}
+
+/*****************************************************************************
+    This routine must allocate memory for the polynomial structures.
+ *****************************************************************************/
+psPolynomial1D* psPolynomial1DAlloc(psS32 n,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+
+    psS32 i = 0;
+    psPolynomial1D* newPoly = NULL;
+
+    newPoly = (psPolynomial1D* ) psAlloc(sizeof(psPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->n = n;
+    newPoly->coeff = (psF32 *)psAlloc(n * sizeof(psF32));
+    newPoly->coeffErr = (psF32 *)psAlloc(n * sizeof(psF32));
+    newPoly->mask = (psU8 *)psAlloc(n * sizeof(psU8));
+    for (i = 0; i < n; i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->mask[i] = 0;
+    }
+
+    return(newPoly);
+}
+
+psPolynomial2D* psPolynomial2DAlloc(psS32 nX, psS32 nY,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psPolynomial2D* newPoly = NULL;
+
+    newPoly = (psPolynomial2D* ) psAlloc(sizeof(psPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = (psF32 **)psAlloc(nX * sizeof(psF32 *));
+    newPoly->coeffErr = (psF32 **)psAlloc(nX * sizeof(psF32 *));
+    newPoly->mask = (psU8 **)psAlloc(nX * sizeof(psU8 *));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF32 *)psAlloc(nY * sizeof(psF32));
+        newPoly->coeffErr[x] = (psF32 *)psAlloc(nY * sizeof(psF32));
+        newPoly->mask[x] = (psU8 *)psAlloc(nY * sizeof(psU8));
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->mask[x][y] = 0;
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial3D* psPolynomial3DAlloc(psS32 nX, psS32 nY, psS32 nZ,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psPolynomial3D* newPoly = NULL;
+
+    newPoly = (psPolynomial3D* ) psAlloc(sizeof(psPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+    newPoly->coeffErr = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+    newPoly->mask = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+        newPoly->coeffErr[x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+        newPoly->mask[x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+            newPoly->coeffErr[x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+            newPoly->mask[x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+        }
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            for (z = 0; z < nZ; z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->mask[x][y][z] = 0;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial4D* psPolynomial4DAlloc(psS32 nW, psS32 nX, psS32 nY, psS32 nZ,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nW, NULL);
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psPolynomial4D* newPoly = NULL;
+
+    newPoly = (psPolynomial4D* ) psAlloc(sizeof(psPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nW = nW;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF32 ****)psAlloc(nW * sizeof(psF32 ***));
+    newPoly->coeffErr = (psF32 ****)psAlloc(nW * sizeof(psF32 ***));
+    newPoly->mask = (psU8 ****)psAlloc(nW * sizeof(psU8 ***));
+    for (w = 0; w < nW; w++) {
+        newPoly->coeff[w] = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+        newPoly->coeffErr[w] = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+        newPoly->mask[w] = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+        for (x = 0; x < nX; x++) {
+            newPoly->coeff[w][x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+            newPoly->coeffErr[w][x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+            newPoly->mask[w][x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+            for (y = 0; y < nY; y++) {
+                newPoly->coeff[w][x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+                newPoly->coeffErr[w][x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+                newPoly->mask[w][x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+            }
+        }
+    }
+    for (w = 0; w < nW; w++) {
+        for (x = 0; x < nX; x++) {
+            for (y = 0; y < nY; y++) {
+                for (z = 0; z < nZ; z++) {
+                    newPoly->coeff[w][x][y][z] = 0.0;
+                    newPoly->coeffErr[w][x][y][z] = 0.0;
+                    newPoly->mask[w][x][y][z] = 0;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psF32 psPolynomial1DEval(const psPolynomial1D* myPoly, psF32 x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial1DEval(x, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial1DEval(x, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial1DEvalVector(const psPolynomial1D *myPoly,
+                                   const psVector *x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+
+    tmp = psVectorAlloc(x->n, PS_TYPE_F32);
+    for (psS32 i=0;i<x->n;i++) {
+        tmp->data.F32[i] = psPolynomial1DEval(myPoly, x->data.F32[i]);
+    }
+
+    return(tmp);
+}
+
+psF32 psPolynomial2DEval(const psPolynomial2D* myPoly, psF32 x, psF32 y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial2DEval(x, y, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial2DEval(x, y, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial2DEvalVector(const psPolynomial2D *myPoly,
+                                   const psVector *x,
+                                   const psVector *y)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the length of the output vector to by the minimum of the x,y vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    // Create output vector to return
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate the polynomial at the specified points
+    for (psS32 i=0; i<vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial2DEval(myPoly,x->data.F32[i],y->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF32 psPolynomial3DEval(const psPolynomial3D* myPoly, psF32 x, psF32 y, psF32 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial3DEval(x, y, z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial3DEval(x, y, z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial3DEvalVector(const psPolynomial3D *myPoly,
+                                   const psVector *x,
+                                   const psVector *y,
+                                   const psVector *z)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the length of output vector from min of the input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial3DEval(myPoly,
+                                              x->data.F32[i],
+                                              y->data.F32[i],
+                                              z->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF32 psPolynomial4DEval(const psPolynomial4D* myPoly, psF32 w, psF32 x, psF32 y, psF32 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial4DEval(w,x,y,z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial4DEval(w,x,y,z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial4DEvalVector(const psPolynomial4D *myPoly,
+                                   const psVector *w,
+                                   const psVector *x,
+                                   const psVector *y,
+                                   const psVector *z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(w, NULL);
+    PS_VECTOR_CHECK_TYPE(w, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=w->n;
+
+    // Determine output vector size from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (x->n < vecLen) {
+        vecLen = x->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial4DEval(myPoly,
+                                              w->data.F32[i],
+                                              x->data.F32[i],
+                                              y->data.F32[i],
+                                              z->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+psDPolynomial1D* psDPolynomial1DAlloc(psS32 n,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+
+    psS32 i = 0;
+    psDPolynomial1D* newPoly = NULL;
+
+    newPoly = (psDPolynomial1D* ) psAlloc(sizeof(psDPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->n = n;
+    newPoly->coeff = (psF64 *)psAlloc(n * sizeof(psF64));
+    newPoly->coeffErr = (psF64 *)psAlloc(n * sizeof(psF64));
+    newPoly->mask = (psU8 *)psAlloc(n * sizeof(psU8));
+    for (i = 0; i < n; i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->mask[i] = 0;
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial2D* psDPolynomial2DAlloc(psS32 nX, psS32 nY,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psDPolynomial2D* newPoly = NULL;
+
+    newPoly = (psDPolynomial2D* ) psAlloc(sizeof(psDPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = (psF64 **)psAlloc(nX * sizeof(psF64 *));
+    newPoly->coeffErr = (psF64 **)psAlloc(nX * sizeof(psF64 *));
+    newPoly->mask = (psU8 **)psAlloc(nX * sizeof(psU8 *));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF64 *)psAlloc(nY * sizeof(psF64));
+        newPoly->coeffErr[x] = (psF64 *)psAlloc(nY * sizeof(psF64));
+        newPoly->mask[x] = (psU8 *)psAlloc(nY * sizeof(psU8));
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->mask[x][y] = 0;
+        }
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial3D* psDPolynomial3DAlloc(psS32 nX, psS32 nY, psS32 nZ,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psDPolynomial3D* newPoly = NULL;
+
+    newPoly = (psDPolynomial3D* ) psAlloc(sizeof(psDPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+    newPoly->coeffErr = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+    newPoly->mask = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+        newPoly->coeffErr[x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+        newPoly->mask[x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+            newPoly->coeffErr[x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+            newPoly->mask[x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+        }
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            for (z = 0; z < nZ; z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->mask[x][y][z] = 0;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial4D* psDPolynomial4DAlloc(psS32 nW, psS32 nX, psS32 nY, psS32 nZ,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nW, NULL);
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psDPolynomial4D* newPoly = NULL;
+
+    newPoly = (psDPolynomial4D* ) psAlloc(sizeof(psDPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nW = nW;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF64 ****)psAlloc(nW * sizeof(psF64 ***));
+    newPoly->coeffErr = (psF64 ****)psAlloc(nW * sizeof(psF64 ***));
+    newPoly->mask = (psU8 ****)psAlloc(nW * sizeof(psU8 ***));
+    for (w = 0; w < nW; w++) {
+        newPoly->coeff[w] = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+        newPoly->coeffErr[w] = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+        newPoly->mask[w] = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+        for (x = 0; x < nX; x++) {
+            newPoly->coeff[w][x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+            newPoly->coeffErr[w][x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+            newPoly->mask[w][x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+            for (y = 0; y < nY; y++) {
+                newPoly->coeff[w][x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+                newPoly->coeffErr[w][x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+                newPoly->mask[w][x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+            }
+        }
+    }
+    for (w = 0; w < nW; w++) {
+        for (x = 0; x < nX; x++) {
+            for (y = 0; y < nY; y++) {
+                for (z = 0; z < nZ; z++) {
+                    newPoly->coeff[w][x][y][z] = 0.0;
+                    newPoly->coeffErr[w][x][y][z] = 0.0;
+                    newPoly->mask[w][x][y][z] = 0;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+
+psF64 psDPolynomial1DEval(const psDPolynomial1D* myPoly, psF64 x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial1DEval(x, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial1DEval(x, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial1DEvalVector(const psDPolynomial1D *myPoly,
+                                    const psVector *x)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+
+    tmp = psVectorAlloc(x->n, PS_TYPE_F64);
+    for (psS32 i=0;i<x->n;i++) {
+        tmp->data.F64[i] = psDPolynomial1DEval(myPoly,
+                                               x->data.F64[i]);
+    }
+
+    return(tmp);
+}
+
+
+psF64 psDPolynomial2DEval(const psDPolynomial2D* myPoly,
+                          psF64 x,
+                          psF64 y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial2DEval(x, y, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial2DEval(x, y, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial2DEvalVector(const psDPolynomial2D *myPoly,
+                                    const psVector *x,
+                                    const psVector *y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the output vector length from minimum length of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate the polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial2DEval(myPoly,x->data.F64[i],y->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+psF64 psDPolynomial3DEval(const psDPolynomial3D* myPoly,
+                          psF64 x,
+                          psF64 y,
+                          psF64 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial3DEval(x, y, z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial3DEval(x, y, z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial3DEvalVector(const psDPolynomial3D *myPoly,
+                                    const psVector *x,
+                                    const psVector *y,
+                                    const psVector *z)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the size of output vector from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial3DEval(myPoly,
+                                               x->data.F64[i],
+                                               y->data.F64[i],
+                                               z->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF64 psDPolynomial4DEval(const psDPolynomial4D* myPoly,
+                          psF64 w,
+                          psF64 x,
+                          psF64 y,
+                          psF64 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial4DEval(w,x,y,z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial4DEval(w,x,y,z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial4DEvalVector(const psDPolynomial4D *myPoly,
+                                    const psVector *w,
+                                    const psVector *x,
+                                    const psVector *y,
+                                    const psVector *z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(w, NULL);
+    PS_VECTOR_CHECK_TYPE(w, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=w->n;
+
+    // Determine the output vector size from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (x->n < vecLen) {
+        vecLen = x->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate the polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial4DEval(myPoly,
+                                               w->data.F64[i],
+                                               x->data.F64[i],
+                                               y->data.F64[i],
+                                               z->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+
+
+//typedef struct {
+//    psS32 n;
+//    psPolynomial1D **spline;
+//    psF32 *p_psDeriv2;
+//    psVector *knots;
+//} psSpline1D;
+
+/*****************************************************************************
+    NOTE: "n" specifies the number of spline polynomials.  Therefore, there
+    must exist n+1 points in "knots".
+ 
+XXX: Ensure that domain[i+1] != domain[i]
+ 
+XXX: What should be the defualty type for knots be?  psF32 is assumed.
+ *****************************************************************************/
+psSpline1D *psSpline1DAlloc(psS32 numSplines,
+                            psS32 order,
+                            psF32 min,
+                            psF32 max)
+{
+    PS_INT_CHECK_NON_NEGATIVE(numSplines, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+    PS_FLOAT_CHECK_NON_EQUAL(max, min, NULL);
+
+    psSpline1D *tmp = NULL;
+    psS32 i;
+    psF32 tmpDomain;
+    psF32 width;
+
+    tmp = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+    tmp->n = numSplines;
+
+    tmp->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (i=0;i<numSplines;i++) {
+        (tmp->spline)[i] = psPolynomial1DAlloc(order+1, PS_POLYNOMIAL_ORD);
+    }
+
+    // This should be set by the psVectorFitSpline1D()
+    tmp->p_psDeriv2 = NULL;
+
+    tmp->knots = psVectorAlloc(numSplines+1, PS_TYPE_F32);
+    width = (max - min) / ((psF32) numSplines);
+
+    tmp->knots->data.F32[0] = min;
+    tmpDomain = min+width;
+    for (i=1;i<numSplines+1;i++) {
+        tmp->knots->data.F32[i] = tmpDomain;
+        tmpDomain+= width;
+    }
+    tmp->knots->data.F32[numSplines] = max;
+
+    psMemSetDeallocator(tmp,(psFreeFcn)spline1DFree);
+    return(tmp);
+}
+
+
+/*****************************************************************************
+XXX: What should be the defualty type for knots be?  psF32 is assumed.
+ *****************************************************************************/
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds,
+                                   psS32 order)
+{
+    PS_VECTOR_CHECK_NULL(bounds, NULL);
+    PS_VECTOR_CHECK_EMPTY(bounds, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+
+    psSpline1D *tmp = NULL;
+    psS32 i;
+    psS32 numSplines;
+
+    tmp = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+
+    numSplines = bounds->n - 1;
+    tmp->n = numSplines;
+
+    tmp->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (i=0;i<numSplines;i++) {
+        (tmp->spline)[i] = psPolynomial1DAlloc(order+1, PS_POLYNOMIAL_ORD);
+    }
+
+    // This should be set by the psVectorFitSpline1D()
+    tmp->p_psDeriv2 = NULL;
+
+    tmp->knots = psVectorAlloc(bounds->n, PS_TYPE_F32);
+
+    for (i=0;i<bounds->n;i++) {
+        tmp->knots->data.F32[i] = bounds->data.F32[i];
+        if (i<(bounds->n-1)) {
+            if (FLT_EPSILON >= fabs(bounds->data.F32[i+1]-bounds->data.F32[i])) {
+                psError(PS_ERR_UNKNOWN, true, "data points must be distinct\n");
+            }
+        }
+    }
+
+    psMemSetDeallocator(tmp,(psFreeFcn)spline1DFree);
+    return(tmp);
+}
+
+/*****************************************************************************
+vectorBinDisectF32(): This is a macro for a private function which takes as
+input a vector an array of data as well as a single value for that data.  The
+input vector values are assumed to be non-decreasing (v[i-1] <= v[i] for all
+i).  This routine does a binary disection of the vector and returns "i" such
+that (v[i] <= x <= v[i+1).  If x lies outside the range of v[], then this
+routine prints a warning message and returns (-2 or -1).
+ *****************************************************************************/
+#define FUNC_MACRO_VECTOR_BIN_DISECT(TYPE) \
+static psS32 vectorBinDisect##TYPE(ps##TYPE *bins, \
+                                   psS32 numBins, \
+                                   ps##TYPE x) \
+{ \
+    psS32 min; \
+    psS32 max; \
+    psS32 mid; \
+    \
+    psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+            "---- Calling vectorBinDisect##TYPE(%f)\n", x); \
+    \
+    if (x < bins[0]) { \
+        psLogMsg(__func__, PS_LOG_WARN, \
+                 "vectorBinDisect%s(): ordinate %f is outside vector range (%f - %f).", \
+                 #TYPE, x, bins[0], bins[numBins-1]); \
+        return(-2); \
+    } \
+    \
+    if (x > bins[numBins-1]) { \
+        psLogMsg(__func__, PS_LOG_WARN, \
+                 "vectorBinDisect%s(): ordinate %f is outside vector range (%f - %f).", \
+                 #TYPE, x, bins[0], bins[numBins-1]); \
+        return(-1); \
+    } \
+    \
+    min = 0; \
+    max = numBins-2; \
+    mid = ((max+1)-min)/2; \
+    \
+    while (min != max) { \
+        psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+                "(min, mid, max) is (%d, %d, %d): (x, bins) is (%f, %f)\n", \
+                min, mid, max, x, bins[mid]); \
+        \
+        if (x == bins[mid]) { \
+            psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+                    "---- Exiting vectorBinDisect##TYPE(): bin %d\n", mid); \
+            return(mid); \
+        } else if (x < bins[mid]) { \
+            max = mid-1; \
+        } else { \
+            min = mid; \
+        } \
+        mid = ((max+1)+min)/2; \
+    } \
+    \
+    psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+            "---- Exiting vectorBinDisect##TYPE(): bin %d\n", min); \
+    return(min); \
+} \
+
+FUNC_MACRO_VECTOR_BIN_DISECT(S8)
+FUNC_MACRO_VECTOR_BIN_DISECT(S16)
+FUNC_MACRO_VECTOR_BIN_DISECT(S32)
+FUNC_MACRO_VECTOR_BIN_DISECT(S64)
+FUNC_MACRO_VECTOR_BIN_DISECT(U8)
+FUNC_MACRO_VECTOR_BIN_DISECT(U16)
+FUNC_MACRO_VECTOR_BIN_DISECT(U32)
+FUNC_MACRO_VECTOR_BIN_DISECT(U64)
+FUNC_MACRO_VECTOR_BIN_DISECT(F32)
+FUNC_MACRO_VECTOR_BIN_DISECT(F64)
+
+/*****************************************************************************
+p_psVectorBinDisect(): A wrapper to the above p_psVectorBinDisect().
+ *****************************************************************************/
+psS32 p_psVectorBinDisect(psVector *bins,
+                          psScalar *x)
+{
+    PS_VECTOR_CHECK_NULL(bins, -4);
+    PS_VECTOR_CHECK_EMPTY(bins, -4);
+    PS_PTR_CHECK_NULL(x, -6);
+    PS_PTR_CHECK_TYPE_EQUAL(x, bins, -3);
+    char* strType;
+
+    switch (x->type.type) {
+    case PS_TYPE_U8:
+        return(vectorBinDisectU8(bins->data.U8, bins->n, x->data.U8));
+    case PS_TYPE_U16:
+        return(vectorBinDisectU16(bins->data.U16, bins->n, x->data.U16));
+    case PS_TYPE_U32:
+        return(vectorBinDisectU32(bins->data.U32, bins->n, x->data.U32));
+    case PS_TYPE_U64:
+        return(vectorBinDisectU64(bins->data.U64, bins->n, x->data.U64));
+    case PS_TYPE_S8:
+        return(vectorBinDisectS8(bins->data.S8, bins->n, x->data.S8));
+    case PS_TYPE_S16:
+        return(vectorBinDisectS16(bins->data.S16, bins->n, x->data.S16));
+    case PS_TYPE_S32:
+        return(vectorBinDisectS32(bins->data.S32, bins->n, x->data.S32));
+    case PS_TYPE_S64:
+        return(vectorBinDisectS64(bins->data.S64, bins->n, x->data.S64));
+    case PS_TYPE_F32:
+        return(vectorBinDisectF32(bins->data.F32, bins->n, x->data.F32));
+    case PS_TYPE_F64:
+        return(vectorBinDisectF64(bins->data.F64, bins->n, x->data.F64));
+    case PS_TYPE_C32:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    case PS_TYPE_C64:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    case PS_TYPE_BOOL:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    }
+    return(-3);
+}
+
+/*****************************************************************************
+p_psVectorInterpolate(): This routine will take as input psVectors domain and
+range, and the x value, assumed to lie with the domain vector.  It produces
+as output the LaGrange interpolated value of a polynomial of the specified
+order around the point x.
+ 
+XXX: This stuff does not currently work with a mask.
+ 
+XXX: add another psScalar argument for the result.
+ 
+XXX: The VectorCopy routines seg fault when I declare range32 as static.
+ *****************************************************************************/
+psScalar *p_psVectorInterpolate(psVector *domain,
+                                psVector *range,
+                                psS32 order,
+                                psScalar *x)
+{
+    PS_VECTOR_CHECK_NULL(domain, NULL);
+    PS_VECTOR_CHECK_NULL(range, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(domain, range, NULL);
+    PS_PTR_CHECK_TYPE_EQUAL(domain, range, NULL);
+    PS_PTR_CHECK_TYPE_EQUAL(domain, x, NULL);
+
+    psVector *range32 = NULL;
+    psVector *domain32 = NULL;
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "---- p_psVectorInterpolate() begin ----\n");
+
+    if (order > (domain->n - 1)) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFunctions_NOT_ENOUGH_DATAPOINTS,
+                order);
+        return(NULL);
+    }
+
+    if (x->type.type == PS_TYPE_F32) {
+        psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+                "---- p_psVectorInterpolate() end ----\n");
+        return(psScalarAlloc(interpolate1DF32(domain->data.F32,
+                                              range->data.F32,
+                                              domain->n,
+                                              order,
+                                              x->data.F32), PS_TYPE_F32));
+    } else if (x->type.type == PS_TYPE_F64) {
+        // XXX: use recycled vectors here.
+        range32 = psVectorCopy(range32, range, PS_TYPE_F32);
+        domain32 = psVectorCopy(domain32, domain, PS_TYPE_F32);
+
+        psScalar *tmpScalar = psScalarAlloc((psF64)
+                                            interpolate1DF32(domain32->data.F32,
+                                                             range32->data.F32,
+                                                             domain32->n,
+                                                             order,
+                                                             (psF32) x->data.F64), PS_TYPE_F64);
+        psFree(range32);
+        psFree(domain32);
+
+        psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+                "---- p_psVectorInterpolate() end ----\n");
+        // XXX: Convert data type to F64?
+        return(tmpScalar);
+
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "return(NULL)\n");
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "---- p_psVectorInterpolate() end ----\n");
+
+    return(NULL);
+}
+
+
+/*****************************************************************************
+psSpline1DEval(): this routine takes an existing spline of arbitrary order
+and an independent x value.  Each determines which spline that x corresponds
+to by doing a bracket disection on the knots of the spline data structure
+(vectorBinDisectF32()).  Then it evaluates the spline at that x location
+by a call to the 1D polynomial functions.
+ 
+XXX: The spline eval functions require input and output to be F32.  however
+     the spline fit functions require F32 and F64.
+ 
+XXX: This only works if spline0>knots if psF32.  Must add support for psU32 and
+psF64.
+ *****************************************************************************/
+psF32 psSpline1DEval(
+    const psSpline1D *spline,
+    psF32 x
+)
+{
+    PS_PTR_CHECK_NULL(spline, NAN);
+    PS_INT_CHECK_NON_NEGATIVE(spline->n, NAN);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NAN);
+
+    psS32 binNum;
+    psS32 n;
+
+    n = spline->n;
+    //XXX    binNum = vectorBinDisectF32(spline->domains, (spline->n)+1, x);
+    binNum = vectorBinDisectF32(spline->knots->data.F32, (spline->n)+1, x);
+    if (binNum < 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "psSpline1DEval(): x ordinate (%f) is outside the spline range (%f - %f).",
+                 x, spline->knots->data.F32[0],
+                 spline->knots->data.F32[n-1]);
+
+        if (x < spline->knots->data.F32[0]) {
+            return(psPolynomial1DEval(spline->spline[0],
+                                      x));
+        } else if (x > spline->knots->data.F32[n-1]) {
+            return(psPolynomial1DEval(spline->spline[n-1],
+                                      x));
+        }
+    }
+
+    return(psPolynomial1DEval(spline->spline[binNum],
+                              x));
+}
+
+// XXX: The spline eval functions require input and output to be F32.
+// however the spline fit functions require F32 and F64.
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x
+)
+{
+    PS_PTR_CHECK_NULL(spline, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NULL);
+
+    psS32 i;
+    psVector *tmpVector;
+
+    tmpVector = psVectorAlloc(x->n, PS_TYPE_F32);
+    if (x->type.type == PS_TYPE_F32) {
+        for (i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(
+                                         spline,
+                                         x->data.F32[i]
+                                     );
+        }
+    } else if (x->type.type == PS_TYPE_F64) {
+        for (i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(
+                                         spline,
+                                         (psF32) x->data.F64[i]
+                                     );
+        }
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return(NULL);
+    }
+
+    return(tmpVector);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psFunctions.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psFunctions.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psFunctions.h	(revision 22271)
@@ -0,0 +1,440 @@
+/** @file psFunctions.h
+ *  @brief Standard Mathematical Functions.
+ *  @ingroup Stats
+ *
+ *  This file will hold the prototypes for procedures which allocate, free,
+ *  and evaluate various polynomials.  Those polynomial structures are also
+ *  defined here.
+ *
+ *  @ingroup Stats
+ *
+ *  @author Someone at IfA
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.44 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-31 23:01:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_FUNCTIONS_H)
+#define PS_FUNCTIONS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+
+/** \addtogroup Stats
+ *  \{
+ */
+
+/** Evaluate a non-normalized Gaussian with the given mean and sigma at the
+ *  given coordianate.  
+ *
+ *  Note that this is not a Gaussian deviate.  The evaluated Gaussian is: 
+ *        \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f] 
+ *
+ *  @return psF32      value on the gaussian curve given the input parameters
+ */
+psF32 psGaussian(
+    psF32 x,                           ///< Value at which to evaluate
+    psF32 mean,                        ///< Mean for the Gaussian
+    psF32 stddev,                      ///< Standard deviation for the Gaussian
+    psBool normal                        ///< Indicates whether result should be normalized
+);
+
+/** Produce a vector of random numbers from a Gaussian distribution with
+ *  the specified mean and sigma 
+ *  
+ *  @return psVector*    vector of random numbers
+ *  
+ */
+psVector* p_psGaussianDev(
+    psF32 mean,                        ///< The mean of the Gaussian
+    psF32 sigma,                       ///< The sigma of the Gaussian
+    psS32 Npts                           ///< The size of the vector
+);
+
+typedef enum {
+    PS_POLYNOMIAL_ORD,                 ///< Ordinary Polynomial
+    PS_POLYNOMIAL_CHEB                 ///< Chebyshev Polynomial
+} psPolynomialType;
+
+/** One-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 n;                             ///< Number of terms
+    psF32 *coeff;                      ///< Coefficients
+    psF32 *coeffErr;                   ///< Error in coefficients
+    psU8 *mask;                        ///< Coefficient mask
+}
+psPolynomial1D;
+
+/** Two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psF32 **coeff;                     ///< Coefficients
+    psF32 **coeffErr;                  ///< Error in coefficients
+    psU8 **mask;                       ///< Coefficients mask
+}
+psPolynomial2D;
+
+/** Three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF32 ***coeff;                    ///< Coefficients
+    psF32 ***coeffErr;                 ///< Error in coefficients
+    psU8 ***mask;                      ///< Coefficients mask
+}
+psPolynomial3D;
+
+/** Four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nW;                            ///< Number of terms in w
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF32 ****coeff;                   ///< Coefficients
+    psF32 ****coeffErr;                ///< Error in coefficients
+    psU8 ****mask;                     ///< Coefficients mask
+}
+psPolynomial4D;
+
+
+/** Allocates a psPolynomial1D structure with n terms
+ *
+ *  @return  psPolynomial1D*    new 1-D polynomial struct
+ */
+psPolynomial1D* psPolynomial1DAlloc(
+    psS32 n,                              ///< Number of terms
+    psPolynomialType type               ///< Polynomial Type
+);
+
+/** Allocates a 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new 2-D polynomial struct
+ */
+psPolynomial2D* psPolynomial2DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new 3-D polynomial struct
+ */
+psPolynomial3D* psPolynomial3DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new 4-D polynomial struct
+ */
+psPolynomial4D* psPolynomial4DAlloc(
+    psS32 nW,                            ///< Number of terms in w
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Evaluates a 1-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial1DEval(
+    const psPolynomial1D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x                           ///< location at which to evaluate
+);
+
+/** Evaluates a 2-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial2DEval(
+    const psPolynomial2D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y                           ///< y location at which to evaluate
+);
+
+/** Evaluates a 3-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial3DEval(
+    const psPolynomial3D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y,                           ///< y location at which to evaluate
+    psF32 z                           ///< z location at which to evaluate
+);
+
+/** Evaluates a 4-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial4DEval(
+    const psPolynomial4D* myPoly,       ///< Coefficients for the polynomial
+    psF32 w,                           ///< w location at which to evaluate
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y,                           ///< y location at which to evaluate
+    psF32 z                           ///< z location at which to evaluate
+);
+
+psVector *psPolynomial1DEvalVector(
+    const psPolynomial1D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x             ///< x locations at which to evaluate
+);
+
+psVector *psPolynomial2DEvalVector(
+    const psPolynomial2D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y             ///< y locations at which to evaluate
+);
+
+psVector *psPolynomial3DEvalVector(
+    const psPolynomial3D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+psVector *psPolynomial4DEvalVector(
+    const psPolynomial4D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *w,             ///< w locations at which to evaluate
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+/*****************************************************************************/
+
+/* Double-precision polynomials, mainly for use in astrometry */
+
+/** Double-precision one-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 n;                             ///< Number of terms
+    psF64 *coeff;                     ///< Coefficients
+    psF64 *coeffErr;                  ///< Error in coefficients
+    psU8 *mask;                        ///< Coefficient mask
+}
+psDPolynomial1D;
+
+/** Double-precision two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psF64 **coeff;                    ///< Coefficients
+    psF64 **coeffErr;                 ///< Error in coefficients
+    psU8 **mask;                       ///< Coefficients mask
+}
+psDPolynomial2D;
+
+/** Double-precision three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF64 ***coeff;                   ///< Coefficients
+    psF64 ***coeffErr;                ///< Error in coefficients
+    psU8 ***mask;                      ///< Coefficient mask
+}
+psDPolynomial3D;
+
+/** Double-precision four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nW;                            ///< Number of terms in w
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF64 ****coeff;                  ///< Coefficients
+    psF64 ****coeffErr;               ///< Error in coefficients
+    psU8 ****mask;                     ///< Coefficients mask
+}
+psDPolynomial4D;
+
+/** Allocates a double-precision 1-D polynomial structure with n terms
+ *
+ *  @return  psPolynomial1D*    new double-precision 1-D polynomial struct
+ */
+psDPolynomial1D* psDPolynomial1DAlloc(
+    psS32 n,                             ///< Number of terms
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new double-precision 2-D polynomial struct
+ */
+psDPolynomial2D* psDPolynomial2DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new double-precision 3-D polynomial struct
+ */
+psDPolynomial3D* psDPolynomial3DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new double-precision 4-D polynomial struct
+ */
+psDPolynomial4D* psDPolynomial4DAlloc(
+    psS32 nW,                            ///< Number of terms in w
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Evaluates a double-precision 1-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial1DEval(
+    const psDPolynomial1D* myPoly,      ///< Coefficients for the polynomial
+    psF64 x                          ///< Value at which to evaluate
+);
+
+/** Evaluates a double-precision 2-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial2DEval(
+    const psDPolynomial2D* myPoly,       ///< Coefficients for the polynomial
+    psF64 x,                           ///< Value x at which to evaluate
+    psF64 y            ///< Value y at which to evaluate
+);
+
+/** Evaluates a double-precision 3-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial3DEval(
+    const psDPolynomial3D* myPoly,      ///< Coefficients for the polynomial
+    psF64 x,                          ///< Value x at which to evaluate
+    psF64 y,                          ///< Value y at which to evaluate
+    psF64 z     ///< Value z at which to evaluate
+);
+
+/** Evaluates a double-precision 4-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial4DEval(
+    const psDPolynomial4D* myPoly,      ///< Coefficients for the polynomial
+    psF64 w,                          ///< Value w at which to evaluate
+    psF64 x,                          ///< Value x at which to evaluate
+    psF64 y,                          ///< Value y at which to evaluate
+    psF64 z     ///< Value z at which to evaluate
+);
+
+psVector *psDPolynomial1DEvalVector(
+    const psDPolynomial1D *myPoly, ///< Coefficients for the polynomial
+    const psVector *x             ///< x locations at which to evaluate
+);
+
+psVector *psDPolynomial2DEvalVector(
+    const psDPolynomial2D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y             ///< y locations at which to evaluate
+);
+
+psVector *psDPolynomial3DEvalVector(
+    const psDPolynomial3D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+psVector *psDPolynomial4DEvalVector(
+    const psDPolynomial4D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *w,             ///< w locations at which to evaluate
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+
+
+typedef struct
+{
+    psS32 n;                       ///< The number of spline polynomials
+    psPolynomial1D **spline;       ///< An array of n pointers to the spline polynomials
+    psF32 *p_psDeriv2;             ///< For cubic splines, the second derivative at each domain point.  Size is n+1.
+    psF32 *domains;                ///< The boundaries between each spline piece.  Size is n+1.
+    psVector *knots;               ///< The boundaries between each spline piece.  Size is n+1.
+}
+psSpline1D;
+
+psSpline1D *psSpline1DAlloc(psS32 n,
+                            psS32 order,
+                            psF32 min,
+                            psF32 max);
+
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds,
+                                   psS32 order);
+
+psF32 psSpline1DEval(
+    const psSpline1D *spline,
+    psF32 x
+);
+
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x
+);
+
+psS32 p_psVectorBinDisect(psVector *bins,
+                          psScalar *x);
+
+psScalar *p_psVectorInterpolate(psVector *domain,
+                                psVector *range,
+                                psS32 order,
+                                psScalar *x);
+
+#if 0
+psF32 p_psNRSpline1DEval(psSpline1D *spline,
+                         const psVector* x,
+                         const psVector* y,
+                         psF32 X);
+#endif
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMatrix.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMatrix.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMatrix.c	(revision 22271)
@@ -0,0 +1,640 @@
+/** @file  psMatrix.c
+ *
+ *  @brief Provides functions for linear algebra operations on psImages and psVectors.
+ *
+ *  Functions are provided to:
+ *      Transpose a psImage
+ *      Compute LUD
+ *      Solve LUD
+ *      Matrix inversion
+ *      Calculate determinant
+ *      Matrix addition
+ *      Matrix subtraction
+ *      Matrix multiplication
+ *      Calculate Eigenvectors
+ *      Convert matrix to vector
+ *      Convert vector to matrix
+ *
+ *  These functions treat psImages as if they were matrices, therefore there is no psMatrix.
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.28.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <string.h>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_permutation.h>
+#include <gsl/gsl_eigen.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psMatrix.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/** Preprocessor macro to generate error for image dimensionality not set to PS_DIMEN_IMAGE */
+#define PS_CHECK_DIMEN_AND_TYPE(NAME, PS_DIMEN, CLEANUP)                                             \
+if (NAME->type.dimen != PS_DIMEN) {                                                                 \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                        \
+            "Invalid operation. %s has incorrect dimensionality %d.", #NAME, PS_DIMEN);             \
+    CLEANUP;                                                                                  \
+} else if(NAME->type.type!=PS_TYPE_F64 && NAME->type.type!=PS_TYPE_F32) {                           \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                        \
+            "Invalid operation. %s not PS_TYPE_F64.", #NAME);                                       \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to check that input is not equal to output */
+#define PS_CHECK_POINTERS(NAME1, NAME2, CLEANUP)                                                     \
+if (NAME1 == NAME2) {                                                                               \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                       \
+            "Invalid operation: Pointer to %s is same as %s.", #NAME1, #NAME2);                     \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to check that an image is square */
+#define PS_CHECK_SQUARE(NAME, CLEANUP)                                                               \
+if (NAME->numCols != NAME->numRows) {                                                               \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: %s not square array.", #NAME);     \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to initalize a GSL matrix. */
+#define PS_GSL_MATRIX_INITIALIZE(LHS_NAME, RHS_NAME)                                                \
+LHS_NAME.size1 = numRows;                                                                           \
+LHS_NAME.size2 = numCols;                                                                           \
+LHS_NAME.tda   = numCols;                                                                           \
+LHS_NAME.data  = RHS_NAME;
+
+
+/*****************************************************************************/
+/* FILE STATIC FUNCTIONS                                                     */
+/*****************************************************************************/
+
+static void  psVectorToGslVector(gsl_vector *outGslVector, const psVector *inVector);
+static void gslVectorToPsVector(psVector *outVector, gsl_vector *inGslVector);
+static void  psImageToGslMatrix(gsl_matrix *outGslMatrix, const psImage *inImage);
+static void gslMatrixToPsImage(psImage *outImage, gsl_matrix *inGslMatrix);
+
+/** Static function to copy psF32 or psF64 vector data to a GSL vector */
+static void  psVectorToGslVector(gsl_vector *outGslVector, const psVector *inVector)
+{
+    psU32 i = 0;
+    psU32 n = 0;
+
+
+    n = inVector->n;
+    for(i=0; i<n; i++) {
+        if(inVector->type.type == PS_TYPE_F32) {
+            outGslVector->data[i] = (psF64)inVector->data.F32[i];
+        } else {
+            outGslVector->data[i] = inVector->data.F64[i];
+        }
+    }
+}
+
+/** Static function to copy GSL vector data to a psF32 or psF64 vector */
+static void gslVectorToPsVector(psVector *outVector, gsl_vector *inGslVector)
+{
+    psU32 i = 0;
+    psU32 n = 0;
+
+
+    n = outVector->n;
+    for(i=0; i<n; i++) {
+        if(outVector->type.type == PS_TYPE_F32) {
+            outVector->data.F32[i] = (psF32)inGslVector->data[i];
+        } else {
+            outVector->data.F64[i] = inGslVector->data[i];
+        }
+    }
+}
+
+/** Static function to copy psF32 or psF64 image data to a GSL matrix */
+static void  psImageToGslMatrix(gsl_matrix *outGslMatrix, const psImage *inImage)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psU32 numRows = 0;
+    psU32 numCols = 0;
+
+
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+    for(i=0; i<numRows; i++) {
+        for(j=0; j<numCols; j++) {
+            if(inImage->type.type == PS_TYPE_F32) {
+                outGslMatrix->data[i*numCols+j] = (psF64)inImage->data.F32[i][j];
+            } else {
+                outGslMatrix->data[i*numCols+j] = inImage->data.F64[i][j];
+            }
+        }
+    }
+}
+
+/** Static function to copy GSL matrix data to a psF32 or psF64 image */
+static void gslMatrixToPsImage(psImage *outImage, gsl_matrix *inGslMatrix)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psU32 numRows = 0;
+    psU32 numCols = 0;
+
+
+    numRows = outImage->numRows;
+    numCols = outImage->numCols;
+    for(i=0; i<numRows; i++) {
+        for(j=0; j<numCols; j++) {
+            if(outImage->type.type == PS_TYPE_F32) {
+                outImage->data.F32[i][j] = (psF32)inGslMatrix->data[i*numCols+j];
+            } else {
+                outImage->data.F64[i][j] = inGslMatrix->data[i*numCols+j];
+            }
+        }
+    }
+}
+
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psImage* psMatrixLUD(psImage* outImage, psVector** outPerm, psImage* inImage)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *lu = NULL;
+    gsl_permutation perm;
+
+
+    #define psMatrixLUD_EXIT {psFree(outImage); return NULL;}
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, psMatrixLUD_EXIT);
+    PS_CHECK_POINTERS(inImage, outImage, psMatrixLUD_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, psMatrixLUD_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(outPerm, psMatrixLUD_EXIT);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    PS_CHECK_SQUARE(inImage, psMatrixLUD_EXIT);
+    PS_CHECK_SQUARE(outImage, psMatrixLUD_EXIT);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Initialize GSL data
+    perm.size = numCols;
+    if (sizeof(size_t) == 4) {
+        *outPerm = psVectorRecycle(*outPerm, numCols, PS_TYPE_S32);
+    } else if (sizeof(size_t) == 8) {
+        *outPerm = psVectorRecycle(*outPerm, numCols, PS_TYPE_S64);
+    } else {
+        psError(PS_ERR_UNKNOWN, true,
+                "Failed to allocate the permutation vector; "
+                "could not determine the cooresponding data type.");
+        psMatrixLUD_EXIT;
+    }
+
+    (*outPerm)->n = numCols;
+    perm.data = (psPtr)((*outPerm)->data.U8);
+    lu = gsl_matrix_alloc(numRows, numCols);
+
+    // Copy psImage data into GSL matrix data
+    psImageToGslMatrix(lu, inImage);
+
+    // Calculate LU decomposition
+    gsl_linalg_LU_decomp(lu, &perm, &signum);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, lu);
+
+    // Free GSL data
+    gsl_matrix_free(lu);
+
+    return outImage;
+}
+
+psVector* psMatrixLUSolve(psVector* outVector, const psImage* inImage, const psVector* inVector,
+                          const psVector* inPerm)
+{
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *lu;
+    gsl_permutation perm;
+    gsl_vector *b = NULL;
+    gsl_vector *x = NULL;
+
+    #define LUSOLVE_CLEANUP {psFree(outVector); return NULL;}
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, LUSOLVE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, LUSOLVE_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, LUSOLVE_CLEANUP);
+    PS_VECTOR_CHECK_NULL_GENERAL(inVector, LUSOLVE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_VECTOR, LUSOLVE_CLEANUP);
+    PS_VECTOR_CHECK_NULL_GENERAL(inPerm, LUSOLVE_CLEANUP);
+
+    outVector = psVectorRecycle(outVector, inImage->numRows, inImage->type.type);
+
+    PS_CHECK_POINTERS(outVector, inVector, LUSOLVE_CLEANUP);
+    PS_CHECK_POINTERS(inVector, inPerm, LUSOLVE_CLEANUP);
+    PS_CHECK_POINTERS(outVector, inPerm, LUSOLVE_CLEANUP);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Initialize GSL data
+    lu = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, inImage);
+    b = gsl_vector_alloc(inVector->n);
+    psVectorToGslVector(b, inVector);
+    x = gsl_vector_alloc(inVector->n);
+
+    outVector->n = numCols;
+    perm.size = inPerm->n;
+    perm.data = (psPtr)(inPerm->data.U8);
+
+    // Solve for {x} in equation: {b} = [A]{x}
+    gsl_linalg_LU_solve(lu, &perm, b, x);
+
+    // Copy GSL vector data to psVector data
+    gslVectorToPsVector(outVector, x);
+
+    // Free GSL data
+    gsl_vector_free(b);
+    gsl_vector_free(x);
+    gsl_matrix_free(lu);
+
+    return outVector;
+}
+
+psImage* psMatrixInvert(psImage* outImage, const psImage* inImage, psF32 *det)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *inv = NULL;
+    gsl_matrix *lu = NULL;
+    gsl_permutation *perm = NULL;
+
+    #define INVERT_CLEANUP { psFree(outImage); return NULL; }
+    // Error checks
+    PS_PTR_CHECK_NULL_GENERAL(det, INVERT_CLEANUP);
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, INVERT_CLEANUP);
+    PS_CHECK_POINTERS(inImage, outImage, INVERT_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, INVERT_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, INVERT_CLEANUP);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    PS_CHECK_SQUARE(inImage, INVERT_CLEANUP);
+    PS_CHECK_SQUARE(outImage, INVERT_CLEANUP);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Initialize GSL data
+    perm = gsl_permutation_alloc(numRows);
+    lu = gsl_matrix_alloc(numRows, numCols);
+    inv = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, inImage);
+
+    // Invert data and calculate determinant
+    gsl_linalg_LU_decomp(lu, perm, &signum);
+    gsl_linalg_LU_invert(lu, perm, inv);
+    *det = (float)gsl_linalg_LU_det(lu, signum);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, inv);
+
+    // Free GSL structs
+    gsl_permutation_free(perm);
+    gsl_matrix_free(lu);
+    gsl_matrix_free(inv);
+
+    return outImage;
+}
+
+psF32 *psMatrixDeterminant(const psImage* inImage)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    psF32 *det = NULL;
+    gsl_matrix *lu = NULL;
+    gsl_permutation *perm = NULL;
+
+    #define DETERMINANT_EXIT { return NULL; }
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, DETERMINANT_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, DETERMINANT_EXIT);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, DETERMINANT_EXIT);
+    PS_CHECK_SQUARE(inImage, DETERMINANT_EXIT);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Allocate GSL structs
+    perm = gsl_permutation_alloc(numRows);
+    lu = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, inImage);
+
+    // Calculate determinant
+    det = (psF32*)psAlloc(sizeof(psF32));
+    gsl_linalg_LU_decomp(lu, perm, &signum);
+    *det = (psF32)gsl_linalg_LU_det(lu, signum);
+
+    // Free GSL structs
+    gsl_permutation_free(perm);
+    gsl_matrix_free(lu);
+
+    return det;
+}
+
+psImage* psMatrixMultiply(psImage* outImage, psImage* inImage1, psImage* inImage2)
+{
+    gsl_matrix *m1 = NULL;
+    gsl_matrix *m2 = NULL;
+    gsl_matrix *m3 = NULL;
+
+    #define MULTIPLY_CLEANUP { psFree(outImage); return NULL; }
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage1, MULTIPLY_CLEANUP);
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage2, MULTIPLY_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage1, MULTIPLY_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage2, MULTIPLY_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage1, PS_DIMEN_IMAGE, MULTIPLY_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage2, PS_DIMEN_IMAGE, MULTIPLY_CLEANUP);
+    PS_CHECK_POINTERS(inImage1, outImage, MULTIPLY_CLEANUP);
+    PS_CHECK_POINTERS(inImage1, inImage2, MULTIPLY_CLEANUP);
+
+    if (inImage1->numRows != inImage2->numCols) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: number of rows of inImage1 != number of cols of inImage2.");
+        MULTIPLY_CLEANUP;
+    }
+    if (inImage1->type.type != inImage2->type.type) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: data types of inImage1 and inImage2 must match.");
+        MULTIPLY_CLEANUP;
+    }
+
+    outImage = psImageRecycle(outImage, inImage1->numCols, inImage2->numRows, inImage2->type.type);
+
+    // Initialize GSL data
+    m1 = gsl_matrix_alloc(inImage1->numRows, inImage1->numCols);
+    psImageToGslMatrix(m1, inImage1);
+    m2 = gsl_matrix_alloc(inImage2->numRows, inImage2->numCols);
+    psImageToGslMatrix(m2, inImage2);
+    m3 = gsl_matrix_alloc(outImage->numRows, outImage->numCols);
+
+    // Perform multiplication
+    gsl_linalg_matmult(m1, m2, m3);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, m3);
+
+    // Free GSL structs
+    gsl_matrix_free(m1);
+    gsl_matrix_free(m2);
+    gsl_matrix_free(m3);
+
+    return outImage;
+}
+
+psImage* psMatrixTranspose(psImage* outImage, const psImage* inImage)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psS32 numRowsIn = 0;
+    psS32 numColsIn = 0;
+    psS32 numRowsOut = 0;
+    psS32 numColsOut = 0;
+
+    #define TRANSPOSE_CLEANUP { psFree(outImage); return NULL; }
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, TRANSPOSE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, TRANSPOSE_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, TRANSPOSE_CLEANUP);
+    PS_CHECK_POINTERS(inImage, outImage, TRANSPOSE_CLEANUP);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    // Initialize data
+    numRowsIn = inImage->numRows;
+    numColsIn = inImage->numCols;
+    numRowsOut = outImage->numRows;
+    numColsOut = outImage->numCols;
+
+    if(numRowsIn!=numColsOut && numRowsOut!=numColsIn) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMatrix_TRANSPOSE_MISMATCH);
+        TRANSPOSE_CLEANUP;
+    }
+
+    if(outImage->type.type == PS_TYPE_F32) {
+        for(i=0; i<numRowsOut; i++) {
+            for(j=0; j<numColsOut; j++) {
+                outImage->data.F32[i][j] = inImage->data.F32[j][i];
+            }
+        }
+    } else {
+        for(i=0; i<numRowsOut; i++) {
+            for(j=0; j<numColsOut; j++) {
+                outImage->data.F64[i][j] = inImage->data.F64[j][i];
+            }
+        }
+    }
+
+    return outImage;
+}
+
+psImage* psMatrixEigenvectors(psImage* outImage, const psImage* inImage)
+{
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_vector *eVals = NULL;
+    gsl_eigen_symmv_workspace *w = NULL;
+    gsl_matrix *out = NULL;
+    gsl_matrix *in = NULL;
+
+    #define EIGENVECTORS_CLEANUP { psFree(outImage); return NULL; }
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, EIGENVECTORS_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, EIGENVECTORS_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, EIGENVECTORS_CLEANUP);
+    PS_CHECK_POINTERS(inImage, outImage, EIGENVECTORS_CLEANUP);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    in = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(in, inImage);
+    out = gsl_matrix_alloc(numRows, numCols);
+
+    // Allocate GSL structs
+    eVals = gsl_vector_alloc(numRows);
+    w = gsl_eigen_symmv_alloc(numRows);
+
+    // Non-square matrices not allowed
+    PS_CHECK_SQUARE(inImage, EIGENVECTORS_CLEANUP);
+    PS_CHECK_SQUARE(outImage, EIGENVECTORS_CLEANUP);
+
+    // Calculate Eigenvalues and Eigenvectors...Eigenvalues not currently used
+    gsl_eigen_symmv(in, eVals, out, w);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, out);
+
+    // Free GSL structs
+    gsl_matrix_free(in);
+    gsl_matrix_free(out);
+    gsl_eigen_symmv_free(w);
+    gsl_vector_free(eVals);
+
+    return outImage;
+}
+
+psVector* psMatrixToVector(psVector* outVector, const psImage* inImage)
+{
+    psS32 size = 0;
+
+    #define psMatrixToVector_EXIT {psFree(outVector); return NULL;}
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, psMatrixToVector_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, psMatrixToVector_EXIT);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, psMatrixToVector_EXIT);
+
+    if (inImage->numRows == 1) {
+        // Create transposed row vector
+        outVector = psVectorRecycle(outVector, inImage->numCols, inImage->type.type);
+        outVector->type.dimen = PS_DIMEN_TRANSV;
+    } else if (inImage->numCols == 1) {
+        // Create non-transposed column vector
+        outVector = psVectorRecycle(outVector, inImage->numRows, inImage->type.type);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Image does not have dim with 1 col or 1 row: (%d x %d).",
+                inImage->numRows, inImage->numCols);
+        psMatrixToVector_EXIT;
+    }
+
+    // More checks
+    if (outVector->type.dimen == PS_DIMEN_VECTOR) {
+        PS_CHECK_DIMEN_AND_TYPE(outVector, PS_DIMEN_VECTOR, psMatrixToVector_EXIT);
+
+        if (outVector->n == 0) {
+            outVector->n = inImage->numRows;
+        }
+
+        if (outVector->n != inImage->numRows) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    inImage->numRows, outVector->n);
+            psMatrixToVector_EXIT;
+        }
+
+        size = PSELEMTYPE_SIZEOF(inImage->type.type) * inImage->numRows;
+
+    } else if (outVector->type.dimen == PS_DIMEN_TRANSV) {
+        PS_CHECK_DIMEN_AND_TYPE(outVector, PS_DIMEN_TRANSV, psMatrixToVector_EXIT);
+
+        if (outVector->n == 0) {
+            outVector->n = inImage->numCols;
+        }
+
+        if (outVector->n != inImage->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    inImage->numCols, outVector->n);
+            psMatrixToVector_EXIT;
+        }
+
+        size = PSELEMTYPE_SIZEOF(inImage->type.type) * inImage->numCols;
+    }
+
+    memcpy(outVector->data.U8, inImage->data.U8[0], size);
+
+    return outVector;
+}
+
+psImage* psVectorToMatrix(psImage* outImage, const psVector* inVector)
+{
+    psS32 size = 0;
+
+    #define VECTORTOMATRIX_CLEANUP {psFree(outImage); return NULL; }
+    // Error checks
+    PS_VECTOR_CHECK_NULL_GENERAL(inVector, VECTORTOMATRIX_CLEANUP);
+
+    if (inVector->type.dimen == PS_DIMEN_VECTOR) {
+        PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_VECTOR, VECTORTOMATRIX_CLEANUP);
+        PS_VECTOR_CHECK_EMPTY_GENERAL(inVector, VECTORTOMATRIX_CLEANUP);
+
+        outImage = psImageRecycle(outImage, 1, inVector->n, inVector->type.type);
+
+        // More checks for PS_DIMEN_VECTOR
+        if (outImage->numCols > 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image has more than 1 column: numCols = %d.",
+                    outImage->numCols);
+            VECTORTOMATRIX_CLEANUP;
+        } else if (outImage->numRows != inVector->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    outImage->numRows, inVector->n);
+            VECTORTOMATRIX_CLEANUP;
+        }
+
+        size = PSELEMTYPE_SIZEOF(outImage->type.type) * outImage->numRows;
+
+    } else if (inVector->type.dimen == PS_DIMEN_TRANSV) {
+        PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_TRANSV, VECTORTOMATRIX_CLEANUP);
+        PS_VECTOR_CHECK_EMPTY_GENERAL(inVector, VECTORTOMATRIX_CLEANUP);
+        outImage = psImageRecycle(outImage, inVector->n, 1, inVector->type.type);
+        // More checks for PS_DIMEN_TRANSV
+        if (outImage->numRows > 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image has more than 1 row: numRows = %d.",
+                    outImage->numRows);
+            VECTORTOMATRIX_CLEANUP;
+        } else if (outImage->numCols != inVector->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    outImage->numCols, inVector->n);
+            VECTORTOMATRIX_CLEANUP;
+        }
+
+        size = PSELEMTYPE_SIZEOF(outImage->type.type) * outImage->numCols;
+    }
+
+    PS_IMAGE_CHECK_NULL_GENERAL(outImage, VECTORTOMATRIX_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(outImage, PS_DIMEN_IMAGE, VECTORTOMATRIX_CLEANUP);
+
+    memcpy(outImage->data.U8[0], inVector->data.U8, size);
+
+    return outImage;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMatrix.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMatrix.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMatrix.h	(revision 22271)
@@ -0,0 +1,166 @@
+
+/** @file  psMatrix.h
+ *
+ *  @brief Provides functions for linear algebra operations on psImages and psVectors.
+ *
+ *  Functions are provided to:
+ *      Transpose a psImage
+ *      Compute LUD
+ *      Solve LUD
+ *      Matrix inversion
+ *      Calculate determinant
+ *      Matrix multiplication
+ *      Calculate Eigenvectors
+ *      Convert matrix to vector
+ *      Convert vector to matrix
+ *
+ *  These functions treat psImages as if they were matrices, therefore there is no psMatrix. These functions
+ *  operate only with the psF64 data type.
+ *
+ *  @ingroup Matrix
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.15.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSMATRIX_H
+#define PSMATRIX_H
+
+/// @addtogroup Matrix
+/// @{
+
+/** LU Decomposition of psImage matrix.
+ *
+ *  Performs a LU decomposition on a psImage matrix and returns the LU matrix. If the user specifies NULL for
+ *  the outImage or outPerm arguments, then they will be automatically created. The input image must
+ *  be square. This function operates only with the psF64 data type. Input and output arguments should not be
+ *  the same. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to LU decomposed psImage.
+ */
+psImage* psMatrixLUD(
+    psImage* outImage,                 ///< Image to return, or NULL.
+    psVector** outPerm,                ///< Output permutation vector used by psMatrixLUSolve.
+    psImage* inImage                   ///< Image to decompose.
+);
+
+/** LU Solution of psImage matrix.
+ *
+ *  Solves for and returns the psVector, {x} in the equation [A]{x} = {b}. If the user specifies NULL as the
+ *  outVector argument, then it will automatically be created. The input image must be square. This function
+ *  operates only with the psF64 data type. Input and output arguments should not be the same. GSL indexes
+ *  the top row as the zero row, not the bottom.
+ *
+ *  @return  psVector* : Pointer to psVector solution of matrix equation.
+ */
+psVector* psMatrixLUSolve(
+    psVector* outVector,               ///< Vector to return, or NULL.
+    const psImage* luImage,            ///< LU-decomposed matrix.
+    const psVector* inVector,          ///< Vector right-hand-side of equation.
+    const psVector* inPerm             ///< Permutation vector resulting from psMatrixLUD function.
+);
+
+/** Invert psImage matrix.
+ *
+ *  Inverts a psImage matrix and returns the determinant as an option through the argument list. If the user
+ *  specifies NULL as the outImage argument, then it will automatically be created. The input image must be
+ *  square. This function operates only with the psF64 data type. Input and output arguments should not be
+ *  the same. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to inverted psImage.
+ */
+psImage* psMatrixInvert(
+    psImage* outImage,                 ///< Image to return, or NULL for in-place substitution.
+    const psImage* inImage,            ///< Image to be inverted
+    psF32 *det                         ///< Determinant to return, or NULL
+);
+
+/** Calculate psImage matrix determinant.
+ *
+ *  Calculates the determinant of a psImage matrix and returns the single precision floating point result. The
+ *  input image must be square. This function operates only with the psF64 data type. GSL indexes the top row
+ *  as the zero row, not the bottom.
+ *
+ *  @return  float: Determinant from psImage.
+ */
+psF32 *psMatrixDeterminant(
+    const psImage* inMatrix        ///< Image used to calculate determinant.
+);
+
+/** Performs psImage matrix multiplication.
+ *
+ *  Performs a classical matrix multiplication involving row and column operations. Input images must be square
+ *  and the same size. If the user specifies NULL as the outImage argument, then it will automatically be
+ *  created. This function operates only with the psF64 data type. GSL indexes the top row as the
+ *  zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to resulting psImage.
+ */
+psImage* psMatrixMultiply(
+    psImage* outImage,                 ///< Matrix to return, or NULL.
+    psImage* inImage1,                 ///< First input image.
+    psImage* inImage2                  ///< Second input image.
+);
+
+/** Transpose matrix.
+ *
+ *  Performs psImage matrix transpose by substituting existing rows for columns. The input image must be
+ *  square. If the user specifies NULL as the outImage argument, then it will automaticallty be created.
+ *  This function operates only with the psF64 data type. GSL indexes the top row as the zero
+ *  row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to transposed psImage.
+ */
+psImage* psMatrixTranspose(
+    psImage* outImage,                 ///< Image to return, or NULL
+    const psImage* inImage             ///< Image to transpose
+);
+
+/** Calculate matrix eigenvectors.
+ *
+ *  Calculates the eigenvectors for a matrix. The input image must be symmetric and square. If the user
+ *  specifies NULL as the outImage argument, then it will automatically be created. This function operates
+ *  only with the psF64 data type. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to matrix of Eigenvectors.
+ */
+psImage* psMatrixEigenvectors(
+    psImage* outImage,                 ///< Eigenvectors to return, or NULL.
+    const psImage* inImage  ///< Input image.
+);
+
+/** Convert matrix to vector.
+ *
+ *  Converts a 1-d psImage matrix into a vector. If the user specifies NULL as the outVector argument, then it
+ *  will automatically be created based on the input image (PS_DIMEN_VECTOR for an input image with 1 col or
+ *  PS_DIMENT_TRANSV for an input image with 1 row). Either the number of rows or the number of colums of the
+ *  input matrix must be 1. This function operates only  with the psF64 data type.
+ *
+ *  @return  psVector* : Pointer to psVector.
+ */
+psVector* psMatrixToVector(
+    psVector* outVector,               ///< Vector to return, or NULL.
+    const psImage* inImage             ///< Image to convert.
+);
+
+/** Convert vector to matrix.
+ *
+ *  Converts a vector into a psImage matrix. If the dimensionality of the vector is PS_DIMEN_VECTOR, then the
+ *  resulting psImage is a 1d column. If the dimensionality of the vector is PS_DIMEN_TRANSV, then the
+ *  resulting psImage is a 1d row. If the user specifies NULL as the outImage argument,  then it will
+ *  automatically be created. This function operates only with the psF64 data type.
+ *
+ *  @return  psVector* : Pointer to psIamge.
+ */
+psImage* psVectorToMatrix(
+    psImage* outImage,                 ///< Matrix to return, or NULL.
+    const psVector* inVector           ///< Vector to convert.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMinimize.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMinimize.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMinimize.c	(revision 22271)
@@ -0,0 +1,2203 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain functions to minimize an arbitrary function at
+ *  a data point, fit an arbitrary function to a set of data points, and
+ *  fit a 1-D polynomial to a set of data points.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.116 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: must follow coding name standards on local functions.
+ *
+ */
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMinimize.h"
+#include "psStats.h"
+#include "psImageIO.h"
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+#define PS_SEG psLib
+#define PS_PWD dataManip
+#define PS_FILE psMinimize
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+static psMinimizeChi2PowellFunc Chi2PowellFunc = NULL;
+static psVector *myValue;
+static psVector *myError;
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/******************************************************************************
+p_psBuildSums1D(x, polyOrder, sums): this routine calculates the powers of
+input parameter "x" between 0 and input parameter polyOrder.  The result is
+returned as a psVector sums.
+ 
+XXX: Use a static vector.
+ *****************************************************************************/
+static void buildSums1D(psF64 x,
+                        psS32 polyOrder,
+                        psVector* sums)
+{
+    psS32 i = 0;
+    psF64 xSum = 0.0;
+
+    if (sums == NULL) {
+        sums = psVectorAlloc(polyOrder, PS_TYPE_F64);
+    }
+    if (polyOrder > sums->n) {
+        sums = psVectorRealloc(sums, polyOrder);
+    }
+
+    xSum = 1.0;
+    for (i = 0; i <= polyOrder; i++) {
+        sums->data.F64[i] = xSum;
+        xSum *= x;
+    }
+}
+
+/*****************************************************************************
+CalculateSecondDerivs(): Given a set of x/y vectors corresponding to a
+tabulated function at n points, this routine calculates the second
+derivatives of the interpolating cubic splines at those n points.
+ 
+The first and second derivatives at the endpoints, undefined in the SDR, are
+here defined to be 0.0.  They can be modified via ypo and yp1.
+ 
+This routine assumes that vectors x and y are of the appropriate types/sizes
+(F32).
+ 
+XXX: This algorithm is derived from the Numerical Recipes.
+XXX: use recycled vectors for internal data.
+XXX: do an F64 version?
+ *****************************************************************************/
+static psF32 *calculateSecondDerivs(const psVector* x,        ///< Ordinates (or NULL to just use the indices)
+                                    const psVector* y)        ///< Coordinates
+{
+    psTrace(".psLib.dataManip.calculateSecondDerivs", 4,
+            "---- calculateSecondDerivs() begin ----\n");
+
+    psS32 i;
+    psS32 k;
+    psF32 sig;
+    psF32 p;
+    psS32 n = y->n;
+    psF32 *u = (psF32 *) psAlloc(n * sizeof(psF32));
+    psF32 *derivs2 = (psF32 *) psAlloc(n * sizeof(psF32));
+    psF32 *X = (psF32 *) & (x->data.F32[0]);
+    psF32 *Y = (psF32 *) & (y->data.F32[0]);
+    psF32 qn;
+
+    // XXX: The second derivatives at the endpoints, undefined in the SDR,
+    // are set in psConstants.h: PS_LEFT_SPLINE_DERIV, PS_RIGHT_SPLINE_DERIV.
+    derivs2[0] = -0.5;
+    u[0]= (3.0/(X[1]-X[0])) * ((Y[1]-Y[0])/(X[1]-X[0]) - PS_LEFT_SPLINE_DERIV);
+
+    for (i=1;i<=(n-2);i++) {
+        sig = (X[i] - X[i-1]) / (X[i+1] - X[i-1]);
+        p = sig * derivs2[i-1] + 2.0;
+        derivs2[i] = (sig - 1.0) / p;
+        u[i] = ((Y[i+1] - Y[i])/(X[i+1]-X[i])) - ((Y[i]-Y[i-1])/(X[i]-X[i-1]));
+        u[i] = ((6.0 * u[i] / (X[i+1] - X[i-1])) - (sig * u[i-1])) / p;
+
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "X[%d] is %f\n", i, X[i]);
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "Y[%d] is %f\n", i, Y[i]);
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "u[%d] is %f\n", i, u[i]);
+    }
+
+    qn = 0.5;
+    u[n-1] = (3.0/(X[n-1]-X[n-2])) * (PS_RIGHT_SPLINE_DERIV - (Y[n-1]-Y[n-2])/(X[n-1]-X[n-2]));
+    derivs2[n-1] = (u[n-1] - (qn * u[n-2])) / ((qn * derivs2[n-2]) + 1.0);
+
+    for (k=(n-2);k>=0;k--) {
+        derivs2[k] = derivs2[k] * derivs2[k+1] + u[k];
+
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "derivs2[%d] is %f\n", k, derivs2[k]);
+    }
+
+    psFree(u);
+    psTrace(".psLib.dataManip.calculateSecondDerivs", 4,
+            "---- calculateSecondDerivs() end ----\n");
+    return(derivs2);
+}
+
+/******************************************************************************
+p_psNRSpline1DEval(): This routine does NR-style evaluation of cubic splines.
+It takes advantage of the 2nd derivatives of the cubic splines, which are
+stored in the psSPline1D data structure, and computes the interpolated value
+directly, without computing (or using) the interpolating cubic spline
+polynomial.
+ 
+This routine is here mostly for a sanity check on the psLib function
+evalSpline() which computes the interpolated value based on the cubic spline
+polynomials which are stored in psSpline1D.
+ 
+XXX: This is F32 only
+ 
+XXX: spline->knots must be psF32
+ *****************************************************************************/
+/*
+psF32 p_psNRSpline1DEval(psSpline1D *spline,
+                         const psVector* x,
+                         const psVector* y,
+                         psF32 X)
+{
+    PS_PTR_CHECK_NULL(spline, NAN);
+    PS_INT_CHECK_NON_NEGATIVE(spline->n, NAN);
+    PS_VECTOR_CHECK_NULL(spline->domains, NAN);
+    PS_PTR_CHECK_NULL(spline->p_psDeriv2, NAN);
+    PS_VECTOR_CHECK_NULL(x, NAN);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_NULL(y, NAN);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NULL);
+ 
+    psS32 n;
+    psS32 klo;
+    psS32 khi;
+    psF32 H;
+    psF32 A;
+    psF32 B;
+    psF32 C;
+    psF32 D;
+    psF32 Y;
+ 
+    n = spline->n;
+    klo = p_psVectorBinDisect32(spline->knots->data.F32, (spline->n)+1, X);
+    if (klo < 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psMinimize.c: p_psNRSpline1DEval(): p_psVectorBinDisect32 returned an error (%d).\n", klo);
+        return(NAN);
+    }
+    khi = klo + 1;
+    H = spline->knots->data.F32[khi] - spline->knots->data.F32[klo];
+    A = (spline->knots->data.F32[khi] - X) / H;
+    B = (X - spline->knots->data.F32[klo]) / H;
+    C = ((A*A*A)-A) * (H*H/6.0);
+    D = ((B*B*B)-B) * (H*H/6.0);
+ 
+    Y = (A * y->data.F32[klo]) +
+        (B * y->data.F32[khi]) +
+        (C * (spline->p_psDeriv2)[klo]) +
+        (D * (spline->p_psDeriv2)[khi]);
+ 
+    return(Y);
+}
+*/
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+/*****************************************************************************
+psVectorFitSpline1D(): given a psSpline1D data structure and a set of x/y
+vectors, this routine generates the linear or cublic splines which satisfy
+those data points.
+ 
+The formula for calculating the spline polynomials is derived from Numerical
+Recipes in C.  The basic idea is that the polynomial is
+ (1)     y = (A * y[0]) +
+ (2)         (B * y[1]) +
+ (3)         ((((A*A*A)-A) * mySpline->p_psDeriv2[0]) * H^2)/6.0 +
+ (4)         ((((B*B*B)-B) * mySpline->p_psDeriv2[1]) * H^2)/6.0
+Where:
+ H = x[1]-x[0]
+ A = (x[1]-x)/H
+ B = (x-x[0])/H
+The bulk of the code in this routine is the expansion of the above equation
+into a polynomial in terms of x, and then saving the coefficients of the
+powers of x in the spline polynomials.  This gets pretty complicated.
+ 
+XXX: usage of yErr is not specified in IfA documentation.
+ 
+XXX: Is the x argument redundant?  What do we do if the x argument is
+supplied, but does not equal the knots specified in mySpline?
+ 
+XXX: can psSpline be NULL?
+ 
+XXX: reimplement this assuming that mySpline is NULL?
+ 
+XXX: What happens if X is NULL, then an index vector is generated for X, but
+that index vector lies outside the range vectors in mySpline?
+ 
+XXX: Assumes mySpline->knots is psF32.  Must add psU32 and psF64.
+ *****************************************************************************/
+psSpline1D *psVectorFitSpline1D(psSpline1D *mySpline,     ///< The spline which will be generated.
+                                const psVector* x,        ///< Ordinates (or NULL to just use the indices)
+                                const psVector* y,        ///< Coordinates
+                                const psVector* yErr)     ///< Errors in coordinates, or NULL
+{
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(y, NULL);
+    if (mySpline != NULL) {
+        PS_VECTOR_CHECK_TYPE(mySpline->knots, PS_TYPE_F32, NULL);
+    }
+
+    psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
+            "---- psVectorFitSpline1D() begin ----\n");
+    psS32 numSplines = (y->n)-1;
+    psF32 tmp;
+    psF32 H;
+    psS32 i;
+    psF32 slope;
+    psVector *x32 = NULL;
+    psVector *y32 = NULL;
+    psVector *yErr32 = NULL;
+    static psVector *x32Static = NULL;
+    static psVector *y32Static = NULL;
+    static psVector *yErr32Static = NULL;
+
+    PS_VECTOR_CONVERT_F64_TO_F32_STATIC(y, y32, y32Static);
+
+    // If yErr==NULL, set all errors equal.
+    if (yErr == NULL) {
+        PS_VECTOR_GEN_YERR_STATIC_F32(yErr32Static, y->n);
+        yErr32 = yErr32Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(yErr, NULL);
+        PS_VECTOR_CONVERT_F64_TO_F32_STATIC(yErr, yErr32, yErr32Static);
+    }
+
+    // If x==NULL, create an x32 vector with x values set to (0:n).
+    if (x == NULL) {
+        PS_VECTOR_GEN_X_INDEX_STATIC_F32(x32Static, y->n);
+        x32 = x32Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+        PS_VECTOR_CONVERT_F64_TO_F32_STATIC(x, x32, x32Static);
+    }
+    PS_VECTOR_CHECK_SIZE_EQUAL(x32, y32, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(yErr32, y32, NULL);
+
+    /*
+        XXX:
+        This can not be implemented until SDR states what order spline should be
+        created.
+        Should we error if mySpline is not NULL?
+        Should we error if mySPline is not NULL?
+    */
+    if (mySpline == NULL) {
+        mySpline = psSpline1DAllocGeneric(x32, 3);
+    }
+    PS_PTR_CHECK_NULL(mySpline, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(mySpline->n, NULL);
+
+    if (y32->n != (1 + mySpline->n)) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "data size / spline size mismatch (%d %d)\n",
+                y32->n, mySpline->n);
+        return(NULL);
+    }
+
+    // If these are linear splines, which means their polynomials will have
+    // two coefficients, then we do the simple calculation.
+    if (2 == (mySpline->spline[0])->n) {
+        for (i=0;i<mySpline->n;i++) {
+            slope = (y32->data.F32[i+1] - y32->data.F32[i]) /
+                    (mySpline->knots->data.F32[i+1] - mySpline->knots->data.F32[i]);
+            (mySpline->spline[i])->coeff[0] = y32->data.F32[i] -
+                                              (slope * mySpline->knots->data.F32[i]);
+
+            (mySpline->spline[i])->coeff[1] = slope;
+            psTrace(".psLib.dataManip.psMinimize.psVectorFitSpline1D", 4,
+                    "---- mySpline %d coeffs are (%f, %f)\n", i,
+                    (mySpline->spline[i])->coeff[0],
+                    (mySpline->spline[i])->coeff[1]);
+        }
+        psTrace(".psLib.dataManip.psMinimize.psVectorFitSpline1D", 4,
+                "---- Exiting psVectorFitSpline1D()()\n");
+        return((psSpline1D *) mySpline);
+    }
+
+    // Check if these are cubic splines (n==4).  If not, psError.
+    if (4 != (mySpline->spline[0])->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Don't know how to generate %d-order splines.",
+                (mySpline->spline[0])->n-1);
+        return(NULL);
+    }
+
+    // If we get here, then we know these are cubic splines.  We first
+    // generate the second derivatives at each data point.
+    mySpline->p_psDeriv2 = calculateSecondDerivs(x32, y32);
+    for (i=0;i<y32->n;i++)
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "Second deriv[%d] is %f\n", i, mySpline->p_psDeriv2[i]);
+
+    // We generate the coefficients of the spline polynomials.  I can't
+    // concisely explain how this code works.  See above function comments
+    // and Numerical Recipes in C.
+    for (i=0;i<numSplines;i++) {
+        H = x32->data.F32[i+1] - x32->data.F32[i];
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
+                "x data (%f - %f) (%f)\n",
+                x32->data.F32[i],
+                x32->data.F32[i+1], H);
+        //
+        // ******** Calculate 0-order term ********
+        //
+        // From (1)
+        (mySpline->spline[i])->coeff[0] = (y32->data.F32[i] * x32->data.F32[i+1]/H);
+        // From (2)
+        ((mySpline->spline[i])->coeff[0])-= ((y32->data.F32[i+1] * x32->data.F32[i])/H);
+        // From (3)
+        tmp = (x32->data.F32[i+1] * x32->data.F32[i+1] * x32->data.F32[i+1]) / (H * H * H);
+        tmp-= (x32->data.F32[i+1] / H);
+        tmp*= (mySpline->p_psDeriv2)[i] * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[0])+= tmp;
+        // From (4)
+        tmp = -(x32->data.F32[i] * x32->data.F32[i] * x32->data.F32[i]) / (H * H * H);
+        tmp+= (x32->data.F32[i] / H);
+        tmp*= (mySpline->p_psDeriv2)[i+1] * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[0])+= tmp;
+
+        //
+        // ******** Calculate 1-order term ********
+        //
+        // From (1)
+        (mySpline->spline[i])->coeff[1] = -(y32->data.F32[i]) / H;
+        // From (2)
+        ((mySpline->spline[i])->coeff[1])+= (y32->data.F32[i+1] / H);
+        // From (3)
+        tmp = -3.0 * (x32->data.F32[i+1] * x32->data.F32[i+1]) / (H * H * H);
+        tmp+= (1.0 / H);
+        tmp*= ((mySpline->p_psDeriv2)[i]) * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[1])+= tmp;
+        // From (4)
+        tmp = 3.0 * (x32->data.F32[i] * x32->data.F32[i]) / (H * H * H);
+        tmp-= (1.0 / H);
+        tmp*= ((mySpline->p_psDeriv2)[i+1]) * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[1])+= tmp;
+
+        //
+        // ******** Calculate 2-order term ********
+        //
+        // From (3)
+        (mySpline->spline[i])->coeff[2] = ((mySpline->p_psDeriv2)[i]) * 3.0 * x32->data.F32[i+1] / (6.0 * H);
+        // From (4)
+        ((mySpline->spline[i])->coeff[2])-= (((mySpline->p_psDeriv2)[i+1]) * 3.0 * x32->data.F32[i] / (6.0 * H));
+
+        //
+        // ******** Calculate 3-order term ********
+        //
+        // From (3)
+        (mySpline->spline[i])->coeff[3] = -((mySpline->p_psDeriv2)[i]) / (6.0 * H);
+        // From (4)
+        ((mySpline->spline[i])->coeff[3])+=  ((mySpline->p_psDeriv2)[i+1]) / (6.0 * H);
+
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[0] is %f\n", i, (mySpline->spline[i])->coeff[0]);
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[1] is %f\n", i, (mySpline->spline[i])->coeff[1]);
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[2] is %f\n", i, (mySpline->spline[i])->coeff[2]);
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[3] is %f\n", i, (mySpline->spline[i])->coeff[3]);
+
+    }
+
+    psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
+            "---- psVectorFitSpline1D() end ----\n");
+    return(mySpline);
+}
+
+
+/******************************************************************************
+XXX: We assume unnormalized gaussians.
+ *****************************************************************************/
+psVector *psMinimizeLMChi2Gauss1D(psImage *deriv,
+                                  const psVector *params,
+                                  const psArray *coords)
+{
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(params, NULL);
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss1D() begin ----\n");
+    psF32 x;
+    psS32 i;
+    psF32 mean = params->data.F32[0];
+    psF32 stdev = params->data.F32[1];
+    psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
+
+    psTrace(".psLib.dataManip.psMinimize", 6,
+            "(mean, stdev) is (%f, %f)\n", mean, stdev);
+
+    if (deriv == NULL) {
+        deriv = psImageAlloc(params->n, coords->n, PS_TYPE_F32);
+    } else {
+        PS_IMAGE_CHECK_SIZE(deriv, params->n, coords->n, NULL);
+        PS_IMAGE_CHECK_TYPE(deriv, PS_TYPE_F32, NULL);
+    }
+
+    for (i=0;i<coords->n;i++) {
+        x = ((psVector *) (coords->data[i]))->data.F32[0];
+        out->data.F32[i] = psGaussian(x, mean, stdev, false);
+    }
+
+    for (i=0;i<coords->n;i++) {
+        x = ((psVector *) (coords->data[i]))->data.F32[0];
+        psF32 tmp = (x - mean) * psGaussian(x, mean, stdev, false);
+        deriv->data.F32[i][0] = tmp / (stdev * stdev);
+        tmp = (x - mean) * (x - mean) *
+              psGaussian(x, mean, stdev, 0);
+        deriv->data.F32[i][1] = tmp / (stdev * stdev * stdev);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss1D() end ----\n");
+    return(out);
+}
+
+/*
+XXX: from bug 230:
+ 
+We first perform a rotation:
+u = - (x-x0)*cos(theta) + (y-y0)*sin(theta)
+v = (x-x0)*cos(theta) + (y-y0)*sin(theta)
+ 
+Here u is the major axis, and v is the minor axis, x0,y0 is the centre, and
+theta is the position angle.
+ 
+Then the flux is
+ 
+flux = norm * exp(-( u*u/2.0/sigmau/sigmau + v*v/2.0/sigmav/sigmav)
+)/2.0/pi/sigmau/sigmav
+ 
+Here sigmau and sigmav are the widths of the major and minor axes.
+ 
+The "norm" parameter in the equation above corresponds to the normalisation.
+ 
+Suggest order:
+ 
+norm
+x0
+y0
+sigma_u
+sigma_v
+theta
+*/
+
+psVector *psMinimizeLMChi2Gauss2D(psImage *deriv,
+                                  const psVector *params,
+                                  const psArray *coords)
+{
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(params, NULL);
+
+    psF64 normalization = params->data.F32[0];
+    psF64 x0 = params->data.F32[1];
+    psF64 y0 = params->data.F32[2];
+    psF64 sigmaX = params->data.F32[3];
+    psF64 sigmaY = params->data.F32[4];
+    psF64 theta = params->data.F32[5];
+    psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
+
+    if (deriv == NULL) {
+        deriv = psImageAlloc(params->n, coords->n, PS_TYPE_F32);
+    } else {
+        PS_IMAGE_CHECK_SIZE(deriv, 6, coords->n, NULL);
+        PS_IMAGE_CHECK_TYPE(deriv, PS_TYPE_F32, NULL);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss2D() begin ----\n");
+
+    for (psS32 i=0;i<coords->n;i++) {
+        psF64 x = ((psVector *) coords->data[i])->data.F32[0];
+        psF64 y = ((psVector *) coords->data[i])->data.F32[0];
+
+        psF64 u = - (x-x0)*cos(theta) + (y-y0)*sin(theta);
+        psF64 v = (x-x0)*cos(theta) + (y-y0)*sin(theta);
+
+        psF64 flux = normalization * exp(-( u*u/(2.0 * sigmaX * sigmaX) +
+                                            v*v/(2.0 * sigmaY * sigmaY)))/
+                     (2.0 * M_PI * sigmaX * sigmaY);
+        out->data.F32[i] = flux;
+
+        // XXX: Calculate these correctly.
+        deriv->data.F32[i][0] = 0.0;
+        deriv->data.F32[i][1] = 0.0;
+        deriv->data.F32[i][2] = 0.0;
+        deriv->data.F32[i][3] = 0.0;
+        deriv->data.F32[i][4] = 0.0;
+        deriv->data.F32[i][5] = 0.0;
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss2D() end ----\n");
+    return(out);
+}
+
+psF64 p_psImageGetElementF64(psImage *a, int i, int j);
+
+// XXX EAM this is my re-implementation of MinLM
+psBool psMinimizeLMChi2(psMinimization *min,
+                        psImage *covar,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *x,
+                        const psVector *y,
+                        const psVector *yErr,
+                        psMinimizeLMChi2Func func)
+{
+    PS_PTR_CHECK_NULL(min, NULL);
+    PS_VECTOR_CHECK_NULL(params, NULL);
+    PS_VECTOR_CHECK_EMPTY(params, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_EMPTY(y, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
+    PS_PTR_CHECK_NULL(func, NULL);
+
+    // this function has test and current values for several things
+    // the current best value is in lower case
+    // the next guess value is in upper case
+
+    // allocate internal arrays (current vs Guess)
+    psImage *alpha   = psImageAlloc (params->n, params->n, PS_TYPE_F64);
+    psImage *Alpha   = psImageAlloc (params->n, params->n, PS_TYPE_F64);
+    psVector *beta   = psVectorAlloc (params->n, PS_TYPE_F64);
+    psVector *Beta   = psVectorAlloc (params->n, PS_TYPE_F64);
+    psVector *Params = psVectorAlloc (params->n, PS_TYPE_F64);
+    psVector *dy     = NULL;
+    psF64 chisq = 0.0;
+    psF64 Chisq = 0.0;
+    psF64 lambda = 0.001;
+
+    // the initial guess on params is provided by the user
+    Params = psVectorCopy (Params, params, PS_TYPE_F32);
+
+    // the user provides the error or NULL.  we need to convert
+    // to appropriate weights
+    dy = psVectorAlloc (y->n, PS_TYPE_F32);
+    if (yErr != NULL) {
+        for (int i = 0; i < dy->n; i++) {
+            dy->data.F32[i] = 1.0 / PS_SQR (yErr->data.F32[i]);
+        }
+    } else {
+        for (int i = 0; i < dy->n; i++) {
+            dy->data.F32[i] = 1.0;
+        }
+    }
+
+    // calculate initial alpha and beta, set chisq (min->value)
+    min->value = p_psMinLM_SetABX (alpha, beta, params, x, y, dy, func);
+    # ifndef PS_NO_TRACE
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
+        p_psImagePrint  (psTraceGetDestination(), alpha, "alpha guess");
+        p_psVectorPrint (psTraceGetDestination(), beta, "beta guess");
+        p_psVectorPrint (psTraceGetDestination(), params, "params guess");
+    }
+    # endif /* PS_NO_TRACE */
+
+
+    // iterate until the tolerance is reached, or give up
+    while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
+
+        // set a new guess for Alpha, Beta, Params
+        p_psMinLM_GuessABP (Alpha, Beta, Params, alpha, beta, params, lambda);
+
+        # ifndef PS_NO_TRACE
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
+            p_psImagePrint  (psTraceGetDestination(), Alpha, "alpha guess");
+            p_psVectorPrint (psTraceGetDestination(), Beta, "beta guess");
+            p_psVectorPrint (psTraceGetDestination(), Params, "params guess");
+        }
+        # endif /* PS_NO_TRACE */
+
+        // calculate Chisq for new guess, update Alpha & Beta
+        Chisq = p_psMinLM_SetABX (Alpha, Beta, Params, x, y, dy, func);
+        psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
+
+        // accept new guess (if improvement), or increase lambda
+        if (Chisq < min->value) {
+            min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
+            min->value = Chisq;
+            alpha  = psImageCopy (alpha, Alpha, PS_TYPE_F64);
+            beta   = psVectorCopy (beta, Beta, PS_TYPE_F64);
+            params = psVectorCopy (params, Params, PS_TYPE_F32);
+            lambda *= 0.1;
+        } else {
+            lambda *= 10.0;
+        }
+        min->iter ++;
+    }
+    psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
+
+    // free the internal temporary data
+    psFree (alpha);
+    psFree (Alpha);
+    psFree (beta);
+    psFree (Beta);
+    psFree (Params);
+    psFree (dy);
+    return (true);
+}
+
+// XXX EAM: this needs to respect the mask on params
+// XXX EAM: check not NULL on alpha, beta, params
+// alpha, beta, params are already allocated
+psF64 p_psMinLM_SetABX (psImage  *alpha,
+                        psVector *beta,
+                        psVector *params,
+                        const psArray  *x,
+                        const psVector *y,
+                        const psVector *dy,
+                        psMinimizeLMChi2Func func)
+{
+
+    psF64 chisq;
+    psF64 delta;
+    psF64 weight;
+    psF64 ymodel;
+    psVector *deriv = psVectorAlloc (params->n, PS_TYPE_F32);
+
+    // zero alpha and beta for summing below
+    for (int j = 0; j < params->n; j++) {
+        for (int k = 0; k < params->n; k++) {
+            alpha->data.F64[j][k] = 0;
+        }
+        beta->data.F64[j] = 0;
+    }
+    chisq = 0.0;
+
+    // calculate chisq, alpha, beta
+    for (int i = 0; i < y->n; i++) {
+        ymodel = func (deriv, params, (psVector *) x->data[i]);
+
+        delta = ymodel - y->data.F32[i];
+        chisq += PS_SQR (delta) * dy->data.F32[i];
+
+        for (int j = 0; j < params->n; j++) {
+            weight = deriv->data.F32[j] * dy->data.F32[i];
+            for (int k = 0; k <= j; k++) {
+                alpha->data.F64[j][k] += weight * deriv->data.F32[k];
+            }
+            beta->data.F64[j] += weight * delta;
+        }
+    }
+
+    // calculate lower-left half of alpha
+    for (int j = 1; j < params->n; j++) {
+        for (int k = 0; k < j; k++) {
+            alpha->data.F64[k][j] = alpha->data.F64[j][k];
+        }
+    }
+    psFree (deriv);
+    return (chisq);
+}
+
+// XXX EAM : can we use static copies of LUv, LUm, A?
+psBool p_psMinLM_GuessABP (psImage  *Alpha,
+                           psVector *Beta,
+                           psVector *Params,
+                           psImage  *alpha,
+                           psVector *beta,
+                           psVector *params,
+                           psF64 lambda)
+{
+
+    # define USE_LU_DECOMP 1
+    # if (USE_LU_DECOMP)
+        psVector *LUv = NULL;
+    psImage  *LUm = NULL;
+    psImage  *A   = NULL;
+    psF32    det;
+
+    // LU decomposition version
+    psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using LUD version");
+
+    // set new guess values (creates matrix A)
+    A = psImageCopy (NULL, alpha, PS_TYPE_F64);
+    for (int j = 0; j < params->n; j++) {
+        A->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
+    }
+
+    // solve A*beta = Beta (Alpha = 1/A)
+    // these operations do not modify the input values (creates LUm, LUv)
+    LUm   = psMatrixLUD (NULL, &LUv, A);
+    Beta  = psMatrixLUSolve (Beta, LUm, beta, LUv);
+    Alpha = psMatrixInvert (Alpha, A, &det);
+
+    # else
+        // gauss-jordan version
+        psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using Gauss-J version");
+
+    // set new guess values (creates matrix A)
+    Beta = psVectorCopy (Beta, beta, PS_TYPE_F64);
+    Alpha = psImageCopy (Alpha, alpha, PS_TYPE_F64);
+    for (int j = 0; j < params->n; j++) {
+        Alpha->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
+    }
+
+    psGaussJordan (Alpha, Beta);
+    # endif
+
+    // apply beta to get new params values
+    for (int j = 0; j < params->n; j++) {
+        Params->data.F32[j] = params->data.F32[j] - Beta->data.F64[j];
+    }
+
+    # if (USE_LU_DECOMP)
+        psFree (A);
+    psFree (LUm);
+    psFree (LUv);
+    # endif
+
+    return true;
+}
+
+// XXX" put this in constants.h
+# define PS_SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
+
+// XXX EAM : temporary gauss-jordan solver based on gene's
+// version based on the Numerical Recipes version
+bool psGaussJordan (psImage *a, psVector *b)
+{
+
+    int *indxc,*indxr,*ipiv;
+    int Nx, icol, irow;
+    int i, j, k, l, ll;
+    float big, dum, pivinv;
+    psF64 *vector;
+    psF64 **matrix;
+
+    Nx = a->numCols;
+    matrix = a->data.F64;
+    vector = b->data.F64;
+
+    indxc = psAlloc (Nx*sizeof(int));
+    indxr = psAlloc (Nx*sizeof(int));
+    ipiv  = psAlloc (Nx*sizeof(int));
+    for (j = 0; j < Nx; j++) {
+        ipiv[j] = 0;
+    }
+
+    irow = icol = 0;
+    big = fabs(matrix[0][0]);
+
+    for (i = 0; i < Nx; i++) {
+        big = 0.0;
+        for (j = 0; j < Nx; j++) {
+            if (!isfinite(matrix[i][j])) {
+                // XXX EAM: this should use the psError stack
+                fprintf (stderr, "GAUSSJ: NaN\n");
+                goto fescape;
+            }
+            if (ipiv[j] != 1) {
+                for (k = 0; k < Nx; k++) {
+                    if (ipiv[k] == 0) {
+                        if (fabs (matrix[j][k]) >= big) {
+                            big  = fabs (matrix[j][k]);
+                            irow = j;
+                            icol = k;
+                        }
+                    } else {
+                        if (ipiv[k] > 1) {
+                            // XXX EAM: this should use the psError stack
+                            fprintf (stderr, "GAUSSJ: Singular Matrix! (1)\n");
+                            goto fescape;
+                        }
+                    }
+                }
+            }
+        }
+        ipiv[icol]++;
+        if (irow != icol) {
+            for (l = 0; l < Nx; l++) {
+                PS_SWAP (matrix[irow][l], matrix[icol][l]);
+            }
+            PS_SWAP (vector[irow], vector[icol]);
+        }
+        indxr[i] = irow;
+        indxc[i] = icol;
+        if (matrix[icol][icol] == 0.0) {
+            // XXX EAM: this should use the psError stack
+            fprintf (stderr, "GAUSSJ: Singular Matrix! (2)\n");
+            goto fescape;
+        }
+        pivinv = 1.0 / matrix[icol][icol];
+        matrix[icol][icol] = 1.0;
+        for (l = 0; l < Nx; l++) {
+            matrix[icol][l] *= pivinv;
+        }
+        vector[icol] *= pivinv;
+
+        for (ll = 0; ll < Nx; ll++) {
+            if (ll != icol) {
+                dum = matrix[ll][icol];
+                matrix[ll][icol] = 0.0;
+                for (l = 0; l < Nx; l++) {
+                    matrix[ll][l] -= matrix[icol][l]*dum;
+                }
+                vector[ll] -= vector[icol]*dum;
+            }
+        }
+    }
+
+    for (l = Nx - 1; l >= 0; l--) {
+        if (indxr[l] != indxc[l]) {
+            for (k = 0; k < Nx; k++) {
+                PS_SWAP (matrix[k][indxr[l]], matrix[k][indxc[l]]);
+            }
+        }
+    }
+    psFree (ipiv);
+    psFree (indxr);
+    psFree (indxc);
+    return (true);
+
+fescape:
+    psFree (ipiv);
+    psFree (indxr);
+    psFree (indxc);
+    return (false);
+}
+
+/******************************************************************************
+psMinimizeLMChi2():  This routine will take an procedure which calculates
+an arbitrary function and it's derivative and minimize the chi-squared match
+between that function at the specified coords and the specified value at
+those coords.
+ 
+XXX: Do this:
+ After checking that all entries in the paramMask are 1 or 0, when
+ forming the A matrix from alpha, try this:
+ 
+     A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i];
+ 
+XXX: This is very different from what is specified in the SDR.  Must
+coordinate with IfA on new SDR.
+ 
+XXX: Do vector/image recycles.
+ 
+XXX: probably yErr will be part of the SDR.
+ 
+XXX: This must work for both F32 and F64.  F32 is currently implemented.
+     Note: since the LUD routines are only implemented in F64, then we
+     will have to convert all F32 input vectors to F64 regardless.  So,
+     the F64 port might be.
+ 
+XXX: Must update the covar matrix.
+ *****************************************************************************/
+psBool psMinimizeLMChi2Old(psMinimization *min,
+                           psImage *covar,
+                           psVector *params,
+                           const psVector *paramMask,
+                           const psArray *x,
+                           const psVector *y,
+                           const psVector *yErr,
+                           psMinimizeLMChi2Func func)
+{
+    PS_PTR_CHECK_NULL(min, NULL);
+    PS_VECTOR_CHECK_NULL(params, NULL);
+    PS_VECTOR_CHECK_EMPTY(params, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_EMPTY(y, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
+    PS_PTR_CHECK_NULL(func, NULL);
+
+    if (paramMask != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(params, paramMask, NULL);
+    }
+    if (yErr != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(y, yErr, NULL);
+    }
+    if (covar != NULL) {
+        PS_IMAGE_CHECK_SIZE(covar, params->n, params->n, NULL);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2() begin ----\n");
+    psS32 numData = y->n;
+    psS32 numParams = params->n;
+    psS32 i;
+    psS32 j;
+    psS32 k;
+    psS32 l;
+    psS32 n;
+    psS32 p;
+    psVector *beta = psVectorAlloc(numParams, PS_TYPE_F64);
+    psVector *perm = NULL;
+
+    psVector *paramDeltasF64 = psVectorAlloc(numParams, PS_TYPE_F64);
+    psVector *origParams = psVectorAlloc(numParams, PS_TYPE_F32);
+    psVector *newParams = psVectorAlloc(numParams, PS_TYPE_F32);
+
+    psImage *alpha = psImageAlloc(numParams, numParams, PS_TYPE_F32);
+    psImage *A = psImageAlloc(numParams, numParams, PS_TYPE_F64);
+    psImage *aOut = psImageAlloc(numParams, numParams, PS_TYPE_F64);
+    psImage *deriv = psImageAlloc(numParams, numData, PS_TYPE_F32);
+    psVector *currValueVec = NULL;
+    psVector *newValueVec = NULL;
+    psF32 currChi2 = 0.0;
+    psF32 newChi2 = 0.0;
+    psF32 lamda = 0.00005;  // XXX EAM : this starting value is VERY small (lamda is mis-spelt)
+    lamda = 0.05;  // XXX EAM : this starting value is quite large (lamda is mis-spelt)
+
+    psTrace(".psLib.dataManip.psMinimize", 6,
+            "min->maxIter is %d\n", min->maxIter);
+    psTrace(".psLib.dataManip.psMinimize", 6,
+            "min->tol is %f\n", min->tol);
+
+    for (p=0;p<numParams;p++) {
+        origParams->data.F32[p] = params->data.F32[p];
+    }
+
+    min->lastDelta = PS_MAX_F32;
+    min->iter = 0;
+
+    while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "------------------------------------------------------\n");
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "Iteration %d.  Delta is %f\n", min->iter, min->lastDelta);
+
+        //
+        // Calculate the current values and chi-squared of the function.
+        //
+        currChi2 = 0.0;
+        // currValueVec = func(deriv, params, x);
+
+        // XXX EAM: use BinaryOp ?
+        // t1 = BinaryOp (NULL, currValueVec, "-", y);
+        // t1 = BinaryOp (t1, t1, "*", t1);
+
+        // XXX EAM: this ignores yErr
+        for (n=0;n<numData;n++) {
+            currChi2+= (currValueVec->data.F32[n] - y->data.F32[n]) *
+                       (currValueVec->data.F32[n] - y->data.F32[n]);
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "data[%d], chi2 calculation+= (%f - %f)^2\n", n,
+                    currValueVec->data.F32[n], y->data.F32[n]);
+        }
+
+        // XXX EAM: this is just for tracing
+        for (p=0;p<numParams;p++) {
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "params->data.F32[%d] is %f.\n", p, params->data.F32[p]);
+        }
+        psTrace(".psLib.dataManip.psMinimize", 6,
+                "Current chi-squared is (%f)\n", currChi2);
+
+        //
+        // Mask elements of the derivative for each data point.
+        // XXX EAM : is this necessary?  probably not...
+        for (p=0;p<numParams;p++) {
+            if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
+                for (n=0;n<numData;n++) {
+                    deriv->data.F32[n][p] = 0.0;
+                }
+            }
+        }
+
+        //
+        // Calculate the BETA vector.
+        // XXX EAM: I think this is wrong
+        for (p=0;p<numParams;p++) {
+            if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
+                continue;
+            }
+            beta->data.F64[p] = 0.0;
+            for (n=0;n<numData;n++) {
+                (beta->data.F64[p])+=
+                    (y->data.F32[n] - currValueVec->data.F32[n]) *
+                    deriv->data.F32[n][p];
+            }
+            // XXX: multiply by -1 here?
+            (beta->data.F64[p])*= -1.0;
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "beta->data.F64[%d] is %f.\n", p, beta->data.F64[p]);
+        }
+        psFree(currValueVec);
+
+        //
+        // Calculate the ALPHA matrix.
+        // XXX EAM: also wrong? (missing yErr)
+        for (k=0;k<numParams;k++) {
+            for (l=0;l<numParams;l++) {
+                alpha->data.F32[k][l] = 0.0;
+                for (n=0;n<numData;n++) {
+                    alpha->data.F32[k][l]+= deriv->data.F32[n][k] *
+                                            deriv->data.F32[n][l];
+                }
+            }
+        }
+
+        //
+        // Calculate the matrix A.
+        //
+        for (j=0;j<numParams;j++) {
+            for (k=0;k<numParams;k++) {
+                if (j == k) {
+                    A->data.F64[j][k] =
+                        (psF64) ((1.0 + lamda) * alpha->data.F32[j][k]);
+                } else {
+                    A->data.F64[j][k] = (psF64) alpha->data.F32[j][k];
+                }
+            }
+        }
+        for (j=0;j<numParams;j++) {
+            psTrace(".psLib.dataManip.psMinimize", 6, "Matrix A[][]:\n");
+            for (k=0;k<numParams;k++) {
+                psTrace(".psLib.dataManip.psMinimize", 6, "%f ", A->data.F64[j][k]);
+            }
+            psTrace(".psLib.dataManip.psMinimize", 6, "Matrix A[][]:\n");
+        }
+
+        //
+        // Solve A * alpha = Beta
+        //
+        // XXX: How do we know if these functions were successful?
+        //
+        aOut = psMatrixLUD(aOut, &perm, A);
+        paramDeltasF64 = psMatrixLUSolve(paramDeltasF64, aOut, beta, perm);
+
+        //
+        // Mask any masked parameters.
+        //
+        for (i=0;i<numParams;i++) {
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "paramDeltasF64->data.F64[%d] is %f.\n", i, paramDeltasF64->data.F64[i]);
+            if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) {
+                newParams->data.F32[i] = origParams->data.F32[i];
+            } else {
+                newParams->data.F32[i] = params->data.F32[i] -
+                                         (psF32) paramDeltasF64->data.F64[i];
+            }
+        }
+
+        psTrace(".psLib.dataManip.psMinimize", 6,
+                "Calling func() with new parameters:\n");
+        for (i=0;i<numParams;i++) {
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "newParams->data.F32[%d] is %f.\n", i, newParams->data.F32[i]);
+        }
+
+
+        //
+        // Calculate new function values.
+        //
+        newChi2 = 0.0;
+        // newValueVec = func(deriv, newParams, x);
+        for (n=0;n<numData;n++) {
+            newChi2+= (newValueVec->data.F32[n] - y->data.F32[n]) *
+                      (newValueVec->data.F32[n] - y->data.F32[n]);
+
+        }
+        psFree(newValueVec);
+
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "old/new chi-squareds are (%f, %f)\n", currChi2, newChi2);
+
+        //
+        // If the new chi-squared is lower, then keep it.
+        //
+        if (currChi2 > newChi2) {
+            min->lastDelta = (currChi2 - newChi2)/currChi2;
+            min->value = newChi2;
+
+            // We already masked params.
+            for (i=0;i<numParams;i++) {
+                params->data.F32[i] = (psF32) newParams->data.F32[i];
+            }
+            lamda*= 0.1;
+            psTrace(".psLib.dataManip.psMinimize", 4, "*** Reducing lamda by factor of 10\n");
+        } else {
+            lamda*= 10.0;
+            psTrace(".psLib.dataManip.psMinimize", 4, "*** Increasing lamda by factor of 10\n");
+        }
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "lamda is %f\n", lamda);
+        min->iter++;
+    }
+    psFree(beta);
+    psFree(perm);
+    psFree(paramDeltasF64);
+    psFree(origParams);
+    psFree(newParams);
+    psFree(alpha);
+    psFree(A);
+    psFree(aOut);
+    psFree(deriv);
+
+    if ((min->iter < min->maxIter) ||
+            (min->lastDelta <= min->tol)) {
+        return(true);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2() end (false) ----\n");
+    return(false);
+}
+
+/******************************************************************************
+vectorFitPolynomial1DCheb():  This routine will fit a Chebyshev polynomial of
+degree myPoly to the data points (x, y) and return the coefficients of that
+polynomial.
+ 
+XXX: yErr is currently ignored.
+*****************************************************************************/
+static psPolynomial1D *vectorFitPolynomial1DCheby(psPolynomial1D* myPoly,
+        const psVector* x,
+        const psVector* y,
+        const psVector* yErr)
+{
+    psS32 j;
+    psS32 k;
+    psS32 n = x->n;
+    psF64 fac;
+    psF64 sum;
+    PS_VECTOR_GEN_STATIC_RECYCLED(f, n, PS_TYPE_F64);
+    psScalar *fScalar;
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F64;
+
+    // XXX: These assignments appear too simple to warrant code and
+    // variable declarations.  I retain them here to maintain coherence
+    // with the NR code.
+    psF64 min = -1.0;
+    psF64 max = 1.0;
+    psF64 bma = 0.5 * (max-min);  // 1
+    psF64 bpa = 0.5 * (max+min);  // 0
+
+    // In this loop, we first calculate the values of X for which the
+    // Chebyshev polynomials are zero (see NR, section 5.4).  Then we
+    // calculate the value of the function we are fitting the Chebyshev
+    // polynomials to at those values of X.  This is a bit tricky since
+    // we don't know that function.  So, we instead do 3-order LaGrange
+    // interpolation at the point X for the psVectors x,y for which we
+    // are fitting this ChebyShev polynomial to.
+
+    for (psS32 i=0;i<n;i++) {
+        // NR 5.8.4
+        psF64 Y = cos(M_PI * (0.5 + ((psF32) i)) / ((psF32) n));
+        psF64 X = (Y + bma + bpa) - 1.0;
+        tmpScalar.data.F64 = X;
+
+        // We interpolate against are tabluated x,y vectors to determine the
+        // function value at X.
+        fScalar = p_psVectorInterpolate((psVector *) x,
+                                        (psVector *) y,
+                                        3,
+                                        &tmpScalar);
+
+        f->data.F64[i] = fScalar->data.F64;
+        psFree(fScalar);
+
+        psTrace(".psLib.dataManip.vectorFitPolynomial1DCheby", 6,
+                "(x, X, y, f(X)) is (%f, %f, %f, %f)\n",
+                x->data.F64[i], X, y->data.F64[i], f->data.F64[i]);
+    }
+
+    // We have the values for f() at the zero points, we now calculate the
+    // coefficients of the Chebyshev polynomial: NR 5.8.7.
+
+    fac = 2.0/((psF32) n);
+    // XXX: is this loop bound correct?
+    for (j=0;j<myPoly->n;j++) {
+        sum = 0.0;
+        for (k=0;k<n;k++) {
+            sum+= f->data.F64[k] *
+                  cos(M_PI * ((psF32) j) * (0.5 + ((psF32) k)) / ((psF32) n));
+        }
+
+        myPoly->coeff[j] = fac * sum;
+    }
+
+    return(myPoly);
+}
+
+/******************************************************************************
+VectorFitPolynomial1DOrd():  This routine will fit an ordinary polynomial of
+degree myPoly to the data points (x, y) and return the coefficients of that
+polynomial.
+ 
+XXX: Use private name?
+XXX: Use recycled vectors.
+ *****************************************************************************/
+static psPolynomial1D* vectorFitPolynomial1DOrd(psPolynomial1D* myPoly,
+        const psVector* x,
+        const psVector* y,
+        const psVector* yErr)
+{
+    psS32 polyOrder = myPoly->n;
+    psImage* A = NULL;
+    psImage* ALUD = NULL;
+    psVector* B = NULL;
+    psVector* outPerm = NULL;
+    psVector* X = NULL;         // NOTE: do we need this?
+    psVector* coeffs = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 k = 0;
+    psVector* xSums = NULL;
+
+    psTrace(".psLib.dataManip.vectorFitPolynomial1DOrd", 4,
+            "---- vectorFitPolynomial1DOrd() begin ----\n");
+    // printf("VectorFitPolynomial1D()\n");
+    // for (i=0;i<x->n;i++) {
+    // printf("(x, y, yErr) is (%f, %f, %f)\n", x->data.F64[i], y->data.F64[i], yErr->data.F64[i]);
+    // }
+
+    A = psImageAlloc(polyOrder, polyOrder, PS_TYPE_F64);
+    ALUD = psImageAlloc(polyOrder, polyOrder, PS_TYPE_F64);
+
+    B = psVectorAlloc(polyOrder, PS_TYPE_F64);
+    coeffs = psVectorAlloc(polyOrder, PS_TYPE_F64);
+    X = psVectorAlloc(x->n, PS_TYPE_F64);
+    xSums = psVectorAlloc(1 + 2 * polyOrder, PS_TYPE_F64);
+
+    // Initialize data structures.
+    for (i = 0; i < polyOrder; i++) {
+        B->data.F64[i] = 0.0;
+        coeffs->data.F64[i] = 0.0;
+        for (j = 0; j < polyOrder; j++) {
+            A->data.F64[i][j] = 0.0;
+            ALUD->data.F64[i][j] = 0.0;
+        }
+    }
+    for (i = 0; i < X->n; i++) {
+        X->data.F64[i] = x->data.F64[i];
+    }
+
+    // Build the B and A data structs.
+    if (yErr == NULL) {
+        for (i = 0; i < X->n; i++) {
+            buildSums1D(X->data.F64[i], 2 * polyOrder, xSums);
+
+            for (k = 0; k < polyOrder; k++) {
+                B->data.F64[k] += y->data.F64[i] * xSums->data.F64[k];
+            }
+
+            for (k = 0; k < polyOrder; k++) {
+                for (j = 0; j < polyOrder; j++) {
+                    A->data.F64[k][j] += xSums->data.F64[k + j];
+                }
+            }
+        }
+    } else {
+        for (i = 0; i < X->n; i++) {
+            buildSums1D(X->data.F64[i], 2 * polyOrder, xSums);
+
+            for (k = 0; k < polyOrder; k++) {
+                B->data.F64[k] += y->data.F64[i] * xSums->data.F64[k] /
+                                  yErr->data.F64[i];
+            }
+
+            for (k = 0; k < polyOrder; k++) {
+                for (j = 0; j < polyOrder; j++) {
+                    A->data.F64[k][j] += xSums->data.F64[k + j] /
+                                         yErr->data.F64[i];
+                }
+            }
+        }
+    }
+
+    // XXX: How do we know if these routines were successful?
+    ALUD = psMatrixLUD(ALUD, &outPerm, A);
+    coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
+
+    for (k = 0; k < polyOrder; k++) {
+        myPoly->coeff[k] = coeffs->data.F64[k];
+        // printf("myPoly->coeff[%d] is %f\n", k, myPoly->coeff[k]);
+    }
+
+    psFree(A);
+    psFree(ALUD);
+    psFree(B);
+    psFree(coeffs);
+    psFree(X);
+    psFree(outPerm);
+    psFree(xSums);
+
+    psTrace(".psLib.dataManip.vectorFitPolynomial1DOrd", 4,
+            "---- vectorFitPolynomial1DOrd() begin ----\n");
+    return (myPoly);
+}
+
+/******************************************************************************
+psVectorFitPolynomial1D():  This routine must fit a polynomial of degree
+myPoly to the data points (x, y) and return the coefficients of that
+polynomial.
+ 
+XXX: type F32 is done via vector conversion only.
+ *****************************************************************************/
+psPolynomial1D* psVectorFitPolynomial1D(psPolynomial1D* myPoly,
+                                        const psVector* x,
+                                        const psVector* y,
+                                        const psVector* yErr)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(myPoly->n, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_EMPTY(y, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(y, NULL);
+
+    psS32 i;
+    psVector *x64 = NULL;
+    psVector *y64 = NULL;
+    psVector *yErr64 = NULL;
+    static psVector *x64Static = NULL;
+    static psVector *y64Static = NULL;
+    static psVector *yErr64Static = NULL;
+
+    PS_VECTOR_CONVERT_F32_TO_F64_STATIC(y, y64, y64Static);
+    // If yErr==NULL, set all errors equal.
+    if (yErr == NULL) {
+        PS_VECTOR_GEN_YERR_STATIC_F64(yErr64Static, y->n);
+        yErr64 = yErr64Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(yErr, NULL);
+        PS_VECTOR_CONVERT_F32_TO_F64_STATIC(yErr, yErr64, yErr64Static);
+    }
+
+    // If x==NULL, create an x64 vector with x values set to (0:n).
+    if (x == NULL) {
+        PS_VECTOR_GEN_X_INDEX_STATIC_F64(x64Static, y->n);
+        if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+            p_psNormalizeVectorRangeF64(x64Static, -1.0, 1.0);
+        }
+        x64 = x64Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+        PS_VECTOR_CONVERT_F32_TO_F64_STATIC(x, x64, x64Static);
+        if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+            p_psNormalizeVectorRangeF64(x64, -1.0, 1.0);
+        }
+    }
+    PS_VECTOR_CHECK_SIZE_EQUAL(x64, y64, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(yErr64, y64, NULL);
+
+    // Call the appropriate vector fitting routine.
+    psPolynomial1D *rc = NULL;
+    if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        rc = vectorFitPolynomial1DCheby(myPoly, x64, y64, yErr64);
+    } else if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        rc = vectorFitPolynomial1DOrd(myPoly, x64, y64, yErr64);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "unknown polynomial type.\n");
+        return(NULL);
+    }
+    if (rc == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Could not fit a polynomial to the data.  Returning NULL.\n");
+        return(NULL);
+    }
+
+    return(myPoly);
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+psMinimization *psMinimizationAlloc(psS32 maxIter,
+                                    psF32 tol)
+{
+    PS_INT_CHECK_NON_NEGATIVE(maxIter, NULL);
+
+    psMinimization *min = psAlloc(sizeof(psMinimization));
+    min->maxIter = maxIter;
+    min->tol = tol;
+    min->value = 0.0;
+    min->iter = 0;
+    min->lastDelta = tol + 1;
+
+    return(min);
+}
+
+// This macro takes as input the vector BASE and adds a multiple of the vector
+// LINE to it.  We assume BASEMASK is non-null.
+#define PS_VECTOR_ADD_MULTIPLE(BASE, BASEMASK, LINE, OUT, MUL) \
+for (psS32 i=0;i<BASE->n;i++) { \
+    if (BASEMASK->data.U8[i] == 0) { \
+        OUT->data.F32[i] = BASE->data.F32[i] + (MUL * LINE->data.F32[i]); \
+    } else { \
+        OUT->data.F32[i] = BASE->data.F32[i]; \
+    } \
+} \
+
+#define PS_VECTOR_F32_CHECK_ZERO_VECTOR(IN, BOOL_VAR) \
+BOOL_VAR = true; \
+for (psS32 i=0;i<IN->n;i++) { \
+    if (fabs(IN->data.F32[i]) >= FLT_EPSILON) { \
+        BOOL_VAR = false; \
+        break; \
+    } \
+} \
+
+#define PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(IN, INMASK, BOOL_VAR) \
+BOOL_VAR = true; \
+for (psS32 i=0;i<IN->n;i++) { \
+    if ((INMASK->data.U8[i] == 0) && (fabs(IN->data.F32[i]) >= FLT_EPSILON)) { \
+        BOOL_VAR = false; \
+        break; \
+    } \
+} \
+
+
+/******************************************************************************
+p_psDetermineBracket():  This routine takes as input an arbitrary function,
+and the parameter to vary, and the line along which it must vary.  This
+function produces as output a bracket [a, b, c] such that
+f(param + b * line) < f(param + a * line)
+f(param + b * line) < f(param + c * line)
+a < b < c
+ 
+Algorithm:
+ 
+XXX completely ad hoc:
+start with the user-supplied starting parameter and
+call that b.  Calculate a/c as a fractional amount smaller/larger than b.
+Repeat this process until a local minimum is found.
+ 
+XXX:
+new algorithm:
+start at x=0, expand in one direction until the function
+decreases.  Then you have two points in the bracket.  Keep going until it
+increases, or x is too large.  If thst does not work, expand in the other
+direction.
+ 
+XXX:
+This is F32 only.
+ 
+XXX:
+output bracket vector should be an input as well.
+*****************************************************************************/
+psVector *p_psDetermineBracket(psVector *params,
+                               psVector *line,
+                               const psVector *paramMask,
+                               const psArray *coords,
+                               psMinimizePowellFunc func)
+{
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psS32 iter = 100;
+    psF32 aDir = 0.0;
+    psF32 cDir = 0.0;
+    psF32 new_aDir = 0.0;
+    psF32 new_cDir = 0.0;
+    psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
+    psF32 stepSize = PS_DETERMINE_BRACKET_STEP_SIZE;
+    psVector *tmp = NULL;
+    psBool boolLineIsNull = true;
+
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() begin ----\n");
+
+    // If the line vector is zero, then return NULL.
+    PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
+    if (boolLineIsNull == true) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 2,
+                "p_psDetermineBracket() called with zero line vector.\n");
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                "---- p_psDetermineBracket() end (NULL) ----\n");
+        psFree(bracket);
+        return(NULL);
+    }
+
+    tmp = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    b = 0;
+    a = -stepSize;
+    c = stepSize;
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
+    fa = func(tmp, coords);
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
+    fb = func(tmp, coords);
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+    fc = func(tmp, coords);
+
+    if (fa < fb) {
+        aDir = -1;
+    } else {
+        aDir = 1;
+    }
+
+    if (fc < fb) {
+        cDir = -1;
+    } else {
+        cDir = 1;
+    }
+
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+            "(a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc);
+
+    while (iter > 0) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "psDetermineBracket(): iteration %d\n", iter);
+        if ((fb < fa) && (fb < fc)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                    "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+        stepSize*= (1.0 + stepSize);
+        a =- stepSize;
+        c =+ stepSize;
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
+        fa = func(tmp, coords);
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+        fc = func(tmp, coords);
+
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "Iter(%d): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
+
+        if (fa < fb) {
+            new_aDir = -1;
+        } else {
+            new_aDir = 1;
+        }
+
+        if (fc < fb) {
+            new_cDir = -1;
+        } else {
+            new_cDir = 1;
+        }
+        if ((new_aDir == 1) && (aDir == -1)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                    "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+
+        if ((new_cDir == 1) && (cDir == -1)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                    "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+        aDir = new_aDir;
+        cDir = new_cDir;
+        iter--;
+    }
+    psFree(tmp);
+    psFree(bracket);
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() end (NULL) ----\n");
+    return(NULL);
+}
+
+
+#define RETURN_FINAL_BRACKET(d) \
+if (a < c) { \
+    bracket->data.F32[0] = a; \
+    bracket->data.F32[1] = b; \
+    bracket->data.F32[2] = c; \
+} else { \
+    bracket->data.F32[0] = c; \
+    bracket->data.F32[1] = b; \
+    bracket->data.F32[2] = a; \
+} \
+psTrace(".psLib.dataManip.p_psDetermineBracket", 4, \
+        "---- p_psDetermineBracket() end ----\n"); \
+psTrace(".psLib.dataManip.p_psDetermineBracket", 4, "Final bracket (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc); \
+return(bracket); \
+
+#define PS_DETERMINE_BRACKET_MAX_ITERATIONS 100
+psVector *p_psDetermineBracket2(psVector *params,
+                                psVector *line,
+                                const psVector *paramMask,
+                                const psArray *coords,
+                                psMinimizePowellFunc func)
+{
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psS32 iter = 0;
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmp, params->n, PS_TYPE_F32);
+    psBool boolLineIsNull = true;
+    psF32 prevMin = 0.0;
+    psS32 countMin = 0;
+
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() begin ----\n");
+
+    // If the line vector is zero, then return NULL.
+    PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
+    if (boolLineIsNull == true) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 2,
+                "p_psDetermineBracket() called with zero line vector.\n");
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                "---- p_psDetermineBracket() end (NULL) ----\n");
+        return(NULL);
+    }
+
+    // We determine in what x-direction does the function decrease.
+    a = 0.0;
+    fa = func(params, coords);
+    b = 0.5;
+    iter = 0;
+    do {
+        b*= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
+        fb = func(tmp, coords);
+    } while ((fabs(fb - fa) < FLT_EPSILON) && (iter++ < 100));
+
+    if (fb > fa) {
+        a = b;
+        fa = fb;
+        b = 0.0;
+        fb = func(params, coords);
+    }
+    c = b;
+
+    // At this point we have (a, b) and we know that (fa >= fb).  Initially, c=b;
+    // We keep stretching b out further from "a" until (fc > previous fc).  If
+    // that happens, then we have our bracket.
+    psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
+    iter = 0;
+    while (iter < PS_DETERMINE_BRACKET_MAX_ITERATIONS) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "psDetermineBracket(): iterationA %d\n", iter);
+        c+= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE) * (c - a);
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+        fc = func(tmp, coords);
+
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "Iteration(%d) (bracket): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
+
+        if ((fb < fa) && (fb < fc)) {
+            RETURN_FINAL_BRACKET();
+        } else {
+            b = c;
+            fb = fc;
+        }
+
+        // This code maintains a count of how many times the minimum fc has
+        // stayed the same.  If it gets too high, we exit this loop.
+        if (fc == prevMin) {
+            countMin++;
+        } else {
+            countMin = 0;
+        }
+        prevMin = fc;
+        if (countMin == 10) {
+            RETURN_FINAL_BRACKET();
+        }
+
+        iter++;
+    }
+
+    psFree(bracket);
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() end (NULL) (BAD) ----\n");
+    return(NULL);
+}
+
+/******************************************************************************
+This routine takes as input a possibly multi-dimensional function, along
+with an initial guess at the parameters of that function and vector "line"
+of the same size as the parameter vector.  It will minimize the function
+along that vector and returns the offset along that vector at which the
+minimum is determined.
+ 
+XXX: This routine is not very efficient in terms of total evaluations of the
+function.
+XXX: This is F32 only
+XXX: Since this is an internal function, many of the parameter checks are
+     redundant.
+XXX: Don't modify the psMinimization argument.
+ *****************************************************************************/
+#define PS_LINEMIN_MAX_ITERATIONS 30
+psF32 p_psLineMin(psMinimization *min,
+                  psVector *params,
+                  psVector *line,
+                  const psVector *paramMask,
+                  const psArray *coords,
+                  psMinimizePowellFunc func)
+{
+    PS_PTR_CHECK_NULL(min, NAN);
+    PS_VECTOR_CHECK_NULL(params, NAN);
+    PS_VECTOR_CHECK_EMPTY(params, NAN);
+    PS_VECTOR_CHECK_TYPE(params, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_NULL(line, NAN);
+    PS_VECTOR_CHECK_EMPTY(line, NAN);
+    PS_VECTOR_CHECK_TYPE(line, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_NULL(paramMask, NAN);
+    PS_VECTOR_CHECK_EMPTY(paramMask, NAN);
+    PS_VECTOR_CHECK_TYPE(paramMask, PS_TYPE_U8, NAN);
+    PS_PTR_CHECK_NULL(coords, NAN);
+    PS_PTR_CHECK_NULL(func, NAN);
+    psVector *bracket;
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 n = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psF32 fn = 0.0;
+    psF32 mul = 0.0;
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpa, params->n, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpb, params->n, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpc, params->n, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpn, params->n, PS_TYPE_F32);
+    psS32 i = 0;
+    psS32 boolLineIsNull = true;
+    psS32 numIterations = 0;
+
+    psTrace(".psLib.dataManip.p_psLineMin", 4, "---- p_psLineMin() begin ----\n");
+    PS_VECTOR_F32_CHECK_ZERO_VECTOR(line, boolLineIsNull);
+
+    if (boolLineIsNull == true) {
+        min->value = func(params, coords);
+        psTrace(".psLib.dataManip.p_psLineMin", 2,
+                "p_psLineMin() called with zero line vector.  Return 0.0.  Function value is %f\n", min->value);
+        return(0.0);
+    }
+
+    for (i=0;i<params->n;i++) {
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "(params, paramMask, line)[%d] is (%f %d %f)\n", i,
+                params->data.F32[i],
+                paramMask->data.U8[i],
+                line->data.F32[i]);
+    }
+
+    bracket = p_psDetermineBracket2(params, line, paramMask, coords, func);
+    if (bracket == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Could not bracket minimum.  Returning NAN.\n");
+        return(NAN);
+    }
+    numIterations = 0;
+    while (numIterations < PS_LINEMIN_MAX_ITERATIONS) {
+        numIterations++;
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "p_psLineMin(): iteration %d\n", numIterations);
+
+        a = bracket->data.F32[0];
+        b = bracket->data.F32[1];
+        c = bracket->data.F32[2];
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpa, a);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpb, b);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpc, c);
+        fa = func(tmpa, coords);
+        fb = func(tmpb, coords);
+        fc = func(tmpc, coords);
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "LineMin: f(%f %f %f) is (%f %f %f)\n", a, b, c, fa, fb, fc);
+
+        // We determine which is the biggest segment in [a,b,c] then split
+        // that with the point n.
+        if ((b-a) > (c-b)) {
+            // This is the golden section formula
+            n = a + (0.69 * (b-a));
+            for (i=0;i<params->n;i++) {
+                tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
+            }
+            fn = func(tmpn, coords);
+
+            if (fn > fb) {
+                // a = n, b = b, c = c
+                bracket->data.F32[0] = n;
+            } else {
+                // a = a, b = n, c = b
+                bracket->data.F32[1] = n;
+                bracket->data.F32[2] = b;
+            }
+        } else {
+            n = b + (0.69 * (c-b));
+            for (i=0;i<params->n;i++) {
+                tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
+            }
+            fn = func(tmpn, coords);
+
+            if (fn > fb) {
+                // a = a, b = b, c = n
+                bracket->data.F32[2] = n;
+            } else {
+                // a = b, b = n, c = c
+                bracket->data.F32[0] = b;
+                bracket->data.F32[1] = n;
+            }
+        }
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "LineMin: new bracket is (%f %f %f)\n", bracket->data.F32[0], bracket->data.F32[1], bracket->data.F32[2]);
+
+        mul = bracket->data.F32[1];
+        if ((fabs(a-b) < min->tol) && (fabs(b-c) < min->tol)) {
+            PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
+            min->value = func(params, coords);
+            psFree(bracket);
+            psTrace(".psLib.dataManip.p_psLineMin", 4,
+                    "---- p_psLineMin() end.a (%f) (%f) ----\n", mul, min->value);
+            return(mul);
+        }
+    }
+
+    mul = bracket->data.F32[1];
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
+    min->value = func(params, coords);
+    psTrace(".psLib.dataManip.p_psLineMin", 4,
+            "---- p_psLineMin() end.b (%f) %f ----\n", mul, min->value);
+
+    psFree(bracket);
+    return(mul);
+}
+
+
+/******************************************************************************
+This routine must minimize a possibly multi-dimensional function.  The
+function to be minimized "func" is:
+    psF32 func(psVector *params, psArray *coords)
+The "params" are the parameters of the function which are varied.  The data
+points at which the function is varied are in the argument "coords" which is
+a psArray of psVectors: each vector represents a different coordinate.
+ 
+XXX: We do not use Brent's method.
+ 
+XXX: The SDR is silent about data types.  F32 is implemented here.
+ 
+XXX: Check for F32 types?
+ *****************************************************************************/
+#define PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS 20
+#define PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE 0.01
+
+psBool psMinimizePowell(psMinimization *min,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *coords,
+                        psMinimizePowellFunc func)
+{
+    PS_PTR_CHECK_NULL(min, NULL);
+    PS_VECTOR_CHECK_NULL(params, NULL);
+    PS_VECTOR_CHECK_EMPTY(params, NULL);
+    PS_VECTOR_CHECK_TYPE(params, PS_TYPE_F32, NULL);
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(func, NULL);
+    psS32 numDims = params->n;
+    PS_VECTOR_GEN_STATIC_RECYCLED(pQP, numDims, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(u, numDims, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(Q, numDims, PS_TYPE_F32);
+    psS32 i = 0;
+    psS32 j = 0;
+    psVector *myParamMask = NULL;
+    psMinimization dummyMin;
+    psF32 mul = 0.0;
+    psF32 baseFuncVal = 0.0;
+    psF32 currFuncVal = 0.0;
+    psS32 biggestIter = 0;
+    psF32 biggestDiff = 0.0;
+    psS32 iterationNumber = 0;
+
+    psTrace(".psLib.dataManip.psMinimizePowell", 4,
+            "---- psMinimizePowell() begin ----\n");
+    psTrace(".psLib.dataManip.psMinimizePowell", 6,
+            "min->maxIter is %d\n", min->maxIter);
+    psTrace(".psLib.dataManip.psMinimizePowell", 6,
+            "min->tol is %f\n", min->tol);
+
+    if (paramMask == NULL) {
+        myParamMask = psVectorRecycle(myParamMask, params->n, PS_TYPE_U8);
+        p_psMemSetPersistent(myParamMask, true);
+        p_psMemSetPersistent(myParamMask->data.U8, true);
+        for (i=0;i<myParamMask->n;i++) {
+            myParamMask->data.U8[i] = 0;
+        }
+    } else {
+        myParamMask = (psVector *) paramMask;
+    }
+    PS_VECTOR_CHECK_SIZE_EQUAL(params, myParamMask, NULL);
+
+    // 1: Set v[i] to be the unit vectors for each dimension in params
+    psArray *v = psArrayAlloc(numDims);
+    for (i=0;i<numDims;i++) {
+        (v->data[i]) = (psVector *) psVectorAlloc(numDims, PS_TYPE_F32);
+        for (j=0;j<numDims;j++) {
+            if (i == j) {
+                ((psVector *) (v->data[i]))->data.F32[j] = 1.0;
+            } else {
+                ((psVector *) (v->data[i]))->data.F32[j] = 0.0;
+            }
+        }
+    }
+
+    // 2: Set Q to be the initial params (P in the ADD)
+    for (i=0;i<numDims;i++) {
+        Q->data.F32[i] = params->data.F32[i];
+    }
+
+    while (iterationNumber < min->maxIter) {
+        iterationNumber++;
+        psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                "psMinimizePowell() iteration %d\n", iterationNumber);
+
+        // 3: For each dimension in params, move Q only in the vector v[i] to
+        //    minimize the function.
+
+        baseFuncVal = func(Q, coords);
+        currFuncVal = baseFuncVal;
+        psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                "Current function value is %f\n", currFuncVal);
+
+        biggestDiff = 0;
+        biggestIter = 0;
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                dummyMin.maxIter = PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS;
+                dummyMin.tol = PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE;
+                mul = p_psLineMin(&dummyMin,
+                                  Q,
+                                  ((psVector *) v->data[i]),
+                                  myParamMask,
+                                  coords,
+                                  func);
+                if (isnan(mul)) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "Could not perform line minimization.  Returning FALSE.\n");
+                    psFree(v);
+                    return(false);
+                }
+                psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                        "LineMin along dimension %d has multiple %f\n", i, mul);
+
+                if (fabs(dummyMin.value - currFuncVal) > biggestDiff) {
+                    biggestDiff = fabs(dummyMin.value - currFuncVal);
+                    biggestIter = i;
+                }
+                currFuncVal = dummyMin.value;
+            }
+        }
+        psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                "New function value is %f\n", currFuncVal);
+
+        // 4: Set the vector u = Q - P
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                u->data.F32[i] = Q->data.F32[i] - params->data.F32[i];
+
+                psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                        "u[i]=Q[i]-P[i] (%f = %f - %f)\n", u->data.F32[i],
+                        Q->data.F32[i],
+                        params->data.F32[i]);
+
+            } else {
+                u->data.F32[i] = 0.0;
+            }
+        }
+
+        // 5: Move Q only in the direction u, and minimize the function.
+        for (i=0;i<numDims;i++) {
+            psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                    "u[i] is %f\n", u->data.F32[i]);
+        }
+
+        mul = p_psLineMin(&dummyMin, params, u, myParamMask, coords, func);
+        if (isnan(mul)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not perform line minimization.  Returning FALSE.\n");
+            psFree(v);
+            return(false);
+        }
+
+        // 6:
+        if (dummyMin.value > currFuncVal) {
+            psFree(v);
+            min->iter = iterationNumber;
+            // XXX: Ensure that currFuncVal is the correct value to use here.
+            min->value = currFuncVal;
+            psTrace(".psLib.dataManip.psMinimizePowell", 4,
+                    "---- psMinimizePowell() end (1)(true) ----\n");
+            return(true);
+        }
+
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                pQP->data.F32[i] = (2 * Q->data.F32[i]) - params->data.F32[i];
+            } else {
+                pQP->data.F32[i] = params->data.F32[i];
+            }
+        }
+        psF32 fqp = func(pQP, coords);
+        psF32 term1 = (baseFuncVal - currFuncVal) - biggestDiff;
+        term1*= term1;
+        term1*= 2.0 * (baseFuncVal - (2.0 * currFuncVal) + fqp);
+        psF32 term2 = baseFuncVal - fqp;
+        term2*= term2 * biggestDiff;
+        if (term1 < term2) {
+            for (i=0;i<numDims;i++) {
+                if (myParamMask->data.U8[i] == 0) {
+                    ((psVector *) v->data[biggestIter])->data.F32[i] = u->data.F32[i];
+                }
+            }
+        }
+
+        // 7: Set P to Q
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                params->data.F32[i] = Q->data.F32[i];
+            }
+        }
+
+        // 8: Go to step 3 until the change is less than some tolerance.
+        if (fabs(baseFuncVal - currFuncVal) <= min->tol) {
+            psFree(v);
+            // XXX: Ensure that currFuncVal is the correct value to use here.
+            min->value = currFuncVal;
+            min->iter = iterationNumber;
+            psTrace(".psLib.dataManip.psMinimizePowell", 4,
+                    "---- psMinimizePowell() end (2) (true) ----\n");
+            return(true);
+        }
+    }
+
+    psFree(v);
+    min->iter = iterationNumber;
+    psTrace(".psLib.dataManip.psMinimizePowell", 4,
+            "---- psMinimizePowell() end (0) (false) ----\n");
+    return(false);
+}
+
+
+/******************************************************************************
+XXX: We assume unnormalized gaussians.
+XXX: Currently, yErr is ignored.
+ *****************************************************************************/
+psVector *psMinimizePowellChi2Gauss1D(const psVector *params,
+                                      const psArray *coords)
+{
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(params, NULL);
+
+    psF32 x;
+    psS32 i;
+    psF32 mean = params->data.F32[0];
+    psF32 stdev = params->data.F32[1];
+    psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
+
+    for (i=0;i<coords->n;i++) {
+        x = ((psVector *) (coords->data[i]))->data.F32[0];
+        out->data.F32[i] = psGaussian(x, mean, stdev, false);
+    }
+
+    return(out);
+}
+
+/******************************************************************************
+This routine is to be used with the psMinimizeChi2Powell() function below.
+and the psMinimizePowell() function above.
+ 
+The basic idea is calculate chi-squared for a set of params/coords/errors.
+This functions uses global variables to receive the function pointer, the
+data values, and the data errors.
+XXX: This is F32 only
+ *****************************************************************************/
+static psF32 myPowellChi2Func(const psVector *params,
+                              const psArray *coords)
+{
+    psTrace(".psLib.dataManip.myPowellChi2Func", 4,
+            "---- myPowellChi2Func() begin ----\n");
+    PS_VECTOR_CHECK_NULL(params, NAN);
+    PS_VECTOR_CHECK_EMPTY(params, NAN);
+    PS_VECTOR_CHECK_NULL(myValue, NAN);
+    PS_VECTOR_CHECK_EMPTY(myValue, NAN);
+    PS_PTR_CHECK_NULL(coords, NAN);
+
+    psF32 chi2 = 0.0;
+    psF32 d;
+    psS32 i;
+    psVector *tmp;
+
+    tmp = Chi2PowellFunc(params, coords);
+    if (myError == NULL) {
+        for (i=0;i<coords->n;i++) {
+            d = (tmp->data.F32[i] - myValue->data.F32[i]);
+            chi2+= d * d;
+        }
+    } else {
+        for (i=0;i<coords->n;i++) {
+            d = (tmp->data.F32[i] - myValue->data.F32[i]) / myError->data.F32[i];
+            chi2+= d * d;
+        }
+    }
+    psFree(tmp);
+    psTrace(".psLib.dataManip.myPowellChi2Func", 4,
+            "---- myPowellChi2Func() end (chi2 is %f) ----\n", chi2);
+    return(chi2);
+}
+
+
+/******************************************************************************
+This routine must minimize the chi-squared match of a set of data points and
+values for a possibly multi-dimensional function.
+ 
+The basic idea is to use the psMinimizePowell() function defined above.  In
+order to do so, we defined above a function myPowellChi2Func() which takes
+the "func" function and returns chi-squared over the params/coords/values.
+We then use that function myPowellChi2Func() in the call to
+psMinimizePowell().
+ *****************************************************************************/
+psBool psMinimizeChi2Powell(psMinimization *min,
+                            psVector *params,
+                            const psVector *paramMask,
+                            const psArray *coords,
+                            const psVector *value,
+                            const psVector *error,
+                            psMinimizeChi2PowellFunc func)
+{
+    myValue = (psVector *) value;
+    myError = (psVector *) error;
+
+    Chi2PowellFunc = func;
+
+    return(psMinimizePowell(min, params, paramMask, coords, myPowellChi2Func));
+}
+
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMinimize.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMinimize.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psMinimize.h	(revision 22271)
@@ -0,0 +1,145 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain function prototypes for various minimization,
+ *  chi-squared minimization, and 1-D polynomial fitting routines.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-05 22:23:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PS_MINIMIZE_H)
+#define PS_MINIMIZE_H
+
+/** \file psMinimize.h
+ *  \brief minimization operations
+ *  \ingroup Stats
+ */
+/** \addtogroup Stats
+ *  \{
+ */
+
+#include "psVector.h"
+#include "psMemory.h"
+#include "psArray.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psFunctions.h"
+#include "psStats.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psConstants.h"
+
+typedef struct
+{
+    psS32 maxIter;                     ///< Convergence limit
+    psF32 tol;                         ///< Error Tolerance
+    psF32 value;                       ///< Value of function at minimum
+    psS32 iter;                        ///< Number of iterations to date
+    psF32 lastDelta;                   ///< The last difference for the fit
+}
+psMinimization;
+
+psMinimization *psMinimizationAlloc(psS32 maxIter,   ///< Number of minimization iterations to perform.
+                                    psF32 tol        ///< Requested error tolerance
+                                   );
+
+/** Derive a polynomial fit.
+ *
+ *  psVectorFitPolynomial1d returns the polynomial that best fits the 
+ *  observations. The input parameters are a polynomial that specifies the 
+ *  fit order, myPoly, which will be altered and returned with the best-fit 
+ *  coefficients; and the observations, x, y and yErr. The independent 
+ *  variable list, x may be NULL, in which case the vector index is used. 
+ *  The dependent variable error, yErr may be null, in which case the solution 
+ *  is determined in the assumption that all data errors are equal. This 
+ *  function must be valid only for types psF32, psF64.
+ *
+ *  @return psPolynomial1D*    polynomial fit
+ */
+psPolynomial1D* psVectorFitPolynomial1D(
+    psPolynomial1D* myPoly,            ///< Polynomial to fit
+    const psVector* x,                 ///< Ordinates (or NULL to just use the indices)
+    const psVector* y,                 ///< Coordinates
+    const psVector* yErr               ///< Errors in coordinates, or NULL
+);
+
+psSpline1D *psVectorFitSpline1D(psSpline1D *mySpline,     ///< The spline which will be generated.
+                                const psVector* x,        ///< Ordinates (or NULL to just use the indices)
+                                const psVector* y,        ///< Coordinates
+                                const psVector* yErr      ///< Errors in coordinates, or NULL
+                               );
+
+// XXX: Add Doxygen comments here.
+typedef
+psF64 (*psMinimizeLMChi2Func)(psVector *deriv,
+                              psVector *params,
+                              psVector *x);
+
+psBool psMinimizeLMChi2(psMinimization *min,
+                        psImage *covar,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *x,
+                        const psVector *y,
+                        const psVector *yErr,
+                        psMinimizeLMChi2Func func);
+
+psBool p_psMinLM_GuessABP (psImage  *Alpha,
+                           psVector *Beta,
+                           psVector *Params,
+                           psImage  *alpha,
+                           psVector *beta,
+                           psVector *params,
+                           psF64 lambda);
+
+psF64 p_psMinLM_SetABX (psImage  *alpha,
+                        psVector *beta,
+                        psVector *params,
+                        const psArray  *x,
+                        const psVector *y,
+                        const psVector *dy,
+                        psMinimizeLMChi2Func func);
+
+typedef
+psF32 (*psMinimizePowellFunc)(const psVector *params,
+                              const psArray *coords);
+
+psBool psMinimizePowell(psMinimization *min,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *coords,
+                        psMinimizePowellFunc func);
+
+psVector *psMinimizeLMChi2Gauss1D(psImage *deriv,
+                                  const psVector *params,
+                                  const psArray *coords);
+
+psVector *psMinimizePowellChi2Gauss1D(const psVector *params,
+                                      const psArray *coords);
+
+typedef
+psVector *(*psMinimizeChi2PowellFunc)(const psVector *params,
+                                      const psArray *coords);
+
+psBool psMinimizeChi2Powell(psMinimization *min,
+                            psVector *params,
+                            const psVector *paramMask,
+                            const psArray *coords,
+                            const psVector *value,
+                            const psVector *error,
+                            psMinimizeChi2PowellFunc func);
+
+// XXX EAM : psGaussJordan provided as an alternate to LU Decomp for psMinimizeLMChi2
+bool psGaussJordan (psImage *a, psVector *b);
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psRandom.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psRandom.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psRandom.c	(revision 22271)
@@ -0,0 +1,140 @@
+/** @file psRandom.c
+*  \brief Random Number Generators
+*  \ingroup Math
+*
+*  This file will hold the functions which allocate, free,
+*  and evaluate random number Generators.
+*
+*  @ingroup Math
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:23 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+#include <time.h>
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+#include "psMemory.h"
+#include "psRandom.h"
+#include "psScalar.h"
+#include "psError.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+psU64 p_psRandomGetSystemSeed()
+{
+    FILE*  fd;
+    psU64  seedVal = 0;
+    time_t timeVal;
+
+    fd = fopen("/dev/urandom","r");
+    if(fd == NULL) {
+        // Read system clock to get seed
+        seedVal = (psU64)time(&timeVal);
+    } else {
+        // Read urandom to get seed
+        fread(&seedVal, sizeof(psU64),1,fd);
+        // Close file
+        fclose(fd);
+    }
+
+    // Send log message of the system seed value used
+    psLogMsg(__func__,PS_LOG_INFO,"System random seed value used  seed = %llX hex",seedVal);
+
+    return seedVal;
+}
+
+psRandom *psRandomAlloc(psRandomType type,
+                        psU64 seed)
+{
+    gsl_rng   *r      = NULL;
+    psRandom  *myRNG  = NULL;
+
+    switch (type) {
+    case PS_RANDOM_TAUS:
+        myRNG = (psRandom*)psAlloc(sizeof(psRandom));
+        r = gsl_rng_alloc(gsl_rng_taus);
+        myRNG->gsl = r;
+        if(seed == 0) {
+            gsl_rng_set(myRNG->gsl,p_psRandomGetSystemSeed());
+        }
+        break;
+
+    default:
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_UNKNOWN_RANDFOM_NUMBER_GENERATOR_TYPE);
+        break;
+    }
+    return(myRNG);
+}
+
+void psRandomReset(psRandom *rand,
+                   psU64 seed)
+{
+    // Check null psRandom
+    if(rand==NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+    } else {
+        // Check seed value to see if system seed should be used
+        if(seed == 0) {
+            gsl_rng_set(rand->gsl,p_psRandomGetSystemSeed());
+        } else {
+            gsl_rng_set(rand->gsl, seed);
+        }
+    }
+}
+
+psF64 psRandomUniform(const psRandom *r)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+        return(0);
+    } else {
+        return(gsl_rng_uniform(r->gsl));
+    }
+}
+
+psF64 psRandomGaussian(const psRandom *r)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+        return(0);
+    } else {
+        // XXX: What should sigma be?
+        return(gsl_ran_gaussian(r->gsl, 1.0));
+    }
+}
+
+psF64 psRandomPoisson(const psRandom *r, psF64 mean)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+        return(0);
+    } else {
+        return((psF64) gsl_ran_poisson(r->gsl, mean));
+    }
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psRandom.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psRandom.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psRandom.h	(revision 22271)
@@ -0,0 +1,59 @@
+/** @file psRandom.h
+*  \brief Random Number Generators
+*  \ingroup Math
+*
+*  This file will hold the prototypes for procedures which allocate, free,
+*  and evaluate random number Generators.
+*
+*  @ingroup Math
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:23 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#if !defined(PS_RANDOM_H)
+#define PS_RANDOM_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+/** \addtogroup Math
+ *  \{
+ */
+
+typedef enum {
+    PS_RANDOM_TAUS    ///< A maximally equidistributed combined Tausworthe generator.
+} psRandomType;
+
+typedef struct
+{
+    psRandomType type; ///< The type of RNG
+    gsl_rng *gsl; ///< The RNG itself
+}
+psRandom;
+
+psRandom *psRandomAlloc(psRandomType type,
+                        psU64 seed);
+
+void psRandomReset(psRandom *rand,
+                   psU64 seed);
+
+psF64 psRandomUniform(const psRandom *r);
+psF64 psRandomGaussian(const psRandom *r);
+psF64 psRandomPoisson(const psRandom *r, psF64 mean);
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psStats.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psStats.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psStats.c	(revision 22271)
@@ -0,0 +1,2328 @@
+/** @file  psStats.c
+ *  \brief basic statistical operations
+ *  @ingroup Stats
+ *
+ *  This file will hold the definition of the histogram and stats data
+ *  structures.  It also contains prototypes for procedures which operate
+ *  on those data structures.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.126 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-26 19:53:30 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include "psMemory.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psStats.h"
+#include "psMinimize.h"
+#include "psFunctions.h"
+#include "psConstants.h"
+
+#include "psDataManipErrors.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+#define PS_GAUSS_WIDTH 5       // The width of the Gaussian or boxcar smoothing.
+#define PS_CLIPPED_NUM_ITER_LB 1
+#define PS_CLIPPED_NUM_ITER_UB 10
+#define PS_CLIPPED_SIGMA_LB 1.0
+#define PS_CLIPPED_SIGMA_UB 10.0
+#define PS_POLY_MEDIAN_MAX_ITERATIONS 10
+
+#define PS_BIN_MIDPOINT(HISTOGRAM, BIN_NUM) \
+(0.5 * (HISTOGRAM->bounds->data.F32[(BIN_NUM)] + HISTOGRAM->bounds->data.F32[(BIN_NUM)+1]))
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+psVector* p_psConvertToF32(psVector* in);
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+psBool p_psGetStatValue(const psStats* stats, psF64 *value)
+{
+
+    switch (stats->options & ~(PS_STAT_USE_RANGE | PS_STAT_USE_BINSIZE | PS_STAT_ROBUST_FOR_SAMPLE)) {
+    case PS_STAT_SAMPLE_MEAN:
+        *value = stats->sampleMean;
+        return true;
+
+    case PS_STAT_SAMPLE_MEDIAN:
+        *value = stats->sampleMedian;
+        return true;
+
+    case PS_STAT_SAMPLE_STDEV:
+        *value = stats->sampleStdev;
+        return true;
+
+    case PS_STAT_ROBUST_MEAN:
+        *value = stats->robustMean;
+        return true;
+
+    case PS_STAT_ROBUST_MEDIAN:
+        *value = stats->robustMedian;
+        return true;
+
+    case PS_STAT_ROBUST_MODE:
+        *value = stats->robustMode;
+        return true;
+
+    case PS_STAT_ROBUST_STDEV:
+        *value = stats->robustStdev;
+        return true;
+
+    case PS_STAT_CLIPPED_MEAN:
+        *value = stats->clippedMean;
+        return true;
+
+    case PS_STAT_CLIPPED_STDEV:
+        *value = stats->clippedStdev;
+        return true;
+
+    case PS_STAT_MAX:
+        *value = stats->max;
+        return true;
+
+    case PS_STAT_MIN:
+        *value = stats->min;
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+/******************************************************************************
+MISC PRIVATE STATISTICAL FUNCTIONS
+ 
+NOTE: it is assumed that any call to these statistical functions will have
+been preceded by a call to the psVectorStats() function.  Various sanity tests
+will only be performed in psVectorStats().  Should we perform the sanity
+checks in each routine anyway?
+ 
+XXX: For many of these private stats routines, what should be done if there
+are no acceptable elements in the input vector (if no elements lie within
+range, or there are no unmasked elements, or the input vector is NULL)?
+Currently we set the value to NAN.
+ 
+XXX: Optimization: many routines have an "empty" boolean variable which keeps
+track of whether or not the vector has any valid elements.  This code can
+possibly be optimized away.
+ *****************************************************************************/
+/******************************************************************************
+p_psVectorSampleMean(myVector, maskVector, maskVal, stats): calculates the
+mean of the input vector.  If there was a problem with the mean calculation,
+this routine sets stats->sampleMean to NAN.
+ *****************************************************************************/
+psS32 p_psVectorSampleMean(const psVector* myVector,
+                           const psVector* errors,
+                           const psVector* maskVector,
+                           psU32 maskVal,
+                           psStats* stats)
+{
+
+    psS32 i = 0;                // Loop index variable
+    psF32 mean = 0.0;           // The mean
+    psS32 count = 0;            // # of points in this mean
+
+    // If PS_STAT_USE_RANGE is requested, then we enter a slightly different
+    // loop.
+    if (errors == NULL) {
+        if (stats->options & PS_STAT_USE_RANGE) {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    // Check if the data is with the specified range
+                    if (!(maskVal & maskVector->data.U8[i]) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        mean += myVector->data.F32[i];
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= (psF32)count;
+                } else {
+                    mean = NAN;
+                }
+
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    if ((stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        mean += myVector->data.F32[i];
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= (psF32)count;
+                } else {
+                    mean = NAN;
+                }
+            }
+        } else {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    if (!(maskVal & maskVector->data.U8[i])) {
+                        mean += myVector->data.F32[i];
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= (psF32)count;
+                } else {
+                    mean = NAN;
+                }
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    mean += myVector->data.F32[i];
+                }
+                mean /= (psF32)myVector->n;
+            }
+        }
+    } else {
+        psF32 errorDivisor = 0.0;
+        psF32 errorSqr = 0.0;
+        if (stats->options & PS_STAT_USE_RANGE) {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    // Check if the data is with the specified range
+                    if (!(maskVal & maskVector->data.U8[i]) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                        mean += myVector->data.F32[i]/errorSqr;
+                        errorDivisor+= (1.0 / errorSqr);
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= errorDivisor;
+                } else {
+                    mean = NAN;
+                }
+
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    if ((stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                        mean += myVector->data.F32[i]/errorSqr;
+                        errorDivisor+= (1.0 / errorSqr);
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= errorDivisor;
+                } else {
+                    mean = NAN;
+                }
+            }
+        } else {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    if (!(maskVal & maskVector->data.U8[i])) {
+                        errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                        mean += myVector->data.F32[i]/errorSqr;
+                        errorDivisor+= (1.0 / errorSqr);
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= errorDivisor;
+                } else {
+                    mean = NAN;
+                }
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                    mean += myVector->data.F32[i]/errorSqr;
+                    errorDivisor+= (1.0 / errorSqr);
+                }
+                mean /= errorDivisor;
+            }
+        }
+    }
+
+    stats->sampleMean = mean;
+    if (isnan(mean)) {
+        return(-1);
+    } else {
+        return(0);
+    }
+
+}
+
+/******************************************************************************
+p_psVectorSampleMax(myVector, maskVector, maskVal, stats): calculates the
+max of the input vector.  If there was a problem with the max calculation,
+this routine sets stats->max to NAN.
+ *****************************************************************************/
+psS32 p_psVectorMax(const psVector* myVector,
+                    const psVector* maskVector,
+                    psU32 maskVal,
+                    psStats* stats)
+{
+    psS32 i = 0;                // Loop index variable
+    psF32 max = -PS_MAX_F32;    // The calculated maximum
+    psS32 empty = true;         // Does this vector have valid elements?
+
+    // If PS_STAT_USE_RANGE is requested, then we enter a different loop.
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if ((myVector->data.F32[i] > max) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        max = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((myVector->data.F32[i] > max) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    max = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if (myVector->data.F32[i] > max) {
+                        max = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if (myVector->data.F32[i] > max) {
+                    max = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    }
+    if (empty == false) {
+        stats->max = max;
+    } else {
+        stats->max = NAN;
+        return(1);
+    }
+    return(0);
+}
+
+/******************************************************************************
+p_psVectorSampleMin(myVector, maskVector, maskVal, stats): calculates the
+minimum of the input vector.  If there was a problem with the min calculation,
+this routine sets stats->min to NAN.
+ *****************************************************************************/
+psS32 p_psVectorMin(const psVector* myVector,
+                    const psVector* maskVector,
+                    psU32 maskVal,
+                    psStats* stats)
+{
+    psS32 i = 0;                // Loop index variable
+    psF32 min = PS_MAX_F32;   // The calculated maximum
+    psS32 empty = true;         // Does this vector have valid elements?
+
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if ((myVector->data.F32[i] < min) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        min = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((myVector->data.F32[i] < min) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    min = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if (myVector->data.F32[i] < min) {
+                        min = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if (myVector->data.F32[i] < min) {
+                    min = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    }
+
+    if (empty == false) {
+        stats->min = min;
+    } else {
+        stats->min = NAN;
+        return(1);
+    }
+    return(0);
+}
+
+/******************************************************************************
+p_psVectorNValues(myVector, maskVector, maskVal, stats): This routine returns
+"true" if the inputPsVector has 1 or more valid elements (a valid element is an
+unmasked element within the specified min/max range).  Otherwise, return
+"false".
+ *****************************************************************************/
+bool p_psVectorCheckNonEmpty(const psVector* myVector,
+                             const psVector* maskVector,
+                             psU32 maskVal,
+                             psStats* stats)
+{
+    PS_VECTOR_CHECK_NULL(myVector, -1);
+    PS_PTR_CHECK_NULL(stats, -1);
+    psS32 i = 0;                // Loop index variable
+
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    return(true);
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    return(true);
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    return(true);
+                }
+            }
+        } else {
+            if (myVector->n > 0) {
+                return(true);
+            }
+        }
+    }
+    return(false);
+}
+
+
+/******************************************************************************
+p_psVectorNValues(myVector, maskVector, maskVal, stats): calculates the
+number of non-masked pixels in the vector that fall within the min/max
+range, if specified.
+ *****************************************************************************/
+psS32 p_psVectorNValues(const psVector* myVector,
+                        const psVector* maskVector,
+                        psU32 maskVal,
+                        psStats* stats)
+{
+    PS_VECTOR_CHECK_NULL(myVector, -1);
+    PS_PTR_CHECK_NULL(stats, -1);
+    psS32 i = 0;                // Loop index variable
+    psS32 numData = 0;          // The number of data points
+
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    numData++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    numData++;
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    numData++;
+                }
+            }
+        } else {
+            numData = myVector->n;
+        }
+    }
+
+    return (numData);
+}
+
+/******************************************************************************
+p_psVectorSampleMedian(myVector, maskVector, maskVal, stats): calculates the
+median of the input vector.  Returns true on success (including if there were
+no valid input vector elements).
+ 
+XXX: Use static vectors for sort arrays.
+ *****************************************************************************/
+bool p_psVectorSampleMedian(const psVector* myVector,
+                            const psVector* maskVector,
+                            psU32 maskVal,
+                            psStats* stats)
+{
+    psVector* unsortedVector = NULL;    // Temporary vector
+    psVector* sortedVector = NULL;      // Temporary vector
+    psS32 i = 0;                        // Loop index variable
+    psS32 count = 0;
+    psS32 nValues = 0;
+
+    // Determine how many data points fit inside this min/max range
+    // and are not masked, if the maskVector is not NULL.
+    nValues = p_psVectorNValues(myVector, maskVector, maskVal, stats);
+
+    // XXX: Is a warning message appropriate?  What value should we set the
+    // sampleMedian to?  Should we generate an error?
+    if (nValues <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleMedian(): no valid elements in input vector.\n");
+        return(true);
+        stats->sampleMedian = NAN;
+    }
+
+    // Allocate temporary vectors for the data.
+    unsortedVector = psVectorAlloc(nValues, PS_TYPE_F32);
+
+    // Determine if we must only use data points within a min/max range.
+    if (stats->options & PS_STAT_USE_RANGE) {
+        // Store all non-masked data points within the min/max range
+        // into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count] = myVector->data.F32[i];
+                    count++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count] = myVector->data.F32[i];
+                    count++;
+                }
+            }
+        }
+    } else {
+        // Store all non-masked data points into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    unsortedVector->data.F32[count] = myVector->data.F32[i];
+                    count++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                unsortedVector->data.F32[i] = myVector->data.F32[i];
+            }
+        }
+    }
+    // Sort the temporary vectors.
+    sortedVector = psVectorSort(sortedVector, unsortedVector);
+    if (sortedVector == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                false,
+                PS_ERRORTEXT_psStats_STATS_SAMPLE_MEDIAN_SORT_PROBLEM);
+        return(false);
+    }
+
+    // Calculate the median exactly.  Use the average if the number of samples
+    // is even.
+    if (0 == (nValues % 2)) {
+        stats->sampleMedian = 0.5 * (sortedVector->data.F32[(nValues / 2) - 1] +
+                                     sortedVector->data.F32[nValues / 2]);
+    } else {
+        stats->sampleMedian = sortedVector->data.F32[nValues / 2];
+    }
+
+    // Free the temporary data structures.
+    psFree(unsortedVector);
+    psFree(sortedVector);
+
+    // Return "true" on success.
+    return(true);
+}
+
+/******************************************************************************
+p_psVectorSmoothHistGaussian(): This routine smoothes the data in the input
+robustHistogram with a Gaussian of width sigma.
+ 
+XXX: Only PS_TYPE_F32 is supported.
+ 
+XXX: Write a general routine which smoothes a psVector.  This routine should
+call that.  Is that possible?
+ *****************************************************************************/
+psVector* p_psVectorSmoothHistGaussian(psHistogram* robustHistogram,
+                                       psF32 sigma)
+{
+    PS_PTR_CHECK_NULL(robustHistogram, NULL);
+    PS_PTR_CHECK_NULL(robustHistogram->bounds, NULL);
+
+    psS32 i = 0;                  // Loop index variable
+    psS32 j = 0;                  // Loop index variable
+    psF32 iMid;
+    psF32 jMid;
+    psS32 numBins = robustHistogram->nums->n;
+    psS32 numBounds = robustHistogram->bounds->n;
+    psVector* smooth = psVectorAlloc(numBins, PS_TYPE_F32);
+    psS32 jMin = 0;
+    psS32 jMax = 0;
+    psF32 firstBound = robustHistogram->bounds->data.F32[0];
+    psF32 lastBound = robustHistogram->bounds->data.F32[numBounds-1];
+    psScalar x;
+
+    x.type.type = PS_TYPE_F32;
+    for (i = 0; i < numBins; i++) {
+        // Determine the midpoint of bin i.
+        iMid = (robustHistogram->bounds->data.F32[i] +
+                robustHistogram->bounds->data.F32[i+1]) / 2.0;
+
+
+        // We determine the bin numbers corresponding to a range of data
+        // values surrounding iMid.  The ranges is of size
+        // s*PS_GAUSS_WIDTH*sigma
+
+        // YYY: The p_psVectorBinDisect() routine does much of the work of
+        // the following conditionals, however, it also reports a warning
+        // message.  I don't want the warning message so I reproduce the
+        // conditionals here.  Maybe p_psVectorBinDisect() should not produce
+        // warnings?
+
+        x.data.F32 = iMid - (PS_GAUSS_WIDTH * sigma);
+        if ((x.data.F32 >= firstBound) && (x.data.F32 <= lastBound)) {
+            jMin = p_psVectorBinDisect(robustHistogram->bounds, &x);
+            if (jMin < 0) {
+                psError(PS_ERR_UNEXPECTED_NULL,
+                        false,
+                        PS_ERRORTEXT_psStats_STATS_VECTOR_BIN_DISECT_PROBLEM);
+                return(NULL);
+            }
+        } else if (x.data.F32 <= firstBound) {
+            jMin = 0;
+        } else if (x.data.F32 >= lastBound) {
+            jMin = robustHistogram->bounds->n - 1;
+        }
+
+        x.data.F32 = iMid + (PS_GAUSS_WIDTH * sigma);
+        if ((x.data.F32 >= firstBound) && (x.data.F32 <= lastBound)) {
+            jMax = p_psVectorBinDisect(robustHistogram->bounds, &x);
+            if (jMax < 0) {
+                psError(PS_ERR_UNEXPECTED_NULL,
+                        false,
+                        PS_ERRORTEXT_psStats_STATS_VECTOR_BIN_DISECT_PROBLEM);
+                return(NULL);
+            }
+        } else if (x.data.F32 <= firstBound) {
+            jMax = 0;
+        } else if (x.data.F32 >= lastBound) {
+            jMax = robustHistogram->bounds->n - 1;
+        }
+
+        smooth->data.F32[i] = 0.0;
+        for (j = jMin ; j <= jMax ; j++) {
+            jMid = (robustHistogram->bounds->data.F32[j] +
+                    robustHistogram->bounds->data.F32[j+1]) / 2.0;
+            smooth->data.F32[i] +=
+                robustHistogram->nums->data.F32[j] *
+                psGaussian(jMid, iMid, sigma, true);
+        }
+    }
+
+    return(smooth);
+}
+
+/******************************************************************************
+p_psVectorSampleQuartiles(myVector, maskVector, maskVal, stats): calculates
+the upper and/or lower quartiles of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+ *****************************************************************************/
+bool p_psVectorSampleQuartiles(const psVector* myVector,
+                               const psVector* maskVector,
+                               psU32 maskVal,
+                               psStats* stats)
+{
+    psVector* unsortedVector = NULL;    // Temporary vector
+    psVector* sortedVector = NULL;      // Temporary vector
+    psS32 i = 0;                  // Loop index variable
+    psS32 count = 0;              // # of points in this mean.
+    psS32 nValues = 0;            // # data points
+
+    // Determine how many data points fit inside this min/max range
+    // and are not maxed, IF the maskVector is not NULL.
+    nValues = p_psVectorNValues(myVector, maskVector, maskVal, stats);
+
+    // Allocate temporary vectors for the data.
+    unsortedVector = psVectorAlloc(nValues, PS_TYPE_F32);
+
+    // Determine if we must only use data points within a min/max range.
+    if (stats->options & PS_STAT_USE_RANGE) {
+        // Store all non-masked data points within the min/max range
+        // into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) && (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count++] = myVector->data.F32[i];
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) && (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count++] = myVector->data.F32[i];
+                }
+            }
+        }
+    } else {
+        // Store all non-masked data points into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    unsortedVector->data.F32[count++] = myVector->data.F32[i];
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                unsortedVector->data.F32[i] = myVector->data.F32[i];
+            }
+        }
+    }
+
+    // Sort the temporary vectors.
+    sortedVector = psVectorSort(sortedVector, unsortedVector);
+    if (sortedVector == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                false,
+                PS_ERRORTEXT_psStats_STATS_SAMPLE_MEDIAN_SORT_PROBLEM);
+        return(false);
+    }
+
+    // Calculate the quartile points exactly.
+    // XXX: We should probably do a bin-midpoint if the quartile is not an
+    // integer.
+    stats->sampleUQ = sortedVector->data.F32[3 * (nValues / 4)];
+    stats->sampleLQ = sortedVector->data.F32[nValues / 4];
+
+    // Free the temporary data structures.
+    psFree(unsortedVector);
+    psFree(sortedVector);
+    return(true);
+}
+
+/******************************************************************************
+p_psVectorSampleStdev(myVector, maskVector, maskVal, stats): calculates the
+stdev of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+ 
+ *****************************************************************************/
+void p_psVectorSampleStdevOLD(const psVector* myVector,
+                              const psVector* errors,
+                              const psVector* maskVector,
+                              psU32 maskVal,
+                              psStats* stats)
+{
+    psS32 i = 0;                  // Loop index variable
+    psS32 countInt = 0;           // # of data points being used
+    psF32 countFloat = 0.0;     // # of data points being used
+    psF32 mean = 0.0;           // The mean
+    psF32 diff = 0.0;           // Used in calculating stdev
+    psF32 sumSquares = 0.0;     // temporary variable
+    psF32 sumDiffs = 0.0;       // temporary variable
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call p_psVectorSampleMean()
+    if (0 != isnan(stats->sampleMean)) {
+        p_psVectorSampleMean(myVector, errors, maskVector, maskVal, stats);
+    }
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (0 != isnan(stats->sampleMean)) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): p_psVectorSampleMean() reported a NAN mean.\n");
+        return;
+    }
+
+    mean = stats->sampleMean;
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                }
+            }
+            countInt = myVector->n;
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                diff = myVector->data.F32[i] - mean;
+                sumSquares += (diff * diff);
+                sumDiffs += diff;
+                countInt++;
+            }
+            countInt = myVector->n;
+        }
+    }
+    if (countInt == 0) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): no valid psVector elements (%d).  Setting stats->sampleStdev = NAN.\n", countInt);
+    } else if (countInt == 1) {
+        stats->sampleStdev = 0.0;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): only one valid psVector elements (%d).  Setting stats->sampleStdev = 0.0.\n", countInt);
+    } else {
+        countFloat = (psF32)countInt;
+        stats->sampleStdev = PS_SQRT_F32((sumSquares - (sumDiffs * sumDiffs / countFloat)) / (countFloat - 1));
+    }
+}
+/******************************************************************************
+p_psVectorSampleStdev(myVector, maskVector, maskVal, stats): calculates the
+stdev of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+ 
+ *****************************************************************************/
+void p_psVectorSampleStdev(const psVector* myVector,
+                           const psVector* errors,
+                           const psVector* maskVector,
+                           psU32 maskVal,
+                           psStats* stats)
+{
+    psS32 i = 0;                  // Loop index variable
+    psS32 countInt = 0;           // # of data points being used
+    psF32 countFloat = 0.0;     // # of data points being used
+    psF32 mean = 0.0;           // The mean
+    psF32 diff = 0.0;           // Used in calculating stdev
+    psF32 sumSquares = 0.0;     // temporary variable
+    psF32 sumDiffs = 0.0;       // temporary variable
+    //    psF32 sum1;
+    //    psF32 sum2;
+    psF32 errorDivisor = 0.0f;
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call p_psVectorSampleMean()
+    if (0 != isnan(stats->sampleMean)) {
+        p_psVectorSampleMean(myVector, errors, maskVector, maskVal, stats);
+    }
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (0 != isnan(stats->sampleMean)) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): p_psVectorSampleMean() reported a NAN mean.\n");
+        return;
+    }
+
+    mean = stats->sampleMean;
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                    if (errors != NULL) {
+                        errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                    if (errors != NULL) {
+                        errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+                    }
+                }
+            }
+            countInt = myVector->n;
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                    if (errors != NULL) {
+                        errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                diff = myVector->data.F32[i] - mean;
+                sumSquares += (diff * diff);
+                sumDiffs += diff;
+                countInt++;
+            }
+            countInt = myVector->n;
+            if (errors != NULL) {
+                errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+            }
+        }
+    }
+
+    if (countInt == 0) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): no valid psVector elements (%d).  Setting stats->sampleStdev = NAN.\n", countInt);
+    } else if (countInt == 1) {
+        stats->sampleStdev = 0.0;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): only one valid psVector elements (%d).  Setting stats->sampleStdev = 0.0.\n", countInt);
+    } else {
+        // XXX: The ADD specifies this as the definition of the standard
+        // deviation if the errors are known.  Verify this with IfA: none of
+        // the data points in the vector are used.  Verify that the masks and
+        // data ranges are used correctly.
+        if (errors != NULL) {
+            stats->sampleStdev = (1.0 / PS_SQRT_F32(errorDivisor));
+        } else {
+            countFloat = (psF32)countInt;
+            stats->sampleStdev = PS_SQRT_F32((sumSquares - (sumDiffs * sumDiffs / countFloat)) / (countFloat - 1));
+
+        }
+    }
+}
+
+/******************************************************************************
+p_psVectorClippedStats(myVector, errors, maskVector, maskVal, stats): calculates the
+clipped stats (mean or stdev) of the input vector.
+ 
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    0 for success.
+    -1: error
+    -2: warning
+ 
+XXX: Do we really need to calculate median and mean?
+ 
+XXX: Use static vectors for tmpMask.
+ 
+XXX: This has not been tested.
+ *****************************************************************************/
+psS32 p_psVectorClippedStats(const psVector* myVector,
+                             const psVector* errors,
+                             const psVector* maskVector,
+                             psU32 maskVal,
+                             psStats* stats)
+{
+    psF32 clippedMean = 0.0;    // self-explanatory
+    psF32 clippedStdev = 0.0;   // self-explanatory
+    psVector* tmpMask = NULL;   // Temporary vector for masks during iterations.
+    static psStats *statsTmp = NULL;   // Temporary psStats struct.
+    psS32 rc = 0;               // Return code.
+
+    // Ensure that stats->clipIter is within the proper range.
+    PS_INT_CHECK_RANGE(stats->clipIter,
+                       PS_CLIPPED_NUM_ITER_LB,
+                       PS_CLIPPED_NUM_ITER_UB, -1);
+
+    // Ensure that stats->clipSigma is within the proper range.
+    PS_INT_CHECK_RANGE(stats->clipSigma,
+                       PS_CLIPPED_SIGMA_LB,
+                       PS_CLIPPED_SIGMA_UB, -1);
+
+    // Allocate a psStats structure for calculating the mean, median, and
+    // stdev.
+    if (statsTmp == NULL) {
+        statsTmp = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        p_psMemSetPersistent(statsTmp, true);
+    }
+
+    // We allocate a temporary mask vector since during the iterative
+    // steps that follow, we will be masking off additional data points.
+    // However, we do no want to modify the original mask vector.
+    tmpMask = psVectorAlloc(myVector->n, PS_TYPE_U8);
+
+    // If we were called with a mask vector, then initialize the temporary
+    // mask vector with those values.  Otherwise, initialize to zero.
+    if (maskVector != NULL) {
+        for (psS32 i = 0; i < tmpMask->n; i++) {
+            tmpMask->data.U8[i] = maskVector->data.U8[i];
+        }
+    } else {
+        for (psS32 i = 0; i < tmpMask->n; i++) {
+            tmpMask->data.U8[i] = 0;
+        }
+    }
+
+    // 1. Compute the sample median.
+    p_psVectorSampleMedian(myVector, maskVector, maskVal, statsTmp);
+    if (isnan(statsTmp->sampleMedian)) {
+        psLogMsg(__func__, PS_LOG_WARN, "Call to p_psVectorSampleMedian returned NAN\n");
+        stats->clippedMean = NAN;
+        stats->clippedStdev = NAN;
+        return(-2);
+    }
+
+    // 2. Compute the sample standard deviation.
+    p_psVectorSampleStdev(myVector, errors, maskVector, maskVal, statsTmp);
+    if (isnan(statsTmp->sampleStdev)) {
+        psLogMsg(__func__, PS_LOG_WARN, "Call to p_psVectorSampleStdev returned NAN\n");
+        stats->clippedMean = NAN;
+        stats->clippedStdev = NAN;
+        return(-2);
+    }
+
+    // 3. Use the sample median as the first estimator of the mean X.
+    clippedMean = statsTmp->sampleMedian;
+
+    // 4. Use the sample stdev as the first estimator of the mean stdev.
+    clippedStdev = statsTmp->sampleStdev;
+
+    // 5. Repeat N (stats->clipIter) times:
+    for (psS32 iter = 0; iter < stats->clipIter; iter++) {
+        // a) Exclude all values x_i for which |x_i - x| > K * stdev
+        if (errors != NULL) {
+            for (psS32 j = 0; j < myVector->n; j++) {
+                if (fabs(myVector->data.F32[j] - clippedMean) >
+                        (stats->clipSigma * errors->data.F32[j])) {
+                    tmpMask->data.U8[j] = 0xff;
+                }
+            }
+        } else {
+            for (psS32 j = 0; j < myVector->n; j++) {
+                if (fabs(myVector->data.F32[j] - clippedMean) >
+                        (stats->clipSigma * clippedStdev)) {
+                    tmpMask->data.U8[j] = 0xff;
+                }
+            }
+        }
+
+        // b) compute new mean and stdev
+        p_psVectorSampleMean(myVector, errors, tmpMask, 0xff, statsTmp);
+        p_psVectorSampleStdev(myVector, errors, tmpMask, 0xff, statsTmp);
+
+        // If the new mean and stdev are NAN, we must exit the loop.
+        // Otherwise, use the new results and continue.
+        if (isnan(statsTmp->sampleMean) || isnan(statsTmp->sampleStdev)) {
+            // Exit loop.  XXX: Should we throw an error/warning here?
+            iter = stats->clipIter;
+            rc = -1;
+        } else {
+            clippedMean = statsTmp->sampleMean;
+            clippedStdev = statsTmp->sampleStdev;
+        }
+    }
+
+    // 7. The last calcuated value of x is the cliped mean.
+    if (stats->options & PS_STAT_CLIPPED_MEAN) {
+        stats->clippedMean = clippedMean;
+    }
+    // 8. The last calcuated value of stdev is the cliped stdev.
+    if (stats->options & PS_STAT_CLIPPED_STDEV) {
+        stats->clippedStdev = clippedStdev;
+    }
+
+    psFree(tmpMask);
+    return(rc);
+}
+
+/*****************************************************************************
+These macros and functions define the following functions:
+ 
+<    p_psNormalizeVectorRange(myData, low, high)
+ 
+That assumes that the low/high arguments are PS_TYPE_F64; the vector myData
+can be of any type.  Arguments low/high will be converted to the appropriate
+type and one of the type-specific functions below will be called:
+ 
+    p_psNormalizeVectorRangeU8(myData, low, high)
+    p_psNormalizeVectorRangeU16(myData, low, high)
+    p_psNormalizeVectorRangeU32(myData, low, high)
+    p_psNormalizeVectorRangeU64(myData, low, high)
+    p_psNormalizeVectorRangeS8(myData, low, high)
+    p_psNormalizeVectorRangeS16(myData, low, high)
+    p_psNormalizeVectorRangeS32(myData, low, high)
+    p_psNormalizeVectorRangeS64(myData, low, high)
+    p_psNormalizeVectorRangeF32(myData, low, high)
+    p_psNormalizeVectorRangeF64(myData, low, high)
+ *****************************************************************************/
+#define PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(TYPE) \
+void p_psNormalizeVectorRange##TYPE(psVector* myData, \
+                                    ps##TYPE outLow, \
+                                    ps##TYPE outHigh) \
+{ \
+    ps##TYPE min = (ps##TYPE) PS_MAX_##TYPE; \
+    ps##TYPE max = (ps##TYPE) -PS_MAX_##TYPE; \
+    psS32 i = 0; \
+    \
+    for (i = 0; i < myData->n; i++) { \
+        if (myData->data.TYPE[i] < min) { \
+            min = myData->data.TYPE[i]; \
+        } \
+        if (myData->data.TYPE[i] > max) { \
+            max = myData->data.TYPE[i]; \
+        } \
+    } \
+    \
+    /* Ensure that max!=min before we divide by (max-min) */ \
+    if (max != min) { \
+        for (i = 0; i < myData->n; i++) { \
+            myData->data.TYPE[i] = (outLow + (myData->data.TYPE[i] - min) * \
+                                    (outHigh - outLow) / (max - min)); \
+        } \
+    } else { \
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: (max==min).  Setting all elements to min.\n"); \
+        for (i = 0; i < myData->n; i++) \
+        { \
+            \
+            myData->data.TYPE[i] = outLow; \
+            \
+        } \
+    } \
+} \
+
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U8)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U16)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U32)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U64)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S8)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S16)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S32)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S64)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(F32)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(F64)
+
+void p_psNormalizeVectorRange(psVector* myData,
+                              psF64 outLow,
+                              psF64 outHigh)
+{
+    switch (myData->type.type) {
+    case PS_TYPE_U8:
+        p_psNormalizeVectorRangeU8(myData, (psU8) outLow, (psU8) outHigh);
+        break;
+    case PS_TYPE_U16:
+        p_psNormalizeVectorRangeU16(myData, (psU16) outLow, (psU16) outHigh);
+        break;
+    case PS_TYPE_U32:
+        p_psNormalizeVectorRangeU32(myData, (psU32) outLow, (psU32) outHigh);
+        break;
+    case PS_TYPE_U64:
+        p_psNormalizeVectorRangeU64(myData, (psU64) outLow, (psU64) outHigh);
+        break;
+    case PS_TYPE_S8:
+        p_psNormalizeVectorRangeS8(myData, (psS8) outLow, (psS8) outHigh);
+        break;
+    case PS_TYPE_S16:
+        p_psNormalizeVectorRangeS16(myData, (psS16) outLow, (psS16) outHigh);
+        break;
+    case PS_TYPE_S32:
+        p_psNormalizeVectorRangeS32(myData, (psS32) outLow, (psS32) outHigh);
+        break;
+    case PS_TYPE_S64:
+        p_psNormalizeVectorRangeS64(myData, (psS64) outLow, (psS64) outHigh);
+        break;
+    case PS_TYPE_F32:
+        p_psNormalizeVectorRangeF32(myData, (psF32) outLow, (psF32) outHigh);
+        break;
+    case PS_TYPE_F64:
+        p_psNormalizeVectorRangeF64(myData, (psF64) outLow, (psF64) outHigh);
+        break;
+    case PS_TYPE_C32:
+    case PS_TYPE_C64:
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Unallowable operation: %s has incorrect type.",
+                myData);
+        break;
+    }
+}
+
+/******************************************************************************
+p_ps1DPolyMedian(myPoly, rangeLow, rangeHigh, midpoint): This routine takes
+as input a 1-D polynomial of arbitrary order (though we are using 2nd-order
+polynomials here) and a range of x-values for which it is defined:
+[rangeLow, rangeHigh].  It determines the x-value of that polynomial such
+that f(x) == midpoint.  This functions uses a binary-search algorithm on the
+range and assumes that the polynomial is monotonically increasing or
+decreasing within that range.
+ 
+XXX: Terminate when f(x)-getThisValue is within some error tolerance.
+ 
+XXX: Create a 2nd-order polynomial version and solve for X analytically.
+ *****************************************************************************/
+psF32 p_ps1DPolyMedian(psPolynomial1D* myPoly,
+                       psF32 rangeLow,
+                       psF32 rangeHigh,
+                       psF32 getThisValue)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+    PS_FLOAT_COMPARE(rangeLow, rangeHigh, NAN);
+    // We ensure that the requested f(y) value, which is getThisValue, is
+    // falls within the range of y-values of the polynomial "myPoly" in the
+    // specified x-range (rangeLow:rangeHigh).
+    psF32 fLo = psPolynomial1DEval(
+                    myPoly,
+                    rangeLow
+                );
+    psF32 fHi = psPolynomial1DEval(
+                    myPoly,
+                    rangeHigh
+                );
+    if (!((fLo <= getThisValue) && (fHi >= getThisValue))) {
+        psError(PS_ERR_UNKNOWN,
+                true,
+                PS_ERRORTEXT_psStats_STATS_POLY_MEDIAN_OUT_OF_RANGE);
+        return(NAN);
+    }
+
+    psS32 numIterations = 0;
+    psF32 midpoint = 0.0;
+    psF32 oldMidpoint = 1.0;
+    psF32 f = 0.0;
+
+    while (numIterations < PS_POLY_MEDIAN_MAX_ITERATIONS) {
+        midpoint = (rangeHigh + rangeLow) / 2.0;
+        if (fabs(midpoint - oldMidpoint) <= FLT_EPSILON) {
+            return (midpoint);
+        }
+        oldMidpoint = midpoint;
+
+        f = psPolynomial1DEval(
+                myPoly,
+                midpoint
+            );
+        if (fabs(f - getThisValue) <= FLT_EPSILON) {
+            return (midpoint);
+        }
+
+        if (f > getThisValue) {
+            rangeHigh = midpoint;
+        } else {
+            rangeLow = midpoint;
+        }
+        numIterations++;
+    }
+    return (midpoint);
+}
+
+/******************************************************************************
+fitQuadraticSearchForYThenReturnX(*xVec, *yVec, binNum, yVal): A general
+routine which fits a quadratic to three points and returns the x-value
+corresponding to the input y-value.  This routine takes psVectors of x/y pairs
+as input, and fits a quadratic to the 3 points surrounding element binNum in
+the vectors (the midpoint between element i and i+1 is used for x[i]).  It
+then determines for what value x does that quadratic f(x) = yVal (the input
+parameter).
+ 
+XXX: After you fit the polynomial, solve for X analytically.
+ 
+XXX: the vectors do not have to be the same length.  Must insert the proper
+tests to ensure that binNum is within acceptable ranges for both vectors.
+*****************************************************************************/
+psF32 fitQuadraticSearchForYThenReturnX(psVector *xVec,
+                                        psVector *yVec,
+                                        psS32 binNum,
+                                        psF32 yVal)
+{
+    PS_VECTOR_CHECK_NULL(xVec, NAN);
+    PS_VECTOR_CHECK_NULL(yVec, NAN);
+    PS_VECTOR_CHECK_TYPE(xVec, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_TYPE(yVec, PS_TYPE_F32, NAN);
+    //    PS_VECTOR_CHECK_SIZE_EQUAL(xVec, yVec, NAN);
+    PS_INT_CHECK_RANGE(binNum, 0, (xVec->n - 1), NAN);
+    PS_INT_CHECK_RANGE(binNum, 0, (yVec->n - 1), NAN);
+
+    //    PS_VECTOR_DECLARE_ALLOC_STATIC(x, 3, PS_TYPE_F64);
+    //    PS_VECTOR_DECLARE_ALLOC_STATIC(y, 3, PS_TYPE_F64);
+    //    PS_VECTOR_DECLARE_ALLOC_STATIC(yErr, 3, PS_TYPE_F64);
+    //    PS_POLY_1D_DECLARE_ALLOC_STATIC(myPoly, 2, PS_POLYNOMIAL_ORD);
+    psVector *x = psVectorAlloc(3, PS_TYPE_F64);
+    psVector *y = psVectorAlloc(3, PS_TYPE_F64);
+    psVector *yErr = psVectorAlloc(3, PS_TYPE_F64);
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(2, PS_POLYNOMIAL_ORD);
+
+    psF32 tmpFloat = 0.0f;
+
+    if ((binNum > 0) && (binNum < (yVec->n - 2))) {
+        // The general case.  We have all three points.
+        x->data.F64[0] = (psF64) (0.5 * (xVec->data.F32[binNum - 1] + xVec->data.F32[binNum]));
+        x->data.F64[1] = (psF64) (0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum+1]));
+        x->data.F64[2] = (psF64) (0.5 * (xVec->data.F32[binNum+1] + xVec->data.F32[binNum+2]));
+        y->data.F64[0] = yVec->data.F32[binNum - 1];
+        y->data.F64[1] = yVec->data.F32[binNum];
+        y->data.F64[2] = yVec->data.F32[binNum + 1];
+
+        // Ensure that yVal is within the range of the bins we are using.
+        if (!((y->data.F64[0] <= yVal) && (yVal <= y->data.F64[2]))) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psStats_YVAL_OUT_OF_RANGE,
+                    (psF64)yVal,y->data.F64[2],y->data.F64[0]);
+        }
+        yErr->data.F64[0] = 1.0;
+        yErr->data.F64[1] = 1.0;
+        yErr->data.F64[2] = 1.0;
+
+        // Determine the coefficients of the polynomial.
+        myPoly = psVectorFitPolynomial1D(myPoly, x, y, yErr);
+        if (myPoly == NULL) {
+            psError(PS_ERR_UNEXPECTED_NULL,
+                    false,
+                    PS_ERRORTEXT_psStats_STATS_FIT_QUADRATIC_POLYNOMIAL_1D_FIT);
+            psFree(myPoly);
+            psFree(x);
+            psFree(y);
+            psFree(yErr);
+            return(NAN);
+        }
+        // Call p_ps1DPolyMedian(), which does a binary search on the
+        // polynomial, looking for the value x such that f(x) = yVal
+        tmpFloat = p_ps1DPolyMedian(myPoly, x->data.F64[0], x->data.F64[2], yVal);
+        if (isnan(tmpFloat)) {
+            psError(PS_ERR_UNEXPECTED_NULL,
+                    false,
+                    PS_ERRORTEXT_psStats_STATS_FIT_QUADRATIC_POLY_MEDIAN);
+            psFree(myPoly);
+            psFree(x);
+            psFree(y);
+            psFree(yErr);
+            return(NAN);
+        }
+
+    } else {
+        // The special case where we have two points only at the beginning of
+        // the vectors x and y.
+        if (binNum == 0) {
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
+                              xVec->data.F32[binNum + 1]);
+        } else if (binNum == (xVec->n - 1)) {
+            // The special case where we have two points only at the end of
+            // the vectors x and y.
+            // XXX: Is this right?
+            tmpFloat = xVec->data.F32[binNum];
+        } else if (binNum == (xVec->n - 2)) {
+            // XXX: Is this right?
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
+                              xVec->data.F32[binNum + 1]);
+        }
+    }
+
+    psFree(myPoly);
+    psFree(x);
+    psFree(y);
+    psFree(yErr);
+    return(tmpFloat);
+}
+
+/******************************************************************************
+p_psVectorRobustStats(myVector, maskVector, maskVal, stats): this procedure
+calculates a variety of robust stat measures:
+    PS_STAT_ROBUST_MEAN
+    PS_STAT_ROBUST_MEDIAN
+    PS_STAT_ROBUST_MODE
+    PS_STAT_ROBUST_STDEV
+    PS_STAT_ROBUST_QUARTILE
+I have included all that computation in a single function, as opposed to
+breaking it across several functions for one primary reason:  they all require
+the same basic initial processing steps (calculate the histogram, etc.).
+ 
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    0 on success.
+ 
+XXX: Check for errors in psLib routines that we call.
+*****************************************************************************/
+psS32 p_psVectorRobustStats(const psVector* myVector,
+                            const psVector* errors,
+                            const psVector* maskVector,
+                            psU32 maskVal,
+                            psStats* stats)
+{
+    psHistogram* robustHistogram = NULL;
+    psVector* robustHistogramVector = NULL;
+    psF32 binSize = 0.0;        // Size of the histogram bins
+    psS32 LQBinNum = -1;          // Bin num for lower quartile
+    psS32 UQBinNum = -1;          // Bin num for upper quartile
+    psS32 medianBinNum = -1;
+    psS32 i = 0;                  // Loop index variable
+    psS32 modeBinNum = 0;
+    psF32 modeBinCount = 0.0;
+    psF32 dL = 0.0;
+    psS32 numBins = 0;
+    psF32 myMean = 0.0;
+    psF32 myStdev = 0.0;
+    psF32 countFloat = 0.0;
+    psF32 diff = 0.0;
+    psF32 sumSquares = 0.0;
+    psF32 sumDiffs = 0.0;
+    psVector* cumulativeRobustSums = NULL;
+    psF32 sumRobust = 0.0;
+    psF32 sumN50 = 0.0;
+    psF32 sumNfit = 0.0;
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F32;
+    psStats* tmpStats = psStatsAlloc(PS_STAT_CLIPPED_STDEV | PS_STAT_CLIPPED_MEAN);
+
+    // Compute the initial bin size of the robust histogram.  This is done
+    // by computing the clipped standard deviation of the vector, and dividing
+    // that by 10.0;
+    //XXX: add errors
+    psS32 rc = p_psVectorClippedStats(myVector, NULL, maskVector, maskVal, tmpStats);
+    if (rc != 0) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                false,
+                PS_ERRORTEXT_psStats_ROBUST_STATS_CLIPPED_STATS);
+        return(1);
+    }
+    binSize = tmpStats->clippedStdev / 10.0f;
+
+    // If stats->clippedStdev == 0.0, then all data elements have the same
+    // value.  Therefore, we can set the appropiate results and return.
+    if (fabs(binSize) <= FLT_EPSILON) {
+        if (stats->options & PS_STAT_ROBUST_MEAN) {
+            stats->robustMean = tmpStats->clippedMean;
+        }
+        if (stats->options & PS_STAT_ROBUST_MEDIAN) {
+            stats->robustMedian = tmpStats->clippedMean;
+        }
+        if (stats->options & PS_STAT_ROBUST_MODE) {
+            stats->robustMode = tmpStats->clippedMean;
+        }
+        if (stats->options & PS_STAT_ROBUST_STDEV) {
+            stats->robustStdev = 0.0;
+        }
+        if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+            stats->robustUQ = tmpStats->clippedMean;
+            stats->robustLQ = tmpStats->clippedMean;
+        }
+        // XXX: Set these to the number of unmasked data points?
+        stats->robustNfit = 0.0;
+        stats->robustN50 = 0.0;
+        psFree(tmpStats);
+        return(0);
+    }
+
+    // Determine minimum and maximum values in the data vector.
+    if (isnan(tmpStats->min)) {
+        if (0 != p_psVectorMin(myVector, maskVector, maskVal, tmpStats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: p_psVectorMin(): p_psVectorMin() reported a NAN mean.\n");
+            return(1);
+        }
+    }
+    if (isnan(tmpStats->max)) {
+        if (0 != p_psVectorMax(myVector, maskVector, maskVal, tmpStats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: p_psVectorMin(): p_psVectorMax() reported a NAN mean.\n");
+            return(1);
+        }
+    }
+
+    // Create the histogram structure.  NOTE: we can not specify the bin size
+    // precisely since the argument to psHistogramAlloc() is the number of
+    // bins, not the binSize.  Also, if we get here, we know that
+    // binSize != 0.0.
+    numBins = (psS32)((tmpStats->max - tmpStats->min) / binSize);
+    robustHistogram = psHistogramAlloc(tmpStats->min, tmpStats->max, numBins);
+
+    // Populate the histogram array.
+    psVectorHistogram(robustHistogram, myVector, errors, maskVector, maskVal);
+
+    // Smooth the histogram, Gaussian-style.
+    robustHistogramVector = p_psVectorSmoothHistGaussian(robustHistogram,
+                            tmpStats->clippedStdev / 4.0f);
+
+    /**************************************************************************
+    Determine the median/lower/upper quartile bin numbers.
+
+    We define a vector called "cumulativeRobustSums" where the value at
+    index position i is equal to the sum of bins 0:i.  This will be used in
+    determining the median and lower/upper quartiles.
+    **************************************************************************/
+    cumulativeRobustSums = psVectorAlloc(robustHistogramVector->n, PS_TYPE_F32);
+    cumulativeRobustSums->data.F32[0] = robustHistogramVector->data.F32[0];
+    for (i = 1; i < robustHistogramVector->n; i++) {
+        cumulativeRobustSums->data.F32[i] = cumulativeRobustSums->data.F32[i - 1] +
+                                            robustHistogramVector->data.F32[i];
+    }
+    sumRobust = cumulativeRobustSums->data.F32[robustHistogramVector->n - 1];
+
+    tmpScalar.data.F32 = sumRobust / 4.0;
+    LQBinNum = p_psVectorBinDisect(cumulativeRobustSums, &tmpScalar);
+    tmpScalar.data.F32 = 3.0 * sumRobust / 4.0;
+    UQBinNum = p_psVectorBinDisect(cumulativeRobustSums, &tmpScalar);
+    tmpScalar.data.F32 = sumRobust / 2.0;
+    medianBinNum = p_psVectorBinDisect(cumulativeRobustSums, &tmpScalar);
+
+    if ((LQBinNum < 0) || (UQBinNum < 0)) {
+        psError(PS_ERR_UNKNOWN, true,
+                PS_ERRORTEXT_psStats_ROBUST_QUARTILE_BINS_FAILED);
+        return(1);
+    }
+    if (medianBinNum < 0) {
+        psError(PS_ERR_UNKNOWN, true,
+                PS_ERRORTEXT_psStats_ROBUST_QUARTILE_BINS_FAILED);
+        return(1);
+    }
+    /**************************************************************************
+    Determine the mode in the range LQ:UQ.
+    **************************************************************************/
+    // Determine the bin with the peak value in the range LQ to UQ.
+    modeBinNum = LQBinNum;
+    modeBinCount = robustHistogramVector->data.F32[LQBinNum];
+    sumN50 = robustHistogram->nums->data.F32[LQBinNum];
+    for (i = LQBinNum + 1; i <= UQBinNum; i++) {
+        if (robustHistogramVector->data.F32[i] > modeBinCount) {
+            modeBinNum = i;
+            modeBinCount = robustHistogramVector->data.F32[i];
+        }
+        sumN50 += robustHistogram->nums->data.F32[i];
+    }
+
+    dL = (UQBinNum - LQBinNum) / 4;
+    /**************************************************************************
+    Determine the mean/stdev for the bins in the range mode-dL to mode+dL
+    **************************************************************************/
+    // Calculate the mean of the smoothed robust histogram in the range
+    // mode-dL to mode+dL.  We use the midpoint of each bin as the mean for
+    // that bin (this is a non-exact approximation).
+    sumNfit = 0.0;
+    myMean = 0.0;
+    for (i = modeBinNum - dL; i <= modeBinNum + dL; i++) {
+        if ((0 <= i) && (i < robustHistogramVector->n)) {
+            myMean += (robustHistogramVector->data.F32[i]) * PS_BIN_MIDPOINT(robustHistogram, i);
+            countFloat += robustHistogramVector->data.F32[i];
+        }
+
+        sumNfit += robustHistogram->nums->data.F32[i];
+    }
+    // XXX: divide by zero?
+    myMean /= countFloat;
+
+    // Calculate the stdev of the smoothed robust histogram in the range
+    // mode-dL to mode+dL.  We use the midpoint of each bin as the mean for
+    // that bin.
+    for (i = modeBinNum - dL; i <= modeBinNum + dL; i++) {
+        if ((0 <= i) && (i < robustHistogramVector->n)) {
+            diff = PS_BIN_MIDPOINT(robustHistogram, i) - myMean;
+            sumSquares += diff * diff * robustHistogramVector->data.F32[i];
+            sumDiffs += diff * robustHistogramVector->data.F32[i];
+        }
+    }
+    myStdev = PS_SQRT_F32((sumSquares - (sumDiffs * sumDiffs / countFloat)) / (countFloat - 1));
+
+    p_psNormalizeVectorRangeF32(robustHistogramVector, 0.0, 1.0);
+
+    if ((stats->options & PS_STAT_ROBUST_MEAN) ||
+            (stats->options & PS_STAT_ROBUST_STDEV)) {
+
+        // Using the above (myMean, myStdev) as initial estimates, we fit a
+        // Gaussian to the robustHistogramVector.
+        psImage *covar = NULL;
+        /* XXX: Old, remove.
+                psMinimization *min = psMinimizationAlloc(100, 0.1);
+                psVector *myParams = psVectorAlloc(2, PS_TYPE_F32);
+                psArray *myCoords = psArrayAlloc(robustHistogramVector->n);
+                psVector *y = psVectorAlloc(robustHistogramVector->n, PS_TYPE_F32);
+                myParams->data.F32[0] = myMean;
+                myParams->data.F32[1] = myStdev;
+                for (i=0;i<robustHistogramVector->n;i++) {
+                    myCoords->data[i] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+                    ((psVector *) (myCoords->data[i]))->data.F32[0] = (psF32) i;
+                    y->data.F32[i] = robustHistogramVector->data.F32[i];
+                }
+        */
+
+        psMinimization *min = psMinimizationAlloc(100, 0.01);
+        psVector *myParams = psVectorAlloc(2, PS_TYPE_F32);
+        psArray *myCoords = psArrayAlloc(2 * dL + 1);
+        psVector *y = psVectorAlloc(2 * dL + 1, PS_TYPE_F32);
+        myParams->data.F32[0] = myMean;
+        myParams->data.F32[1] = myStdev;
+
+        for (i = modeBinNum - dL; i <= modeBinNum + dL; i++) {
+            int index = i - modeBinNum + dL;
+            myCoords->data[index] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+            ((psVector *) (myCoords->data[index]))->data.F32[0] = PS_BIN_MIDPOINT(robustHistogram, i);
+            y->data.F32[index] = robustHistogramVector->data.F32[i];
+        }
+
+
+        psBool rc = psMinimizeLMChi2(min,
+                                     NULL,
+                                     myParams,
+                                     NULL,
+                                     myCoords,
+                                     y,
+                                     NULL,
+                                     (psMinimizeLMChi2Func) psMinimizeLMChi2Gauss1D);
+        if (rc == false) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: failed to minimize with psMinimizeLMChi2().\n");
+        }
+
+        // XXX: Verify this with IfA
+        // XXX: The check on the minimization is better than the difference from myMean.
+        //      Do they still want this code?
+
+        if (stats->options & PS_STAT_ROBUST_MEAN) {
+            if (fabs((myParams->data.F32[0] - myMean)/myMean) > 0.1) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: the fitted Gaussian has more than 10%% error for the mean.\n");
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: Using the calculated mean instead of Gaussian-fitted mean.");
+                //                     "WARNING: Using the calculated mean instead of Gaussian-fitted mean.(calc, fit) is (%f, %f)\n",
+                //                     myMean, myParams->data.F32[0]);
+                stats->robustMean = myMean;
+            } else {
+                stats->robustMean = myParams->data.F32[0];
+            }
+        }
+
+        if (stats->options & PS_STAT_ROBUST_STDEV) {
+            if (fabs((myParams->data.F32[1] - myStdev)/myStdev) > 0.1) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: the fitted Gaussian has more than 10%% error for the stdev.\n");
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: Using the calculated stdev instead of Gaussian-fitted stdev.");
+                //                     "WARNING: Using the calculated stdev instead of Gaussian-fitted stdev.(calc, fit) is (%f, %f)\n",
+                //                     myStdev, myParams->data.F32[1]);
+                stats->robustStdev = myStdev;
+            } else {
+                stats->robustStdev = myParams->data.F32[1];
+            }
+        }
+        psFree(covar);
+        psFree(min);
+        psFree(myCoords);
+        psFree(y);
+        psFree(myParams);
+    }
+
+
+    /**************************************************************************
+    Set the appropriate members in the output stats struct.
+    **************************************************************************/
+
+    if (stats->options & PS_STAT_ROBUST_MODE) {
+        stats->robustMode = PS_BIN_MIDPOINT(robustHistogram, modeBinNum);
+    }
+
+
+    // To determine the median, we fit a quadratic y=f(x) to the three bins
+    // surrounding the bin containing the median (x is the midpoint of each
+    // bin and y is the value of each bin).  Then we figure out what value
+    // of x corresponds to f(x) being the median (half of all points).
+    if (stats->options & PS_STAT_ROBUST_MEDIAN) {
+        // Take a psVector.  Fit a polynomial y = f(x) to the 3 data elements
+        // surrounding medianBinNum, then find the x-value corresponding y = sumRobust/2.0.
+
+        //XXX: robustHistogram->bounds and cumulativeRobustSums are of different lengths.
+        //Determine id that is okay.
+        stats->robustMedian = fitQuadraticSearchForYThenReturnX(
+                                  robustHistogram->bounds,
+                                  cumulativeRobustSums,
+                                  medianBinNum,
+                                  sumRobust/2.0);
+    }
+
+    // To determine the quartiles, we fit a quadratic y=f(x) to the three bins
+    // surrounding the bin containing LQ/UQ (x is the midpoint of each
+    // bin and y is the value of each bin).  Then we figure out what value
+    // of x corresponds to f(x) being the LQ/UQ.
+    if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+        countFloat = cumulativeRobustSums->data.F32[robustHistogramVector->n - 1];
+
+        //XXX: robustHistogram->bounds and cumulativeRobustSums are of different lengths.
+        //Determine id that is okay.
+        stats->robustLQ = fitQuadraticSearchForYThenReturnX(
+                              robustHistogram->bounds,
+                              cumulativeRobustSums,
+                              LQBinNum,
+                              countFloat/4.0);
+        //XXX: robustHistogram->bounds and cumulativeRobustSums are of different lengths.
+        //Determine id that is okay.
+        stats->robustUQ = fitQuadraticSearchForYThenReturnX(
+                              robustHistogram->bounds,
+                              cumulativeRobustSums,
+                              UQBinNum,
+                              3.0 * countFloat/4.0);
+    }
+    // XXX: I think sumNfit == sumN50 here.
+    stats->robustNfit = sumNfit;
+    stats->robustN50 = sumN50;
+
+    psFree(tmpStats);
+    psFree(robustHistogram);
+    psFree(robustHistogramVector);
+    psFree(cumulativeRobustSums);
+    return(0);
+}
+
+/*****************************************************************************/
+
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+
+/*****************************************************************************/
+
+static void histogramFree(psHistogram* myHist);
+
+/******************************************************************************
+    psStatsAlloc(): This routine must create a new psStats data structure.
+ *****************************************************************************/
+psStats* psStatsAlloc(psStatsOptions options)
+{
+    psStats* newStruct = NULL;
+
+    newStruct = (psStats* ) psAlloc(sizeof(psStats));
+    newStruct->sampleMean = NAN;
+    newStruct->sampleMedian = NAN;
+    newStruct->sampleStdev = NAN;
+    newStruct->sampleUQ = NAN;
+    newStruct->sampleLQ = NAN;
+    newStruct->robustMean = NAN;
+    newStruct->robustMedian = NAN;
+    newStruct->robustMode = NAN;
+    newStruct->robustStdev = NAN;
+    newStruct->robustUQ = NAN;
+    newStruct->robustLQ = NAN;
+    newStruct->robustN50 = 0;
+    newStruct->robustNfit = 0;
+    newStruct->clippedMean = NAN;
+    newStruct->clippedStdev = NAN;
+    newStruct->clipSigma = 3.0;
+    newStruct->clipIter = 3.0;
+    newStruct->min = NAN;
+    newStruct->max = NAN;
+    newStruct->binsize = NAN;
+    newStruct->options = options;
+
+    return (newStruct);
+}
+
+/******************************************************************************
+psHistogramAlloc(lower, upper, n): allocate a uniform histogram structure
+with the specifed upper and lower limits, and the specifed number of bins.
+This routine will also set the bounds for each of the bins.
+ 
+Input:
+    lower
+    upper
+    n
+Returns:
+    The histogram structure
+ *****************************************************************************/
+psHistogram* psHistogramAlloc(psF32 lower, psF32 upper, psS32 n)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+    PS_FLOAT_COMPARE(lower, upper, NULL);
+
+    psS32 i = 0;                  // Loop index variable
+    psHistogram* newHist = NULL;        // The new histogram structure
+    psF32 binSize = 0.0;        // The histogram bin size
+
+    // Allocate memory for the new histogram structure.  If there are N
+    // bins, then there are N+1 bounds to those bins.
+    newHist = (psHistogram* ) psAlloc(sizeof(psHistogram));
+    psMemSetDeallocator(newHist, (psFreeFcn) histogramFree);
+    newHist->bounds = psVectorAlloc(n + 1, PS_TYPE_F32);
+    newHist->bounds->n = newHist->bounds->nalloc;
+
+    // Calculate the bounds for each bin.
+    binSize = (upper - lower) / (psF32)n;
+    // XXX: Is the following necessary? It prevents the max data point
+    // from being in a non-existant bin.
+    binSize += FLT_EPSILON;
+    for (i = 0; i < n + 1; i++) {
+        newHist->bounds->data.F32[i] = lower + (binSize * (psF32)i);
+    }
+
+    // Allocate the bins, and initialize them to zero.
+    newHist->nums = psVectorAlloc(n, PS_TYPE_F32);
+    for (i = 0; i < newHist->nums->n; i++) {
+        newHist->nums->data.F32[i] = 0.0;
+    }
+
+    // Initialize the other members.
+    newHist->minNum = 0;
+    newHist->maxNum = 0;
+    newHist->uniform = true;
+
+    return (newHist);
+}
+
+/******************************************************************************
+psHistogramAllocGeneric(bounds): allocate a non-uniform histogram structure
+with the specifed bounds.
+ 
+Input:
+    bounds
+Returns:
+    The histogram structure
+ *****************************************************************************/
+psHistogram* psHistogramAllocGeneric(const psVector* bounds)
+{
+    PS_VECTOR_CHECK_NULL(bounds, NULL);
+    PS_VECTOR_CHECK_TYPE(bounds, PS_TYPE_F32, NULL);
+    PS_INT_COMPARE(2, bounds->n, NULL);
+
+    psHistogram* newHist = NULL;        // The new histogram structure
+    psS32 i;                      // Loop index variable
+
+    // Allocate memory for the new histogram structure.
+    newHist = (psHistogram* ) psAlloc(sizeof(psHistogram));
+    psMemSetDeallocator(newHist, (psFreeFcn) histogramFree);
+    newHist->bounds = psVectorAlloc(bounds->n, PS_TYPE_F32);
+    newHist->bounds->n = newHist->bounds->nalloc;
+    for (i = 0; i < bounds->n; i++) {
+        newHist->bounds->data.F32[i] = bounds->data.F32[i];
+    }
+
+    // Allocate the bins, and initialize them to zero.  If there are N bounds,
+    // then there are N-1 bins.
+    newHist->nums = psVectorAlloc((bounds->n) - 1, PS_TYPE_F32);
+    for (i = 0; i < newHist->nums->n; i++) {
+        newHist->nums->data.F32[i] = 0.0;
+    }
+
+    // Initialize the other members.
+    newHist->minNum = 0;
+    newHist->maxNum = 0;
+    newHist->uniform = false;
+
+    return (newHist);
+}
+
+static void histogramFree(psHistogram* myHist)
+{
+    psFree(myHist->bounds);
+    psFree(myHist->nums);
+}
+
+/*****************************************************************************
+UpdateHistogramBins(binNum, out, data, error): This routine is to be used when
+updating the histogram in the presence of errors in the input data.  We treat
+the data point as a boxcar PDF and update a range of points surrounding the
+histogram bin which contains the point.  The width of that boxcar is defined
+as 2.35 * error.  Inputs:
+    binNum: the bin number of the data point in the histogram
+    out: the histogram structure
+    data: the data point value
+    error: the error in that data point
+ 
+XXX: Must test this.
+ *****************************************************************************/
+psS32 UpdateHistogramBins(psS32 binNum,
+                          psHistogram* out,
+                          psF32 data,
+                          psF32 error)
+{
+    PS_PTR_CHECK_NULL(out, -1);
+    PS_PTR_CHECK_NULL(out->bounds, -1);
+    PS_PTR_CHECK_NULL(out->nums, -1);
+    PS_INT_CHECK_RANGE(binNum, 0, ((out->nums->n)-1), -2);
+    PS_FLOAT_COMPARE(0.0, error, -3);
+    PS_FLOAT_CHECK_RANGE(data, out->bounds->data.F32[0], out->bounds->data.F32[(out->bounds->n)-1], -4);
+
+    psF32 boxcarWidth = 2.35 * error;
+    psF32 boxcarCenter = (out->bounds->data.F32[binNum] +
+                          out->bounds->data.F32[binNum+1]) / 2.0;
+    psF32 boxcarLeft = boxcarCenter - (boxcarWidth / 2.0);
+    psF32 boxcarRight = boxcarCenter + (boxcarWidth / 2.0);
+    psS32 bin;
+    psS32 boxcarLeftBinNum = 0;
+    psS32 boxcarRightBinNum = 0;
+
+    // Determine the left endpoint of the boxcar for the PDF.
+    for (bin=binNum ; bin >= 0 ; bin--) {
+        if (out->nums->data.F32[bin] <= boxcarLeft) {
+            boxcarLeftBinNum = bin;
+            break;
+        }
+    }
+
+    // Determine the right endpoint of the boxcar for the PDF.
+    for (bin=binNum ; bin < out->nums->n ; bin++) {
+        if (out->nums->data.F32[bin] >= boxcarRight) {
+            boxcarRightBinNum = bin;
+            break;
+        }
+    }
+
+    //
+    // If the boxcar fits entirely inside this bin, then simply add 1.0 to the
+    // bin and return.
+    //
+    if (boxcarLeftBinNum == boxcarRightBinNum) {
+        out->nums->data.F32[binNum]+= 1.0;
+        return(0);
+    }
+
+    //
+    // If we get here, multiple bins must be updated.  We handle the left
+    // endpoint, and right endpoint differently.
+    //
+    out->nums->data.F32[boxcarLeftBinNum]+=
+        (out->bounds->data.F32[boxcarLeftBinNum+1] - boxcarLeft) / boxcarWidth;
+
+    //
+    // Loop through the center bins, if any.
+    //
+    for (bin = boxcarLeftBinNum + 1 ; bin < (boxcarRightBinNum - 1) ; bin++) {
+        out->nums->data.F32[bin]+=
+            (out->bounds->data.F32[bin+1] - out->bounds->data.F32[bin]) / boxcarWidth;
+    }
+
+    //
+    // Handle the right endpoint differently.
+    //
+    out->nums->data.F32[boxcarRightBinNum]+=
+        (boxcarRight - out->bounds->data.F32[boxcarRightBinNum]) / boxcarWidth;
+
+    //
+    // Return 0 on success.
+    //
+    return(0);
+}
+
+
+/*****************************************************************************
+psVectorHistogram(out, in, errors, mask, maskVal): this procedure takes as
+input a preallocated and initialized histogram structure.  It fills the bins
+in that histogram structure in accordance with the input data "in" and the,
+possibly NULL, mask vector.
+ 
+Inputs:
+    out
+    in
+    mask
+    maskVal
+Returns:
+    The histogram structure "out".
+ *****************************************************************************/
+psHistogram* psVectorHistogram(psHistogram* out,
+                               const psVector* in,
+                               const psVector* errors,
+                               const psVector* mask,
+                               psU32 maskVal)
+{
+    PS_PTR_CHECK_NULL(out, NULL);
+    PS_VECTOR_CHECK_NULL(out->bounds, NULL);
+    PS_VECTOR_CHECK_TYPE(out->bounds, PS_TYPE_F32, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(out->bounds->n, NULL);
+    PS_VECTOR_CHECK_NULL(out->nums, NULL);
+    PS_VECTOR_CHECK_TYPE(out->nums, PS_TYPE_F32, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(out->nums->n, NULL);
+    PS_VECTOR_CHECK_NULL(in, out);
+    if (mask != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(in, mask, NULL);
+        PS_VECTOR_CHECK_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    if (errors != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(in, errors, NULL);
+        PS_VECTOR_CHECK_TYPE(errors, in->type.type, NULL);
+    }
+
+    psS32 i = 0;                  // Loop index variable
+    psF32 binSize = 0.0;          // Histogram bin size
+    psS32 binNum = 0;             // A temporary bin number
+    psS32 numBins = 0;            // The total number of bins
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F32;
+    psVector* inF32 = NULL;
+    psVector* errorsF32 = NULL;
+    psS32 mustFreeVectorIn = 1;
+    psS32 mustFreeVectorErrors = 1;
+
+    // Convert input and errors vectors to F32 if necessary.
+    inF32 = p_psConvertToF32((psVector *) in);
+    if (inF32 == NULL) {
+        inF32 = (psVector *) in;
+        mustFreeVectorIn = 0;
+    }
+    errorsF32 = p_psConvertToF32((psVector *) errors);
+    if (errorsF32 == NULL) {
+        errorsF32 = (psVector *) errors;
+        mustFreeVectorErrors = 0;
+    }
+
+    numBins = out->nums->n;
+    for (i = 0; i < inF32->n; i++) {
+        // Check if this pixel is masked, and if so, skip it.
+        if ((mask == NULL) || ((mask != NULL) && (!(mask->data.U8[i] & maskVal)))) {
+            if (inF32->data.F32[i] < out->bounds->data.F32[0]) {
+                // If this pixel is below minimum value, count it, then skip.
+                out->minNum++;
+            } else if (inF32->data.F32[i] > out->bounds->data.F32[numBins]) {
+                // If this pixel is above maximum value, count it, then skip.
+                out->maxNum++;
+            } else {
+                // If this is a uniform histogram, determining the correct
+                // number is trivial.
+                if (out->uniform == true) {
+                    binSize = out->bounds->data.F32[1] - out->bounds->data.F32[0];
+                    binNum = (psS32)((inF32->data.F32[i] - out->bounds->data.F32[0]) / binSize);
+                    if (errorsF32 != NULL) {
+                        // XXX: Check return codes.
+                        UpdateHistogramBins(binNum, out,
+                                            inF32->data.F32[i],
+                                            errorsF32->data.F32[i]);
+                    } else {
+                        // XXX: This if-statement really shouldn't be necessary.
+                        // However, due to numerical lack of precision, we
+                        // occasionally produce a binNum outside the range.
+                        if (binNum >= out->nums->n) {
+                            binNum = out->nums->n - 1;
+                        }
+                        (out->nums->data.F32[binNum])+= 1.0;
+                    }
+
+                } else {
+                    // If this is a non-uniform histogram, determining the
+                    // correct bin number requires a bit more work.
+                    tmpScalar.data.F32 = inF32->data.F32[i];
+                    binNum = p_psVectorBinDisect(out->bounds, &tmpScalar);
+                    if (binNum < 0) {
+                        psLogMsg(__func__, PS_LOG_WARN,
+                                 "WARNING: psVectorHistogram(): element outside histogram bounds.\n");
+                    } else {
+                        if (errorsF32 != NULL) {
+                            // XXX: Check return codes.
+                            UpdateHistogramBins(binNum, out,
+                                                inF32->data.F32[i],
+                                                errors->data.F32[i]);
+                        } else {
+                            (out->nums->data.F32[binNum])+= 1.0;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if (mustFreeVectorIn == 1) {
+        psFree(inF32);
+    }
+    if (mustFreeVectorErrors == 1) {
+        psFree(errorsF32);
+    }
+    return (out);
+}
+
+/******************************************************************************
+p_psConvertToF32(in): this is the cheap way to support a variety of vector
+data types: we simply convert the input vector to F32 at the beginning, and
+write all of our functions in F32.  If the vast majority of all vector stat
+operations are F32 (or any other single type), then this is probably the
+best way to go.  Otherwise, when the algorithms stablize, we will then macro
+everything and put type support in the various stat functions.
+ 
+XXX: Should the default data type be F64?  Since we are buying Opterons...
+ *****************************************************************************/
+psVector* p_psConvertToF32(psVector* in)
+{
+    if (in == NULL) {
+        return(NULL);
+    }
+    psS32 i = 0;
+    psVector* tmp = NULL;
+
+    if (in->type.type == PS_TYPE_S8) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.S8[i];
+        }
+    } else if (in->type.type == PS_TYPE_S16) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32) in->data.S16[i];
+        }
+    } else if (in->type.type == PS_TYPE_S32) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.S32[i];
+        }
+    } else if (in->type.type == PS_TYPE_S64) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.S64[i];
+        }
+    } else if (in->type.type == PS_TYPE_U8) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U8[i];
+        }
+    } else if (in->type.type == PS_TYPE_U16) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U16[i];
+        }
+    } else if (in->type.type == PS_TYPE_U32) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U32[i];
+        }
+    } else if (in->type.type == PS_TYPE_U64) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U64[i];
+        }
+    } else if (in->type.type == PS_TYPE_F64) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.F64[i];
+        }
+    } else if (in->type.type == PS_TYPE_F32) {
+        // do nothing
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType, in->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psStats_VECTOR_TYPE_UNSUPPORTED,
+                strType);
+    }
+    return (tmp);
+}
+
+/******************************************************************************
+psVectorStats(myVector, maskVector, maskVal, stats): this is the public API
+function which calls the above private stats functions based on what bits
+were set in stats->options.
+ 
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    The stats structure.
+ *****************************************************************************/
+psStats* psVectorStats(psStats* stats,
+                       const psVector* in,
+                       const psVector* errors,
+                       const psVector* mask,
+                       psU32 maskVal)
+{
+    PS_PTR_CHECK_NULL(stats, NULL);
+    PS_VECTOR_CHECK_NULL(in, stats);
+    if (mask != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(mask, in, stats);
+        PS_VECTOR_CHECK_TYPE(mask, PS_TYPE_U8, stats);
+    }
+    if (errors != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(errors, in, stats);
+        PS_VECTOR_CHECK_TYPE(errors, in->type.type, stats);
+    }
+
+    psVector* inF32 = NULL;
+    psVector* errorsF32 = NULL;
+    psS32 mustFreeVectorIn = 1;
+    psS32 mustFreeVectorErrors = 1;
+
+    inF32 = p_psConvertToF32((psVector *) in);
+    if (inF32 == NULL) {
+        inF32 = (psVector *) in;
+        mustFreeVectorIn = 0;
+    }
+    errorsF32 = p_psConvertToF32((psVector *) errors);
+    if (errorsF32 == NULL) {
+        errorsF32 = (psVector *) errors;
+        mustFreeVectorErrors = 0;
+    }
+
+    if ((stats->options & PS_STAT_USE_RANGE) && (stats->min >= stats->max)) {
+        PS_FLOAT_COMPARE(stats->min, stats->max, stats);
+    }
+
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_MEAN) {
+        if (0 != p_psVectorSampleMean(inF32, errorsF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleMean() returned an error.\n");
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_MEDIAN) {
+        if (false == p_psVectorSampleMedian(inF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleMedian() returned an error.\n");
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_STDEV) {
+        if (0 != p_psVectorSampleMean(inF32, errorsF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleMean() returned an error.\n");
+        }
+        p_psVectorSampleStdev(inF32, errorsF32, mask, maskVal, stats);
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_QUARTILE) {
+        if (false == p_psVectorSampleQuartiles(inF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleQuartiles() returned an error.\n");
+        }
+    }
+    // Since the various robust stats quantities share much computation, they
+    // are grouped together in a single private function:
+    // p_psVectorRobustStats()
+    if ((stats->options & PS_STAT_ROBUST_MEAN) ||
+            (stats->options & PS_STAT_ROBUST_MEDIAN) ||
+            (stats->options & PS_STAT_ROBUST_MODE) ||
+            (stats->options & PS_STAT_ROBUST_STDEV) ||
+            (stats->options & PS_STAT_ROBUST_QUARTILE)) {
+        if (0 != p_psVectorRobustStats(inF32, errorsF32, mask, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psStats_STATS_FAILED);
+            // XXX: Set to NAN
+        }
+    }
+
+    // XXX: Different conditions for return -1 and -2?
+    if ((stats->options & PS_STAT_CLIPPED_MEAN) || (stats->options & PS_STAT_CLIPPED_STDEV)) {
+        psS32 rc = p_psVectorClippedStats(inF32, errorsF32, mask, maskVal, stats);
+        if (-1 == rc) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to calculate clipped statistics for input psVector.\n");
+            stats->clippedMean = NAN;
+            stats->clippedStdev = NAN;
+        } else if (-2 == rc) {
+            psLogMsg(__func__, PS_LOG_WARN, "Failed to calculate clipped statistics for input psVector.");
+            stats->clippedMean = NAN;
+            stats->clippedStdev = NAN;
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_MAX) {
+        if (0 != p_psVectorMax(inF32, mask, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to calculate vector maximum");
+            stats->max = NAN;
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_MIN) {
+        if (0 != p_psVectorMin(inF32, mask, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to calculate vector minimum");
+            stats->min = NAN;
+        }
+    }
+
+    if (mustFreeVectorIn == 1) {
+        psFree(inF32);
+    }
+    if (mustFreeVectorErrors == 1) {
+        psFree(errorsF32);
+    }
+    return (stats);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psStats.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psStats.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psStats.h	(revision 22271)
@@ -0,0 +1,191 @@
+
+/** @file  psStats.h
+ *  \brief basic statistical operations
+ *  @ingroup Stats
+ *
+xd *  This file will hold the definition of the histogram and stats data
+ *  structures.  It also contains prototypes for procedures which operate
+ *  on those data structures.
+ *
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.40 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-29 22:34:59 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_STATS_H)
+#define PS_STATS_H
+
+#include "psVector.h"
+
+/// @addtogroup Stats
+/// @{
+
+/******************************************************************************
+    Statistical functions and data structures.
+ *****************************************************************************/
+
+/** enumeration of statistical calculation options
+ *
+ *  @see psStats, psVectorStats, psImageStats
+ */
+// XXX: Is PS_STAT_ROBUST_FOR_SAMPLE obsolete?
+typedef enum {
+    PS_STAT_SAMPLE_MEAN = 0x000001,
+    PS_STAT_SAMPLE_MEDIAN = 0x000002,
+    PS_STAT_SAMPLE_STDEV = 0x000004,
+    PS_STAT_SAMPLE_QUARTILE = 0x000008,
+    PS_STAT_ROBUST_MEAN = 0x000010,
+    PS_STAT_ROBUST_MEDIAN = 0x000020,
+    PS_STAT_ROBUST_MODE = 0x000040,
+    PS_STAT_ROBUST_STDEV = 0x000080,
+    PS_STAT_ROBUST_QUARTILE = 0x000100,
+    PS_STAT_CLIPPED_MEAN = 0x000200,
+    PS_STAT_CLIPPED_STDEV = 0x000400,
+    PS_STAT_MAX =  0x000800,
+    PS_STAT_MIN =  0x001000,
+    PS_STAT_USE_RANGE =  0x002000,
+    PS_STAT_USE_BINSIZE = 0x004000,
+    PS_STAT_ROBUST_FOR_SAMPLE = 0x008000
+} psStatsOptions;
+
+/** This is the generic statistics structure.  It contails the data members
+    for the various statistic values.  It also contains the options member to
+    specifiy which statistics should be calculated. */
+typedef struct
+{
+    psF64 sampleMean;          ///< formal mean of sample
+    psF64 sampleMedian;        ///< formal median of sample
+    psF64 sampleStdev;         ///< standard deviation of sample
+    psF64 sampleUQ;            ///< upper quartile of sample
+    psF64 sampleLQ;            ///< lower quartile of sample
+    psF64 robustMean;          ///< robust mean of array
+    psF64 robustMedian;        ///< robust median of array
+    psF64 robustMode;          ///< Robust mode of array
+    psF64 robustStdev;         ///< robust standard deviation of array
+    psF64 robustUQ;            ///< robust upper quartile
+    psF64 robustLQ;            ///< robust lower quartile
+    psS32 robustN50;              ///<
+    psS32 robustNfit;             ///<
+    psF64 clippedMean;         ///< Nsigma clipped mean
+    psF64 clippedStdev;        ///< standard deviation after clipping
+    psS32 clippedNvalues;         ///< ???
+    psF64 clipSigma;           ///< Nsigma used for clipping; user input
+    psS32 clipIter;               ///< Number of clipping iterations; user input
+    psF64 min;                 ///< minimum data value in array
+    psF64 max;                 ///< maximum data value in array
+    psF64 binsize;             ///<
+    psStatsOptions options;     ///< bitmask of calculated values
+}
+psStats;
+
+/** Performs statistical calculations on a vector.
+ *
+ *  @return psStats*    the statistical results as specified by stats->options
+ */
+psStats* psVectorStats(
+    psStats* stats,    ///< stats structure defines stats to be calculated and how
+    const psVector* in,      ///< Vector to be analysed.
+    const psVector* errors,  ///< Errors.
+    const psVector* mask,    ///< Ignore elements where (maskVector & maskVal) != 0: must be INT or NULL
+    psU32 maskVal      ///< Only mask elements with one of these bits set in maskVector
+);
+
+/** Allocator of the psStats structure.
+ *
+ *  @return psStats*    A new psStats struct with the options member set to the
+ *                      value given.
+ */
+psStats* psStatsAlloc(
+    psStatsOptions options             ///< Statistics to calculate
+);
+
+/******************************************************************************
+    Histogram functions and data structures.
+ *****************************************************************************/
+
+/** The basic histogram structure which contains bounds and bins.
+ *
+ *  In this structure, the vector bounds specifies the boundaries of the 
+ *  histogram bins, and must of type psF32, while nums specifies the number 
+ *  of entries in the bin, and must of type psU32. The value of bounds.n must 
+ *  therefore be 1 greater than than nums.n. The two values minNum and maxNum 
+ *  are the number of data values which fell below the lower limit bound or 
+ *  above the upper limit bound, respectively.
+ */
+typedef struct
+{
+    psVector* bounds;                  ///< Bounds for the bins (type F32)
+    psVector* nums;                    ///< Number in each of the bins (INT)
+    psS32 minNum;                        ///< Number below the minimum
+    psS32 maxNum;                        ///< Number above the maximum
+    psBool uniform;                      ///< Is it a uniform distribution?
+}
+psHistogram;
+
+/** Allocator for psHistogram where the bounds of the bins are implicitly
+ *  specified through simply specifying an upper and lower limit along with 
+ *  the size of the bins. 
+ *
+ *  @return psHistogram*    Newly allocated psHistogram
+ */
+psHistogram* psHistogramAlloc(
+    psF32 lower,                       ///< Lower limit for the bins
+    psF32 upper,                       ///< Upper limit for the bins
+    psS32 n                              ///< Number of bins
+);
+
+/** Allocator for psHistogram where the bounds of the bins are explicitly
+ *  specified. 
+ *
+ *  @return psHistogram*    Newly allocated psHistogram
+ */
+psHistogram* psHistogramAllocGeneric(
+    const psVector* bounds             ///< Bounds for the bins
+);
+
+/** Calculate a histogram
+ *
+ *  The following function populates the histogram bins from the specified 
+ *  vector (in). It alters and returns the histogram out structure. The input
+ *  vector may be of types psU8, psU16, psF32, psF64.
+ *
+ *  @return psHistogram*   histogram result
+ */
+psHistogram* psVectorHistogram(
+    psHistogram* out,                  ///< Histogram data
+    const psVector* in,                ///< Vector to analyse
+    const psVector* errors,            ///< Errors
+    const psVector* mask,              ///< Mask dat for input vector
+    psU32 maskVal                      ///< Mask value
+);
+
+/** Extracts the statistic value specified by stats->options.
+ *
+ *  @return psBool    If more than one statistic result is set in stats->options,
+ *                  false is returned and the value parameter is not set, 
+ *                  otherwise true is returned.
+ */
+psBool p_psGetStatValue(
+    const psStats* stats,
+    ///< the statistic struct to operate on
+
+    psF64 *value
+    ///< if return is true, this is set to the specified statistic value by stats->options
+);
+
+void p_psNormalizeVectorRangeU8(psVector* myData, psU8 low, psU8 high);
+void p_psNormalizeVectorRangeU16(psVector* myData, psU16 low, psU16 high);
+void p_psNormalizeVectorRangeU32(psVector* myData, psU32 low, psU32 high);
+void p_psNormalizeVectorRangeU64(psVector* myData, psU64 low, psU64 high);
+void p_psNormalizeVectorRangeS8(psVector* myData, psS8 low, psS8 high);
+void p_psNormalizeVectorRangeS16(psVector* myData, psS16 low, psS16 high);
+void p_psNormalizeVectorRangeS32(psVector* myData, psS32 low, psS32 high);
+void p_psNormalizeVectorRangeS64(psVector* myData, psS64 low, psS64 high);
+void p_psNormalizeVectorRangeF32(psVector* myData, psF32 low, psF32 high);
+void p_psNormalizeVectorRangeF64(psVector* myData, psF64 low, psF64 high);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psUnaryOp.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psUnaryOp.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psUnaryOp.c	(revision 22271)
@@ -0,0 +1,388 @@
+/** @file  psUnary.c
+ *
+ *  @brief Provides unary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.2.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************
+ *  INCLUDE FILES                                                             *
+ ******************************************************************************/
+#include <string.h>
+#include <complex.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+/*****************************************************************************
+ *  FUNCTION IMPLEMENTATION - LOCAL                                          *
+ *****************************************************************************/
+
+// Conversion for degrees to radians
+#define D2R 0.01745329252111111  /* PI/180 */
+
+// Conversion for radians to degrees
+#define R2D 57.29577950924861   /* 180.0/PI */
+
+
+// Unary SCALAR operations
+#define SCALAR(OUT,IN,OP,TYPE)                                                                               \
+{                                                                                                            \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    o  = &((psScalar* )OUT)->data.TYPE;                                                                      \
+    i1 = &((psScalar* )IN)->data.TYPE;                                                                       \
+    *o = OP;                                                                                                 \
+}
+
+// Unary IMAGE operations
+#define VECTOR(OUT,IN,OP,TYPE)                                                                               \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 nIn = 0;                                                                                           \
+    psS32 nOut = 0;                                                                                          \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    nIn = ((psVector* )IN)->n;                                                                               \
+    nOut = ((psVector* )OUT)->n;                                                                             \
+    if(nIn != nOut) {                                                                                        \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                         \
+                nIn, nOut);                                                                                  \
+        if (OUT != IN) {                                                                                     \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = ((psVector* )IN)->data.TYPE;                                                                        \
+    for(i = 0; i < nIn; i++, o++, i1++) {                                                                    \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+// Unary IMAGE operations
+#define IMAGE(OUT,IN,OP,TYPE)                                                                                \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 j = 0;                                                                                             \
+    psS32 numRowsIn = 0;                                                                                     \
+    psS32 numColsIn = 0;                                                                                     \
+    psS32 numRowsOut = 0;                                                                                    \
+    psS32 numColsOut = 0;                                                                                    \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    numRowsIn = ((psImage* )IN)->numRows;                                                                    \
+    numColsIn = ((psImage* )IN)->numCols;                                                                    \
+    numRowsOut = ((psImage* )OUT)->numRows;                                                                  \
+    numColsOut = ((psImage* )OUT)->numCols;                                                                  \
+    if(numRowsIn!=numRowsOut || numColsIn!=numColsOut) {                                                     \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_IMAGE_SIZE_DIFFERS,                                                    \
+                numColsIn, numRowsIn, numColsOut, numRowsOut);                                               \
+        if (OUT != IN) {                                                                                     \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    for(j = 0; j < numRowsIn; j++) {                                                                         \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = ((psImage* )IN)->data.TYPE[j];                                                                  \
+        for(i = 0; i < numColsIn; i++, o++, i1++) {                                                          \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Preprocessor macro function to create arithmetic function based on input type
+#define UNARY_TYPE(DIM,OUT,IN,OP)                                                                            \
+switch (IN->type) {                                                                                          \
+case PS_TYPE_S32:                                                                                            \
+    DIM(OUT,IN,OP,S32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_F32:                                                                                            \
+    DIM(OUT,IN,OP,F32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_F64:                                                                                            \
+    DIM(OUT,IN,OP,F64);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_C32:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_S8:                                                                                             \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U8:                                                                                             \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_S16:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U16:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U32:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_S64:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U64:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_C64:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+default: {                                                                                                     \
+        char* strType;                                                                                           \
+        PS_TYPE_NAME(strType, IN->type);                                                                         \
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                                 \
+                PS_ERRORTEXT_psMatrix_TYPE_MISMATCH,                                                             \
+                strType);                                                                                        \
+        if (OUT != IN) {                                                                                         \
+            psFree(OUT);                                                                                         \
+        }                                                                                                        \
+        return NULL;                                                                                             \
+    } \
+}
+
+// Preprocessor macro function to create arithmetic function operation name. Functions below that add
+// FLT_EPSILON are done so to align results with a 64 bit computing architecture
+#define UNARY_OP(DIM,OUT,IN,OP)                                                                              \
+if(!strncmp(OP, "abs", 3)) {                                                                                 \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cabs(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,fabs(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "exp", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cexp(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,exp(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "ln", 2)) {                                                                           \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,clog(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,log(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "ten", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cpow(10.0,*i1));                                                               \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,pow(10.0,*i1));                                                                \
+    }                                                                                                        \
+} else if(!strncmp(OP, "log", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,clog(*i1)/log(10.0));                                                          \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,log10(*i1));                                                                   \
+    }                                                                                                        \
+} else if(!strncmp(OP, "sin", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,csin(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,sin(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dsin", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,csin(*i1*D2R));                                                                \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,sin(*i1*D2R));                                                                 \
+    }                                                                                                        \
+} else if(!strncmp(OP, "cos", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ccos(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cos(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dcos", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ccos(*i1*D2R));                                                                \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cos(*i1*D2R));                                                                 \
+    }                                                                                                        \
+} else if(!strncmp(OP, "tan", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ctan(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,tan(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dtan", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ctan(*i1*D2R));                                                                \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,tan(*i1*D2R));                                                                 \
+    }                                                                                                        \
+} else if(!strncmp(OP, "asin", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,casin(*i1));                                                                   \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,asin(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dasin", 5)) {                                                                        \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*casin(*i1));                                                               \
+    } else if(PS_IS_PSELEMTYPE_INT(IN->type)) {                                                              \
+        UNARY_TYPE(DIM,OUT,IN,(R2D*asin(*i1)));                                                              \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,(R2D*asin(*i1)));                                                              \
+    }                                                                                                        \
+} else if(!strncmp(OP, "acos", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cacos(*i1));                                                                   \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,acos(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dacos", 5)) {                                                                        \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*cacos(*i1));                                                               \
+    } else if(PS_IS_PSELEMTYPE_INT(IN->type)) {                                                              \
+        UNARY_TYPE(DIM,OUT,IN,(R2D*acos(*i1)));                                                              \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*acos(*i1));                                                                \
+    }                                                                                                        \
+} else if(!strncmp(OP, "atan", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,catan(*i1));                                                                   \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,atan(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "datan", 5)) {                                                                        \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*catan(*i1));                                                               \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*atan(*i1));                                                                \
+    }                                                                                                        \
+} else {                                                                                                     \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                                \
+            PS_ERRORTEXT_psMatrix_OPERATION_UNSUPPORTED,                                                     \
+            OP);                                                                                             \
+    if (OUT != IN) {                                                                                         \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+psPtr psUnaryOp(psPtr out, const psPtr in, const char *op)
+{
+    #define psUnaryOp_EXIT { \
+                             if (out != in) { \
+                             psFree(out); \
+                             } \
+                             return NULL; \
+                           }
+
+    psType* psTypeIn = (psType* ) in;
+
+    PS_PTR_CHECK_NULL_GENERAL(in, psUnaryOp_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(op, psUnaryOp_EXIT);
+
+    psDimen dimIn = psTypeIn->dimen;
+    psElemType elTypeIn = psTypeIn->type;
+
+    switch (dimIn) {
+    case PS_DIMEN_SCALAR:
+        if (out == NULL ||
+                ((psType*)out)->dimen != PS_DIMEN_SCALAR ||
+                ((psScalar*)out)->type.type != elTypeIn) {
+            psFree(out);
+            out = psScalarAlloc(0.0,elTypeIn);
+        }
+        UNARY_OP(SCALAR, out, psTypeIn, op);    // scalar
+        break;
+    case PS_DIMEN_VECTOR:
+    case PS_DIMEN_TRANSV:
+        if (((psVector*)in)->n == 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psMatrix_VECTOR_EMPTY);
+            psUnaryOp_EXIT;
+        }
+
+        out = psVectorRecycle(out,
+                              ((psVector*)in)->n,
+                              elTypeIn);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+            psUnaryOp_EXIT;
+        }
+
+        UNARY_OP(VECTOR, out, psTypeIn, op);    // vector
+        break;
+    case PS_DIMEN_IMAGE:
+        if (((psImage* ) in)->numCols == 0 || ((psImage* ) in)->numRows == 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psMatrix_IMAGE_EMPTY);
+            psUnaryOp_EXIT;
+        }
+
+        out = psImageRecycle(out,
+                             ((psImage*)in)->numCols,
+                             ((psImage*)in)->numRows,
+                             elTypeIn);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+            psUnaryOp_EXIT;
+        }
+
+        UNARY_OP(IMAGE, out, psTypeIn, op);     // image
+        break;
+    default:
+        if (out != in) {
+            psFree(out);
+        }
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                "in", dimIn);
+        psUnaryOp_EXIT;
+    }
+
+    // Automtically free psScalar types, since they are usually allocated in the argument list when this
+    // function is called, provided that the input is not the output.
+    if(psTypeIn->dimen==PS_DIMEN_SCALAR && in!=out) {
+        psFree(in);
+    }
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psUnaryOp.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psUnaryOp.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psUnaryOp.h	(revision 22271)
@@ -0,0 +1,69 @@
+/** @file  psUnaryOp.h
+ *
+ *  @brief Provides unary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSUNARY_OP_H
+#define PSUNARY_OP_H
+
+/// @addtogroup MatrixArithmetic
+/// @{
+
+
+/** Perform simple unary arithmetic with images or vectors
+ *
+ *  Performs absolute value, exponent, natural log, power of 10, log, sine, cosine, tangent, arcsine,
+ *  arccosine, or arctan. operations with images and vectors. Uses the form:
+ *
+ *     out = op(in),
+ *
+ *     Where op is: "abs", "exp", "ln", "ten", "log", "sin", "cos", "tan" "asin", "acos", "atan", "dsin",
+ *                  "dcos", dtan", "dasin", "dacos", or "datan".
+ *
+ *  Trigometric Operations with "d" prefix use units of degrees. Those without are in radians.
+ *
+ *  This function only supports vector-vector or image-image opertions.
+ *
+ *  @return  psType* : Pointer to either psImage or psVector.
+ */
+psType* psUnaryOp(
+    psPtr out,                         ///< Output type, either psImage or psVector.
+    const psPtr in,   ///< Input, either psImage or psVector.
+    const char *op   ///< operator.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psVectorFFT.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psVectorFFT.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psVectorFFT.c	(revision 22271)
@@ -0,0 +1,418 @@
+/** @file  psVectorFFT.c
+ *
+ *  @brief Contains FFT transform related functions for psVector
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.31 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+
+#include "psVectorFFT.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psImageExtraction.h"
+
+#include "psDataManipErrors.h"
+
+#define P_FFTW_PLAN_RIGOR FFTW_ESTIMATE
+
+static psBool p_fftwWisdomImported = false;
+
+psVector* psVectorFFT(psVector* out, const psVector* in, psFFTFlags direction)
+{
+    psU32 numElements;
+    psElemType type;
+    fftwf_plan plan;
+
+    /* got good image data? */
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+
+    /* make sure the system-level wisdom information is imported. */
+    if (!p_fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        p_fftwWisdomImported = true;
+    }
+
+    numElements = in->n;
+
+    out = psVectorCopy(out, in, PS_TYPE_C32);
+    out->n = numElements;
+
+    if ((direction & PS_FFT_FORWARD) != 0) {
+        plan = fftwf_plan_dft_1d(numElements,
+                                 (fftwf_complex *) out->data.C32,
+                                 (fftwf_complex *) out->data.C32, FFTW_FORWARD, P_FFTW_PLAN_RIGOR);
+    } else if ((direction & PS_FFT_REVERSE) != 0) {
+        plan = fftwf_plan_dft_1d(numElements,
+                                 (fftwf_complex *) out->data.C32,
+                                 (fftwf_complex *) out->data.C32, FFTW_BACKWARD, P_FFTW_PLAN_RIGOR);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psVectorFFT_DIRECTION_NOTSET);
+        psFree(out);
+        return NULL;
+    }
+
+    /* check if a plan exists now */
+    if (plan == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_FFTW_PLAN_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    /* finally, call FFTW with the plan made above */
+    fftwf_execute(plan);
+
+    fftwf_destroy_plan(plan);
+
+    if ((direction & PS_FFT_REAL_RESULT) != 0) {
+        for (psS32 i = 0; i < numElements; i++) {
+            out->data.F32[i] = out->data.C32[i];
+        }
+        out->type.type = PS_TYPE_F32;
+        out->data.U8 = psRealloc(out->data.U8,PSELEMTYPE_SIZEOF(PS_TYPE_F32)*out->nalloc);
+    }
+
+    return out;
+}
+
+psVector* psVectorReal(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numElements = in->n;
+
+    /* if not a complex number, this is logically just a copy */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        // Warn user, as this is probably not expected
+        psLogMsg(__func__, PS_LOG_WARN, "Real portion of a non-Complex type called called for. "
+                 "Just a vector copy was performed.");
+        out = psVectorRecycle(out, numElements, type);
+        out->n = numElements;
+        memcpy(out->data.U8, in->data.U8, numElements * PSELEMTYPE_SIZEOF(type));
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outVec;
+        psC32* inVec = in->data.C32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F32);
+        out->n = numElements;
+        outVec = out->data.F32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = crealf(inVec[i]);
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outVec;
+        psC64* inVec = in->data.C64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F64);
+        out->n = numElements;
+        outVec = out->data.F64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = creal(inVec[i]);
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_TYPE_UNSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorImaginary(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numElements = in->n;
+
+    /* if not a complex number, this is logically just zeroed image of same size */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        // Warn user, as this is probably not expected
+        psLogMsg(__func__, PS_LOG_WARN, "Imaginary portion of a non-Complex type called for. "
+                 "A zeroed vector was returned.");
+        out = psVectorRecycle(out, numElements, type);
+        out->n = numElements;
+        memset(out->data.U8, 0, PSELEMTYPE_SIZEOF(type) * numElements);
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outVec;
+        psC32* inVec = in->data.C32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F32);
+        out->n = numElements;
+        outVec = out->data.F32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = cimagf(inVec[i]);
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outVec;
+        psC64* inVec = in->data.C64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F64);
+        out->n = numElements;
+        outVec = out->data.F64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = cimag(inVec[i]);
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_TYPE_UNSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorComplex(psVector* out, const psVector* real, const psVector* imag)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (real == NULL || imag == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = real->type.type;
+    if (real->n < imag->n) {
+        numElements = real->n;
+    } else {
+        numElements = imag->n;
+    }
+
+    if (imag->type.type != type) {
+        char* typeStrReal;
+        char* typeStrImag;
+        PS_TYPE_NAME(typeStrReal,type);
+        PS_TYPE_NAME(typeStrImag,imag->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_REAL_IMAG_TYPE_MISMATCH,
+                typeStrReal,typeStrImag);
+        psFree(out);
+        return NULL;
+    }
+
+    if (type == PS_TYPE_F32) {
+        psC32* outVec;
+        psF32* realVec = real->data.F32;
+        psF32* imagVec = imag->data.F32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C32);
+        out->n = numElements;
+        outVec = out->data.C32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = realVec[i] + I * imagVec[i];
+        }
+    } else if (type == PS_TYPE_F64) {
+        psC64* outVec;
+        psF64* realVec = real->data.F64;
+        psF64* imagVec = imag->data.F64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C64);
+        out->n = numElements;
+        outVec = out->data.C64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = realVec[i] + I * imagVec[i];
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_NONREAL_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorConjugate(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numElements = in->n;
+
+    /* if not a complex number, this is logically just a image copy */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        // Warn user, as this is probably not expected
+        psLogMsg(__func__, PS_LOG_WARN, "Complex Conjugate of a non-Complex type called for. "
+                 "Vector copy was performed instead.");
+
+        out = psVectorRecycle(out, numElements, type);
+        out->n = numElements;
+        memcpy(out->data.U8, in->data.U8, PSELEMTYPE_SIZEOF(type) * numElements);
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psC32* outVec;
+        psC32* inVec = in->data.C32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C32);
+        out->n = numElements;
+        outVec = out->data.C32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = crealf(inVec[i]) - I * cimagf(inVec[i]);
+        }
+    } else if (type == PS_TYPE_C64) {
+        psC64* outVec;
+        psC64* inVec = in->data.C64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C64);
+        out->n = numElements;
+        outVec = out->data.C64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = creal(inVec[i]) - I * cimag(inVec[i]);
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorPowerSpectrum(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 outNumElements;
+    psU32 inNumElements;
+    psU32 inHalfNumElements;
+    psU32 inNumElementsSquared;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    inNumElements = in->n;
+    inNumElementsSquared = inNumElements * inNumElements;
+    inHalfNumElements = inNumElements / 2;
+    outNumElements = inHalfNumElements + 1;
+
+    if (type == PS_TYPE_C32) {
+        psF32* outVec;
+        psC32* inVec = in->data.C32;
+        psF32 inAbs1;
+        psF32 inAbs2;
+
+        out = psVectorRecycle(out, outNumElements, PS_TYPE_F32);
+        out->n = outNumElements;
+        outVec = out->data.F32;
+
+        // from ADD: P_0 = |C_0|^2/N^2
+        inAbs1 = cabsf(inVec[0]);
+        outVec[0] = inAbs1 * inAbs1 / inNumElementsSquared;
+
+        // from ADD: P_j = (|C_j|^2+|C_N-j|^2)/N^2, where j = 1,2,...,(N/2-1)
+        for (psU32 i = 1; i < inHalfNumElements; i++) {
+            inAbs1 = cabsf(inVec[i]);
+            inAbs2 = cabsf(inVec[inNumElements - i]);
+            outVec[i] = (inAbs1 * inAbs1 + inAbs2 * inAbs2) / inNumElementsSquared;
+        }
+
+        // from ADD: P_N/2 = |C_N/2|^2/N^2
+        inAbs1 = cabsf(inVec[inHalfNumElements]);
+        outVec[inHalfNumElements] = inAbs1 * inAbs1 / inNumElementsSquared;
+    } else if (type == PS_TYPE_C64) {
+        psF64* outVec;
+        psC64* inVec = in->data.C64;
+        psF64 inAbs1;
+        psF64 inAbs2;
+
+        out = psVectorRecycle(out, outNumElements, PS_TYPE_F64);
+        out->n = outNumElements;
+        outVec = out->data.F64;
+
+        // from ADD: P_0 = |C_0|^2/N^2
+        inAbs1 = cabs(inVec[0]);
+        outVec[0] = inAbs1 * inAbs1 / inNumElementsSquared;
+
+        // from ADD: P_j = (|C_j|^2+|C_N-j|^2)/N^2, where j = 1,2,...,(N/2-1)
+        for (psU32 i = 1; i < inHalfNumElements; i++) {
+            inAbs1 = cabs(inVec[i]);
+            inAbs2 = cabs(inVec[inNumElements - i]);
+            outVec[i] = (inAbs1 * inAbs1 + inAbs2 * inAbs2) / inNumElementsSquared;
+        }
+
+        // from ADD: P_N/2 = |C_N/2|^2/N^2
+        inAbs1 = cabs(inVec[inHalfNumElements]);
+        outVec[inHalfNumElements] = inAbs1 * inAbs1 / inNumElementsSquared;
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psVectorFFT.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psVectorFFT.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/dataManip/psVectorFFT.h	(revision 22271)
@@ -0,0 +1,99 @@
+/** @file  psVectorFFT.h
+ *
+ *  @brief Contains FFT transform related functions for psVector
+ *
+ *  @ingroup Transform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_VECTOR_FFT_H
+#define PS_VECTOR_FFT_H
+
+#include "psVector.h"
+
+/// @addtogroup Transform
+/// @{
+
+/** Specify direction of FFT */
+typedef enum {
+    /// psImageFFT/psVectorFFT should perform a forward FFT.
+    PS_FFT_FORWARD = 1,
+
+    /// psImageFFT/psVectorFFT should perform a reverse FFT.
+    PS_FFT_REVERSE = 2,
+
+    /// psImageFFT/psVectorFFT should perform a reverse FFT with a real result.
+    PS_FFT_REAL_RESULT = 4
+} psFFTFlags;
+
+
+/** Forward and reverse FFT calculations.
+ *
+ *  This takes as input the vector of interest (in) and the direction 
+ *  (direction), which is specified by an enumerated type psFftDirection.
+ *  The input vector may be of type psF32 or psC32, the result is always 
+ *  psC32. If the input vector is psF32, the direction must be forward. 
+ *  
+ *  @return psVector* the FFT transformation result
+ */
+psVector* psVectorFFT(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in,                ///< the vector to apply transform to
+    psFFTFlags direction               ///< the direction of the transform
+);
+
+/** extract the real portion of a complex vector
+ * 
+ *  @return psVector*   real portion of the input vector.
+ */
+psVector* psVectorReal(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                ///< the psVector to extract real portion from
+);
+
+/** extract the imaginary portion of a complex vector
+ * 
+ *  @return psVector*   imaginary portion of the input vector.
+ */
+psVector* psVectorImaginary(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                 ///< the psVector to extract imaginary portion from
+);
+
+/** creates a complex vector from separate real and imaginary vectors
+ * 
+ *  @return psVector*   resulting complex vector
+ */
+psVector* psVectorComplex(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* real,              ///< the real vector
+    const psVector* imag               ///< the imaginary vector
+);
+
+/** computes the complex conjugate of a vector
+ * 
+ *  @return psVector*   the complex conjugate of the 'in' vector
+ */
+psVector* psVectorConjugate(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                 ///< the psVector to compute conjugate of
+);
+
+/** computes the power spectrum of a vector
+ * 
+ *  @return psVector*   the power spectrum of the 'in' vector
+ */
+psVector* psVectorPowerSpectrum(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                 ///< the psVector to power spectrum of
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/db/psDB.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/db/psDB.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/db/psDB.c	(revision 22271)
@@ -0,0 +1,1596 @@
+/** @file  psDB.c
+ *
+ * -*- mode: C; c-basic-indent: 4; tab-width: 8; indent-tabs-mode: nil -*-
+ * vim: set cindent ts=8 sw=4 expandtab:
+ *
+ *  @brief database functions
+ *
+ *  This file contains functions that perform basic database operations.  MySQL
+ *  4.1.2 or newer is required.
+ *
+ *  @author Aaron Culliney
+ *  @author Joshua Hoblitt
+ *
+ *  @version $Revision: 1.16.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2005 Joshua Hoblitt, University of Hawaii
+ */
+
+#ifdef BUILD_PSDB
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <mysql.h>
+#include <mysql_com.h> // enum_field_types
+
+#include "psDB.h"
+#include "psMemory.h"
+#include "psAbort.h"
+#include "psError.h"
+#include "psString.h"
+
+
+typedef struct
+{
+    enum enum_field_types type;
+    bool            isUnsigned;
+}
+mysqlType;
+
+// database utility functions
+static bool     psDBRunQuery(psDB *dbh, const char *query);
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount);
+
+// SQL generation functions
+static char    *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, psU64 limit);
+static char    *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row);
+static char    *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values);
+static char    *psDBGenerateDeleteRowSQL(const char *tableName, psMetadata *where);
+static char    *psDBGenerateWhereSQL(psMetadata *where);
+static char    *psDBGenerateSetSQL(psMetadata *set
+                                  );
+
+// lookup table functions
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags);
+static char    *psDBPTypeToSQL(psElemType pType);
+static mysqlType *psDBPTypeToMySQL(psElemType pType);
+
+static psHash  *psDBGetPTypeToSQLTable(void);
+static void     psDBPTypeToSQLTableCleanup(void);
+
+static psHash  *psDBGetSQLToPTypeTable(void);
+static void     psDBSQLToPTypeTableCleanup(void);
+
+static psHash  *psDBGetMySQLToSQLTable(void);
+static void     psDBMySQLToSQLTableCleanup(void);
+
+static psHash  *psDBGetPTypeToMySQLTable(void);
+static void     psDBPTypeToMySQLTableCleanup(void);
+
+static psPtr    psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned);
+static void     psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string);
+static void     psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value);
+
+// pType utility functions
+static psPtr    psDBGetPTypeNaN(psElemType pType);
+static bool     psDBIsPTypeNaN(psElemType pType, psPtr data);
+
+// string utility functions
+static char    *psDBIntToString(psU64 n);
+
+
+// public functions
+/*****************************************************************************/
+
+psDB *psDBInit(const char *host, const char *user, const char *passwd, const char *dbname)
+{
+    MYSQL           *mysql;
+    psDB            *dbh;
+
+    mysql = mysql_init(NULL);
+    if (!mysql) {
+        psAbort(__func__, "mysql_init(), out of memory.");
+    }
+
+    if (!mysql_real_connect(mysql, host, user, passwd, dbname, 0, NULL, 0)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to connect to database.  Error: %s", mysql_error(mysql));
+
+        mysql_close(mysql);
+
+        return NULL;
+    }
+
+    dbh = psAlloc(sizeof(psDB));
+
+    dbh->mysql = mysql;
+
+    return dbh;
+}
+
+void psDBCleanup(psDB *dbh)
+{
+    mysql_close(dbh->mysql);
+    dbh->mysql = NULL;
+    psFree(dbh);
+
+    // ASC WARNING NOTE: the psDBSQLToPTypeTableCleanup cleanup routine
+    // needs to be called first because it refers to
+    // psDBGetPTypeToSQLTable ...
+    psDBSQLToPTypeTableCleanup();
+    psDBMySQLToSQLTableCleanup();
+    psDBPTypeToSQLTableCleanup();
+    psDBPTypeToMySQLTableCleanup();
+}
+
+bool psDBCreate(psDB *dbh, const char *dbname)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "CREATE DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_create_db() is deprecated
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create new database.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBChange(psDB *dbh, const char *dbname)
+{
+    if (mysql_select_db(dbh->mysql, dbname) != 0) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to change database.  Error: %s", mysql_error(dbh->mysql));
+
+        return false;
+    }
+
+    return true;
+}
+
+bool psDBDrop(psDB *dbh, const char *dbname)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "DROP DATABASE %s", dbname);
+
+    // the MySQL C API notes that mysql_drop_db() is deprecated
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop database.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBCreateTable(psDB *dbh, const char *tableName, const psMetadata *md)
+{
+    char            *query;
+    bool            status;
+
+    query = psDBGenerateCreateTableSQL(tableName, md);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to create table.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+bool psDBDropTable(psDB *dbh, const char *tableName)
+{
+    char            *query = NULL;
+    bool            status;
+
+    psStringAppend(&query, "DROP TABLE %s", tableName);
+
+    status = psDBRunQuery(dbh, query);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to drop table.");
+    }
+
+    psFree(query);
+
+    return status;
+}
+
+psArray *psDBSelectColumn(psDB *dbh, const char *tableName, const char *col, psU64 limit)
+{
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    char            *query;             // SQL query
+    my_ulonglong    rowCount;           // number of rows in db result set
+    unsigned long   dataSize;           // size of field
+    unsigned int    fieldCount;         // number of fields in db result set
+    psArray         *column = NULL;     // return array
+    psPtr           data;               // copy of result field
+
+    query = psDBGenerateSelectRowSQL(tableName, col, NULL, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+        return NULL;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+        psFree(query);
+        return NULL;
+    }
+
+    psFree(query);
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query returned no data.  If it's non-zero
+        // then something bad has happened.
+        if (fieldCount != 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+            return NULL;
+        }
+    }
+
+    rowCount = mysql_num_rows(result);
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    column = psArrayAlloc(rowCount);
+    column->n = 0;
+
+    while ((row = mysql_fetch_row(result))) {
+        // get the first element of lengths array that is part of the
+        // result set
+        dataSize = *(mysql_fetch_lengths(result));
+
+        // represent NULL as an empty string
+        if (row[0] == NULL) {
+            data = psStringCopy("");
+        } else {
+            data = psAlloc(dataSize+1);
+            memcpy(data, row[0], dataSize);
+            ((char*)data)[dataSize] = '\0';
+        }
+
+        // add field to return array
+        psArrayAdd(column, 0, data);
+        psFree(data);
+    }
+
+    mysql_free_result(result);
+
+    return column;
+}
+
+// dest = assign to, source = source string psArray, conv = conversion function,
+// type = type to cast to, pType = psElemType
+#define PS_STR_ARRAY_TO_PTYPE(dest, source, conv, type, pType) \
+{ \
+    psPtr           myNaN; \
+    int             i; \
+    \
+    for (i = 0; i < source->n; i++) { \
+        if (strlen(source->data[i])) { \
+            dest[i] = (type)conv(source->data[i]); \
+        } else { \
+            myNaN = psDBGetPTypeNaN(pType); \
+            dest[i] = *(type *)myNaN; \
+            psFree(myNaN); \
+        } \
+    } \
+}
+
+psVector *psDBSelectColumnNum(psDB *dbh, const char *tableName, const char *col, psElemType pType, const psU64 limit)
+{
+    psArray         *stringColumn;      // source psArray
+    psVector        *column;            // dest psVector
+
+    stringColumn = psDBSelectColumn(dbh, tableName, col, limit);
+    if (!stringColumn) {
+        // could be an error or the result set was just empty
+        return NULL;
+    }
+
+    column = psVectorAlloc(stringColumn->n, pType);
+
+    // conversion functions are a portability issue
+    switch (pType) {
+    case PS_TYPE_S8:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S8, stringColumn, atoi, psS8, PS_TYPE_S8);
+        break;
+    case PS_TYPE_S16:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S16, stringColumn, atoi, psS16, PS_TYPE_S16);
+        break;
+    case PS_TYPE_S32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S32, stringColumn, atoi, psS32, PS_TYPE_S32);
+        break;
+    case PS_TYPE_S64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.S64, stringColumn, atoll, psS64, PS_TYPE_S64);
+        break;
+    case PS_TYPE_U8:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U8, stringColumn, atoi, psU8, PS_TYPE_U8);
+        break;
+    case PS_TYPE_U16:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U16, stringColumn, atoi, psU16, PS_TYPE_U16);
+        break;
+    case PS_TYPE_U32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U32, stringColumn, atoi, psU32, PS_TYPE_U32);
+        break;
+    case PS_TYPE_U64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.U64, stringColumn, atoll, psU64, PS_TYPE_U64);
+        break;
+    case PS_TYPE_F32:
+        PS_STR_ARRAY_TO_PTYPE(column->data.F32, stringColumn, atof, psF32, PS_TYPE_F32);
+        break;
+    case PS_TYPE_F64:
+        PS_STR_ARRAY_TO_PTYPE(column->data.F64, stringColumn, atof, psF64, PS_TYPE_F64);
+        break;
+    case PS_TYPE_C32:
+        // this is a bogus SQL type
+        PS_STR_ARRAY_TO_PTYPE(column->data.C32, stringColumn, atof, psC32, PS_TYPE_C32);
+        break;
+    case PS_TYPE_C64:
+        // this is a bogus SQL type
+        PS_STR_ARRAY_TO_PTYPE(column->data.C64, stringColumn, atof, psC64, PS_TYPE_C64);
+        break;
+    case PS_TYPE_BOOL:
+        // valid for psVector?
+        break;
+    }
+
+    psFree(stringColumn);
+
+    return column;
+}
+
+psArray *psDBSelectRows(psDB *dbh, const char *tableName, psMetadata *where, const psU64 limit)
+{
+    MYSQL_RES       *result;            // complete db result set
+    MYSQL_ROW       row;                // single row of db result set
+    MYSQL_FIELD     *field;             // field type info
+    char            *query;             // SQL query
+    my_ulonglong    rowCount;           // number of rows in db result set
+    unsigned int    fieldCount;         // number of fields in db result set
+    unsigned long   *fieldLength;       // field sizes
+    long            len;                // field length
+    psArray         *resultSet;         // return array
+    int             i;                  // field index
+    psMetadata      *md;                // a row
+    psU32           pType;              // psElemType of a field
+    psPtr           data;               // copy of result field
+
+    query = psDBGenerateSelectRowSQL(tableName, NULL, where, limit);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Query execution failed.");
+
+        psFree(query);
+
+        return NULL;
+    }
+
+    psFree(query);
+
+    result = mysql_store_result(dbh->mysql);
+    if (!result) {
+        // no result set
+        fieldCount = mysql_field_count(dbh->mysql);
+
+        // if field count is zero the query should have returned no data.  If
+        // it's non-zero then something bad has happened.
+        if (fieldCount != 0) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Query returned no data.  Error: %s", mysql_error(dbh->mysql));
+
+            return NULL;
+        }
+    }
+
+    rowCount = mysql_num_rows(result);
+
+    // pre-allocate enough elements to hold the complete result set
+    // then reset n to 0 so elements are added from the beginning of
+    // the array
+    resultSet = psArrayAlloc(rowCount);
+    resultSet->n = 0;
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    while ((row = mysql_fetch_row(result))) {
+        // allocate new psMetadata to represent a row
+        md = psMetadataAlloc();
+
+        fieldLength = mysql_fetch_lengths(result);
+
+        for (i = 0; i < fieldCount; i++) {
+            // lookup MySQL column type
+            pType = psDBMySQLToPType(field[i].type, field[i].flags);
+
+            len = fieldLength[i];
+            if (len) {
+                data = psAlloc(len+1);
+                memcpy(data, row[i], len);
+                ((char*)data)[len] = '\0';
+            } else {
+                data = psDBGetPTypeNaN(pType);
+            }
+
+            // copy field data and convert NULLs to the appropriate NaN value
+            if (pType == PS_META_STR) {
+                psMetadataAddStr(md, 0, field[i].name, "", data);
+            } else if (pType == PS_META_S32) {
+                psMetadataAddS32(md, 0, field[i].name, "", atoll(data));
+            } else if (pType == PS_META_F32) {
+                psMetadataAddF32(md, 0, field[i].name, "", atof(data));
+            } else if (pType == PS_META_F64) {
+                psMetadataAddF64(md, 0, field[i].name, "", atof(data));
+            } else {
+                // XXX: assume binary string ...
+                psMetadataAddStr(md, 0, field[i].name, "", data);
+            }
+
+            psFree(data);
+        }
+
+        // add row to result set
+        psArrayAdd(resultSet, 0, md);
+        psFree(md);
+    }
+
+    mysql_free_result(result);
+
+    return resultSet;
+}
+
+bool psDBInsertOneRow(psDB *dbh, const char *tableName, const psMetadata *row)
+{
+    psArray         *rowSet;            // psArray of row to insert
+
+    rowSet = psArrayAlloc(1);
+    rowSet->n = 0;
+    psArrayAdd(rowSet, 0, row);
+
+    if (!psDBInsertRows(dbh, tableName, rowSet)) {
+        psError(PS_ERR_UNKNOWN, false, "Insert failed.");
+
+        psFree(rowSet);
+
+        return false;
+    }
+
+    psFree(rowSet);
+
+    return true;
+}
+
+bool psDBInsertRows(psDB *dbh, const char *tableName, const psArray *rowSet)
+{
+    psMetadata      *row;               // row of data
+    char            *query;             // SQL query
+    MYSQL_STMT      *stmt;              // prepared db statement
+    MYSQL_BIND      *bind;              // field values to insert
+    unsigned long   paramCount;         // number of placeholders in query
+    psU64           j;                  // row index
+
+    // we are assuming that all rows in the set have an identical with reguard
+    // to field count and type
+    row = rowSet->data[0];
+
+    query = psDBGenerateInsertRowSQL(tableName, row);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return NULL;
+    }
+
+    stmt = mysql_stmt_init(dbh->mysql);
+    if (!stmt) {
+        psAbort(__func__, "mysql_stmt_init(), out of memory.");
+    }
+
+    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_stmt_close(stmt);
+        psFree(query);
+
+        return false;
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    paramCount = mysql_stmt_param_count(stmt);
+
+    // structure larger enough to hold one field of data per place holder
+    bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+
+    // loop over rows
+    for (j = 0; j < rowSet->n; j++) {
+        row = rowSet->data[j];
+
+        // reset bind for each row
+        memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
+
+        if (!psDBPackRow(bind, row, paramCount)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to pack params into bind structure.");
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+        }
+
+        if (mysql_stmt_bind_param(stmt, bind)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+
+            return false;
+        }
+
+        if (mysql_stmt_execute(stmt)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s",
+                    mysql_stmt_error(stmt));
+
+            mysql_rollback(dbh->mysql);
+
+            psFree(bind);
+            mysql_stmt_close(stmt);
+
+            return false;
+        }
+    } // end loop over rows
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    psFree(bind);
+    mysql_stmt_close(stmt);
+
+    return true;
+}
+
+psArray *psDBDumpRows(psDB *dbh, const char *tableName)
+{
+    return psDBSelectRows(dbh, tableName, NULL, 0);
+}
+
+psMetadata *psDBDumpCols(psDB *dbh, const char *tableName)
+{
+    MYSQL_RES       *result;
+    MYSQL_FIELD     *field;
+    unsigned int    fieldCount;
+    psMetadata      *table;
+    psU32           pType;
+    unsigned int    i;
+    psPtr           column;
+
+    // find column types
+    result = mysql_list_fields(dbh->mysql, tableName, NULL);
+    if (!result) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Failed to retrieve column types.");
+    }
+
+    field = mysql_fetch_fields(result);
+    fieldCount = mysql_num_fields(result);
+
+    table = psMetadataAlloc();
+
+    // fetch each column and load into psMetadata
+    for (i =0; i < fieldCount; i++) {
+        // find ptype of column
+        pType = psDBMySQLToPType(field[i].type, field[i].flags);
+        //psLogMsg( __func__, PS_LOG_INFO, "pType=[%ld]\n", pType );
+
+        // if the ptype is PS_TYPE_PTR assume that it's a string and fetch the
+        // column as an psArray of strings; otherwise fetch the column as a
+        // psVector.
+        if (pType == PS_META_STR) {
+            // PS_META_UNKNOWN -> PS_META_ARRAY ?
+            column = psDBSelectColumn(dbh, tableName, field[i].name, 0);
+            psMetadataAddStr(table, 0, field[i].name, "", column);
+            psFree(column);
+        } else {
+            column = psDBSelectColumnNum(dbh, tableName, field[i].name, pType, 0);
+            psMetadataAddVector(table, 0, field[i].name, "", column);
+            psFree(column);
+        }
+    }
+
+    return table;
+}
+
+psS64 psDBUpdateRows(psDB *dbh, const char *tableName, const psMetadata *where, const psMetadata *values)
+{
+    char            *query;
+    MYSQL_STMT      *stmt;              // prepared db statement
+    unsigned long   paramCount;         // number of placeholders in query
+    MYSQL_BIND      *bind;              // field values to insert
+    my_ulonglong    rowsAffected;       // number of rows affected by query
+
+    query = psDBGenerateUpdateRowSQL(tableName, where, values);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return -1;
+    }
+
+    stmt = mysql_stmt_init(dbh->mysql);
+    if (!stmt) {
+        psAbort(__func__, "mysql_stmt_init(), out of memory.");
+    }
+
+    if (mysql_stmt_prepare(stmt, query, (unsigned long)strlen(query))) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to prepare query.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_stmt_close(stmt);
+        psFree(query);
+
+        return -1;
+    }
+
+    psFree(query);
+
+    // how many place holders are in our query
+    paramCount = mysql_stmt_param_count(stmt);
+
+    // structure large enough to hold one field of data per place holder
+    bind = psAlloc(sizeof(MYSQL_BIND) * paramCount);
+
+    // init bind
+    memset(bind, 0, sizeof(MYSQL_BIND) * paramCount);
+
+    if (!psDBPackRow(bind, values, paramCount)) {
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    if (mysql_stmt_bind_param(stmt, bind)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to bind params.  Error: %s", mysql_stmt_error(stmt));
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    if (mysql_stmt_execute(stmt)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to execute prepared statement.  Error: %s",
+                mysql_stmt_error(stmt));
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(bind);
+        mysql_stmt_close(stmt);
+
+        return -1;
+    }
+
+    psFree(bind);
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    rowsAffected = mysql_stmt_affected_rows(stmt);
+
+    mysql_stmt_close(stmt);
+
+    return rowsAffected;
+}
+
+psS64 psDBDeleteRows(psDB *dbh, const char *tableName, const psMetadata *where)
+{
+    char            *query;
+
+    query = psDBGenerateDeleteRowSQL(tableName, where);
+    if (!query) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Query generation failed.");
+
+        return -1;
+    }
+
+    if (!psDBRunQuery(dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "Delete failed.");
+
+        mysql_rollback(dbh->mysql);
+
+        psFree(query);
+
+        return -1;
+    }
+
+    psFree(query);
+
+    if (!where) {
+        // we truncated the table so mysql_affected_rows() won't work
+        return 1;
+    }
+
+    // point of no return
+    mysql_commit(dbh->mysql);
+
+    return (psS64)mysql_affected_rows(dbh->mysql);
+}
+
+// database utility functions
+/*****************************************************************************/
+
+static bool psDBRunQuery(psDB *dbh, const char *query)
+{
+    if (mysql_real_query(dbh->mysql, query, (unsigned long)strlen(query)) !=0) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to execute query.  Error: %s\n", mysql_error(dbh->mysql));
+
+        return false;
+    }
+
+    return true;
+}
+
+static inline bool psDBPackRow(MYSQL_BIND *bind, psMetadata *values, psU32 paramCount)
+{
+    psListIterator  *cursor;            // row iterator
+    psMetadataItem  *item;              // field in row
+    mysqlType       *mType;             // type tmp variable
+    static bool     isNull = true;      // used in a MYSQL_BIND to indicate NULL,
+    // this will be used outside of this func
+    psU32           i;                  // field index
+
+    // check size of values == paramCount ?
+
+    cursor = psListIteratorAlloc(values->list, 0, false);
+
+    // loop over fields
+    i = 0;
+    while ((item = psListGetAndIncrement(cursor))) {
+        // lookup pType -> mysql type
+        mType = psDBPTypeToMySQL(item->type);
+
+        bind[i].buffer_type = mType->type;
+        bind[i].is_unsigned = mType->isUnsigned;
+
+        psFree(mType);
+
+        // input data length is determined by the MYSQL_TYPE_* unless it's a string
+        if (item->type == PS_TYPE_S32) {
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.S32;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S32)
+                              ? (my_bool *)&isNull
+                              : NULL;
+        } else if (item->type == PS_TYPE_F32) {
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.F32;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F32)
+                              ? (my_bool *)&isNull
+                              : NULL;
+        } else if (item->type == PS_TYPE_F64) {
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.F64;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64)
+                              ? (my_bool *)&isNull
+                              : NULL;
+            // convert NaNs to NULL and set the buffer_length for strings
+            //} else if ((item->type == PS_META_STR) && (item->pType == PS_TYPE_PTR)) {
+        } else if (item->type == PS_META_STR) {
+
+            bind[i].buffer_length = (unsigned long)strlen((char *)item->data.V);
+            bind[i].length  = &bind[i].buffer_length;
+            bind[i].buffer  = item->data.V;
+            bind[i].is_null = *(char *)item->data.V == '\0'
+                              ? (my_bool *)&isNull
+                              : NULL;
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE , true,
+                    "FIXME: Only type of PS_TYPE_S32 (PS_META_S32), "
+                    "PS_TYPE_F32 (PS_META_F32), PS_TYPE_F64 (PS_META_F64), "
+                    "and PS_META_STR are supported.");
+
+            psFree(cursor);
+
+            return false;
+        }
+
+        // increment field index
+        i++;
+    }
+
+    psFree(cursor);
+
+    return true;
+}
+
+
+// SQL generation functions
+/*****************************************************************************/
+
+static char *psDBGenerateCreateTableSQL(const char *tableName, psMetadata *table)
+{
+    char            *query = NULL;      // complete query
+    psMetadataItem  *item;              // column description
+    psListIterator  *cursor;            // column iterator
+    char            *colType;           // type lookup table
+
+    if (!table) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "table param may not be null.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "CREATE TABLE %s (", tableName);
+
+    cursor = psListIteratorAlloc(table->list, 0, false);
+
+    // find column name and type
+    while ((item = psListGetAndIncrement(cursor))) {
+        if ((item->type == PS_META_S32) || (item->type == PS_META_F32) || (item->type == PS_META_F64) ||
+                (item->type == PS_TYPE_S32) || (item->type == PS_TYPE_F32) || (item->type == PS_TYPE_F64)) {
+            // + column name + _ + column type
+            colType = psDBPTypeToSQL(item->type);
+            psStringAppend(&query, "%s %s", item->name, colType);
+            psFree(colType);
+        } else if (item->type == PS_META_STR) {
+            // + column name + _ + varchar( + length + )
+            psStringAppend(&query, "%s VARCHAR(%s)", item->name, item->data.V);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "FIXME: Only type of PS_META_S32, PS_META_F32, PS_META_F64, "
+                    "and PS_META_STR are supported, (not %d).", item->type);
+
+            psFree(query);
+            psFree(cursor);
+
+            return NULL;
+        }
+
+        // add a , after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psListIteratorSet(cursor, 0);
+
+    // find database indexes
+    while ((item = psListGetAndIncrement(cursor))) {
+        if ((strncmp(item->comment, "Primary Key", strlen("Primary Key"))) == 0) {
+            psStringAppend(&query, ", PRIMARY KEY(%s)", item->name);
+        } else if ((strncmp(item->comment, "Key", strlen("Key"))) == 0) {
+            psStringAppend(&query, ", KEY(%s)", item->name);
+        }
+    }
+
+    psFree(cursor);
+
+    // end column types + table type
+    psStringAppend(&query, ") ENGINE=innodb");
+
+    return query;
+}
+
+static char *psDBGenerateSelectRowSQL(const char *tableName, const char *col, psMetadata *where, const psU64 limit)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+    char            *limitString;
+
+    // select all columns if col is NULL
+    if (col) {
+        psStringAppend(&query, "SELECT %s FROM %s", col, tableName);
+    } else {
+        psStringAppend(&query, "SELECT * FROM %s", tableName);
+    }
+
+    // select all rows if where is NULL
+    if (where) {
+        whereSQL = psDBGenerateWhereSQL(where);
+        if (!whereSQL) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+            psFree(query);
+
+            return NULL;
+        }
+        psStringAppend(&query, " %s", whereSQL);
+        psFree(whereSQL);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        limitString = psDBIntToString(limit);
+        psStringAppend(&query, " LIMIT %s", limitString);
+        psFree(limitString);
+    }
+
+    return query;
+}
+
+static char *psDBGenerateInsertRowSQL(const char *tableName, psMetadata *row)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    psStringAppend(&query, "INSERT INTO %s (", tableName);
+
+    cursor = psListIteratorAlloc(row->list, 0, false);
+
+    // get field names
+    while ((item = psListGetAndIncrement(cursor))) {
+        psStringAppend(&query, item->name);
+
+        // + , + _ between every field name
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    // end of field names
+    psStringAppend(&query, ") VALUES (");
+
+    psListIteratorSet(cursor, 0);
+
+    // create value place holders
+    while ((item = psListGetAndIncrement(cursor))) {
+        psStringAppend(&query, "?");
+
+        // + ", " between every place holder
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ", ");
+        }
+    }
+
+    psFree(cursor);
+
+    // end of values
+    psStringAppend(&query, ")");
+
+    return query;
+}
+
+static char *psDBGenerateUpdateRowSQL(const char *tableName, psMetadata *where, psMetadata *values)
+{
+    char            *query = NULL;
+    char            *setSQL;
+    char            *whereSQL;
+
+    if ((!values) || (!where)) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "values and where params may not be NULL.");
+
+        return NULL;
+    }
+
+    setSQL = psDBGenerateSetSQL(values);
+    if (!setSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    whereSQL = psDBGenerateWhereSQL(where);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "UPDATE %s %s %s", tableName, setSQL, whereSQL);
+    psFree(setSQL);
+    psFree(whereSQL);
+
+    return query;
+}
+
+static char *psDBGenerateDeleteRowSQL(const char *tableName, psMetadata *where)
+{
+    char            *query = NULL;
+    char            *whereSQL;
+
+    // delete all rows if where is NULL
+    if (!where) {
+        psStringAppend(&query, "TRUNCATE TABLE %s", tableName);
+
+        return query;
+    }
+
+    whereSQL = psDBGenerateWhereSQL(where);
+    if (!whereSQL) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "SQL substring generation failed.");
+
+        return NULL;
+    }
+
+    psStringAppend(&query, "DELETE FROM %s %s", tableName, whereSQL);
+    psFree(whereSQL);
+
+    return query;
+}
+
+static char *psDBGenerateWhereSQL(psMetadata *where)
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    if (!where) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "where param may not be NULL.");
+
+        return NULL;
+    }
+
+    query = psStringCopy("WHERE ");
+
+    cursor = psListIteratorAlloc(where->list, 0, false);
+
+    // find column name and match pattern
+    while ((item = psListGetAndIncrement(cursor))) {
+        // item->data must be a string
+        if ((item->type == PS_META_S32) || (item->type == PS_TYPE_S32)) {
+            psStringAppend(&query, "%s=%d", item->name, (int)(item->data.S32));
+        } else if ((item->type == PS_META_F32) || (item->type == PS_TYPE_F32)) {
+            psStringAppend(&query, "%s=%g", item->name, (double)(item->data.F32));
+        } else if ((item->type == PS_META_F64) || (item->type == PS_TYPE_F64)) {
+            psStringAppend(&query, "%s=%g", item->name, (double)(item->data.F64));
+        } else if (item->type == PS_META_STR) {
+            // + column name + _ + like + _ + ' + value + '
+            if (*(char *)item->data.V == '\0') {
+                psStringAppend(&query, "%s IS NULL", item->name);
+            } else {
+                // XXX ASC NOTE: we should have a better match for
+                // char & varchar columns than this.  LIKE is OK for
+                // very large TEXT columns that really shouldn't be
+                // used in a where clause...
+                psStringAppend(&query, "%s LIKE '%s'", item->name, item->data.V);
+            }
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    "Only types PS_META_S32, PS_META_F32, PS_META_F64, PS_META_STR is supported");
+
+            psFree(cursor);
+            psFree(query);
+
+            return NULL;
+        }
+
+        // + " and " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, " AND ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+static char *psDBGenerateSetSQL(psMetadata *set
+                               )
+{
+    char            *query = NULL;
+    psListIterator  *cursor;
+    psMetadataItem  *item;
+
+    if (!set
+       ) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true, "set param may not be NULL.");
+
+        return NULL;
+    }
+
+    query = psStringCopy("SET ");
+
+    cursor = psListIteratorAlloc(set
+                                 ->list, 0, false);
+
+    // find column name
+    while ((item = psListGetAndIncrement(cursor))) {
+        // + column name + _ + = + _ + ?
+        psStringAppend(&query, "%s = ?", item->name);
+
+        // + ", " after every column declaration except the last one
+        if (!cursor->offEnd) {
+            psStringAppend(&query, ",  ");
+        }
+    }
+
+    psFree(cursor);
+
+    return query;
+}
+
+
+// lookup table functions
+/*****************************************************************************/
+
+static psElemType psDBMySQLToPType(enum enum_field_types type, unsigned int flags)
+{
+    psHash          *mysqlToSQLTable;   // type lookup table
+    psHash          *sqlToPSTable;      // type lookup table
+    char            *key;               // hash tmp value
+    char            *value;             // hash tmp value
+    char            *sqlType;           // copy of lookup table result
+    psU32           pType;              // psElemType of a field
+
+    mysqlToSQLTable = psDBGetMySQLToSQLTable();
+
+    // lookup MySQL column type
+    key     = psDBIntToString((psU64)type);
+    sqlType = psHashLookup(mysqlToSQLTable, key);
+    psFree(key);
+    psFree(mysqlToSQLTable);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+        return -1;
+    }
+
+    // MySQL column types can not be directly translated to PS
+    // types as the the mysql types do not tell you if the value is
+    // signed or unsigned.  The result is this ugly conversion from
+    // mysql -> ascii -> ptype
+    if (flags & UNSIGNED_FLAG) {
+        psStringPrepend(&sqlType, "UNSIGNED ");
+    }
+
+    //psLogMsg( __func__, PS_LOG_INFO, "sqlType=[%s]\n", sqlType );
+
+    // convert MySQL type to PS type
+    sqlToPSTable = psDBGetSQLToPTypeTable();
+    value = psHashLookup(sqlToPSTable, sqlType);
+    psFree(sqlToPSTable);
+
+    if (!value) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return -1;
+    }
+
+    pType = (psU32)atol(value);
+
+    return pType;
+}
+
+static char *psDBPTypeToSQL(psElemType pType)
+{
+    psHash          *pTypeToSQLTable;   // type lookup table
+    char            *key;               // hash tmp value
+    char            *sqlType;             // hash tmp value
+
+    pTypeToSQLTable = psDBGetPTypeToSQLTable();
+
+    key = psDBIntToString((psU64)pType);
+    sqlType = psHashLookup(pTypeToSQLTable, key);
+    psFree(key);
+    psFree(pTypeToSQLTable);
+
+    if (!sqlType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(sqlType);
+
+    return sqlType;
+}
+
+static mysqlType *psDBPTypeToMySQL(psElemType pType)
+{
+    psHash          *pTypeToMySQLTable; // type lookup table
+    char            *key;               // hash tmp value
+    mysqlType       *mType;             // mysqlType struct to return
+
+    pTypeToMySQLTable = psDBGetPTypeToMySQLTable();
+
+    key = psDBIntToString((psU64)pType);
+    mType = psHashLookup(pTypeToMySQLTable, key);
+    psFree(key);
+    psFree(pTypeToMySQLTable);
+
+    if (!mType) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "type lookup failed.");
+
+        return NULL;
+    }
+
+    psMemIncrRefCounter(mType);
+
+    return mType;
+}
+
+static psHash *psDBGetPTypeToSQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(14);
+
+        // no support for CHAR, TEXT or GLOB
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S8,  "TINYINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S16, "SMALLINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S32, "INT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_S64, "BIGINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U8,  "UNSIGNED TINYINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U16, "UNSIGNED SMALLINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U32, "UNSIGNED INT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_U64, "UNSIGNED BIGINT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_F32, "FLOAT");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_F64, "DOUBLE");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_C32, "PS_TYPE_C32 is not supported");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_C64, "PS_TYPE_C64 is not supported");
+        psDBAddToLookupTable(lookupTable, PS_TYPE_BOOL,"BOOLEAN");
+        psDBAddToLookupTable(lookupTable, PS_META_STR, "VARCHAR");
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBPTypeToSQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetPTypeToSQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetSQLToPTypeTable(void)
+{
+    static psHash   *lookupTable = NULL;
+    psHash          *psToSQLTable;
+    psList          *list;
+    psListIterator  *cursor;
+    char            *key;
+    char            *value;
+
+    if (!lookupTable) {
+        // invert the PSToSQL table
+        psToSQLTable = psDBGetPTypeToSQLTable();
+        lookupTable = psHashAlloc(psToSQLTable->nbucket);
+
+        list = psHashKeyList(psToSQLTable);
+        cursor = psListIteratorAlloc(list, 0, false);
+
+        while ((key = psListGetAndIncrement(cursor))) {
+            value = psHashLookup(psToSQLTable, key);
+            // switch key and value
+            psHashAdd(lookupTable, value, key);
+        }
+
+        // Add BLOB & TEXT reverse mappings
+        value = psDBIntToString((psU64)PS_META_STR);
+        psHashAdd(lookupTable, "BLOB",    value);
+        psHashAdd(lookupTable, "TEXT",    value);
+        psFree(value);
+
+        // DECIMAL does not exist in the pType to SQL table
+        value = psDBIntToString(0);
+        psHashAdd(lookupTable, "DECIMAL", value);
+        psFree(value);
+
+        psFree(cursor);
+        psFree(list);
+        psFree(psToSQLTable);
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBSQLToPTypeTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetSQLToPTypeTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetMySQLToSQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(20);
+
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TINY,      "TINYINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_SHORT,     "SMALLINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_LONG,      "INT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_INT24,     "MEDIUMINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_LONGLONG,  "BIGINT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DECIMAL,   "DECIMAL");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_FLOAT,     "FLOAT");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DOUBLE,    "DOUBLE");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TIMESTAMP, "TIMESTAMP");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DATE,      "DATE");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_TIME,      "TIME");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_DATETIME,  "DATETIME");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_YEAR,      "YEAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_STRING,    "CHAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_VAR_STRING,"VARCHAR");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_BLOB,      "BLOB");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_SET,       "SET");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_ENUM,      "ENUM");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_NULL,      "NULL-type");
+        psDBAddToLookupTable(lookupTable, FIELD_TYPE_CHAR,      "TINYINT");
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBMySQLToSQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetMySQLToSQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psHash *psDBGetPTypeToMySQLTable(void)
+{
+    static psHash   *lookupTable = NULL;
+
+    if (!lookupTable) {
+        lookupTable = psHashAlloc(14);
+
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_S64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U8,     psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U16,    psDBMySQLTypeAlloc(MYSQL_TYPE_SHORT,      true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U32,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONG,       true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_U64,    psDBMySQLTypeAlloc(MYSQL_TYPE_LONGLONG,   true));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_F32,    psDBMySQLTypeAlloc(MYSQL_TYPE_FLOAT,      false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_F64,    psDBMySQLTypeAlloc(MYSQL_TYPE_DOUBLE,     false));
+        // bogus type
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_C32,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        // bogus type
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_C64,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_TYPE_BOOL,   psDBMySQLTypeAlloc(MYSQL_TYPE_TINY,       true));
+        // XXX: removed PS_TYPE_PTR, can this be removed too?
+        // psDBAddVoidToLookupTable(lookupTable, PS_TYPE_PTR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+
+        psDBAddVoidToLookupTable(lookupTable, PS_META_STR,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_VEC,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_IMG,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_HASH,   psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_LOOKUPTABLE,
+                                 psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_JPEG,   psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_PNG,    psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_ASTROM, psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+        psDBAddVoidToLookupTable(lookupTable, PS_META_UNKNOWN,psDBMySQLTypeAlloc(MYSQL_TYPE_VAR_STRING, false));
+    }
+
+    // simulate true ref counting
+    psMemIncrRefCounter(lookupTable);
+
+    return lookupTable;
+}
+
+static void psDBPTypeToMySQLTableCleanup(void)
+{
+    psHash          *lookupTable;
+
+    lookupTable = psDBGetPTypeToMySQLTable();
+
+    psMemDecrRefCounter(lookupTable);
+    psFree(lookupTable);
+}
+
+static psPtr psDBMySQLTypeAlloc(enum enum_field_types type, bool isUnsigned)
+{
+    mysqlType       *mType;
+
+    mType = psAlloc(sizeof(mysqlType));
+    mType->type       = type;
+    mType->isUnsigned = isUnsigned;
+
+    return mType;
+}
+
+static void psDBAddToLookupTable(psHash *lookupTable, psU32 type, const char *string)
+{
+    char            *key;
+    char            *value;
+
+    key = psDBIntToString((psU64)type);
+    value = psStringCopy(string);
+
+    psHashAdd(lookupTable, key, value);
+
+    psFree(key);
+    psFree(value);
+}
+
+static void psDBAddVoidToLookupTable(psHash *lookupTable, psU32 type, psPtr value)
+{
+    char            *key;
+
+    key = psDBIntToString((psU64)type);
+
+    psHashAdd(lookupTable, key, value);
+
+    // destructive of value parameter
+    psFree(value);
+    psFree(key);
+}
+
+
+// pType utility functions
+/*****************************************************************************/
+
+#define PS_NAN_ALLOC(dest, type, nan) \
+dest = psAlloc(sizeof(type)); \
+*(type *)dest = nan;
+
+static psPtr psDBGetPTypeNaN(psElemType pType)
+{
+    psPtr           myNaN;
+
+    switch (pType) {
+    case PS_TYPE_S8:
+        PS_NAN_ALLOC(myNaN, psS8, PS_MAX_S8);
+        break;
+    case PS_TYPE_S16:
+        PS_NAN_ALLOC(myNaN, psS16, PS_MAX_S16);
+        break;
+    case PS_TYPE_S32:
+        PS_NAN_ALLOC(myNaN, psS32, PS_MAX_S32);
+        break;
+    case PS_TYPE_S64:
+        PS_NAN_ALLOC(myNaN, psS64, PS_MAX_S64);
+        break;
+    case PS_TYPE_U8:
+        PS_NAN_ALLOC(myNaN, psU8, PS_MAX_U8);
+        break;
+    case PS_TYPE_U16:
+        PS_NAN_ALLOC(myNaN, psU16, PS_MAX_U16);
+        break;
+    case PS_TYPE_U32:
+        PS_NAN_ALLOC(myNaN, psU32, PS_MAX_U32);
+        break;
+    case PS_TYPE_U64:
+        PS_NAN_ALLOC(myNaN, psU64, PS_MAX_U64);
+        break;
+    case PS_TYPE_F32:
+        PS_NAN_ALLOC(myNaN, psF32, NAN);
+        break;
+    case PS_TYPE_F64:
+        PS_NAN_ALLOC(myNaN, psF64, NAN);
+        break;
+    case PS_TYPE_C32:
+        // this is a bogus SQL type
+        PS_NAN_ALLOC(myNaN, psC32, NAN);
+        break;
+    case PS_TYPE_C64:
+        // this is a bogus SQL type
+        PS_NAN_ALLOC(myNaN, psC64, NAN);
+        break;
+    case PS_TYPE_BOOL:
+        // what is NaN for a bool?
+        break;
+    }
+
+    return myNaN;
+}
+
+#define PS_IS_NAN(type, data, nan) *(type *)data == nan
+
+static bool psDBIsPTypeNaN(psElemType pType, psPtr data)
+{
+    bool    isNaN;
+
+    switch (pType) {
+    case PS_TYPE_S8:
+        isNaN = PS_IS_NAN(psS8, data, PS_MAX_S8);
+        break;
+    case PS_TYPE_S16:
+        isNaN = PS_IS_NAN(psS16, data, PS_MAX_S16);
+        break;
+    case PS_TYPE_S32:
+        isNaN = PS_IS_NAN(psS32, data, PS_MAX_S32);
+        break;
+    case PS_TYPE_S64:
+        isNaN = PS_IS_NAN(psS64, data, PS_MAX_S64);
+        break;
+    case PS_TYPE_U8:
+        isNaN = PS_IS_NAN(psU8, data, PS_MAX_U8);
+        break;
+    case PS_TYPE_U16:
+        isNaN = PS_IS_NAN(psU16, data, PS_MAX_U16);
+        break;
+    case PS_TYPE_U32:
+        isNaN = PS_IS_NAN(psU32, data, PS_MAX_U32);
+        break;
+    case PS_TYPE_U64:
+        isNaN = PS_IS_NAN(psU64, data, PS_MAX_U64);
+        break;
+    case PS_TYPE_F32:
+        isNaN = PS_IS_NAN(psF32, data, NAN);
+        break;
+    case PS_TYPE_F64:
+        isNaN = PS_IS_NAN(psF64, data, NAN);
+        break;
+    case PS_TYPE_C32:
+        // this is a bogus SQL type
+        isNaN = PS_IS_NAN(psC32, data, NAN);
+        break;
+    case PS_TYPE_C64:
+        // this is a bogus SQL type
+        isNaN = PS_IS_NAN(psC64, data, NAN);
+        break;
+    case PS_TYPE_BOOL:
+        // what is NaN for a bool?
+        break;
+    }
+
+    return isNaN;
+}
+
+
+// string utility functions
+/*****************************************************************************/
+
+static char *psDBIntToString(psU64 n)
+{
+    char            *string;
+    size_t          length;
+
+    // length of string + \0
+    // if n is 0, length is 1 char + \0
+    length = n ? (size_t)log10((double)n) + 1
+             : 2;
+    string = psAlloc(length);
+    sprintf(string, "%li", (long int)n);
+
+    return string;
+}
+
+#endif // BUILD_PSDB
Index: /tags/pap_tags/pap_branch_050518/psLib/src/db/psDB.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/db/psDB.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/db/psDB.h	(revision 22271)
@@ -0,0 +1,267 @@
+/** @file  psDB.h
+ *
+ *  @brief database types and functions
+ *
+ *  This file defines the abstract database type and functions that
+ *  perform basic database operations.
+ *
+ *  @ingroup DataBase
+ *
+ *  @author Joshua Hoblitt
+ *
+ *  @version $Revision: 1.5.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2005 Joshua Hoblitt, University of Hawaii
+ */
+
+#ifndef PS_DB_H
+#define PS_DB_H 1
+
+#ifdef BUILD_PSDB
+
+#include "psType.h"
+#include "psMetadata.h"
+
+/// @addtogroup DataBase
+/// @{
+
+/** Database handle
+ *
+ *  An opaque object representing a database connection.
+ *
+ */
+typedef struct
+{
+    void* mysql;   ///< MySQL database handle
+}
+psDB;
+
+/** Opens a new database connection
+ *
+ *  @return A new psDB object if the database connection is successful or NULL on
+ *  failure.
+ */
+psDB *psDBInit(
+    const char *host,                   ///< Database server hostname
+    const char *user,                   ///< Database username
+    const char *passwd,                 ///< Database password
+    const char *dbname                  ///< Database namespace
+);
+
+/** Closes a database connection
+ */
+void psDBCleanup(
+    psDB *dbh                           ///< Database handle
+);
+
+/** Creates a new database namespace
+ *
+ * @return true on success
+ */
+bool psDBCreate(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< New database namespace
+);
+
+/** Changes the current database namespace
+ *
+ * @return true on success
+ */
+bool psDBChange(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< Database namespace
+);
+
+/** Drops a database namespace
+ *
+ * @return true on success
+ */
+bool psDBDrop(
+    psDB *dbh,                          ///< Database handle
+    const char *dbname                  ///< Database namespace
+);
+
+/** Creates a new database table
+ *
+ * This function generates and executes the SQL needed to create a table named
+ * "tableName", with the column names and data types as described in "md".  Each
+ * data item in the psMetadata collection represents a single table field.  The
+ * name of the field is given by the name of the psMetadataItem and the data
+ * type is give by the psMetadataItem.type and psMetadataItem.ptype entries.  A
+ * lookup table should be used to convert from PSLib types into MySQL
+ * compatible SQL data types.  For example, a PS_META_STR would map to an SQL99
+ * varchar.  If the value of type is PS_META_STR then the psMetadataItem.data
+ * element is set to a string with the length for the field written as a text
+ * string.  The value of the psMetadataItem.data element is unused for the
+ * PS_META_PRIMITIVE types.  Other psMetadata types beyond PS_META_STR and
+ * PS_META_PRIMITIVE are not allowed in a table definition.  
+ *
+ * Database indexes can be specified setting the "comment" field to "Primary
+ * Key" or "Key".  Comments are otherwise ignored.
+ *
+ * @return true on success
+ */
+bool psDBCreateTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psMetadata *md  ///< Column names, types, and indexes
+);
+
+/** Deletes a database table
+ *
+ * @return true on success
+*/
+bool psDBDropTable(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Selects a column from a table
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.
+ *
+ * @return A psArray of strings or NULL on failure
+ */
+psArray *psDBSelectColumn(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const char *col,                    ///< Column name
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Selects a column from a table and casts it to a given type
+ *
+ * This function generates and executes the SQL needed to select an entire
+ * column from a table or up to "limit" rows from it.  If "limit" is 0, the
+ * entire range is returned.  The data in the column is cast to to "pType".
+ *
+ * @return A psVector or NULL on failure
+ */
+psVector *psDBSelectColumnNum(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const char *col,                    ///< Column name
+    psElemType pType,                   ///< Resulting psVector type
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Selects a set of rows from a table
+ *
+ * This function returns rows from the specified table which match the
+ * restrictions given by "where".  The restrictions are specified as field /
+ * value pairs.  The psMetadata collection "where" must consist of valid
+ * database fields.  The selected rows are returned as a psArray of psMetadata
+ * values, one per row.
+ *
+ * Currently, the "where" specification only supports the PS_META_STR type.
+ * The string value can be a SQL match pattern, e.g. "%foo%", or an empty
+ * string, e.g. "", to match NULL field values.
+ *
+ * @return A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBSelectRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    psMetadata *where,                  ///< Row match criteria
+    const psU64 limit                   ///< Maximum number of elements to return
+);
+
+/** Insert a single row into a table
+ *
+ * This function inserts the data from "row" into "tableName".
+ *
+ * The "row" specification uses the psMetadataItem name as the column name.
+ * The field values may be specified in any order.  psMetadata types beyond
+ * PS_META_STR and PS_META_PRIMITIVE are not supported.  If fields are
+ * specified in "row" that do not exist in "tableName", the insert will fail.
+ *
+ * @return true on success
+ */
+bool psDBInsertOneRow(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psMetadata *row  ///< Row description
+);
+
+/** Insert a set of rows into a table
+ *
+ * This function inserts the data from "rowSet" into "tableName".
+ *
+ * "rowSet" is a psArray of psMetadata containing row specifications identical to
+ * those used in psDBInsertOneRow().
+ *
+ * @return true on success
+ */
+bool psDBInsertRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psArray *rowSet  ///< Set of rows to insert
+);
+
+/** Retrieves all rows from a table
+ *
+ * This function fetches all rows as an psArray of psMetadata.  The rows are in
+ * the same psMetadata format as used in psDBInsertOneRow() & psDBInsertRows().
+ *
+ * @return A psArray of psMetadata or NULL on failure
+ */
+psArray *psDBDumpRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Retrieves all columns from a table
+ *
+ * This function fetches all columns, as either a psVector or a psArray
+ * depending on whether or not the column is numeric, and return them in a
+ * psMetadata structure where psMetadataItem.name contains the column's name.
+ *
+ * @return A psMetadata containing either a psArrays or psVector per column
+ */
+psMetadata *psDBDumpCols(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName               ///< Table name
+);
+
+/** Updates the field values, as specified, in a table
+ *
+ * This function updates the fields contained in "values" in the row(s) that
+ * have a field with the value indicated by "where".  Where "where" is in the
+ * same format as used in psDBSelectRows().
+ *
+ * The "values" specification uses the same format as the row specification
+ * used in psDBInsertOneRow(), etc.
+ *
+ * @return The number of rows modified or a negative value on error
+ */
+psS64 psDBUpdateRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,              ///< Table name
+    const psMetadata *where,  ///< Row match criteria
+    const psMetadata *values  ///< new field values
+);
+
+/** Deletes rows, as specified, in a table
+ *
+ * Delete the rows that are matched by "where" using the same semantics for
+ * "where" as in psDBUpdateRow().
+ *
+ * If "where" is NULL, all rows in the table will be removed and regardless of
+ * the number of rows that were dropped, only 1 will be returned on success.
+ *
+ * @return The number of rows removed or a negative value on error
+ */
+psS64 psDBDeleteRows(
+    psDB *dbh,                          ///< Database handle
+    const char *tableName,             ///< Table name
+    const psMetadata *where  ///< Row match criteria
+);
+
+/// @}
+
+#endif // BUILD_PSDB
+
+#endif // PS_DB_H
Index: /tags/pap_tags/pap_branch_050518/psLib/src/fft/psImageFFT.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/fft/psImageFFT.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/fft/psImageFFT.c	(revision 22271)
@@ -0,0 +1,455 @@
+/** @file  psImageFFT.c
+ *
+ *  @brief Contains FFT transform related functions for psImage.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-15 00:12:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <unistd.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+
+#include "psImageFFT.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psImageExtraction.h"
+#include "psImageIO.h"
+
+#include "psImageErrors.h"
+
+#define PS_FFTW_PLAN_RIGOR FFTW_ESTIMATE
+
+static psBool p_fftwWisdomImported = false;
+
+psImage* psImageFFT(psImage* out, const psImage* in, psFFTFlags direction)
+{
+    psU32 numCols;
+    psU32 numRows;
+    psElemType type;
+    fftwf_plan plan;
+
+    /* got good image data? */
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    if ( ((direction & PS_FFT_FORWARD) != 0) ) {
+        if ((direction & PS_FFT_REVERSE) != 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageFFT_FORWARD_REVERSE);
+            psFree(out);
+            return NULL;
+        }
+        if ((direction & PS_FFT_REAL_RESULT) != 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageFFT_REAL_FORWARD_NOTSUPPORTED);
+            psFree(out);
+            return NULL;
+        }
+    } else if ((direction & PS_FFT_REVERSE) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageFFT_NO_DIRECTION_OPTION);
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+
+    /* make sure the system-level wisdom information is imported. */
+    if (!p_fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        p_fftwWisdomImported = true;
+    }
+
+    numRows = in->numRows;
+    numCols = in->numCols;
+
+    // n.b. FFTW can perform a in-place transform at the same rate or faster than out-of-place.
+    psS32 sign = ((direction & PS_FFT_FORWARD) != 0) ? FFTW_FORWARD : FFTW_BACKWARD;
+
+    fftwf_complex* outBuffer = fftwf_malloc(numRows*numCols*sizeof(fftwf_complex));
+    p_psImageCopyToRawBuffer(outBuffer, in, PS_TYPE_C32);
+
+    plan = fftwf_plan_dft_2d(numRows, numCols,
+                             outBuffer,
+                             outBuffer,
+                             sign,
+                             PS_FFTW_PLAN_RIGOR);
+
+    /* check if a plan exists now -- if not, it is a real problem at this point */
+    if (plan == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_FFTW_PLAN_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    /* finally, call FFTW with the plan made above */
+    fftwf_execute(plan);
+
+    fftwf_destroy_plan(plan);
+
+    if (direction & PS_FFT_REAL_RESULT) {
+        // n.b., we do this instead of using fftwf_plan_dft_c2r because that
+        // plan requires a half-image, which would require a image reordering
+        // that is not as simple as performing a normal complex transform and
+        // then taking the real part of the result.  If performance here
+        // becomes an issue, the use of fftwf_plan_dft_r2c should be considered
+        // as well as fftwf_plan_dft_c2r.
+        out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32);
+        int index = 0;
+        for (int row=0; row < numRows; row++) {
+            psF32* outRow = out->data.F32[row];
+            for (int col=0; col < numCols; col++) {
+                outRow[col] = crealf(outBuffer[index++]); // take just the real part
+            }
+        }
+    } else {
+        out = psImageRecycle(out,numCols,numRows,PS_TYPE_C32);
+        int index = 0;
+        for (int row=0; row < numRows; row++) {
+            psC32* outRow = out->data.C32[row];
+            for (int col=0; col < numCols; col++) {
+                outRow[col] = outBuffer[index++]; // take just the real part
+            }
+        }
+        //        memcpy(out->rawDataBuffer, outBuffer, numRows*numCols*sizeof(fftwf_complex));
+    }
+
+    fftwf_free(outBuffer);
+
+    return out;
+
+}
+
+psImage* psImageReal(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    /* if not a complex number, this is logically just a copy then */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        return psImageCopy(out, in, type);
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outRow;
+        psC32* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = crealf(inRow[col]);
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outRow;
+        psC64* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = creal(inRow[col]);
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                typeStr);
+
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psImage* psImageImaginary(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    /* if not a complex image type, this is logically just zeroed image of same size */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        out = psImageRecycle(out, numCols, numRows, type);
+        memset(out->data.V[0], 0, PSELEMTYPE_SIZEOF(type) * numCols * numRows);
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outRow;
+        psC32* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = cimagf(inRow[col]);
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outRow;
+        psC64* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = cimag(inRow[col]);
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psImage* psImageComplex(psImage* out, const psImage* real, const psImage* imag)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (real == NULL || imag == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = real->type.type;
+    numCols = real->numCols;
+    numRows = real->numRows;
+
+    if (imag->type.type != type) {
+        char* typeStrReal;
+        char* typeStrImag;
+        PS_TYPE_NAME(typeStrReal,type);
+        PS_TYPE_NAME(typeStrImag,imag->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_REAL_IMAG_TYPE_MISMATCH,
+                typeStrReal,typeStrImag);
+        psFree(out);
+        return NULL;
+    }
+
+    if (imag->numCols != numCols || imag->numRows != numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageFFT_REAL_IMAG_SIZE_MISMATCH,
+                numCols, numRows, imag->numCols, imag->numRows);
+        psFree(out);
+        return NULL;
+    }
+
+    if (type == PS_TYPE_F32) {
+        psC32* outRow;
+        psF32* realRow;
+        psF32* imagRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C32);
+
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C32[row];
+            realRow = real->data.F32[row];
+            imagRow = imag->data.F32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = realRow[col] + I * imagRow[col];
+            }
+        }
+    } else if (type == PS_TYPE_F64) {
+        psC64* outRow;
+        psF64* realRow;
+        psF64* imagRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C64[row];
+            realRow = real->data.F64[row];
+            imagRow = imag->data.F64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = realRow[col] + I * imagRow[col];
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_NONREAL_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+
+    return out;
+}
+
+psImage* psImageConjugate(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    /* if not a complex image, this is logically just a image copy */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        return psImageCopy(out, in, type);
+    }
+
+    if (type == PS_TYPE_C32) {
+        psC32* outRow;
+        psC32* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = crealf(inRow[col]) - I * cimagf(inRow[col]);
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psC64* outRow;
+        psC64* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = creal(inRow[col]) - I * cimag(inRow[col]);
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psImage* psImagePowerSpectrum(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    if (type == PS_TYPE_C32) {
+        psF32* outRow;
+        psC32* inRow;
+        psF32 real;
+        psF32 imag;
+        psF32 numElementsSquared = numCols * numCols * numRows * numRows;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                real = crealf(inRow[col]);
+                imag = cimagf(inRow[col]);
+                outRow[col] = (real * real + imag * imag) / numElementsSquared;
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outRow;
+        psC64* inRow;
+        psF64 real;
+        psF64 imag;
+        psF64 numElementsSquared = numCols * numCols * numRows * numRows;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                real = creal(inRow[col]);
+                imag = cimag(inRow[col]);
+                outRow[col] = (real * real + imag * imag) / numElementsSquared;
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/fft/psImageFFT.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/fft/psImageFFT.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/fft/psImageFFT.h	(revision 22271)
@@ -0,0 +1,87 @@
+/** @file  psImageFFT.h
+ *
+ *  @brief Contains FFT transform related functions for psImage
+ *
+ *  @ingroup Transform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_FFT_H
+#define PS_IMAGE_FFT_H
+
+#include "psImage.h"
+#include "psVectorFFT.h"               // for psFFTFlags
+
+/// @addtogroup Transform
+/// @{
+
+/** Forward and reverse FFT calculations.
+ *
+ *  This takes as input the image of interest (in) and the direction 
+ *  (direction), which is specified by an enumerated type psFftDirection.
+ *  The input image may be of type psF32 or psC32, the result is always 
+ *  psC32. If the input vector is psF32, the direction must be forward. 
+ *  
+ *  @return psImage* the FFT transformation result
+ */
+psImage* psImageFFT(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in,                 ///< the psImage to apply transform to
+    psFFTFlags direction               ///< the direction of the transform
+);
+
+/** extract the real portion of a complex image
+ * 
+ *  @return psImage*   real portion of the input image.
+ */
+psImage* psImageReal(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                  ///< the psImage to extract real portion from
+);
+
+/** extract the imaginary portion of a complex image
+ * 
+ *  @return psImage*   imaginary portion of the input image.
+ */
+psImage* psImageImaginary(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                  ///< the psImage to extract imaginary portion from
+);
+
+/** creates a complex image from separate real and imaginary plane images
+ * 
+ *  @return psImage*   resulting complex image
+ */
+psImage* psImageComplex(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* real,               ///< the real plane image
+    const psImage* imag                ///< the imaginary plane image
+);
+
+/** computes the complex conjugate of an image
+ * 
+ *  @return psImage*   the complex conjugate of the 'in' image
+ */
+psImage* psImageConjugate(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                  ///< the psImage to compute conjugate of
+);
+
+/** computes the power spectrum of an image
+ * 
+ *  @return psImage*   the power spectrum of the 'in' image
+ */
+psImage* psImagePowerSpectrum(
+    psImage* out,                       ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                   ///< the psImage to power spectrum of
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/fft/psVectorFFT.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/fft/psVectorFFT.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/fft/psVectorFFT.c	(revision 22271)
@@ -0,0 +1,418 @@
+/** @file  psVectorFFT.c
+ *
+ *  @brief Contains FFT transform related functions for psVector
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.31 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+
+#include "psVectorFFT.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psImageExtraction.h"
+
+#include "psDataManipErrors.h"
+
+#define P_FFTW_PLAN_RIGOR FFTW_ESTIMATE
+
+static psBool p_fftwWisdomImported = false;
+
+psVector* psVectorFFT(psVector* out, const psVector* in, psFFTFlags direction)
+{
+    psU32 numElements;
+    psElemType type;
+    fftwf_plan plan;
+
+    /* got good image data? */
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+
+    /* make sure the system-level wisdom information is imported. */
+    if (!p_fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        p_fftwWisdomImported = true;
+    }
+
+    numElements = in->n;
+
+    out = psVectorCopy(out, in, PS_TYPE_C32);
+    out->n = numElements;
+
+    if ((direction & PS_FFT_FORWARD) != 0) {
+        plan = fftwf_plan_dft_1d(numElements,
+                                 (fftwf_complex *) out->data.C32,
+                                 (fftwf_complex *) out->data.C32, FFTW_FORWARD, P_FFTW_PLAN_RIGOR);
+    } else if ((direction & PS_FFT_REVERSE) != 0) {
+        plan = fftwf_plan_dft_1d(numElements,
+                                 (fftwf_complex *) out->data.C32,
+                                 (fftwf_complex *) out->data.C32, FFTW_BACKWARD, P_FFTW_PLAN_RIGOR);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psVectorFFT_DIRECTION_NOTSET);
+        psFree(out);
+        return NULL;
+    }
+
+    /* check if a plan exists now */
+    if (plan == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_FFTW_PLAN_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    /* finally, call FFTW with the plan made above */
+    fftwf_execute(plan);
+
+    fftwf_destroy_plan(plan);
+
+    if ((direction & PS_FFT_REAL_RESULT) != 0) {
+        for (psS32 i = 0; i < numElements; i++) {
+            out->data.F32[i] = out->data.C32[i];
+        }
+        out->type.type = PS_TYPE_F32;
+        out->data.U8 = psRealloc(out->data.U8,PSELEMTYPE_SIZEOF(PS_TYPE_F32)*out->nalloc);
+    }
+
+    return out;
+}
+
+psVector* psVectorReal(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numElements = in->n;
+
+    /* if not a complex number, this is logically just a copy */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        // Warn user, as this is probably not expected
+        psLogMsg(__func__, PS_LOG_WARN, "Real portion of a non-Complex type called called for. "
+                 "Just a vector copy was performed.");
+        out = psVectorRecycle(out, numElements, type);
+        out->n = numElements;
+        memcpy(out->data.U8, in->data.U8, numElements * PSELEMTYPE_SIZEOF(type));
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outVec;
+        psC32* inVec = in->data.C32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F32);
+        out->n = numElements;
+        outVec = out->data.F32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = crealf(inVec[i]);
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outVec;
+        psC64* inVec = in->data.C64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F64);
+        out->n = numElements;
+        outVec = out->data.F64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = creal(inVec[i]);
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_TYPE_UNSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorImaginary(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numElements = in->n;
+
+    /* if not a complex number, this is logically just zeroed image of same size */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        // Warn user, as this is probably not expected
+        psLogMsg(__func__, PS_LOG_WARN, "Imaginary portion of a non-Complex type called for. "
+                 "A zeroed vector was returned.");
+        out = psVectorRecycle(out, numElements, type);
+        out->n = numElements;
+        memset(out->data.U8, 0, PSELEMTYPE_SIZEOF(type) * numElements);
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outVec;
+        psC32* inVec = in->data.C32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F32);
+        out->n = numElements;
+        outVec = out->data.F32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = cimagf(inVec[i]);
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outVec;
+        psC64* inVec = in->data.C64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_F64);
+        out->n = numElements;
+        outVec = out->data.F64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = cimag(inVec[i]);
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_TYPE_UNSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorComplex(psVector* out, const psVector* real, const psVector* imag)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (real == NULL || imag == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = real->type.type;
+    if (real->n < imag->n) {
+        numElements = real->n;
+    } else {
+        numElements = imag->n;
+    }
+
+    if (imag->type.type != type) {
+        char* typeStrReal;
+        char* typeStrImag;
+        PS_TYPE_NAME(typeStrReal,type);
+        PS_TYPE_NAME(typeStrImag,imag->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_REAL_IMAG_TYPE_MISMATCH,
+                typeStrReal,typeStrImag);
+        psFree(out);
+        return NULL;
+    }
+
+    if (type == PS_TYPE_F32) {
+        psC32* outVec;
+        psF32* realVec = real->data.F32;
+        psF32* imagVec = imag->data.F32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C32);
+        out->n = numElements;
+        outVec = out->data.C32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = realVec[i] + I * imagVec[i];
+        }
+    } else if (type == PS_TYPE_F64) {
+        psC64* outVec;
+        psF64* realVec = real->data.F64;
+        psF64* imagVec = imag->data.F64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C64);
+        out->n = numElements;
+        outVec = out->data.C64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = realVec[i] + I * imagVec[i];
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_NONREAL_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorConjugate(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 numElements;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numElements = in->n;
+
+    /* if not a complex number, this is logically just a image copy */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        // Warn user, as this is probably not expected
+        psLogMsg(__func__, PS_LOG_WARN, "Complex Conjugate of a non-Complex type called for. "
+                 "Vector copy was performed instead.");
+
+        out = psVectorRecycle(out, numElements, type);
+        out->n = numElements;
+        memcpy(out->data.U8, in->data.U8, PSELEMTYPE_SIZEOF(type) * numElements);
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psC32* outVec;
+        psC32* inVec = in->data.C32;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C32);
+        out->n = numElements;
+        outVec = out->data.C32;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = crealf(inVec[i]) - I * cimagf(inVec[i]);
+        }
+    } else if (type == PS_TYPE_C64) {
+        psC64* outVec;
+        psC64* inVec = in->data.C64;
+
+        out = psVectorRecycle(out, numElements, PS_TYPE_C64);
+        out->n = numElements;
+        outVec = out->data.C64;
+
+        for (psU32 i = 0; i < numElements; i++) {
+            outVec[i] = creal(inVec[i]) - I * cimag(inVec[i]);
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psVector* psVectorPowerSpectrum(psVector* out, const psVector* in)
+{
+    psElemType type;
+    psU32 outNumElements;
+    psU32 inNumElements;
+    psU32 inHalfNumElements;
+    psU32 inNumElementsSquared;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    inNumElements = in->n;
+    inNumElementsSquared = inNumElements * inNumElements;
+    inHalfNumElements = inNumElements / 2;
+    outNumElements = inHalfNumElements + 1;
+
+    if (type == PS_TYPE_C32) {
+        psF32* outVec;
+        psC32* inVec = in->data.C32;
+        psF32 inAbs1;
+        psF32 inAbs2;
+
+        out = psVectorRecycle(out, outNumElements, PS_TYPE_F32);
+        out->n = outNumElements;
+        outVec = out->data.F32;
+
+        // from ADD: P_0 = |C_0|^2/N^2
+        inAbs1 = cabsf(inVec[0]);
+        outVec[0] = inAbs1 * inAbs1 / inNumElementsSquared;
+
+        // from ADD: P_j = (|C_j|^2+|C_N-j|^2)/N^2, where j = 1,2,...,(N/2-1)
+        for (psU32 i = 1; i < inHalfNumElements; i++) {
+            inAbs1 = cabsf(inVec[i]);
+            inAbs2 = cabsf(inVec[inNumElements - i]);
+            outVec[i] = (inAbs1 * inAbs1 + inAbs2 * inAbs2) / inNumElementsSquared;
+        }
+
+        // from ADD: P_N/2 = |C_N/2|^2/N^2
+        inAbs1 = cabsf(inVec[inHalfNumElements]);
+        outVec[inHalfNumElements] = inAbs1 * inAbs1 / inNumElementsSquared;
+    } else if (type == PS_TYPE_C64) {
+        psF64* outVec;
+        psC64* inVec = in->data.C64;
+        psF64 inAbs1;
+        psF64 inAbs2;
+
+        out = psVectorRecycle(out, outNumElements, PS_TYPE_F64);
+        out->n = outNumElements;
+        outVec = out->data.F64;
+
+        // from ADD: P_0 = |C_0|^2/N^2
+        inAbs1 = cabs(inVec[0]);
+        outVec[0] = inAbs1 * inAbs1 / inNumElementsSquared;
+
+        // from ADD: P_j = (|C_j|^2+|C_N-j|^2)/N^2, where j = 1,2,...,(N/2-1)
+        for (psU32 i = 1; i < inHalfNumElements; i++) {
+            inAbs1 = cabs(inVec[i]);
+            inAbs2 = cabs(inVec[inNumElements - i]);
+            outVec[i] = (inAbs1 * inAbs1 + inAbs2 * inAbs2) / inNumElementsSquared;
+        }
+
+        // from ADD: P_N/2 = |C_N/2|^2/N^2
+        inAbs1 = cabs(inVec[inHalfNumElements]);
+        outVec[inHalfNumElements] = inAbs1 * inAbs1 / inNumElementsSquared;
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVectorFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/fft/psVectorFFT.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/fft/psVectorFFT.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/fft/psVectorFFT.h	(revision 22271)
@@ -0,0 +1,99 @@
+/** @file  psVectorFFT.h
+ *
+ *  @brief Contains FFT transform related functions for psVector
+ *
+ *  @ingroup Transform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_VECTOR_FFT_H
+#define PS_VECTOR_FFT_H
+
+#include "psVector.h"
+
+/// @addtogroup Transform
+/// @{
+
+/** Specify direction of FFT */
+typedef enum {
+    /// psImageFFT/psVectorFFT should perform a forward FFT.
+    PS_FFT_FORWARD = 1,
+
+    /// psImageFFT/psVectorFFT should perform a reverse FFT.
+    PS_FFT_REVERSE = 2,
+
+    /// psImageFFT/psVectorFFT should perform a reverse FFT with a real result.
+    PS_FFT_REAL_RESULT = 4
+} psFFTFlags;
+
+
+/** Forward and reverse FFT calculations.
+ *
+ *  This takes as input the vector of interest (in) and the direction 
+ *  (direction), which is specified by an enumerated type psFftDirection.
+ *  The input vector may be of type psF32 or psC32, the result is always 
+ *  psC32. If the input vector is psF32, the direction must be forward. 
+ *  
+ *  @return psVector* the FFT transformation result
+ */
+psVector* psVectorFFT(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in,                ///< the vector to apply transform to
+    psFFTFlags direction               ///< the direction of the transform
+);
+
+/** extract the real portion of a complex vector
+ * 
+ *  @return psVector*   real portion of the input vector.
+ */
+psVector* psVectorReal(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                ///< the psVector to extract real portion from
+);
+
+/** extract the imaginary portion of a complex vector
+ * 
+ *  @return psVector*   imaginary portion of the input vector.
+ */
+psVector* psVectorImaginary(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                 ///< the psVector to extract imaginary portion from
+);
+
+/** creates a complex vector from separate real and imaginary vectors
+ * 
+ *  @return psVector*   resulting complex vector
+ */
+psVector* psVectorComplex(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* real,              ///< the real vector
+    const psVector* imag               ///< the imaginary vector
+);
+
+/** computes the complex conjugate of a vector
+ * 
+ *  @return psVector*   the complex conjugate of the 'in' vector
+ */
+psVector* psVectorConjugate(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                 ///< the psVector to compute conjugate of
+);
+
+/** computes the power spectrum of a vector
+ * 
+ *  @return psVector*   the power spectrum of the 'in' vector
+ */
+psVector* psVectorPowerSpectrum(
+    psVector* out,                     ///< a psVector to recycle.  If NULL, a new psVector is made.
+    const psVector* in                 ///< the psVector to power spectrum of
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/fits/psFits.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/fits/psFits.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/fits/psFits.c	(revision 22271)
@@ -0,0 +1,1702 @@
+/** @file  psFits.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.29.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+#include "psFileUtilsErrors.h"
+#include "psImageExtraction.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+// list of FITS header keys to ignore.
+static char* standardFitsKeys[] = {
+                                      NULL
+                                  };
+
+static psElemType convertFitsToPsType(int datatype)
+{
+    switch (datatype) {
+    case TBYTE:
+        return PS_TYPE_U8;
+    case TSBYTE:
+        return PS_TYPE_S8;
+    case TSHORT:
+        return PS_TYPE_S16;
+    case TUSHORT:
+        return PS_TYPE_U16;
+    case TLONG:
+        if (sizeof(long) == 8) {
+            return PS_TYPE_S64;
+        }
+        // no break
+    case TINT:
+        return PS_TYPE_S32;
+    case TULONG:
+        if (sizeof(unsigned long) == 8) {
+            return PS_TYPE_U64;
+        }
+        // no break
+    case TUINT:
+        return PS_TYPE_U32;
+    case TLONGLONG:
+        return PS_TYPE_S64;
+    case TFLOAT:
+        return PS_TYPE_F32;
+    case TDOUBLE:
+        return PS_TYPE_F64;
+    case TCOMPLEX:
+        return PS_TYPE_C32;
+    case TDBLCOMPLEX:
+        return PS_TYPE_C64;
+    case TLOGICAL:
+        return PS_TYPE_BOOL;
+    default:
+        psError(PS_ERR_IO, true,
+                "Unknown FITS datatype, %d.",
+                datatype);
+        return 0;
+    }
+}
+
+static bool convertPsTypeToFits(psElemType type, int* bitPix, double* bZero, int* dataType)
+{
+
+    int bitpix;
+    int datatype;
+    double bzero = 0.0;
+
+    switch (type) {
+
+    case PS_TYPE_U8:
+        bitpix = BYTE_IMG;
+        datatype = TBYTE;
+        break;
+
+    case PS_TYPE_BOOL:
+    case PS_TYPE_S8:
+        bitpix = BYTE_IMG;
+        bzero = INT8_MIN;
+        datatype = TSBYTE;
+        break;
+
+    case PS_TYPE_U16:
+        bitpix = SHORT_IMG;
+        bzero = -1.0 * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+
+    case PS_TYPE_S16:
+        bitpix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+
+    case PS_TYPE_U32:
+        bitpix = LONG_IMG;
+        bzero = -1.0 * INT32_MIN;
+        datatype = TUINT;
+        break;
+
+    case PS_TYPE_S32:
+        bitpix = LONG_IMG;
+        datatype = TINT;
+        break;
+
+    case PS_TYPE_F32:
+        bitpix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+
+    case PS_TYPE_F64:
+        bitpix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED,
+                    typeStr);
+            return false;
+        }
+    }
+
+    // pass the requested parameters  (NULL parameters are not set, of course).
+    if (bitPix != NULL) {
+        *bitPix = bitpix;
+    }
+
+    if (dataType != NULL) {
+        *dataType = datatype;
+    }
+
+    if (bZero != NULL) {
+        *bZero = bzero;
+    }
+
+    return true;
+}
+
+static bool convertMetadataTypeToBinaryTForm(psMetadataType type, char** fitsType)
+{
+    switch (type) {
+    case PS_META_BOOL:
+        *fitsType = psStringCopy("1L");
+        break;
+    case PS_META_S32:
+        *fitsType = psStringCopy("1J");
+        break;
+    case PS_META_F32:
+        *fitsType = psStringCopy("1E");
+        break;
+    case PS_META_F64:
+        *fitsType = psStringCopy("1D");
+        break;
+        // XXX: Handle other types, e.g., Vectors, etc.
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool isHDUEmpty(const psFits* fits)
+{
+    /* check for keys - no keys means this is really an empty HDU */
+    int keysexist = -1;
+    int morekeys;
+    int status = 0;
+
+    fits_get_hdrspace(fits->p_fd, &keysexist, &morekeys, &status);
+
+    // if no keys exist and not primary HDU, this really is an empty HDU
+    if (keysexist == 0) {
+        return true;
+    }
+
+    return false;
+
+}
+
+static void fitsFree(psFits* fits)
+{
+    int status = 0;
+
+    if (fits != NULL) {
+        (void)fits_close_file(fits->p_fd, &status);
+        psFree((void*)fits->filename);
+    }
+}
+
+psFits* psFitsAlloc(const char* name)
+{
+    int status = 0;
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_FILENAME_NULL);
+        return NULL;
+    }
+
+    /* Open/Create the FITS file */
+    if (access(name, F_OK) == 0) {     // file exists
+        (void)fits_open_file(&fptr, name, READWRITE, &status);
+        if (fptr == NULL) { // if failed, try openning as just read-only
+            status = 0;
+            (void)fits_open_file(&fptr, name, READONLY, &status);
+        }
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psFits_FILENAME_INVALID,
+                    name, fitsErr);
+            return NULL;
+        }
+    } else {  // file does not exist, so create.
+        (void)fits_create_file(&fptr, name, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED,
+                    name, fitsErr);
+            return NULL;
+        }
+    }
+
+    psFits* fits = psAlloc(sizeof(psFits));
+    fits->filename = psAlloc(strlen(name)+1);
+    fits->p_fd = fptr;
+    strcpy((char*)fits->filename,name);
+    psMemSetDeallocator(fits,(psFreeFcn)fitsFree);
+
+    return fits;
+}
+
+bool psFitsMoveExtName(const psFits* fits,
+                       const char* extname)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (extname == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_EXTNAME_NULL);
+        return false;
+    }
+
+
+    if (fits_movnam_hdu(fits->p_fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNAME_INVALID,
+                extname, fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsMoveExtNum(const psFits* fits,
+                      int extnum,
+                      bool relative)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    int status = 0;
+    int hdutype = 0;
+
+    if (relative) {
+        fits_movrel_hdu(fits->p_fd, extnum, &hdutype, &status);
+        if (status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_LOCATION_INVALID, true,
+                    PS_ERRORTEXT_psFits_EXTNUM_REL_MOVE_FAILED,
+                    extnum, fits->filename, fitsErr);
+            return false;
+        }
+    } else {
+        fits_movabs_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+        if (status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_LOCATION_INVALID, true,
+                    PS_ERRORTEXT_psFits_EXTNUM_ABS_MOVE_FAILED,
+                    extnum, fits->filename, fitsErr);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int psFitsGetExtNum(const psFits* fits)
+{
+    int hdunum;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+
+    return fits_get_hdu_num(fits->p_fd,&hdunum) - 1;
+}
+
+char* psFitsGetExtName(const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    int status = 0;
+    char name[MAX_STRING_LENGTH];
+
+    if (fits_read_key_str(fits->p_fd, "EXTNAME", name, NULL, &status) != 0) {
+        status = 0;
+        if (fits_read_key_str(fits->p_fd, "HDUNAME", name, NULL, &status) != 0) {
+            int num = psFitsGetExtNum(fits);
+            snprintf(name, MAX_STRING_LENGTH, "EXT-%3d",num);
+        }
+    }
+    return psStringCopy(name);
+}
+
+bool psFitsSetExtName(const psFits* fits, const char* name)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_EXTNAME_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    if (fits_update_key_str(fits->p_fd, "EXTNAME", (char*)name, NULL, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+int psFitsGetSize(const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return 0;
+    }
+
+    int num = 0;
+    int status = 0;
+
+    if (fits_get_num_hdus(fits->p_fd, &num, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED,
+                fits->filename, fitsErr);
+        return 0;
+    }
+
+    return num;
+}
+
+psFitsType psFitsGetExtType(const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = PS_FITS_TYPE_NONE;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED,
+                fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    if (hdutype == PS_FITS_TYPE_IMAGE &&
+            psFitsGetExtNum(fits) > 0 &&
+            isHDUEmpty(fits)) {
+        return PS_FITS_TYPE_ANY;
+    }
+
+    return hdutype;
+}
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psMetadataAlloc();
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psMetadata container.");
+            return NULL;
+        }
+    }
+
+    // Get number of key names
+    int numKeys = 0;
+    int keyNum = 0;
+    int status = 0;
+    fits_get_hdrpos(fits->p_fd, &numKeys, &keyNum, &status);
+
+    // Get each key name. Keywords start at one.
+    char keyType;
+    char keyName[MAX_STRING_LENGTH];
+    char keyValue[MAX_STRING_LENGTH];
+    char keyComment[MAX_STRING_LENGTH];
+    psBool tempBool;
+    psBool success;
+    psBool stdKey;
+    for (int i = 1; i <= numKeys; i++) {
+
+        fits_read_keyn(fits->p_fd, i, keyName, keyValue, keyComment, &status);
+
+        stdKey = false;
+
+        int stdKeyIdx = 0;
+        while (standardFitsKeys[stdKeyIdx] != NULL && ! stdKey) {
+            if (strcmp(keyName,standardFitsKeys[stdKeyIdx++]) == 0) {
+                stdKey = true;
+            }
+        }
+
+        if (keyValue[0] != 0) { // blank values are not handled by fits_get_keytype
+            fits_get_keytype(keyValue, &keyType, &status);
+        } else {
+            keyType = 'C';
+        }
+        if (status != 0) {
+            break;
+        }
+
+        if (! stdKey) {
+            switch (keyType) {
+            case 'X': // bit
+            case 'I': // short int.
+            case 'J': // int.
+            case 'B': // byte
+                success = psMetadataAdd(out,
+                                        PS_LIST_TAIL,
+                                        keyName,
+                                        PS_META_S32 | PS_META_DUPLICATE_OK,
+                                        keyComment,
+                                        atoi(keyValue));
+                break;
+            case 'U': // unsigned int. may not fit in a psS32
+            case 'K': // long int. can't all fit in a psS32
+            case 'F':
+                success = psMetadataAdd(out,
+                                        PS_LIST_TAIL,
+                                        keyName,
+                                        PS_META_F64 | PS_META_DUPLICATE_OK,
+                                        keyComment,
+                                        atof(keyValue));
+                break;
+            case 'C':
+                // remove the single-quotes at front/end
+                if (keyValue[0] == '\'' && keyValue[strlen(keyValue)-1] == '\'') {
+                    keyValue[strlen(keyValue)-1] = '\0';
+                    success = psMetadataAdd(out,
+                                            PS_LIST_TAIL,
+                                            keyName,
+                                            PS_META_STR | PS_META_DUPLICATE_OK,
+                                            keyComment,
+                                            keyValue+1);
+                } else {
+                    success = psMetadataAdd(out,
+                                            PS_LIST_TAIL,
+                                            keyName,
+                                            PS_META_STR | PS_META_DUPLICATE_OK,
+                                            keyComment,
+                                            keyValue);
+                }
+                break;
+            case 'L':
+                tempBool = (keyValue[0] == 'T') ? 1 : 0;
+                success = psMetadataAdd(out,
+                                        PS_LIST_TAIL,
+                                        keyName,
+                                        PS_META_BOOL | PS_META_DUPLICATE_OK,
+                                        keyComment,
+                                        tempBool);
+                break;
+            default:
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psFits_METATYPE_INVALID,
+                        keyType);
+                return out;
+            }
+
+            if (!success) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                        keyName);
+                return out;
+            }
+        }
+
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                fitsErr);
+        return false;
+    }
+
+    return out;
+}
+
+psHash* psFitsReadHeaderSet(psHash* out,
+                            const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psHashAlloc(10);
+        // XXX: what is the appropriate number of buckets? Got 10 from psMetadataAlloc.
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psHash container.");
+            return NULL;
+        }
+    }
+
+    int size = psFitsGetSize(fits);
+
+    int origPosition = psFitsGetExtNum(fits);
+
+    for (int lcv=0; lcv < size; lcv++) {
+        psFitsMoveExtNum(fits, lcv, false);
+
+        char* name = NULL;
+        if (lcv == 0) {
+            name = psStringCopy("PHU");
+        } else {
+            name = psFitsGetExtName(fits);
+        }
+
+        psMetadata* header = psFitsReadHeader(NULL, fits);
+        if (name != NULL && header != NULL) {
+            psHashAdd(out, name, header);
+        } else { // XXX: is this a warning or error?
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "Failed to read HDU#%d header data.",
+                     lcv);
+        }
+
+        psFree(name);
+        psFree(header);
+    }
+
+    // reposition to the original position
+    psFitsMoveExtNum(fits, origPosition, false);
+
+    return out;
+}
+
+psImage* psFitsReadImage(psImage* output, // a psImage to recycle.
+                         const psFits* fits,    // the psFits object
+                         psRegion region, // the region in the FITS image to read
+                         int z)           // the z-plane in the FITS image cube to read
+{
+    psS32 status = 0;           /* CFITSIO file vars */
+    psS32 nAxis = 0;
+    psS32 anynull = 0;
+    psS32 bitPix = 0;           /* Pixel type */
+    long nAxes[3];
+    long firstPixel[3];         /* lower-left corner of image subset */
+    long lastPixel[3];          /* upper-right corner of image subset */
+    long increment[3];          /* increment for image subset */
+    char fitsErr[80] = "";      /* CFITSIO error message string */
+    psS32 fitsDatatype = 0;
+    psS32 datatype = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        psFree(output);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on an image HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != IMAGE_HDU) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return NULL;
+    }
+
+    /* Get the data type 'bitPix' from the FITS image */
+    if (fits_get_img_equivtype(fits->p_fd, &bitPix, &status) != 0) {
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_DATATYPE_UNKNOWN,
+                fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Get the dimensions 'nAxis' from the FITS image */
+    if (fits_get_img_dim(fits->p_fd, &nAxis, &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_DIM_UNKNOWN,
+                fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Validate the number of axis */
+    if ((nAxis < 2) || (nAxis > 3)) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_DIMENSION_UNSUPPORTED,
+                nAxis);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Get the Image size from the FITS file */
+    if (fits_get_img_size(fits->p_fd, nAxis, nAxes, &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_SIZE_UNKNOWN,
+                fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    firstPixel[0] = region.x0 + 1;
+    firstPixel[1] = region.y0 + 1;
+    firstPixel[2] = z + 1;
+
+    if (region.x1 > 0) {
+        lastPixel[0] = region.x1;
+    } else {
+        lastPixel[0] = nAxes[0] + region.x1; // n.b., region.x1 < 0
+    }
+    if (region.y1 > 0) {
+        lastPixel[1] = region.y1;
+    } else {
+        lastPixel[1] = nAxes[1] + region.y1; // n.b., region.y1 < 0
+    }
+    lastPixel[2] = z + 1;
+
+    increment[0] = 1;
+    increment[1] = 1;
+    increment[2] = 1;
+
+    switch (bitPix) {
+    case BYTE_IMG:
+        datatype = PS_TYPE_U8;
+        fitsDatatype = TBYTE;
+        break;
+    case SBYTE_IMG:
+        datatype = PS_TYPE_S8;
+        fitsDatatype = TSBYTE;
+        break;
+    case USHORT_IMG:
+        datatype = PS_TYPE_U16;
+        fitsDatatype = TUSHORT;
+        break;
+    case SHORT_IMG:
+        datatype = PS_TYPE_S16;
+        fitsDatatype = TSHORT;
+        break;
+    case ULONG_IMG:
+        datatype = PS_TYPE_U32;
+        fitsDatatype = TUINT;
+        break;
+    case LONG_IMG:
+        datatype = PS_TYPE_S32;
+        fitsDatatype = TINT;
+        break;
+    case LONGLONG_IMG:
+        datatype = PS_TYPE_S64;
+        fitsDatatype = TLONGLONG;
+        break;
+    case FLOAT_IMG:
+        datatype = PS_TYPE_F32;
+        fitsDatatype = TFLOAT;
+        break;
+    case DOUBLE_IMG:
+        datatype = PS_TYPE_F64;
+        fitsDatatype = TDOUBLE;
+        break;
+    default:
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_FITS_TYPE_UNSUPPORTED,
+                bitPix);
+        psFree(output);
+        return NULL;
+    }
+
+    output = psImageRecycle(output,
+                            lastPixel[0]-firstPixel[0]+1,
+                            lastPixel[1]-firstPixel[1]+1,
+                            datatype);
+
+    if (output == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Failed to allocate a properly sized image.");
+        return false;
+    }
+
+    // n.b., this assumes contiguous image buffer
+    if (fits_read_subset(fits->p_fd, fitsDatatype, firstPixel, lastPixel, increment,
+                         NULL, output->data.V[0], &anynull, &status) != 0) {
+        psFree(output);
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_READ_FAILED,
+                fitsErr);
+        return NULL;
+    }
+
+    return output;
+
+}
+
+bool psFitsWriteImage(const psFits* fits,
+                      const psMetadata* header,
+                      const psImage* input,
+                      int numZPlanes,
+                      char* extname)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    int status = 0;
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    int naxis = 3;
+    long naxes[3];
+
+    naxes[0] = numCols;
+    naxes[1] = numRows;
+    naxes[2] = numZPlanes;
+
+    if (numZPlanes < 2) {
+        naxis = 2;
+    }
+
+    fits_create_img(fits->p_fd, bitPix, naxis, naxes, &status);
+
+    if (extname != NULL) {
+        fits_update_key_str(fits->p_fd, "EXTNAME", (char*)extname, NULL, &status);
+    }
+
+    if (bZero != 0) {        // set the bscale/bzero
+        fits_write_key_dbl(fits->p_fd, "BZERO", bZero, 12, "Pixel Value Offset", &status);
+        fits_write_key_dbl(fits->p_fd, "BSCALE", 1.0, 12, "Pixel Value Scale", &status);
+        fits_set_bscale(fits->p_fd, 1.0, bZero, &status);
+    }
+
+    // write the header, if any.
+    if (header != NULL) {
+        psFitsWriteHeader(header, fits);
+    }
+
+    if (input->parent == NULL) { // if no parent, assume that the image data is contiguous
+        fits_write_img(fits->p_fd,
+                       dataType,              // datatype
+                       1,                     // writing to the first z-plane
+                       numCols*numRows,       // number of elements to write, i.e., the whole image
+                       input->data.V[0],      // the data
+                       &status);
+    } else { // image data may not be contiguous; write one row at a time
+        int firstPixel = 1;
+        for (int row = 0; row < numRows; row++) {
+            fits_write_img(fits->p_fd,
+                           dataType,          // datatype
+                           firstPixel,
+                           numCols,           // number of elements to write, i.e., one row's worth
+                           input->data.V[row],// the raw row data
+                           &status);
+            firstPixel += numCols;  // move to next row
+        }
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+
+}
+
+bool psFitsUpdateImage(const psFits* fits,
+                       const psImage* input,
+                       psRegion region,
+                       int z)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+
+    // check to see if we are positioned on an image HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != IMAGE_HDU) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return NULL;
+    }
+
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    //check to see if the HDU has the same datatype
+    int fileBitpix;
+    int naxis;
+    long nAxes[3];
+    nAxes[2] = 1;
+    fits_get_img_param(fits->p_fd, 3, &fileBitpix, &naxis, nAxes, &status);
+
+    //check to see if the HDU has the same datatype
+    if (bitPix != fileBitpix) {
+        char* fitsTypeStr;
+        char* imageTypeStr;
+        PS_TYPE_NAME(fitsTypeStr,fileBitpix);
+        PS_TYPE_NAME(imageTypeStr,input->type.type);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_IMAGE_UPDATE_TYPE_MISMATCH,
+                fitsTypeStr, imageTypeStr);
+        return false;
+    }
+
+    //check if the HDU has the z-plane requested
+    if (z >= nAxes[2]) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFits_FITS_Z_SMALL,
+                nAxes[2],z);
+        return false;
+    }
+
+    // determine the region in the FITS file domain
+    long firstPixel[3];
+    long lastPixel[3];
+
+    firstPixel[0] = region.x0 + 1;
+    firstPixel[1] = region.y0 + 1;
+    firstPixel[2] = z + 1;
+
+    if (region.x1 > 0) {
+        lastPixel[0] = region.x1;
+    } else {
+        lastPixel[0] = nAxes[0] + region.x1; // n.b., region.x1 < 0
+    }
+    if (region.y1 > 0) {
+        lastPixel[1] = region.y1;
+    } else {
+        lastPixel[1] = nAxes[1] + region.y1; // n.b., region.y1 < 0
+    }
+    lastPixel[2] = z + 1;
+
+    if (firstPixel[0] < 1 || firstPixel[0] > nAxes[0] ||
+            firstPixel[1] < 1 || firstPixel[1] > nAxes[1] ||
+            lastPixel[0] < 1 || lastPixel[0] > nAxes[0] ||
+            lastPixel[1] < 1 || lastPixel[1] > nAxes[1]) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Specified region [%d:%d,%d:%d], is not valid given the %dx%d FITS image.",
+                region.y0,region.y1-1,region.x0,region.x1-1);
+        return false;
+
+    }
+
+    int dx = lastPixel[0] - firstPixel[0];
+    int dy = lastPixel[1] - firstPixel[1];
+    if (dx > numCols ||
+            dy > numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "The region [%d:%d,%d:%d], is not valid given the input %dx%d image.",
+                firstPixel[1]-1,lastPixel[1]-1,
+                firstPixel[0]-1,lastPixel[0]-1,
+                numCols, numRows);
+        return false;
+    }
+
+    psImage* subset;
+    if (dx != numCols || dy != numRows) {
+        // the input image needs to be subsetted
+        subset = psImageSubset((psImage*)input,0,0,dx+1,dy+1);
+    } else {
+        subset = psMemIncrRefCounter((psImage*)input);
+    }
+
+    fits_write_subset(fits->p_fd, dataType, firstPixel, lastPixel, subset->data.V[0], &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsWriteHeader(const psMetadata* header,
+                       const psFits* fits)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (header == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_METADATA_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    //transverse the metadata list and add each key.
+
+    psListIterator* iter = psListIteratorAlloc(header->list,PS_LIST_HEAD,true);
+    psMetadataItem* item;
+    while ( (item=psListGetAndIncrement(iter)) != NULL ) {
+        switch (item->type) {
+        case PS_META_BOOL: {
+                int value = item->data.B;
+                fits_update_key(fits->p_fd,
+                                TLOGICAL,
+                                item->name,
+                                &value,
+                                item->comment,
+                                &status);
+                break;
+            }
+        case PS_META_S32:
+            fits_update_key(fits->p_fd,
+                            TINT,
+                            item->name,
+                            &item->data.S32,
+                            item->comment,
+                            &status);
+            break;
+        case PS_META_F32:
+            fits_update_key(fits->p_fd,
+                            TFLOAT,
+                            item->name,
+                            &item->data.F32,
+                            item->comment,
+                            &status);
+            break;
+        case PS_META_F64:
+            fits_update_key(fits->p_fd,
+                            TDOUBLE,
+                            item->name,
+                            &item->data.F64,
+                            item->comment,
+                            &status);
+            break;
+        case PS_META_STR:
+            fits_update_key(fits->p_fd,
+                            TSTRING,
+                            item->name,
+                            item->data.V,
+                            item->comment,
+                            &status);
+            break;
+        default:  // all other META types are ignored
+            break;
+        }
+
+        if ( status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            (void)fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_WRITE_FAILED,
+                    fits->filename, fitsErr);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+psMetadata* psFitsReadTableRow(const psFits* fits,
+                               int row)
+{
+    long numRows;
+    int numCols;
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    fits_get_hdu_type(fits->p_fd,&hdutype, &status);
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // get the size of the FITS table
+    fits_get_num_rows(fits->p_fd, &numRows, &status);
+    fits_get_num_cols(fits->p_fd, &numCols, &status);
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_TABLE_SIZE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+
+    psTrace(".psFits.psFitsReadTableRow",5,"Table size is %ix%i\n",numCols, numRows);
+    // the row parameter in the proper range?
+    if (row < 0 || row >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psFits_ROW_INVALID,
+                row,numRows);
+        return NULL;
+    }
+
+    psMetadata* data = psMetadataAlloc();
+
+    int typecode;
+    long repeat;
+    long width;
+    char name[60];
+    for (int col = 1; col <= numCols; col++) {
+        // get the column name
+        if (hdutype == BINARY_TBL) {
+            fits_get_bcolparms(fits->p_fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        } else {
+            fits_get_acolparms(fits->p_fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        }
+        // get the column type
+        fits_get_coltype(fits->p_fd, col, &typecode, &repeat, &width, &status);
+
+        if (status == 0) {
+
+            #define READ_TABLE_ROW_CASE(FITSTYPE, NATIVETYPE, TYPE) \
+        case FITSTYPE: { \
+                NATIVETYPE value = 0; \
+                int anynul = 0; \
+                fits_read_col(fits->p_fd, FITSTYPE, col,row+1, \
+                              1, 1, NULL, &value, &anynul, &status); \
+                psTrace(".psFits.psFitsReadTableRow",5,"Column #%i, '%s', is type %i, repeat %i, Value = %g\n", \
+                        col, name, typecode, repeat, (double)value); \
+                psMetadataAdd(data,PS_LIST_TAIL, name, \
+                              PS_META_##TYPE, \
+                              "", (ps##TYPE)value); \
+                break; \
+            }
+
+            switch (typecode) {
+            case TBYTE:
+            case TSHORT:
+            case TLONGLONG:
+                READ_TABLE_ROW_CASE(TLONG, long, S32)
+                READ_TABLE_ROW_CASE(TFLOAT, float, F32)
+                READ_TABLE_ROW_CASE(TDOUBLE, double, F64)
+                READ_TABLE_ROW_CASE(TLOGICAL, bool, BOOL);
+            case TSTRING: {
+                    char* value = psAlloc(repeat+1);
+                    int anynul = 0;
+                    fits_read_col(fits->p_fd, TSTRING, col,row+1,
+                                  1, 1, NULL, &value, &anynul, &status);
+                    psTrace(".psFits.psFitsReadTableRow",5,"Column #%i, '%s', is type %i, repeat %i, value = %s\n",
+                            col, name, typecode, repeat, value);
+                    if (anynul == 0) {
+                        psMetadataAdd(data,PS_LIST_TAIL, name,
+                                      PS_META_STR,
+                                      "", value);
+                    }
+                    psFree(value);
+                    break;
+                }
+            default:
+                psTrace("psFits.psFitsReadTableRow", 2,
+                        "Column %d or row %d was of a non primitive type, %d",
+                        col, row, typecode);
+            }
+        }
+
+        if ( status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            (void)fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_GET_TABLE_ELEMENT,
+                    col,row,fitsErr);
+            psFree(data);
+            return NULL;
+        }
+
+    }
+
+    return data;
+}
+
+psArray* psFitsReadTableColumn(const psFits* fits,
+                               const char* colname)
+{
+    int colnum = 0;
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // find the column by name
+    if ( fits_get_colnum(fits->p_fd, CASESEN, (char*)colname, &colnum, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_FIND_COLUMN,
+                colname, fitsErr);
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = 0;
+    fits_get_num_rows(fits->p_fd, &numRows, &status);
+
+    // get the column length.
+    int width;
+    if ( fits_get_col_display_width(fits->p_fd, colnum, &width, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_COLTYPE,
+                fitsErr);
+        return NULL;
+    }
+
+    // allocate the buffers
+    psArray* result = psArrayAlloc(numRows);
+    for (int row = 0; row < numRows; row++) {
+        result->data[row] = psAlloc((width+1)*sizeof(char));
+    }
+    result->n = numRows;
+
+    fits_read_col_str(fits->p_fd,
+                      colnum,
+                      1, // firstrow
+                      1, // firestelem
+                      numRows,
+                      "", // nulstr
+                      (char**)result->data,
+                      NULL,
+                      &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_TABLE_READ_COL,
+                fitsErr);
+        return NULL;
+    }
+
+    return result;
+}
+
+psVector* psFitsReadTableColumnNum(const psFits* fits,
+                                   const char* colname)
+{
+    int status = 0;
+    int colnum = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // find the column by name
+    if ( fits_get_colnum(fits->p_fd, CASESEN, (char*)colname, &colnum, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_FIND_COLUMN,
+                colname, fitsErr);
+        return NULL;
+    }
+
+    // get the number of rows
+    long numRows = 0;
+    fits_get_num_rows(fits->p_fd,
+                      &numRows,
+                      &status);
+
+    // get the column datatype.
+    int typecode;
+    long repeat;
+    long width;
+    if ( fits_get_eqcoltype(fits->p_fd, colnum, &typecode, &repeat, &width, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_COLTYPE,
+                fitsErr);
+        return NULL;
+    }
+
+    psVector* result = psVectorAlloc(numRows, convertFitsToPsType(typecode));
+
+    fits_read_col(fits->p_fd,
+                  typecode,
+                  colnum,
+                  1 /* firstrow */,
+                  1 /* firstelem */,
+                  numRows,
+                  NULL,
+                  (psPtr)(result->data.U8),
+                  NULL,
+                  &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_TABLE_READ_COL,
+                fitsErr);
+        return NULL;
+    }
+
+    return result;
+}
+
+
+psArray* psFitsReadTable(const psFits* fits)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return NULL;
+    }
+
+    // get the size of the FITS table
+    long numRows = 0;
+    fits_get_num_rows(fits->p_fd, &numRows, &status);
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_TABLE_SIZE_FAILED,
+                fitsErr);
+        return NULL;
+    }
+
+
+    psArray* table = psArrayAlloc(numRows);
+
+    for (int row = 0; row < numRows; row++) {
+        psTrace(".psFits.psFitsReadTable",5,"Reading row %i of %i\n",row, numRows);
+        table->data[row] = psFitsReadTableRow(fits,row);
+    }
+
+    return table;
+}
+
+bool psFitsWriteTable(const psFits* fits,
+                      const psMetadata* header,
+                      const psArray* table,
+                      char* extname)
+{
+    int status = 0;
+    psMetadataItem* item;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+
+    int rows = table->n;
+    if (rows < 1) {
+        // no table data, what can I do?
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFits_TABLE_EMPTY);
+        return false;
+    }
+
+    // find all the columns needed
+    psArray* columns = psArrayAlloc(((psMetadata*)table->data[0])->list->size);
+    columns->n=0;
+
+    // find the unique items in the array of metadata 'rows'
+    for (int row=0; row < rows; row++) {
+        psMetadata* rowMeta = table->data[row];
+        if (rowMeta != NULL) {
+            psListIterator* iter = psListIteratorAlloc(rowMeta->list,
+                                   PS_LIST_HEAD,true);
+            while ( (item=psListGetAndIncrement(iter)) != NULL) {
+                if (PS_META_IS_PRIMITIVE(item->type)) {
+                    bool found = false;
+                    for (int n=0; n < columns->n && ! found; n++) {
+                        if (strcmp(item->name,
+                                   ((psMetadataItem*)(columns->data[n]))->name) == 0) {
+                            found = true;
+                        }
+                    }
+                    if (! found) {
+                        psArrayAdd(columns, columns->nalloc, item);
+                    }
+                }
+            }
+            psFree(iter);
+        }
+    }
+
+    if (columns->n == 0) { // no table columns found
+        // XXX: Error?
+        return false;
+    }
+
+    //create list of column names and types.
+    psArray* columnNames = psArrayAlloc(columns->n);
+    psArray* columnTypes = psArrayAlloc(columns->n);
+    for (int n=0; n < columns->n; n++) {
+        char* fitsType;
+        columnNames->data[n] = psMemIncrRefCounter(((psMetadataItem*)columns->data[n])->name);
+        if ( ! convertMetadataTypeToBinaryTForm(((psMetadataItem*)columns->data[n])->type,
+                                                &fitsType)) {
+            // XXX: error message
+            return false;
+        }
+        columnTypes->data[n] = fitsType;
+    }
+
+    fits_create_tbl(fits->p_fd,
+                    BINARY_TBL,
+                    table->n, // number of rows in table
+                    columns->n, // number of columns in table
+                    (char**)columnNames->data, // names of the columns
+                    (char**)columnTypes->data, // format of the columns
+                    NULL, // physical unit of columns
+                    extname, // extension name
+                    &status);
+
+    psFree(columnNames);
+    psFree(columnTypes);
+
+    // fill in the table elements with data
+    for (int n = 0; n < columns->n; n++) {
+        int row;
+        item = columns->data[n];
+        if (PS_META_IS_PRIMITIVE(item->type)) {
+            psVector* col = NULL;
+            switch (item->type) {
+            case PS_META_S32:
+                col = psVectorAlloc(table->n, PS_TYPE_S32);
+                for (row = 0; row < table->n; row++) {
+                    col->data.S32[row] = psMetadataLookupS32(NULL,
+                                         table->data[row],
+                                         item->name);
+                }
+                fits_write_col_int(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   col->data.S32,
+                                   &status);
+                break;
+            case PS_META_F32:
+                col = psVectorAlloc(table->n, PS_TYPE_F32);
+                for (row = 0; row < table->n; row++) {
+                    col->data.F32[row] = psMetadataLookupF32(NULL,
+                                         table->data[row],
+                                         item->name);
+                }
+                fits_write_col_flt(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   col->data.F32,
+                                   &status);
+                break;
+            case PS_META_F64:
+                col = psVectorAlloc(table->n, PS_TYPE_F64);
+                for (row = 0; row < table->n; row++) {
+                    col->data.F64[row] = psMetadataLookupF64(NULL,
+                                         table->data[row],
+                                         item->name);
+                }
+                fits_write_col_dbl(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   col->data.F64,
+                                   &status);
+                break;
+            case PS_META_BOOL:
+                col = psVectorAlloc(table->n, PS_TYPE_BOOL);
+                for (row = 0; row < table->n; row++) {
+                    col->data.S8[row] = psMetadataLookupBool(NULL,
+                                        table->data[row],
+                                        item->name);
+                }
+                fits_write_col_log(fits->p_fd,
+                                   n+1, // column number
+                                   1, // firstrow
+                                   1, // firstelem
+                                   table->n, // nelements
+                                   (char*)col->data.S8,
+                                   &status);
+                break;
+            default:
+                // XXX: error message?
+                break;
+            }
+            psFree(col);
+        } else if (item->type == PS_META_STR) {
+            psArray* col = psArrayAlloc(table->n);
+            for (row = 0; row < table->n; row++) {
+                col->data[row] = item->data.V;
+            }
+            fits_write_col_str(fits->p_fd,
+                               n, // column number
+                               1, // firstrow
+                               1, // firstelem
+                               table->n, // nelements
+                               (char**)col->data,
+                               &status);
+            psFree(col);
+        }
+    }
+
+    psFree(columns);
+
+    return true;
+}
+
+bool psFitsUpdateTable(const psFits* fits,
+                       psMetadata* data,
+                       int row)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+
+    // check to see if we even are positioned on a table HDU
+    int hdutype;
+    if ( fits_get_hdu_type(fits->p_fd,&hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_GET_HDU_TYPE_FAILED,
+                fitsErr);
+        return false;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_TABLE_TYPE);
+        return false;
+    }
+
+    psMetadataIterator* iter = psMetadataIteratorAlloc(data,PS_LIST_HEAD,NULL);
+
+    psMetadataItem* item;
+
+    while ( (item=psMetadataGetAndIncrement(iter)) != NULL) {
+        if (PS_META_IS_PRIMITIVE(item->type)) {
+            // operating on primitive data type, i.e., not a complex object
+            int colnum = 0;
+
+            if ( fits_get_colnum(fits->p_fd, CASESEN, item->name, &colnum, &status) == 0) {
+                // cooresponding column found in table
+                int dataType = 0;
+                convertPsTypeToFits(item->type, NULL, NULL, &dataType);
+
+                if (fits_write_col(fits->p_fd, dataType, colnum, row+1, 1, 1, &item->data,&status) != 0) {
+                    char fitsErr[MAX_STRING_LENGTH];
+                    (void)fits_get_errstatus(status, fitsErr);
+                    psError(PS_ERR_IO, true,
+                            PS_ERRORTEXT_psFits_WRITE_FAILED,
+                            fits->filename, fitsErr);
+                    psFree(iter);
+                    return false;
+                }
+            } else {
+                // the column was not found.
+                psWarning("No column with the name '%s' exists in the table.",
+                          item->name);
+            }
+        }
+    }
+
+    psFree(iter);
+
+    return true;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/fits/psFits.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/fits/psFits.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/fits/psFits.h	(revision 22271)
@@ -0,0 +1,268 @@
+/** @file  psFits.h
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.10.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITS_H
+#define PS_FITS_H
+
+#include<fitsio.h>
+
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psHash.h"
+#include "psImage.h"
+
+/// @addtogroup FileIO
+/// @{
+
+typedef enum {
+    PS_FITS_TYPE_NONE = -1,
+    PS_FITS_TYPE_IMAGE = IMAGE_HDU,
+    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL,
+    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,
+    PS_FITS_TYPE_ANY = ANY_HDU
+} psFitsType;
+
+/** FITS file object.
+ *
+ *  This object should be considered opaque to the user; no item in this
+ *  struct should be accessed directly.
+ *
+ */
+typedef struct
+{
+    fitsfile* p_fd;                    ///< the CFITSIO fits files handle.
+    const char* filename;              ///< the filename of the fits file
+}
+psFits;
+
+/** Opens a FITS file and allocates the associated psFits object.
+ *
+ *  @return psFits*    new psFits object for the FITS files specified or
+ *                     NULL if the open of the FITS file failed
+ */
+psFits* psFitsAlloc(
+    const char* name                   ///< the FITS file name
+);
+
+/** Moves the FITS HDU to the specified extension name.
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+bool psFitsMoveExtName(
+    const psFits* fits,                ///< the psFits object to move
+    const char* extname                ///< the extension name
+);
+
+/** Moves the FITS HDU to the specified extension number
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+bool psFitsMoveExtNum(
+    const psFits* fits,                ///< the psFits object to move
+    int extnum,                        ///< the extension number to move to (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Get the current extension number, where 0 is the primary HDU.
+ *
+ *  @return int        Current HDU number of the psFits file or < 0 if an error
+ *                     occurred.
+ */
+int psFitsGetExtNum(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Get the current extension name.
+ *
+ *  @return int        Current HDU name of the psFits file or NULL if an
+ *                     error occurred.
+ */
+char* psFitsGetExtName(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Set the current extension's name
+ *
+ *  @return bool       TRUE if the extension was successfully set, otherwise FALSE.
+ */
+bool psFitsSetExtName(
+    const psFits* fits,                ///< the psFits object
+    const char* name                   ///< the extension name
+);
+
+/** Get the total number of HDUs in the FITS file.
+ *
+ *  @return int        The total number of HDUs in the FITS file or < 0 if an
+ *                     error occurred.
+ */
+int psFitsGetSize(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Get the extension type of the current HDU.
+ *
+ *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN,
+ *                     the type could not be determined.
+ */
+psFitsType psFitsGetExtType(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of the current HDU.
+ *
+ *  @return psMetadata*   the header data
+ */
+psMetadata* psFitsReadHeader(
+    psMetadata* out,
+    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.
+
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of all HDUs.  The current HDU is not changed.
+ *
+ *  @return psHash*      the header data
+ */
+psHash* psFitsReadHeaderSet(
+    psHash* out,
+    ///< The psHash to add the header data via psMetadata items.  If null, a
+    ///< new psHash is created.  The keys of the psHash are the extension names
+    ///< of the cooresponding HDUs.
+
+    const psFits* fits                       ///< the psFits object
+);
+
+/** Writes the values of the metadata to the current HDU header.
+ *
+ *  @return bool        if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteHeader(
+    const psMetadata* header,          ///< the psMetadata data in which to write
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads an image, given the desired region and z-plane.
+ *
+ *  @return psImage*     the read image or NULL if there was an error.
+ */
+psImage* psFitsReadImage(
+    psImage* out,                      ///< a psImage to recycle.
+    const psFits* fits,                ///< the psFits object
+    psRegion region,                   ///< the region in the FITS image to read
+    int z                              ///< the z-plane in the FITS image cube to read
+);
+
+/** Writes an image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImage(
+    const psFits* fits,                ///< the psFits object
+    const psMetadata* header,          ///< header items for the new HDU.  Can be NULL.
+    const psImage* input,              ///< the image to output
+    int depth,                         ///< the number of z-planes of the FITS image data cube
+    char* extname                      ///< extension name
+);
+
+/** Updates the FITS file image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsUpdateImage(
+    const psFits* fits,                ///< the psFits object
+    const psImage* input,              ///< the image to output
+    psRegion region,                   ///< the region in the FITS image to write
+    int z                              ///< the z-planes of the FITS image data cube to write
+);
+
+/** Reads a table row.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psMetadata*    The table row's data.  The keys are the column names.
+ */
+psMetadata* psFitsReadTableRow(
+    const psFits* fits,                ///< the psFits object
+    int row                            ///< row number to read
+);
+
+/** Reads a table column.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*    Array of data items for the specified column or NULL
+ *                      if an error occurred.
+ */
+psArray* psFitsReadTableColumn(
+    const psFits* fits,                ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+/** Reads a table column of numbers.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psVector*    Vector of data for the specified column or NULL
+ *                       if an error occurred.
+ */
+psVector* psFitsReadTableColumnNum(
+    const psFits* fits,                ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+
+/** Reads a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*     Array of psMetadata items, which contains the output
+ *                       data items of each row.
+ *
+ *  @see psFitsReadTableRow
+ */
+psArray* psFitsReadTable(
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Writes a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *
+ *  @see psFitsReadTableRow
+ */
+bool psFitsWriteTable(
+    const psFits* fits,                ///< the psFits object
+    const psMetadata* header,  ///< header items for the new HDU.  Can be NULL.
+    const psArray* table,
+    ///< Array of psMetadata items, which contains the output data items of each row.
+    char* extname                      ///< extension name
+);
+
+
+/** Updates a FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *
+ *  @see psFitsWriteTable
+ */
+bool psFitsUpdateTable(
+    const psFits* fits,                ///< the psFits object
+    psMetadata* data,
+    ///< Array of psMetadata items, which contains the output data items of each row.
+    int row                            ///< the row number to update.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/.cvsignore	(revision 22271)
@@ -0,0 +1,7 @@
+Makefile.in
+.deps
+.libs
+Makefile
+*.lo
+*.la
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/Makefile.am	(revision 22271)
@@ -0,0 +1,36 @@
+#Makefile for image functions of psLib
+#
+INCLUDES = \
+	-I$(top_srcdir)/src/astronomy \
+	-I$(top_srcdir)/src/collections \
+	-I$(top_srcdir)/src/dataManip \
+	-I$(top_srcdir)/src/dataIO \
+	-I$(top_srcdir)/src/sysUtils \
+	$(all_includes)
+
+noinst_LTLIBRARIES = libpslibimage.la
+
+libpslibimage_la_SOURCES = \
+	psImage.c \
+	psImageExtraction.c \
+	psImageIO.c \
+	psImageManip.c \
+	psImageStats.c \
+	psImageFFT.c \
+	psImageConvolve.c
+
+BUILT_SOURCES = psImageErrors.h
+EXTRA_DIST = psImageErrors.dat psImageErrors.h image.i
+
+psImageErrors.h: psImageErrors.dat
+	perl $(top_srcdir)/src/parseErrorCodes.pl --data=$? $@
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psImage.h \
+	psImageExtraction.h \
+	psImageIO.h \
+	psImageManip.h \
+	psImageStats.h \
+	psImageFFT.h \
+	psImageConvolve.h
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/image.i
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/image.i	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/image.i	(revision 22271)
@@ -0,0 +1,10 @@
+/* image headers */
+%include "psImageConvolve.h"
+%include "psImageErrors.h"
+%include "psImageExtraction.h"
+%include "psImageFFT.h"
+%include "psImage.h"
+%include "psImageIO.h"
+%include "psImageManip.h"
+%include "psImageStats.h"
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImage.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImage.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImage.c	(revision 22271)
@@ -0,0 +1,805 @@
+/** @file  psImage.c
+ *
+ *  @brief Contains basic image definitions and operations.
+ *
+ *  This file defines the basic type for an image struct and functions useful
+ *  in manupulating images.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.65.2.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  That is the routine used to generate matrices.
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psString.h"
+
+#include "psImageErrors.h"
+
+#define SQUARE(x) ((x)*(x))
+#define MIN(x,y) (((x) > (y)) ? (y) : (x))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+static void imageFree(psImage* image)
+{
+    if (image == NULL) {
+        return;
+    }
+
+    if (image->parent != NULL) {
+        psArrayRemove(image->parent->children,image);
+        image->parent = NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psFree(image->rawDataBuffer);
+    psFree(image->data.V);
+}
+
+psImage* psImageAlloc(psU32 numCols,
+                      psU32 numRows,
+                      const psElemType type)
+{
+    psS32 area = 0;
+    psS32 elementSize = PSELEMTYPE_SIZEOF(type);  // element size in bytes
+    psS32 rowSize = numCols * elementSize;        // row size in bytes.
+
+    area = numCols * numRows;
+
+    if (area < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_AREA_NEGATIVE,
+                numRows, numCols);
+        return NULL;
+    }
+
+    psImage* image = (psImage* ) psAlloc(sizeof(psImage));
+
+    psMemSetDeallocator(image, (psFreeFcn) imageFree);
+
+    image->data.V = psAlloc(sizeof(psPtr ) * numRows);
+
+    image->rawDataBuffer = psAlloc(area * elementSize);
+
+    // set the row pointers.
+    image->data.V[0] = image->rawDataBuffer;
+    for (psS32 i = 1; i < numRows; i++) {
+        image->data.V[i] = (psPtr )((int8_t *) image->data.V[i - 1] + rowSize);
+    }
+
+    *(psS32 *)&image->col0 = 0;
+    *(psS32 *)&image->row0 = 0;
+    *(psU32 *)&image->numCols = numCols;
+    *(psU32 *)&image->numRows = numRows;
+    *(psDimen* ) & image->type.dimen = PS_DIMEN_IMAGE;
+    *(psElemType* ) & image->type.type = type;
+    image->parent = NULL;
+    image->children = NULL;
+
+    return image;
+}
+
+psRegion* psRegionAlloc(psF32 x0,
+                        psF32 x1,
+                        psF32 y0,
+                        psF32 y1)
+{
+    psRegion* out = psAlloc(sizeof(psRegion));
+
+    out->x0 = x0;
+    out->y0 = y0;
+    out->x1 = x1;
+    out->y1 = y1;
+
+    return out;
+}
+
+psRegion* psRegionFromString(const char* region)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (region == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(region,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                region);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return psRegionAlloc(col0,col1,row0,row1);
+}
+
+char* psRegionToString(const psRegion* region)
+{
+    char tmpText[256]; // big enough to store any region as text
+
+    if (region == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_REGION_NULL);
+        return NULL;
+    }
+
+    snprintf(tmpText,256,"[%g:%g,%g:%g]",
+             region->x0, region->x1,
+             region->y0, region->y1);
+
+    return psStringCopy(tmpText);
+}
+
+psImage* psImageRecycle(psImage* old,
+                        psU32 numCols,
+                        psU32 numRows,
+                        const psElemType type)
+{
+    psS32 elementSize = PSELEMTYPE_SIZEOF(type);  // element size in bytes
+    psS32 rowSize = numCols * elementSize;        // row size in bytes.
+
+    if (old == NULL) {
+        old = psImageAlloc(numCols, numRows, type);
+        return old;
+    }
+
+    if (old->type.dimen != PS_DIMEN_IMAGE) {
+        psFree(old);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return NULL;
+    }
+
+    /* image already the right size/type? */
+    if (numCols == old->numCols && numRows == old->numRows &&
+            type == old->type.type) {
+        return old;
+    }
+    // Resize the image buffer
+    old->rawDataBuffer = psRealloc(old->data.V[0],
+                                   numCols * numRows * elementSize);
+    old->data.V = (psPtr *)psRealloc(old->data.V, numRows * sizeof(psPtr ));
+
+    // recreate the row pointers
+    old->data.V[0] = old->rawDataBuffer;
+    for (psS32 i = 1; i < numRows; i++) {
+        old->data.V[i] = (psPtr )((int8_t *) old->data.V[i - 1] + rowSize);
+    }
+
+    *(psU32 *)&old->numCols = numCols;
+    *(psU32 *)&old->numRows = numRows;
+    *(psElemType* ) & old->type.type = type;
+
+    return old;
+}
+
+psImage* psImageCopy(psImage* output,
+                     const psImage* input,
+                     psElemType type)
+{
+    psElemType inDatatype;
+    psS32 elementSize;
+    psS32 elements;
+    psS32 numRows;
+    psS32 numCols;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(output);
+        return NULL;
+    }
+
+    if (input == output) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_INPLACE_NOTSUPPORTED);
+        psFree(output);
+        return NULL;
+    }
+
+    if (input->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        psFree(output);
+        return NULL;
+    }
+
+    inDatatype = input->type.type;
+    numRows = input->numRows;
+    numCols = input->numCols;
+    elements = numRows * numCols;
+    elementSize = PSELEMTYPE_SIZEOF(inDatatype);
+
+    output = psImageRecycle(output, numCols, numRows, type);
+
+    // Preserve col0,row0
+    output->col0 = input->col0;
+    output->row0 = input->row0;
+
+    // cover the trival case of copy of the same
+    // datatype.
+    if (type == inDatatype) {
+        for (psS32 row=0;row<numRows;row++) {
+            memcpy(output->data.V[row], input->data.V[row], elementSize * numCols);
+        }
+        return output;
+    }
+
+    #define PSIMAGE_ELEMENT_COPY(IN,INTYPE,OUT,OUTTYPE,ELEMENTS) { \
+        ps##INTYPE *in; \
+        ps##OUTTYPE *out; \
+        for(psS32 row=0;row<numRows;row++) { \
+            in = IN->data.INTYPE[row]; \
+            out = OUT->data.OUTTYPE[row]; \
+            for (psS32 col=0;col<numCols;col++) { \
+                *(out++) = *(in++); \
+            } \
+        } \
+    }
+
+    #define PSIMAGE_COPY_CASE(OUT,OUTTYPE) { \
+        switch (inDatatype) { \
+        case PS_TYPE_S8: \
+            PSIMAGE_ELEMENT_COPY(input,S8,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_ELEMENT_COPY(input,S16,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S32: \
+            PSIMAGE_ELEMENT_COPY(input,S32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S64: \
+            PSIMAGE_ELEMENT_COPY(input,S64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U8: \
+            PSIMAGE_ELEMENT_COPY(input,U8,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_ELEMENT_COPY(input,U16,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U32: \
+            PSIMAGE_ELEMENT_COPY(input,U32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U64: \
+            PSIMAGE_ELEMENT_COPY(input,U64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_ELEMENT_COPY(input,F32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_ELEMENT_COPY(input,F64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_ELEMENT_COPY(input,C32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_ELEMENT_COPY(input,C64,OUT,OUTTYPE,elements); \
+            break; \
+        default: \
+            break; \
+        } \
+    }
+
+    switch (type) {
+    case PS_TYPE_S8:
+        PSIMAGE_COPY_CASE(output, S8);
+        break;
+    case PS_TYPE_S16:
+        PSIMAGE_COPY_CASE(output, S16);
+        break;
+    case PS_TYPE_S32:
+        PSIMAGE_COPY_CASE(output, S32);
+        break;
+    case PS_TYPE_S64:
+        PSIMAGE_COPY_CASE(output, S64);
+        break;
+    case PS_TYPE_U8:
+        PSIMAGE_COPY_CASE(output, U8);
+        break;
+    case PS_TYPE_U16:
+        PSIMAGE_COPY_CASE(output, U16);
+        break;
+    case PS_TYPE_U32:
+        PSIMAGE_COPY_CASE(output, U32);
+        break;
+    case PS_TYPE_U64:
+        PSIMAGE_COPY_CASE(output, U64);
+        break;
+    case PS_TYPE_F32:
+        PSIMAGE_COPY_CASE(output, F32);
+        break;
+    case PS_TYPE_F64:
+        PSIMAGE_COPY_CASE(output, F64);
+        break;
+    case PS_TYPE_C32:
+        PSIMAGE_COPY_CASE(output, C32);
+        break;
+    case PS_TYPE_C64:
+        PSIMAGE_COPY_CASE(output, C64);
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(output);
+
+            break;
+        }
+    }
+    return output;
+}
+
+bool p_psImageCopyToRawBuffer(void* buffer,
+                              const psImage* input,
+                              psElemType type)
+{
+    psElemType inDatatype;
+    psS32 numRows;
+    psS32 numCols;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return false;
+    }
+
+    if (input->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return false;
+    }
+
+    inDatatype = input->type.type;
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    // cover the trival case of copy of the same
+    // datatype.
+    if (type == inDatatype) {
+        int rowSize = PSELEMTYPE_SIZEOF(inDatatype)*numCols;
+        for (psS32 row=0;row<numRows;row++) {
+            memcpy(&((psS8*)buffer)[row*rowSize], input->data.V[row], rowSize);
+        }
+        return true;
+    }
+
+    #define PSIMAGE_BUFFER_COPY(INTYPE,OUTTYPE) { \
+        ps##INTYPE *in; \
+        ps##OUTTYPE *out = buffer; \
+        for(psS32 row=0;row<numRows;row++) { \
+            in = input->data.INTYPE[row]; \
+            for (psS32 col=0;col<numCols;col++) { \
+                *(out++) = *(in++); \
+            } \
+        } \
+    }
+
+    #define PSIMAGE_BUFFER_COPY_CASE(OUT,OUTTYPE) { \
+        switch (inDatatype) { \
+        case PS_TYPE_S8: \
+            PSIMAGE_BUFFER_COPY(S8,OUTTYPE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_BUFFER_COPY(S16,OUTTYPE); \
+            break; \
+        case PS_TYPE_S32: \
+            PSIMAGE_BUFFER_COPY(S32,OUTTYPE); \
+            break; \
+        case PS_TYPE_S64: \
+            PSIMAGE_BUFFER_COPY(S64,OUTTYPE); \
+            break; \
+        case PS_TYPE_U8: \
+            PSIMAGE_BUFFER_COPY(U8,OUTTYPE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_BUFFER_COPY(U16,OUTTYPE); \
+            break; \
+        case PS_TYPE_U32: \
+            PSIMAGE_BUFFER_COPY(U32,OUTTYPE); \
+            break; \
+        case PS_TYPE_U64: \
+            PSIMAGE_BUFFER_COPY(U64,OUTTYPE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_BUFFER_COPY(F32,OUTTYPE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_BUFFER_COPY(F64,OUTTYPE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_BUFFER_COPY(C32,OUTTYPE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_BUFFER_COPY(C64,OUTTYPE); \
+            break; \
+        default: \
+            break; \
+        } \
+    }
+
+    switch (type) {
+    case PS_TYPE_S8:
+        PSIMAGE_BUFFER_COPY_CASE(output, S8);
+        break;
+    case PS_TYPE_S16:
+        PSIMAGE_BUFFER_COPY_CASE(output, S16);
+        break;
+    case PS_TYPE_S32:
+        PSIMAGE_BUFFER_COPY_CASE(output, S32);
+        break;
+    case PS_TYPE_S64:
+        PSIMAGE_BUFFER_COPY_CASE(output, S64);
+        break;
+    case PS_TYPE_U8:
+        PSIMAGE_BUFFER_COPY_CASE(output, U8);
+        break;
+    case PS_TYPE_U16:
+        PSIMAGE_BUFFER_COPY_CASE(output, U16);
+        break;
+    case PS_TYPE_U32:
+        PSIMAGE_BUFFER_COPY_CASE(output, U32);
+        break;
+    case PS_TYPE_U64:
+        PSIMAGE_BUFFER_COPY_CASE(output, U64);
+        break;
+    case PS_TYPE_F32:
+        PSIMAGE_BUFFER_COPY_CASE(output, F32);
+        break;
+    case PS_TYPE_F64:
+        PSIMAGE_BUFFER_COPY_CASE(output, F64);
+        break;
+    case PS_TYPE_C32:
+        PSIMAGE_BUFFER_COPY_CASE(output, C32);
+        break;
+    case PS_TYPE_C64:
+        PSIMAGE_BUFFER_COPY_CASE(output, C64);
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            break;
+        }
+    }
+    return true;
+}
+
+
+psS32 psImageFreeChildren(psImage* image)
+{
+    psS32 numFreed = 0;
+
+    if (image == NULL) {
+        return numFreed;
+    }
+
+    if (image->children != NULL) {
+        psImage** children = (psImage**)image->children->data;
+        numFreed = image->children->n;
+
+        // orphan the children first
+        // (so psFree doesn't try to modify the parent's children array while I'm using it)
+        for (psS32 i=0;i<numFreed;i++) {
+            children[i]->parent = NULL;
+        }
+
+        psFree(image->children);
+        image->children = NULL;
+    }
+
+    return numFreed;
+}
+
+psC64 psImagePixelInterpolate(const psImage* input,
+                              float x,
+                              float y,
+                              const psImage* mask,
+                              psU32 maskVal,
+                              psC64 unexposedValue,
+                              psImageInterpolateMode mode)
+{
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return unexposedValue;
+    }
+
+    #define PSIMAGE_PIXEL_INTERPOLATE_CASE(TYPE)                             \
+case PS_TYPE_##TYPE:                                                 \
+    switch (mode) {                                                  \
+    case PS_INTERPOLATE_FLAT:                                        \
+        return p_psImagePixelInterpolateFLAT_##TYPE(                 \
+                input,                                               \
+                x,                                                   \
+                y,                                                   \
+                mask,                                                \
+                maskVal,                                             \
+                unexposedValue);                                     \
+        break;                                                       \
+    case PS_INTERPOLATE_BILINEAR:                                    \
+        return p_psImagePixelInterpolateBILINEAR_##TYPE(             \
+                input,                                               \
+                x,                                                   \
+                y,                                                   \
+                mask,                                                \
+                maskVal,                                             \
+                unexposedValue);                                     \
+        break;                                                       \
+    case PS_INTERPOLATE_BILINEAR_VARIANCE:                           \
+        return p_psImagePixelInterpolateBILINEAR_VARIANCE_##TYPE(    \
+                input,                                               \
+                x,                                                   \
+                y,                                                   \
+                mask,                                                \
+                maskVal,                                             \
+                unexposedValue);                                     \
+        break;                                                       \
+    default:                                                         \
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,                     \
+                PS_ERRORTEXT_psImage_INTERPOLATE_METHOD_INVALID,     \
+                mode);                                               \
+    }                                                                \
+    break;
+
+    switch (input->type.type) {
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U8);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U16);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U64);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S8);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S16);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S64);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(F32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(F64);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(C32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(C64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return unexposedValue;
+}
+
+psF64 p_psImageGetElementF64(psImage* image,
+                             int col,
+                             int row)
+{
+    if (image == NULL) {
+        return NAN;
+    }
+    if (col < 0 || col >= image->numCols) {
+        return NAN;
+    }
+    if (row < 0 || row >= image->numRows) {
+        return NAN;
+    }
+
+    switch (image->type.type) {
+    case PS_TYPE_U8:
+        return image->data.U8[row][col];
+        break;
+    case PS_TYPE_U16:
+        return image->data.U16[row][col];
+        break;
+    case PS_TYPE_U32:
+        return image->data.U32[row][col];
+        break;
+    case PS_TYPE_U64:
+        return image->data.U64[row][col];
+        break;
+    case PS_TYPE_S8:
+        return image->data.S8[row][col];
+        break;
+    case PS_TYPE_S16:
+        return image->data.S16[row][col];
+        break;
+    case PS_TYPE_S32:
+        return image->data.S32[row][col];
+        break;
+    case PS_TYPE_S64:
+        return image->data.S64[row][col];
+        break;
+    case PS_TYPE_F32:
+        return image->data.F32[row][col];
+        break;
+    case PS_TYPE_F64:
+        return image->data.F64[row][col];
+    default:
+        return NAN;
+    }
+}
+
+#define PSIMAGE_PIXEL_INTERPOLATE_FLAT(TYPE,RETURNTYPE) \
+inline RETURNTYPE p_psImagePixelInterpolateFLAT_##TYPE( \
+        const psImage* input, \
+        float x, \
+        float y, \
+        const psImage* mask, \
+        psU32 maskVal, \
+        RETURNTYPE unexposedValue) \
+{ \
+    psS32 intX = (psS32) round((psF64)(x) - 0.5 + FLT_EPSILON); \
+    psS32 intY = (psS32) round((psF64)(y) - 0.5 + FLT_EPSILON); \
+    psS32 lastX = input->numCols - 1; \
+    psS32 lastY = input->numRows - 1; \
+    \
+    if ((intX < 0) || \
+            (intX > lastX) || \
+            (intY < 0) || \
+            (intY > lastY) || \
+            ( (mask!=NULL) && \
+              ((mask->data.PS_TYPE_MASK_DATA[intY][intX] & maskVal) != 0) ) ) { \
+        return unexposedValue; \
+    } \
+    \
+    return input->data.TYPE[intY][intX]; \
+}
+
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U8,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U16,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U32,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U64,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S8,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S16,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S32,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S64,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(F32,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(F64,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(C32,psC64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(C64,psC64)
+
+#define PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(TYPE, RETURNTYPE, SUFFIX, FRACFUNC) \
+inline RETURNTYPE p_psImagePixelInterpolateBILINEAR_##SUFFIX( \
+        const psImage* input, \
+        float x, \
+        float y, \
+        const psImage* mask, \
+        psU32 maskVal, \
+        RETURNTYPE unexposedValue) \
+{ \
+    double floorX = floor((psF64)(x) - 0.5); \
+    double floorY = floor((psF64)(y) - 0.5); \
+    psF64 fracX = x - 0.5 - floorX; \
+    psF64 fracY = y - 0.5 - floorY; \
+    psS32 intFloorX = (psS32) floorX; \
+    psS32 intFloorY = (psS32) floorY; \
+    psS32 lastX = input->numCols - 1; \
+    psS32 lastY = input->numRows - 1; \
+    ps##TYPE V00 = 0; \
+    ps##TYPE V01 = 0; \
+    ps##TYPE V10 = 0; \
+    ps##TYPE V11 = 0; \
+    psBool valid00 = false; \
+    psBool valid01 = false; \
+    psBool valid10 = false; \
+    psBool valid11 = false; \
+    \
+    if (intFloorY >= 0 && intFloorY <= lastY) { \
+        if (intFloorX >= 0 && intFloorX <= lastX) { \
+            V00 = input->data.TYPE[intFloorY][intFloorX]; \
+            valid00 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY][intFloorX] & maskVal) == 0); \
+        } \
+        if (intFloorX >= -1 && intFloorX < lastX) { \
+            V10 = input->data.TYPE[intFloorY][intFloorX+1]; \
+            valid10 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY][intFloorX+1] & maskVal) == 0); \
+        } \
+    } \
+    if (intFloorY >= -1 && intFloorY < lastY) { \
+        if (intFloorX >= 0 && intFloorX <= lastX) { \
+            V01 = input->data.TYPE[intFloorY+1][intFloorX]; \
+            valid01 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY+1][intFloorX] & maskVal) == 0); \
+        } \
+        if (intFloorX >= -1 && intFloorX < lastX) { \
+            V11 = input->data.TYPE[intFloorY+1][intFloorX+1]; \
+            valid11 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY+1][intFloorX+1] & maskVal) == 0); \
+        } \
+    } \
+    \
+    /* cover likely case of all pixels being valid more efficiently */  \
+    if (valid00 && valid10 && valid01 && valid11) { \
+        /* formula from the ADD */ \
+        return V00*FRACFUNC((1.0-fracX)*(1.0-fracY)) + V10*FRACFUNC(fracX*(1.0-fracY)) + \
+               V01*FRACFUNC(fracY*(1.0-fracX)) + V11*FRACFUNC(fracX*fracY); \
+    } \
+    \
+    /* OK, at least one pixel is not valid - need to do it piecemeal */ \
+    \
+    RETURNTYPE V0 = 0.0; \
+    psBool valid0 = true; \
+    if (valid00 && valid10) { \
+        V0 = V00*FRACFUNC(1-fracX)+V10*FRACFUNC(fracX); \
+    } else if (valid00) { \
+        V0 = V00; \
+    } else if (valid10) { \
+        V0 = V10; \
+    } else { \
+        valid0 = false; \
+    } \
+    \
+    RETURNTYPE V1 = 0.0; \
+    psBool valid1 = true; \
+    if (valid01 && valid11) { \
+        V1 = V01*FRACFUNC(1-fracX)+V11*FRACFUNC(fracX); \
+    } else if (valid01) { \
+        V1 = V01; \
+    } else if (valid11) { \
+        V1 = V11; \
+    } else { \
+        valid1 = false; \
+    } \
+    \
+    if (valid0 && valid1) { \
+        return V0*FRACFUNC(1-fracY) + V1*FRACFUNC(fracY); \
+    } else if (valid0) { \
+        return V0; \
+    } else if (valid1) { \
+        return V1; \
+    } \
+    \
+    return unexposedValue; \
+}
+
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U8,psF64,U8,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U16,psF64,U16,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U32,psF64,U32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U64,psF64,U64,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S8,psF64,S8,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S16,psF64,S16,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S32,psF64,S32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S64,psF64,S64,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F32,psF64,F32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F64,psF64,F64,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C32,psC64,C32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C64,psC64,C64,)
+
+// Variance Version
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U8,psF64,VARIANCE_U8,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U16,psF64,VARIANCE_U16,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U32,psF64,VARIANCE_U32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U64,psF64,VARIANCE_U64,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S8,psF64,VARIANCE_S8,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S16,psF64,VARIANCE_S16,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S32,psF64,VARIANCE_S32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S64,psF64,VARIANCE_S64,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F32,psF64,VARIANCE_F32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F64,psF64,VARIANCE_F64,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C32,psC64,VARIANCE_C32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C64,psC64,VARIANCE_C64,SQUARE)
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImage.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImage.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImage.h	(revision 22271)
@@ -0,0 +1,244 @@
+/** @file  psImage.h
+ *
+ *  @brief Contains basic image definitions and operations
+ *
+ *  This file defines the basic type for an image struct and functions useful
+ *  in manupulating images.
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.51.2.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_H
+#define PS_IMAGE_H
+
+#include <complex.h>
+
+#include "psType.h"
+#include "psArray.h"
+
+/// @addtogroup Image
+/// @{
+
+/** enumeration of options in interpolation
+ *
+ */
+typedef enum {
+    PS_INTERPOLATE_FLAT,               ///< 'flat' interpolation (nearest pixel)
+    PS_INTERPOLATE_BILINEAR,           ///< bi-linear interpolation
+    PS_INTERPOLATE_LANCZOS2,           ///< Sinc interpolation with 4x4 pixel kernel
+    PS_INTERPOLATE_LANCZOS3,           ///< Sinc interpolation with 6x6 pixel kernel
+    PS_INTERPOLATE_LANCZOS4,           ///< Sinc interpolation with 8x8 pixel kernel
+    PS_INTERPOLATE_BILINEAR_VARIANCE,  ///< Variance version of PS_INTERPOLATE_BILINEAR
+    PS_INTERPOLATE_LANCZOS2_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS2
+    PS_INTERPOLATE_LANCZOS3_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS3
+    PS_INTERPOLATE_LANCZOS4_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS4
+    PS_INTERPOLATE_NUM_MODES           ///< enum end-marker; does not coorespond to a interpolation mode
+} psImageInterpolateMode;
+
+/** Basic image data structure.
+ *
+ * Struct for maintaining image data of varying types. It also contains
+ * information about image size, parent images and children images.
+ *
+ */
+typedef struct psImage
+{
+    const psType type;                 ///< Image data type and dimension.
+    const psU32 numCols;               ///< Number of columns in image
+    const psU32 numRows;               ///< Number of rows in image.
+    psS32 col0;          ///< Column position relative to parent.
+    psS32 row0;          ///< Row position relative to parent.
+
+    union {
+        psU8**  U8;                    ///< Unsigned 8-bit integer data.
+        psU16** U16;                   ///< Unsigned 16-bit integer data.
+        psU32** U32;                   ///< Unsigned 32-bit integer data.
+        psU64** U64;                   ///< Unsigned 64-bit integer data.
+        psS8**  S8;                    ///< Signed 8-bit integer data.
+        psS16** S16;                   ///< Signed 16-bit integer data.
+        psS32** S32;                   ///< Signed 32-bit integer data.
+        psS64** S64;                   ///< Signed 64-bit integer data.
+        psF32** F32;                   ///< Single-precision float data.
+        psF64** F64;                   ///< Double-precision float data.
+        psC32** C32;                   ///< Single-precision complex data.
+        psC64** C64;                   ///< Double-precision complex data.
+        psPtr** PTR;                   ///< Void pointers.
+        psPtr*  V;                     ///< Pointer to data.
+    } data;                            ///< Union for data types.
+    const struct psImage* parent;      ///< Parent, if a subimage.
+    psArray* children;                 ///< Children of this region.
+
+    psPtr rawDataBuffer;
+}
+psImage;
+
+/** Basic image region structure.
+ *
+ * Struct for specifying a rectangular area in an image.
+ *
+ */
+typedef struct
+{
+    psF32 x0;                         ///< the first column of the region.
+    psF32 x1;                         ///< the last column of the region.
+    psF32 y0;                         ///< the first row of the region.
+    psF32 y1;                         ///< the last row of the region.
+}
+psRegion;
+
+/** Create an image of the specified size and type.
+ *
+ * Uses psLib memory allocation functions to create an image struct of the
+ * specified size and type.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageAlloc(
+    psU32 numCols,                     ///< Number of rows in image.
+    psU32 numRows,                     ///< Number of columns in image.
+    const psElemType type              ///< Type of data for image.
+);
+
+/** Create a psRegion with the specified attributes.
+ *
+ * Uses psLib memory allocation functions to create a psRegion the
+ * specified x0, x1, y0, and y1.
+ *
+ * @return psRegion* : Pointer to psRegion.
+ *
+ */
+psRegion* psRegionAlloc(
+    psF32 x0,                         ///< the first column of the region.
+    psF32 x1,                         ///< the last column of the region.
+    psF32 y0,                         ///< the first row of the region.
+    psF32 y1                          ///< the last row of the region.
+);
+
+/** Create a psRegion with the attribute values given as a string.
+ *
+ *  Create a psRegion with the attribute values given as a string.  The format
+ *  shall be of the standard IRAF form '[x0:x1,y0:y1]'
+ *
+ *  @return psRegion*:  A new psRegion struct, or NULL is not successful.
+ */
+psRegion* psRegionFromString(
+    const char* region   ///< image rectangular region in the form '[x0:x1,y0:y1]'
+);
+
+/** Create a string of the standard IRAF form '[x0:x1,y0:y1]' from a psRegion.
+ *
+ *  @return char*:  A new string representing the psRegion as text, or NULL
+ *                  is not successful.
+ */
+char* psRegionToString(
+    const psRegion* region  ///< the psRegion to convert to a string
+);
+
+/** Resize a given image to the given size/type.
+ *
+ *  @return psImage* Resized psImage.
+ *
+ */
+psImage* psImageRecycle(
+    psImage* old,                      ///< the psImage to recycle by resizing image buffer
+    psU32 numCols,                     ///< the desired number of columns in image
+    psU32 numRows,                     ///< the desired number of rows in image
+    const psElemType type              ///< the desired datatype of the image
+);
+
+/** Makes a copy of a psImage
+ *
+ * @return psImage* Copy of the input psImage.  This may not be equal to the
+ * output parameter
+ *
+ */
+psImage* psImageCopy(
+    psImage* output,                   ///< if not NULL, a psImage that could be recycled.
+    const psImage* input,              ///< the psImage to copy
+    psElemType type                    ///< the desired datatype of the returned copy
+);
+
+bool p_psImageCopyToRawBuffer(
+    void* buffer,
+    const psImage* input,
+    psElemType type
+);
+
+/** Frees all children of a psImage.
+ *
+ *  @return psS32      Number of children freed.
+ *
+ */
+psS32 psImageFreeChildren(
+    psImage* image                     ///< psImage in which all children shall be deallocated
+);
+
+/** get an element of an image as a psF64.
+ *
+ *  @return psF64   pixel value at specified location
+ */
+psF64 p_psImageGetElementF64(
+    psImage* image,                    ///< input image
+    int col,                           ///< pixel column
+    int row                            ///< pixel row
+);
+
+/** Interpolate image pixel value given floating point coordinates.
+ *
+ *  @return psF32    Pixel value interpolated from image or unexposedValue if
+ *                   given x,y doesn't coorespond to a valid image location
+ */
+psC64 psImagePixelInterpolate(
+    const psImage* input,              ///< input image for interpolation
+    float x,                           ///< column location to derive value of
+    float y,                           ///< row location ot derive value of
+    const psImage* mask,               ///< if not NULL, the mask of the input image
+    psU32 maskVal,              ///< the mask value
+    psC64 unexposedValue,              ///< return value if x,y location is not in image.
+    psImageInterpolateMode mode        ///< interpolation mode
+);
+
+#define PIXEL_INTERPOLATE_FCN_PROTOTYPE(SUFFIX, RETURNTYPE) \
+inline RETURNTYPE p_psImagePixelInterpolate##SUFFIX( \
+        const psImage* input,          /**< input image for interpolation */ \
+        float x,                       /**< column location to derive value of */ \
+        float y,                       /**< row location ot derive value of */ \
+        const psImage* mask,           /**< if not NULL, the mask of the input image */ \
+        psU32 maskVal,                 /**< the mask value */ \
+        RETURNTYPE unexposedValue      /**< return value if x,y location is not in image. */ \
+                                                   );
+
+#define PIXEL_INTERPOLATE_FCNS(MODE) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U8,psF64)  \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U16,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U32,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U64,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S8,psF64)  \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S16,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S32,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S64,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_F32,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_F64,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_C32,psC64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_C64,psC64)
+
+#ifndef SWIG
+PIXEL_INTERPOLATE_FCNS(FLAT)
+PIXEL_INTERPOLATE_FCNS(BILINEAR)
+PIXEL_INTERPOLATE_FCNS(BILINEAR_VARIANCE)
+#endif // ! SWIG
+
+#undef PIXEL_INTERPOLATE_FCN_PROTOTYPE
+#undef PIXEL_INTERPOLATE_FCNS
+
+/// @}
+
+#endif // PS_IMAGE_H
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageConvolve.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageConvolve.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageConvolve.c	(revision 22271)
@@ -0,0 +1,479 @@
+/*  @file  psImageConvolve.c
+ *
+ *  @brief Contains FFT transform related functions for psImage.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-15 00:12:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+
+#include "psImageConvolve.h"
+#include "psImageFFT.h"
+#include "psImageExtraction.h"
+#include "psBinaryOp.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psImageIO.h"
+
+#include "psImageErrors.h"
+
+#define FOURIER_PADDING 32 /* padding amount in every side of the image for fourier convolution */
+
+static void freeKernel(psKernel* ptr);
+
+psKernel* psKernelAlloc(psS32 xMin, psS32 xMax, psS32 yMin, psS32 yMax)
+{
+    psKernel* result;
+    psS32 numRows;
+    psS32 numCols;
+
+    // following is explicitly spelled out in the SDRS as a requirement
+    if (yMin > yMax) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "Specified yMin, %d, was greater than yMax, %d.  Values swapped.",
+                 yMin, yMax);
+
+        psS32 temp = yMin;
+        yMin = yMax;
+        yMax = temp;
+    }
+
+    // following is explicitly spelled out in the SDRS as a requirement
+    if (xMin > xMax) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "Specified xMin, %d, was greater than xMax, %d.  Values swapped.",
+                 xMin, xMax);
+
+        psS32 temp = xMin;
+        xMin = xMax;
+        xMax = temp;
+    }
+
+    numRows = yMax - yMin + 1;
+    numCols = xMax - xMin + 1;
+
+    result = psAlloc(sizeof(psKernel));
+    result->xMin = xMin;
+    result->xMax = xMax;
+    result->yMin = yMin;
+    result->yMax = yMax;
+    result->image = psImageAlloc(numCols,numRows,PS_TYPE_KERNEL);
+    memset(result->image->rawDataBuffer,0,numCols*numRows*PSELEMTYPE_SIZEOF(PS_TYPE_KERNEL));
+    result->p_kernelRows = psAlloc(sizeof(psKernelType*)*numRows);
+
+    psKernelType** kernelRows = result->p_kernelRows;
+    psKernelType** imageRows = result->image->data.PS_TYPE_KERNEL_DATA;
+    for (psS32 i = 0; i < numRows; i++) {
+        kernelRows[i] = imageRows[i] - xMin;
+    }
+    result->kernel = kernelRows - yMin;
+
+    psMemSetDeallocator(result,(psFreeFcn)freeKernel);
+
+    return result;
+}
+
+void freeKernel(psKernel* ptr)
+{
+    if (ptr != NULL) {
+        psFree(ptr->image);
+        psFree(ptr->p_kernelRows);
+    }
+}
+
+psKernel* psKernelGenerate(const psVector* tShifts,
+                           const psVector* xShifts,
+                           const psVector* yShifts,
+                           psBool relative)
+{
+    psS32 lastX;
+    psS32 lastY;
+    psS32 lastT;
+    psS32 x;
+    psS32 y;
+    psS32 t;
+    psS32 xMin = 0;
+    psS32 xMax = 0;
+    psS32 yMin = 0;
+    psS32 yMax = 0;
+    psS32 length = 0;
+    psKernelType normalizeTime = 1.0;  // fraction of total time for each shift clock
+    psKernel* result = NULL;
+    psKernelType** kernel = NULL;
+
+    // got non-NULL vectors?
+    if (tShifts == NULL || xShifts == NULL || yShifts == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageConvolve_SHIFT_NULL);
+        return NULL;
+    }
+
+    // types match?
+    if (xShifts->type.type != yShifts->type.type ||
+            tShifts->type.type != xShifts->type.type) {
+        char* typeXStr;
+        char* typeYStr;
+        char* typeTStr;
+        PS_TYPE_NAME(typeXStr,xShifts->type.type);
+        PS_TYPE_NAME(typeYStr,yShifts->type.type);
+        PS_TYPE_NAME(typeTStr,tShifts->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageConvolve_SHIFT_TYPE_MISMATCH,
+                typeTStr, typeXStr, typeYStr);
+        return NULL;
+    }
+
+    // sizes match?
+    length = xShifts->n;
+    if (length != yShifts->n ||
+            length != tShifts->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Shift vectors can not be of different sizes.");
+        return NULL;
+    }
+
+    // if no shifts, the kernel is just a 1 at 0,0
+    if (length < 1) {
+        result = psKernelAlloc(0,0,0,0);
+        result->kernel[0][0] = 1;
+        return result;
+    }
+
+    #define KERNEL_GENERATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *tShiftData = tShifts->data.TYPE; \
+        ps##TYPE *xShiftData = xShifts->data.TYPE; \
+        ps##TYPE *yShiftData = yShifts->data.TYPE; \
+        lastX = xShiftData[length-1]; \
+        lastY = yShiftData[length-1]; \
+        lastT = tShiftData[length-1]; \
+        \
+        for (int lcv = 0; lcv < length; lcv++) { \
+            x = lastX - xShiftData[lcv]; \
+            y = lastY - yShiftData[lcv]; \
+            \
+            if (x < xMin) { \
+                xMin = x; \
+            } else if (x > xMax) { \
+                xMax = x; \
+            } \
+            if (y < yMin) { \
+                yMin = y; \
+            } else if (y > yMax) { \
+                yMax = y; \
+            } \
+        } \
+        \
+        normalizeTime = 1.0 / (psKernelType)(tShiftData[length-1]); \
+        result = psKernelAlloc(xMin,xMax,yMin,yMax); \
+        kernel = result->kernel; \
+        \
+        psS32 prevT = 0; \
+        for (int i = 0; i < length; i++) { \
+            t = tShiftData[i] - prevT; \
+            x = lastX - xShiftData[i]; \
+            y = lastY - yShiftData[i]; \
+            \
+            kernel[y][x] += (psKernelType)t / (psKernelType)lastT; \
+            prevT = tShiftData[i]; \
+        } \
+        break; \
+    }
+
+    #define RELATIVE_KERNEL_GENERATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *tShiftData = tShifts->data.TYPE; \
+        ps##TYPE *xShiftData = xShifts->data.TYPE; \
+        ps##TYPE *yShiftData = yShifts->data.TYPE; \
+        \
+        x = 0; \
+        y = 0; \
+        t = 0; \
+        \
+        for (int lcv = length-1; lcv >= 0; lcv--) { \
+            t += tShiftData[lcv]; \
+            \
+            if (x < xMin) { \
+                xMin = x; \
+            } else if (x > xMax) { \
+                xMax = x; \
+            } \
+            if (y < yMin) { \
+                yMin = y; \
+            } else if (y > yMax) { \
+                yMax = y; \
+            } \
+            x -= xShiftData[lcv]; \
+            y -= yShiftData[lcv]; \
+            \
+        } \
+        result = psKernelAlloc(xMin,xMax,yMin,yMax); \
+        kernel = result->kernel; \
+        \
+        normalizeTime = 1.0 / (psKernelType)t; \
+        x = 0; \
+        y = 0; \
+        for (psS32 i = length-1; i >= 0; i--) { \
+            kernel[y][x] += (psKernelType)(tShiftData[i]) * normalizeTime; \
+            x -= xShiftData[i]; \
+            y -= yShiftData[i]; \
+            \
+        } \
+        break; \
+    }
+
+    if (relative) {
+        switch (xShifts->type.type) {
+            RELATIVE_KERNEL_GENERATE_CASE(U8);
+            RELATIVE_KERNEL_GENERATE_CASE(U16);
+            RELATIVE_KERNEL_GENERATE_CASE(U32);
+            RELATIVE_KERNEL_GENERATE_CASE(U64);
+            RELATIVE_KERNEL_GENERATE_CASE(S8);
+            RELATIVE_KERNEL_GENERATE_CASE(S16);
+            RELATIVE_KERNEL_GENERATE_CASE(S32);
+            RELATIVE_KERNEL_GENERATE_CASE(S64);
+            RELATIVE_KERNEL_GENERATE_CASE(F32);
+            RELATIVE_KERNEL_GENERATE_CASE(F64);
+            RELATIVE_KERNEL_GENERATE_CASE(C32);
+            RELATIVE_KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
+        }
+    } else {
+        switch (xShifts->type.type) {
+            KERNEL_GENERATE_CASE(U8);
+            KERNEL_GENERATE_CASE(U16);
+            KERNEL_GENERATE_CASE(U32);
+            KERNEL_GENERATE_CASE(U64);
+            KERNEL_GENERATE_CASE(S8);
+            KERNEL_GENERATE_CASE(S16);
+            KERNEL_GENERATE_CASE(S32);
+            KERNEL_GENERATE_CASE(S64);
+            KERNEL_GENERATE_CASE(F32);
+            KERNEL_GENERATE_CASE(F64);
+            KERNEL_GENERATE_CASE(C32);
+            KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
+        }
+    }
+
+    return result;
+}
+
+psImage* psImageConvolve(psImage* out, const psImage* in, const psKernel* kernel, psBool direct)
+{
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    if (kernel == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageConvolve_KERNEL_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 xMin = kernel->xMin;
+    psS32 xMax = kernel->xMax;
+    psS32 yMin = kernel->yMin;
+    psS32 yMax = kernel->yMax;
+    psKernelType** kData = kernel->kernel;
+
+    // make the output image to the proper size and type
+    psS32 numRows = in->numRows;
+    psS32 numCols = in->numCols;
+
+
+
+    if (direct) {
+        // spatial convolution
+
+        #define SPATIAL_CONVOLVE_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            out = psImageRecycle(out, numCols, numRows, PS_TYPE_##TYPE); \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    ps##TYPE pixel = 0.0; \
+                    for (psS32 kRow = yMin; kRow < yMax; kRow++) { \
+                        if (row-kRow >= 0 && row-kRow < numRows) { \
+                            for (psS32 kCol = xMin; kCol < xMax; kCol++) { \
+                                if (col-kCol >= 0 && col-kCol < numCols) { \
+                                    pixel += kData[kRow][kCol] * inData[row-kRow][col-kCol]; \
+                                } \
+                            } \
+                        } \
+                    } \
+                    outRow[col] = pixel; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (in->type.type) {
+            SPATIAL_CONVOLVE_CASE(U8)
+            SPATIAL_CONVOLVE_CASE(U16)
+            SPATIAL_CONVOLVE_CASE(U32)
+            SPATIAL_CONVOLVE_CASE(U64)
+            SPATIAL_CONVOLVE_CASE(S8)
+            SPATIAL_CONVOLVE_CASE(S16)
+            SPATIAL_CONVOLVE_CASE(S32)
+            SPATIAL_CONVOLVE_CASE(S64)
+            SPATIAL_CONVOLVE_CASE(F32)
+            SPATIAL_CONVOLVE_CASE(F64)
+            SPATIAL_CONVOLVE_CASE(C32)
+            SPATIAL_CONVOLVE_CASE(C64)
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,in->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+
+            }
+        }
+
+
+    } else {
+        // fourier convolution
+        psS32 paddedCols = numCols+2*FOURIER_PADDING;
+        psS32 paddedRows = numRows+2*FOURIER_PADDING;
+
+        // check to see if kernel is smaller, otherwise padding it up will fail.
+        psS32 kRows = kernel->image->numRows;
+        psS32 kCols = kernel->image->numCols;
+        if (kRows >= numRows || kCols >= numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psImageConvolve_KERNEL_TOO_LARGE,
+                    kCols,kRows,
+                    numCols, numRows);
+            psFree(out);
+            return NULL;
+        }
+
+        // pad the image
+        psImage* paddedImage = psImageAlloc(paddedCols,paddedRows,in->type.type);
+        psS32 elementSize = PSELEMTYPE_SIZEOF(in->type.type);
+
+        // zero out padded area on top and bottom
+        memset(paddedImage->data.U8[0],0,FOURIER_PADDING*paddedCols*elementSize);
+        memset(paddedImage->data.U8[FOURIER_PADDING+numRows-1],0,FOURIER_PADDING*paddedCols*elementSize);
+
+        // fill in the image-containing rows.
+        psS32 sidePaddingSize = FOURIER_PADDING*elementSize;
+        psS32 imageRowSize = numCols*elementSize;
+        psU8* paddedData = paddedImage->data.U8[FOURIER_PADDING];
+        for (psS32 row=0;row<numRows;row++) {
+            // zero out padded area on left edge.
+            memset(paddedData,0,sidePaddingSize);
+            paddedData += sidePaddingSize;
+            memcpy(paddedData,in->data.U8[row],imageRowSize);
+            paddedData += imageRowSize;
+            // zero out padded area on right edge.
+            memset(paddedData,0,sidePaddingSize);
+            paddedData += sidePaddingSize;
+        }
+
+        // pad the kernel to the same size of paddedImage
+        psImage* paddedKernel = psImageAlloc(paddedCols,paddedRows,PS_TYPE_KERNEL);
+        memset(paddedKernel->data.U8[0],0,sizeof(psKernelType)*numCols*numRows); // zero-out image
+        psS32 yMax = kernel->yMax;
+        psS32 xMax = kernel->xMax;
+        for (psS32 row = kernel->yMin; row <= yMax;row++) {
+            psS32 padRow = row;
+            if (padRow < 0) {
+                padRow += paddedRows;
+            }
+            psKernelType* padData = paddedKernel->data.PS_TYPE_KERNEL_DATA[padRow];
+            psKernelType* kernelRow = kernel->kernel[row];
+            for (psS32 col = kernel->xMin; col <= xMax; col++) {
+                if (col < 0) {
+                    padData[col+paddedCols] = kernelRow[col];
+                } else {
+                    padData[col] = kernelRow[col];
+                }
+            }
+        }
+
+        psImage* kernelFourier = psImageFFT(NULL, paddedKernel, PS_FFT_FORWARD);
+        if (kernelFourier == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psImageConvolve_KERNEL_FFT_FAILED);
+            psFree(out);
+            return NULL;
+        }
+
+        psImage* inFourier = psImageFFT(NULL, paddedImage, PS_FFT_FORWARD);
+        if (inFourier == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psImageConvolve_FFT_FAILED);
+            psFree(out);
+            return NULL;
+        }
+
+        // convolution in fourier domain is just a pixel-wise multiplication
+        for (int row = 0; row < paddedRows; row++) {
+            psC32* inRow = inFourier->data.C32[row];
+            psC32* kRow = kernelFourier->data.C32[row];
+            for (int col = 0; col < paddedCols; col++) {
+                inRow[col] *= kRow[col];
+            }
+        }
+
+        psImage* complexOut = psImageFFT(NULL, inFourier,
+                                         PS_FFT_REVERSE);
+        if (complexOut == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psImageConvolve_FFT_FAILED);
+            psFree(out);
+            return NULL;
+        }
+
+        // subset out the padded area now.
+        psImage* complexOutSansPad = psImageSubset(complexOut,
+                                     FOURIER_PADDING,FOURIER_PADDING,
+                                     FOURIER_PADDING+numCols,FOURIER_PADDING+numRows);
+
+        out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32);
+        float factor = 1.0f/(float)paddedCols/(float)paddedRows;
+        for (psS32 row = 0; row < numRows; row++) {
+            psF32* outRow = out->data.F32[row];
+            psC32* resultRow = complexOutSansPad->data.C32[row];
+            for (psS32 col = 0; col < numCols; col++) {
+                outRow[col] = crealf(resultRow[col])*factor;
+            }
+        }
+
+        psFree(complexOut); // frees complexOutSansPad, as it is a child.
+        psFree(kernelFourier);
+        psFree(inFourier);
+        psFree(paddedImage);
+        psFree(paddedKernel);
+
+    }
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageConvolve.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageConvolve.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageConvolve.h	(revision 22271)
@@ -0,0 +1,121 @@
+/** @file  psImageConvolve.h
+ *
+ *  @brief image convolution functionality
+ *
+ *  @ingroup Transform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_CONVOLVE_H
+#define PS_IMAGE_CONVOLVE_H
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psType.h"
+
+#define PS_TYPE_KERNEL PS_TYPE_F32     /**< the data member to use for kernel image */
+#define PS_TYPE_KERNEL_DATA F32        /**< the data member to use for kernel image */
+#define PS_TYPE_KERNEL_NAME "psF32"    /**< the data type for kernel as a string */
+
+typedef psF32 psKernelType;
+
+/** A convolution kernel */
+typedef struct
+{
+    psImage* image;                    ///< Kernel data, in the form of an image
+    psS32 xMin;                          ///< Most negative x index
+    psS32 yMin;                          ///< Most negative y index
+    psS32 xMax;                          ///< Most positive x index
+    psS32 yMax;                          ///< Most positive y index
+    psKernelType** kernel;             ///< Pointer to the kernel data
+    psKernelType** p_kernelRows;       ///< Pointer to the rows of the kernel data; not intended for user use.
+}
+psKernel;
+
+/** Allocates a convolution kernel of the given range
+ *
+ *  In order to perform a convolution, we need to define the convolution 
+ *  kernel. We need a more general object than a psImage so that we can 
+ *  incorporate the offset from the (0, 0) pixel to the (0, 0) value of the 
+ *  kernel. It might be convenient to allow both positive and negative 
+ *  indices to convey the positive and negative shifts. One might consider 
+ *  setting the x0 and y0 members of a psImage to the appropriate offsets, 
+ *  but this is not the purpose of these members, and doing so may affect the 
+ *  behavior of other psImage operations.
+ *
+ *  This construction allows the kernel member to use negative indices, while 
+ *  preserving the location of psMemBlocks relative to allocated memory.
+ *
+ *  The maximum extent of the kernel shifts shall be defined by the xMin, 
+ *  xMax, yMin and yMax members. Note that xMin and yMin, under normal 
+ *  circumstances, should be negative numbers. That is, 
+ *  myKernel->kernel[-3][-2] may be defined if yMin and xMin are equal to or 
+ *  more negative than -3 and -2, respectively.
+ *
+ *  In the event that one of the minimum values is greater than the 
+ *  corresponding maximum value, the function shall generate a warning, and 
+ *  the offending values shall be exchanged.
+ *
+ *  @return psKernel*          A new kernel object
+ */
+psKernel* psKernelAlloc(
+    psS32 xMin,                          ///< Most negative x index
+    psS32 xMax,                          ///< Most positive x index
+    psS32 yMin,                          ///< Most negative y index
+    psS32 yMax                           ///< Most positive y index
+);
+
+/** Generates a kernel given a list of shift values
+ *
+ *  Given a list of values (e.g., shifts made in the course of OT guiding), 
+ *  psKernelGenerate shall return the appropriate kernel.  The vectors xShifts 
+ *  and yShifts, which are a list of shifts relative to some starting point, 
+ *  will be supplied by the user. The elements of the vectors should be of an 
+ *  integer type; otherwise the values shall be truncated to integers. The 
+ *  output kernel shall be normalized such that the sum over the kernel is 
+ *  unity. 
+ *
+ *  If the vectors are not of the same number of elements, then the function 
+ *  shall generate a warning shall be generated, following which, the longer 
+ *  vector trimmed to the length of the shorter, and the function shall continue.
+ *
+ *  @return psKernel*    new Kernel object
+ */
+psKernel* psKernelGenerate(
+    const psVector* tShifts,           ///< list of time shifts
+    const psVector* xShifts,           ///< list of x-axis shifts
+    const psVector* yShifts,           ///< list of y-axis shifts
+    psBool relative
+);
+
+/** convolve an image with a kernel
+ *
+ *  Given an input image and the convolution kernel, psImageConvolve shall 
+ *  convolve the input image, in, with the kernel, kernel and return the 
+ *  convolved image, out.
+ * 
+ *  Two methods shall be available for the convolution: if direct is true, 
+ *  then the convolution shall be performed in real space (appropriate for 
+ *  small kernels); otherwise, the convolution shall be performed using Fast 
+ *  Fourier Transforms (FFTs; appropriate for larger kernels). The latter 
+ *  option involves padding the input image, copying the kernel into an image 
+ *  of the same size as the padded input image, performing an FFT on each, 
+ *  multiplying the FFTs, and performing an inverse FFT before trimming the 
+ *  image back to the original size.
+ *
+ *  @return psImage*  resulting image 
+ */
+psImage* psImageConvolve(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in,                 ///< the psImage to convolve
+    const psKernel* kernel,            ///< kernel to colvolve with
+    psBool direct                        ///< specifies method, true=direct convolution, false=fourier
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageErrors.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageErrors.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageErrors.dat	(revision 22271)
@@ -0,0 +1,81 @@
+#
+#  This file is used to generate psImageErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERRORTEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+# psImage
+psImage_AREA_NEGATIVE                  Specified number of rows (%d) or columns (%d) is invalid.
+psImage_NOT_AN_IMAGE                   The input psImage must have a PS_DIMEN_IMAGE dimension type.
+psImage_IMAGE_NULL                     Can not operate on a NULL psImage.
+psImage_IMAGE_TYPE_UNSUPPORTED         Specified psImage type, %s, is not supported.
+psImage_INTERPOLATE_METHOD_INVALID     Specified interpolation method (%d) is not supported.
+psImage_REGION_NULL                    Specified psRegion is NULL.  Operation could not be performed.
+psImage_SUBSET_RANGE_INVALID           Specified subset range, [%d:%d,%d:%d], is invalid or outside input psImage's boundaries, [0:%d,0:%d].
+psImage_SUBSET_RANGE_MALFORMED         Specified subset range, [%d:%d,%d:%d], is invalid.  Ranges must be incremental.
+psImage_SUBSECTION_NULL                Specified subsection string can not be NULL.
+psImage_SUBSECTION_INVALID             Specified subsection string, '%s', can not be parsed.  Must be in the form '[x1:x2,y1:y2]'.
+psImage_NOT_PARENT                     Specified psImage can not be a child of another psImage.
+psImage_INPLACE_NOTSUPPORTED           Specified input and output psImage can not reference the same psImage.
+psImage_SUBSET_ZERO_SIZE               Specified subset, [%d:%d,%d:%d], contains no pixel data.
+psImage_IMAGE_MASK_SIZE                Input psImage mask size, %dx%d, does not match psImage input size, %dx%d.
+psImage_IMAGE_MASK_TYPE                Input psImage mask type, %s, is not the supported mask datatype of %s.
+psImage_BAD_STAT                       Specified statistic option, %d, is not valid.  Must specify one and only one statistic type.
+psImage_NO_STAT_OPTIONS                Specified statistic option did not indicate any operation to perform.
+psImage_STAT_NULL                      Specified statistic can not be NULL.
+psImage_SLICE_DIRECTION_INVALID        Specified slice direction, %d, is invalid.
+psImage_PARAMETER_OUTOF_TYPERANGE      Specified %s value, %g, is outside of psImage type's range (%s: %g to %g).
+psImage_nSamples_TOOSMALL              Specified number of samples, %d, must be greater than 1 to make a line.
+psImage_LINE_NOT_IN_IMAGE              Specified line, (%f,%f)->(%f,%f), does not entirely lie in psImage's boundaries, [0:%d,0:%d].
+psImage_RADII_VECTOR_NULL              Specified radii vector can not be NULL.
+psImage_CENTER_NOT_IN_IMAGE            Specified center, (%g,%g), is outside of the psImage boundaries, [0:%d,0:%d].
+psImage_RADII_VECTOR_TOOSMALL          Input radii vector size, %d, can not be less than 2.
+#
+psImageFFT_IMAGE_TYPE_UNSUPPORTED      Input psImage type (%s) is not supported. Valid image types are psF32 and psC32.
+psImageFFT_REVERSE_NOT_COMPLEX         Input psImage (%s) is not complex.  Reverse FFT operation requires a complex psImage input.
+psImageFFT_FORWARD_NOT_REAL            Input psImage (%s) is not real.  Forward FFT operation requires a real psImage input.
+psImageFFT_FFTW_PLAN_NULL              Could not create a valid FFT plan to perform the transform.
+psImageFFT_REAL_IMAG_TYPE_MISMATCH     Real psImage type (%s) and imaginary psImage type (%s) must be the same.
+psImageFFT_REAL_IMAG_SIZE_MISMATCH     Real psImage size (%dx%d) and imaginary psImage size (%dx%d) must be the same.
+psImageFFT_NONREAL_NOTSUPPORTED        Input psImage type, %s, is required to be either psF32 or psF64.
+psImageFFT_NONCOMPLEX_NOTSUPPORTED     Input psImage type, %s, is required to be either psC32 or psC64.
+psImageFFT_REAL_FORWARD_NOTSUPPORTED   The PS_FFT_FORWARD and PS_FFT_REAL_RESULT combinition is not supported.
+psImageFFT_FORWARD_REVERSE             Can not specify both PS_FFT_FORWARD and PS_FFT_REVERSE options.
+psImageFFT_NO_DIRECTION_OPTION         Must specify either PS_FFT_FORWARD or PS_FFT_REVERSE option.
+#
+psImageIO_FILENAME_NULL                Specified filename can not be NULL.
+psImageIO_FILENAME_INVALID             Could not open file,'%s'.\nCFITSIO Error: %s
+psImageIO_EXTNAME_INVALID              Could not find HDU with extension name '%s' in file %s.\nCFITSIO Error: %s
+psImageIO_EXTNUM_INVALID               Could not find HDU #%d in file %s.\nCFITSIO Error: %s
+psImageIO_DATATYPE_UNKNOWN             Could not determine image data type for file %s.\nCFITSIO Error: %s
+psImageIO_IMAGE_DIM_UNKNOWN            Could not determine image dimensions for file %s.\nCFITSIO Error: %s
+psImageIO_IMAGE_SIZE_UNKNOWN           Could not determine image size for file %s.\nCFITSIO Error: %s
+psImageIO_IMAGE_DIMENSION_UNSUPPORTED  Image number of dimensions, %d, is not valid.  Only two or three dimensions supported for FITS I/O.
+psImageIO_FITS_TYPE_UNSUPPORTED        FITS image type, BITPIX=%d, in file %s is not supported.
+psImageIO_READ_FAILED                  Reading from FITS file %s failed.\nCFITSIO Error: %s
+psImageIO_TYPE_UNSUPPORTED             Input psImage type, %s, is not supported.
+psImageIO_WRITE_EXTNUM_INVALID         Specified extension number, %d, must not exceed number of HDUs, %d, by more than one.
+psImageIO_FILENAME_CREATE_FAILED       Could not create file,'%s'.\nCFITSIO Error: %s
+psImageIO_CREATE_EXTENSION_FAILED      Could not create EXTNAME keyword for file,'%s'.\nCFITSIO Error: %s
+psImageIO_CREATE_HDU_FAILED            Could not create HDU for writing psImage in file,'%s'.\nCFITSIO Error: %s
+psImageIO_WRITE_FAILED                 Could not write psImage data to file,'%s'.\nCFITSIO Error: %s
+#
+psImageManip_MAXMIN                    Specified min value, %g, can not be greater than the specified max value, %g.
+psImageManip_MAXMIN_REAL               Specified real-portion of min value, %g, can not be greater than the real-portion of max value, %g.
+psImageManip_MAXMIN_IMAG               Specified imaginary-portion of min value, %g, can not be greater than the imaginary-portion of max value, %g.
+psImageManip_OPERATION_NULL            Operation can not be NULL.
+psImageManip_OVERLAY_TYPE_MISMATCH     Input overlay psImage type, %s, must match input psImage type, %s.
+psImageManip_CLIP_VALUE_INVALID        Specified %s value, %g%+gi, is not the the range of input psImage's valid pixel values (%s), i.e. [%g:%g].
+psImageManip_OVERLAY_OPERATOR_INVALID  Specified operation, '%s', is not supported.
+psImageManip_SCALE_NOT_POSITIVE        Specified scale value, %d, must be a positive value.
+psImageManip_INTERPOLATION_MODE_UNSUPPORTED Specified interpolation mode, %d, is unsupported.
+psImageConvolve_SHIFT_NULL             Specified shift vectors can not be NULL.
+psImageConvolve_KERNEL_NULL            Specified psKernel can not be NULL.
+psImageConvolve_SHIFT_TYPE_MISMATCH    Input t-, x-, and y-shift vector types (%s/%s/%s) must match.
+psImageConvolve_FFT_FAILED             Failed to perform a fourier transform of input image.
+psImageConvolve_KERNEL_FFT_FAILED      Failed to perform a fourier transform of kernel.
+psImageConvolve_KERNEL_TOO_LARGE       Specified psKernel size, %dx%d, can not be larger than input psImage size, %dx%d.
+psImage_COEFF_NULL                     Polynomial coefficients cannot be NULL.
+psImageManip_TRANSFORM_NULL            Specified input transform can not be NULL.
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageErrors.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageErrors.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageErrors.h	(revision 22271)
@@ -0,0 +1,101 @@
+/** @file  psImageErrors.h
+ *
+ *  @brief Contains the error text for the image functions
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_ERRORS_H
+#define PS_IMAGE_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psImageErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psImageErrors.dat lines)
+ *     $2  The error text (rest of the line in psImageErrors.dat)
+ *     $n  The order of the source line in psImageErrors.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_psImage_AREA_NEGATIVE "Specified number of rows (%d) or columns (%d) is invalid."
+#define PS_ERRORTEXT_psImage_NOT_AN_IMAGE "The input psImage must have a PS_DIMEN_IMAGE dimension type."
+#define PS_ERRORTEXT_psImage_IMAGE_NULL "Can not operate on a NULL psImage."
+#define PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED "Specified psImage type, %s, is not supported."
+#define PS_ERRORTEXT_psImage_INTERPOLATE_METHOD_INVALID "Specified interpolation method (%d) is not supported."
+#define PS_ERRORTEXT_psImage_REGION_NULL "Specified psRegion is NULL.  Operation could not be performed."
+#define PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID "Specified subset range, [%d:%d,%d:%d], is invalid or outside input psImage's boundaries, [0:%d,0:%d]."
+#define PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED "Specified subset range, [%d:%d,%d:%d], is invalid.  Ranges must be incremental."
+#define PS_ERRORTEXT_psImage_SUBSECTION_NULL "Specified subsection string can not be NULL."
+#define PS_ERRORTEXT_psImage_SUBSECTION_INVALID "Specified subsection string, '%s', can not be parsed.  Must be in the form '[x1:x2,y1:y2]'."
+#define PS_ERRORTEXT_psImage_NOT_PARENT "Specified psImage can not be a child of another psImage."
+#define PS_ERRORTEXT_psImage_INPLACE_NOTSUPPORTED "Specified input and output psImage can not reference the same psImage."
+#define PS_ERRORTEXT_psImage_SUBSET_ZERO_SIZE "Specified subset, [%d:%d,%d:%d], contains no pixel data."
+#define PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE "Input psImage mask size, %dx%d, does not match psImage input size, %dx%d."
+#define PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE "Input psImage mask type, %s, is not the supported mask datatype of %s."
+#define PS_ERRORTEXT_psImage_BAD_STAT "Specified statistic option, %d, is not valid.  Must specify one and only one statistic type."
+#define PS_ERRORTEXT_psImage_NO_STAT_OPTIONS "Specified statistic option did not indicate any operation to perform."
+#define PS_ERRORTEXT_psImage_STAT_NULL "Specified statistic can not be NULL."
+#define PS_ERRORTEXT_psImage_SLICE_DIRECTION_INVALID "Specified slice direction, %d, is invalid."
+#define PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE "Specified %s value, %g, is outside of psImage type's range (%s: %g to %g)."
+#define PS_ERRORTEXT_psImage_nSamples_TOOSMALL "Specified number of samples, %d, must be greater than 1 to make a line."
+#define PS_ERRORTEXT_psImage_LINE_NOT_IN_IMAGE "Specified line, (%f,%f)->(%f,%f), does not entirely lie in psImage's boundaries, [0:%d,0:%d]."
+#define PS_ERRORTEXT_psImage_RADII_VECTOR_NULL "Specified radii vector can not be NULL."
+#define PS_ERRORTEXT_psImage_CENTER_NOT_IN_IMAGE "Specified center, (%g,%g), is outside of the psImage boundaries, [0:%d,0:%d]."
+#define PS_ERRORTEXT_psImage_RADII_VECTOR_TOOSMALL "Input radii vector size, %d, can not be less than 2."
+#define PS_ERRORTEXT_psImageFFT_IMAGE_TYPE_UNSUPPORTED "Input psImage type (%s) is not supported. Valid image types are psF32 and psC32."
+#define PS_ERRORTEXT_psImageFFT_REVERSE_NOT_COMPLEX "Input psImage (%s) is not complex.  Reverse FFT operation requires a complex psImage input."
+#define PS_ERRORTEXT_psImageFFT_FORWARD_NOT_REAL "Input psImage (%s) is not real.  Forward FFT operation requires a real psImage input."
+#define PS_ERRORTEXT_psImageFFT_FFTW_PLAN_NULL "Could not create a valid FFT plan to perform the transform."
+#define PS_ERRORTEXT_psImageFFT_REAL_IMAG_TYPE_MISMATCH "Real psImage type (%s) and imaginary psImage type (%s) must be the same."
+#define PS_ERRORTEXT_psImageFFT_REAL_IMAG_SIZE_MISMATCH "Real psImage size (%dx%d) and imaginary psImage size (%dx%d) must be the same."
+#define PS_ERRORTEXT_psImageFFT_NONREAL_NOTSUPPORTED "Input psImage type, %s, is required to be either psF32 or psF64."
+#define PS_ERRORTEXT_psImageFFT_NONCOMPLEX_NOTSUPPORTED "Input psImage type, %s, is required to be either psC32 or psC64."
+#define PS_ERRORTEXT_psImageFFT_REAL_FORWARD_NOTSUPPORTED "The PS_FFT_FORWARD and PS_FFT_REAL_RESULT combinition is not supported."
+#define PS_ERRORTEXT_psImageFFT_FORWARD_REVERSE "Can not specify both PS_FFT_FORWARD and PS_FFT_REVERSE options."
+#define PS_ERRORTEXT_psImageFFT_NO_DIRECTION_OPTION "Must specify either PS_FFT_FORWARD or PS_FFT_REVERSE option."
+#define PS_ERRORTEXT_psImageIO_FILENAME_NULL "Specified filename can not be NULL."
+#define PS_ERRORTEXT_psImageIO_FILENAME_INVALID "Could not open file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_EXTNAME_INVALID "Could not find HDU with extension name '%s' in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_EXTNUM_INVALID "Could not find HDU #%d in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_DATATYPE_UNKNOWN "Could not determine image data type for file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_IMAGE_DIM_UNKNOWN "Could not determine image dimensions for file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_IMAGE_SIZE_UNKNOWN "Could not determine image size for file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_IMAGE_DIMENSION_UNSUPPORTED "Image number of dimensions, %d, is not valid.  Only two or three dimensions supported for FITS I/O."
+#define PS_ERRORTEXT_psImageIO_FITS_TYPE_UNSUPPORTED "FITS image type, BITPIX=%d, in file %s is not supported."
+#define PS_ERRORTEXT_psImageIO_READ_FAILED "Reading from FITS file %s failed.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_TYPE_UNSUPPORTED "Input psImage type, %s, is not supported."
+#define PS_ERRORTEXT_psImageIO_WRITE_EXTNUM_INVALID "Specified extension number, %d, must not exceed number of HDUs, %d, by more than one."
+#define PS_ERRORTEXT_psImageIO_FILENAME_CREATE_FAILED "Could not create file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_CREATE_EXTENSION_FAILED "Could not create EXTNAME keyword for file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_CREATE_HDU_FAILED "Could not create HDU for writing psImage in file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageIO_WRITE_FAILED "Could not write psImage data to file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psImageManip_MAXMIN "Specified min value, %g, can not be greater than the specified max value, %g."
+#define PS_ERRORTEXT_psImageManip_MAXMIN_REAL "Specified real-portion of min value, %g, can not be greater than the real-portion of max value, %g."
+#define PS_ERRORTEXT_psImageManip_MAXMIN_IMAG "Specified imaginary-portion of min value, %g, can not be greater than the imaginary-portion of max value, %g."
+#define PS_ERRORTEXT_psImageManip_OPERATION_NULL "Operation can not be NULL."
+#define PS_ERRORTEXT_psImageManip_OVERLAY_TYPE_MISMATCH "Input overlay psImage type, %s, must match input psImage type, %s."
+#define PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID "Specified %s value, %g%+gi, is not the the range of input psImage's valid pixel values (%s), i.e. [%g:%g]."
+#define PS_ERRORTEXT_psImageManip_OVERLAY_OPERATOR_INVALID "Specified operation, '%s', is not supported."
+#define PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE "Specified scale value, %d, must be a positive value."
+#define PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED "Specified interpolation mode, %d, is unsupported."
+#define PS_ERRORTEXT_psImageConvolve_SHIFT_NULL "Specified shift vectors can not be NULL."
+#define PS_ERRORTEXT_psImageConvolve_KERNEL_NULL "Specified psKernel can not be NULL."
+#define PS_ERRORTEXT_psImageConvolve_SHIFT_TYPE_MISMATCH "Input t-, x-, and y-shift vector types (%s/%s/%s) must match."
+#define PS_ERRORTEXT_psImageConvolve_FFT_FAILED "Failed to perform a fourier transform of input image."
+#define PS_ERRORTEXT_psImageConvolve_KERNEL_FFT_FAILED "Failed to perform a fourier transform of kernel."
+#define PS_ERRORTEXT_psImageConvolve_KERNEL_TOO_LARGE "Specified psKernel size, %dx%d, can not be larger than input psImage size, %dx%d."
+#define PS_ERRORTEXT_psImage_COEFF_NULL "Polynomial coefficients cannot be NULL."
+#define PS_ERRORTEXT_psImageManip_TRANSFORM_NULL "Specified input transform can not be NULL"
+//~End
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageExtraction.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageExtraction.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageExtraction.c	(revision 22271)
@@ -0,0 +1,812 @@
+/** @file  psImageExtraction.c
+ *
+ *  @brief Contains basic image extraction operations, as specified in the 
+ *         PSLIB SDRS sections "Image Pixel Extractions" and "Image Structure
+ *         Manipulation".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.36 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-10 01:57:47 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <string.h>
+
+#include "psMemory.h"
+#include "psImageExtraction.h"
+#include "psError.h"
+
+#include "psImageErrors.h"
+
+static psImage* imageSubset(psImage* out,
+                            psImage* image,
+                            psS32 col0,
+                            psS32 row0,
+                            psS32 col1,
+                            psS32 row1)
+{
+    psU32 elementSize;          // size of image element in bytes
+    psU32 inputColOffset;       // offset in bytes to first subset pixel in input row
+
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (image->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 = image->numCols + col1;
+    }
+    if (row1 < 1) {
+        row1 = image->numRows + row1;
+    }
+
+    if (    col1 <= col0 ||
+            row1 <= row0 ||
+            col0 >= image->numCols ||
+            row0 >= image->numRows ||
+            col1 > image->numCols ||
+            row1 > image->numRows ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        return NULL;
+    }
+    psS32 numRows = row1-row0;
+    psS32 numCols = col1-col0;
+
+    elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+
+    if (image->parent != NULL) { // if this is a child, we need to start working with parent.
+        col0 += image->col0;
+        col1 += image->col0;
+        row0 += image->row0;
+        row1 += image->row0;
+        image = (psImage*)image->parent;
+    }
+
+    // increment the raw data buffer before freeing anything in the 'out'
+    psPtr rawData = psMemIncrRefCounter(image->rawDataBuffer);
+
+    if (out != NULL) {
+        // if a child, need to orphan (disassociate from parent) first
+        if (out->parent != NULL) {
+            psArrayRemove(out->parent->children,out); // remove from parent's knowledge
+            out->parent = NULL; // break link to parent
+        }
+
+        psFree(out->rawDataBuffer); // free the previous data reference
+    } else {
+        out = psAlloc(sizeof(psImage));
+        out->data.V = NULL;
+    }
+
+    out->data.V = psRealloc(out->data.V,sizeof(psPtr)*numRows); // resize row pointer array
+    *(psType*)&out->type = image->type;
+    *(psU32*)&out->numCols = numCols;
+    *(psU32*)&out->numRows = numRows;
+    *(psS32*)&out->row0 = row0;
+    *(psS32*)&out->col0 = col0;
+    out->parent = image;
+    out->children = NULL;
+    out->rawDataBuffer = rawData;
+
+    // set the new psImage's deallocator to the same as the input image
+    psMemSetDeallocator(out,psMemGetDeallocator(image));
+
+    inputColOffset = elementSize * col0;
+    for (psS32 row = 0; row < numRows; row++) {
+        out->data.V[row] = image->data.U8[row0 + row] + inputColOffset;
+    }
+
+    // add output image as a child of the input image.
+    psS32 n = 0;
+    psArray* children = image->children;
+    if (children == NULL) {
+        children = psArrayAlloc(16); // start with a reasonable size for growth
+    } else if (children->nalloc == children->n) { // full?
+        n = children->n;
+        children = psArrayRealloc(children,n*2); // double the array size
+    } else {
+        n = children->n;
+    }
+    children->data[n] = out;
+    children->n = n+1;
+    image->children = children; // push back any change (esp. if children==NULL before)
+
+    return (out);
+}
+
+psImage* psImageSubset(psImage* image,
+                       psS32 col0,
+                       psS32 row0,
+                       psS32 col1,
+                       psS32 row1)
+{
+    return imageSubset(NULL,image,col0,row0,col1,row1);
+}
+
+psImage* psImageSubsection(psImage* image,
+                           const char* section)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (section == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(section,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                section);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return imageSubset(NULL,image,col0,row0,col1+1,row1+1);
+}
+
+psImage* psImageTrim(psImage* image, psS32 col0, psS32 row0, psS32 col1, psS32 row1)
+{
+    if (image == NULL || image->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (image->parent != NULL) {
+        return imageSubset(image,
+                           (psImage*)image->parent,
+                           col0+image->col0,
+                           row0+image->row0,
+                           col1+image->col0,
+                           row1+image->row0);
+    }
+
+    if (col1 < 1) {
+        col1 += image->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += image->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > image->numCols ||
+            row1 > image->numRows ||
+            col0 >= col1 ||
+            row0 >= row1 ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1-1, row0, row1-1,
+                image->numCols-1, image->numRows-1);
+        psFree(image);
+        return NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psU32 elementSize = PSELEMTYPE_SIZEOF(image->type.type);
+    psU32 numCols = col1-col0;
+    psU32 numRows = row1-row0;
+    psU32 rowSize = elementSize*numCols;
+    psU32 colOffset = elementSize * col0;
+    psU8* imageData = image->rawDataBuffer;
+    for (psS32 row = row0; row < row1; row++) {
+        memmove(imageData,image->data.U8[row] + colOffset,rowSize);
+        imageData += rowSize;
+    }
+
+    *(psU32*)&image->numRows = numRows;
+    *(psU32*)&image->numCols = numCols;
+
+    // XXX: should I really resize the buffers?
+    image->data.V = psRealloc(image->data.V,sizeof(psPtr)*numRows);
+    image->rawDataBuffer = psRealloc(image->rawDataBuffer,rowSize*numRows);
+
+    image->data.V[0] = image->rawDataBuffer;
+    for (psS32 r = 1; r < numRows; r++) {
+        image->data.U8[r] = image->data.U8[r-1] + rowSize;
+    }
+
+    return (image);
+}
+
+psVector* psImageSlice(psVector* out,
+                       psVector* slicePositions,
+                       const psImage* restrict in,
+                       const psImage* restrict mask,
+                       psU32 maskVal,
+                       psS32 col0,
+                       psS32 row0,
+                       psS32 col1,
+                       psS32 row1,
+                       psImageCutDirection direction,
+                       const psStats* stats)
+{
+    double statVal;
+    psStats* myStats;
+    psElemType type;
+    psS32 inRows;
+    psS32 inCols;
+    psS32 delta = 1;
+    psF64* outData;
+
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (col1 < 1) {
+        col1 += in->numCols;
+    }
+
+    if (row1 < 1) {
+        row1 += in->numRows;
+    }
+
+    if (    col0 < 0 ||
+            row0 < 0 ||
+            col1 > in->numCols ||
+            row1 > in->numRows ||
+            col0 >= col1 ||
+            row0 >= row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, col1, row0, row1,
+                in->numCols, in->numRows);
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    inRows = in->numRows;
+    inCols = in->numCols;
+
+    if (direction == PS_CUT_X_NEG || direction == PS_CUT_Y_NEG) {
+        delta = -1;
+    }
+
+    if (mask != NULL) {
+        if (inRows != mask->numRows || inCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    inCols, inRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a
+    // single stats operation
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                PS_ERRORTEXT_psImage_BAD_STAT,stats->options);
+        psFree(out);
+        return NULL;
+    }
+    // since stats input is const, I need to
+    // create a 'scratch' stats struct
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    psS32 numCols = col1-col0;
+    psS32 numRows = row1-row0;
+
+    if (direction == PS_CUT_X_POS || direction == PS_CUT_X_NEG) {
+        psVector* imgVec = psVectorAlloc(numRows, type);
+        psVector* maskVec = NULL;
+        psMaskType* maskData = NULL;
+        psU32* outPosition = NULL;
+
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats is
+        // psStats.
+        out = psVectorRecycle(out, numCols, PS_TYPE_F64);
+        if (slicePositions != NULL) {
+            slicePositions = psVectorRecycle(slicePositions, numCols, PS_TYPE_U32);
+            outPosition = slicePositions->data.U32;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numCols - 1;
+            if (outPosition != NULL) {
+                outPosition += numCols - 1;
+            }
+        }
+
+        if (mask != NULL) {
+            maskVec = psVectorAlloc(numRows, mask->type.type);
+        }
+        #define PSIMAGE_CUT_VERTICAL(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            psMaskType* maskVecData = NULL; \
+            for (psS32 c=col0;c<col1;c++) { \
+                ps##TYPE *imgData = in->data.TYPE[row0] + c; \
+                ps##TYPE *imgVecData = imgVec->data.TYPE; \
+                if (maskVec != NULL) { \
+                    maskVecData = maskVec->data.U8; \
+                    maskData = (psMaskType* )(mask->data.U8[row0]) + c; \
+                } \
+                for (psS32 r=row0;r<row1;r++) { \
+                    *(imgVecData++) = *imgData; \
+                    imgData += inCols; \
+                    if (maskVecData != NULL) { \
+                        *(maskVecData++) = *maskData; \
+                        maskData += inCols; \
+                    } \
+                } \
+                myStats = psVectorStats(myStats,imgVec,NULL,maskVec,maskVal); \
+                (void)p_psGetStatValue(myStats,&statVal); \
+                *outData = statVal; \
+                if (outPosition != NULL) { \
+                    *outPosition = c; \
+                    outPosition += delta; \
+                } \
+                outData += delta; \
+            } \
+            break; \
+        }
+
+        switch (type) {
+            PSIMAGE_CUT_VERTICAL(U8);  // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U16);
+            PSIMAGE_CUT_VERTICAL(U32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(U64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S8);
+            PSIMAGE_CUT_VERTICAL(S16); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(S64); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(F32);
+            PSIMAGE_CUT_VERTICAL(F64);
+            PSIMAGE_CUT_VERTICAL(C32); // Not a requirement
+            PSIMAGE_CUT_VERTICAL(C64); // Not a requirement
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                out = NULL;
+            }
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else if (direction == PS_CUT_Y_POS || direction == PS_CUT_Y_NEG) {
+        // Cut in Y direction
+        psVector* imgVec = NULL;
+        psVector* maskVec = NULL;
+        psS32 elementSize = PSELEMTYPE_SIZEOF(type);
+        psU32* outPosition = NULL;
+
+        // fill in psVector to fake out the statistics functions.
+        imgVec = psAlloc(sizeof(psVector));
+        imgVec->type = in->type;
+        imgVec->n = *(int*)&imgVec->nalloc = numCols;
+        if (mask != NULL) {
+            maskVec = psAlloc(sizeof(psVector));
+            maskVec->type = mask->type;
+            maskVec->n = *(int*)&maskVec->nalloc = numCols;
+        }
+        // recycle output to make a proper sized/type output structure
+        // n.b. type is double as that is the type given for all stats in
+        // psStats.
+        out = psVectorRecycle(out, numRows, PS_TYPE_F64);
+        if (slicePositions != NULL) {
+            slicePositions = psVectorRecycle(slicePositions, numRows, PS_TYPE_U32);
+            outPosition = slicePositions->data.U32;
+        }
+        outData = out->data.F64;
+        if (delta < 0) {
+            outData += numRows-1;
+            if (outPosition != NULL) {
+                outPosition += numRows-1;
+            }
+        }
+
+        for (psS32 r = row0; r < row1; r++) {
+            // point the vector struct to the
+            // data to calculate the stats
+            imgVec->data.U8 = (psPtr )(in->data.U8[r] + col0 * elementSize);
+            if (maskVec != NULL) {
+                maskVec->data.U8 = (psPtr )(mask->data.U8[r] + col0 * sizeof(psMaskType));
+            }
+            myStats = psVectorStats(myStats, imgVec, NULL, maskVec, maskVal);
+            (void)p_psGetStatValue(myStats, &statVal);  // we know it works cause we tested it above
+            *outData = statVal;
+            if (outPosition != NULL) {
+                *outPosition = r;
+                outPosition += delta;
+
+            }
+            outData += delta;
+        }
+        psFree(imgVec);
+        psFree(maskVec);
+    } else { // don't know what the direction flag is
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SLICE_DIRECTION_INVALID,
+                direction);
+        psFree(out);
+        out = NULL;
+    }
+
+    psFree(myStats);
+
+    return out;
+}
+
+psVector* psImageCut(psVector* out,
+                     psVector* cutCols,
+                     psVector* cutRows,
+                     const psImage* in,
+                     const psImage* restrict mask,
+                     psU32 maskVal,
+                     float startCol,
+                     float startRow,
+                     float endCol,
+                     float endRow,
+                     psU32 nSamples,
+                     psImageInterpolateMode mode)
+{
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = in->numCols;
+    psS32 numRows = in->numRows;
+
+    if (nSamples < 2) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_nSamples_TOOSMALL,
+                nSamples);
+        psFree(out);
+        return NULL;
+    }
+
+    if (startCol < 0 || startCol >= numCols ||
+            startRow < 0 || startRow >= numRows ||
+            endCol < 0 || endCol >= numCols ||
+            endRow < 0 || endRow >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_LINE_NOT_IN_IMAGE,
+                startCol,startRow,endCol,endRow,
+                numCols-1,numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    numCols-1, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    //resize the vectors for the coordinate output
+    psF32* cutColsData = NULL;
+    psF32* cutRowsData = NULL;
+    if (cutCols != NULL) {
+        cutCols = psVectorRecycle(cutCols, nSamples, PS_TYPE_F32);
+        cutColsData = cutCols->data.F32;
+    }
+    if (cutRows != NULL) {
+        cutRows = psVectorRecycle(cutRows, nSamples, PS_TYPE_F32);
+        cutRowsData = cutRows->data.F32;
+    }
+
+    out = psVectorRecycle(out, nSamples, in->type.type);
+
+    float dX = (endCol - startCol) / (float)(nSamples-1);
+    float dY = (endRow - startRow) / (float)(nSamples-1);
+
+    #define LINEAR_CUT_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE* outData = out->data.TYPE; \
+        for (psS32 i = 0; i < nSamples; i++) { \
+            float x = startCol + (float)i*dX; \
+            float y = startRow + (float)i*dY; \
+            /* store off the location of the sample. */ \
+            if (cutColsData != NULL) { \
+                cutColsData[i] = x; \
+            } \
+            if (cutRowsData != NULL) { \
+                cutRowsData[i] = y; \
+            } \
+            outData[i] = psImagePixelInterpolate(in,x,y,mask,maskVal,0,mode); \
+        } \
+    } \
+    break;
+
+
+    switch (in->type.type) {
+        LINEAR_CUT_CASE(U8);
+        LINEAR_CUT_CASE(U16);
+        LINEAR_CUT_CASE(U32);
+        LINEAR_CUT_CASE(U64);
+        LINEAR_CUT_CASE(S8);
+        LINEAR_CUT_CASE(S16);
+        LINEAR_CUT_CASE(S32);
+        LINEAR_CUT_CASE(S64);
+        LINEAR_CUT_CASE(F32);
+        LINEAR_CUT_CASE(F64);
+        LINEAR_CUT_CASE(C32);
+        LINEAR_CUT_CASE(C64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psVector* psImageRadialCut(psVector* out,
+                           const psImage* in,
+                           const psImage* restrict mask,
+                           psU32 maskVal,
+                           float centerCol,
+                           float centerRow,
+                           const psVector* radii,
+                           const psStats* stats)
+{
+    double statVal;
+
+    /* check the parameters */
+
+    if (in == NULL || in->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 numCols = in->numCols;
+    psS32 numRows = in->numRows;
+
+    if (mask != NULL) {
+        if (numRows != mask->numRows || numCols != mask->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_SIZE,
+                    mask->numCols,mask->numRows,
+                    numCols, numRows);
+            psFree(out);
+            return NULL;
+        }
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            return NULL;
+        }
+    }
+
+    if (centerCol < 0 || centerCol >= numCols ||
+            centerRow < 0 || centerRow >= numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_CENTER_NOT_IN_IMAGE,
+                centerCol, centerRow,
+                numCols-1, numRows-1);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_RADII_VECTOR_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (radii->n < 2) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psImage_RADII_VECTOR_TOOSMALL,
+                radii->n);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    // verify that the stats struct specifies a
+    // single stats operation
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                PS_ERRORTEXT_psImage_BAD_STAT,
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    /* completed checking the parameters */
+
+    // size the output vector to proper size.
+    psS32 numOut = radii->n - 1;
+    out = psVectorRecycle(out, numOut, PS_TYPE_F64);
+    psF64* outData = out->data.F64;
+
+    psVector* rSqVec = psVectorCopy(NULL, radii, PS_TYPE_F32);
+    psF32* rSq = rSqVec->data.F32;
+
+    psS32 startRow = centerRow - rSq[numOut];
+    psS32 endRow = centerRow + rSq[numOut];
+    psS32 startCol = centerCol - rSq[numOut];
+    psS32 endCol = centerCol + rSq[numOut];
+
+    if (startRow < 0) {
+        startRow = 0;
+    }
+
+    if (startCol < 0) {
+        startCol = 0;
+    }
+
+    if (endRow >= numRows) {
+        endRow = numRows - 1;
+    }
+
+    if (endCol >= numCols) {
+        endCol = numCols - 1;
+    }
+
+    // Square the radii data
+    for (psS32 d = 0; d <= numOut; d++) {
+        rSq[d] *= rSq[d];
+    }
+
+    // create temporary vectors for the data binning step
+    psVector** buffer = psAlloc(sizeof(psVector*)*numOut);
+    psVector** bufferMask = psAlloc(sizeof(psVector*)*numOut);
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        // n.b. alloc enough for the data by making the vectors slightly larger
+        // than the area of the region of interest.
+        buffer[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                    in->type.type);
+        buffer[lcv]->n = 0;
+
+        bufferMask[lcv] = NULL;
+        if (mask != NULL) {
+            bufferMask[lcv] = psVectorAlloc(1+4*(rSq[lcv+1]-rSq[lcv]),
+                                            PS_TYPE_MASK);
+            bufferMask[lcv]->n = 0;
+        }
+    }
+
+    float dX;
+    float dY;
+    float dist;
+    for (psS32 row=startRow; row <= endRow; row++) {
+        psF32* inRow = in->data.F32[row];
+        psMaskType* maskRow = NULL;
+        if (mask != NULL) {
+            maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        }
+        for (psS32 col=startCol; col <= endCol; col++) {
+            dX = centerCol - (float)col - 0.5f;
+            dY = centerRow - (float)row - 0.5f;
+            dist = dX*dX+dY*dY;
+            for (psS32 r = 0; r < numOut; r++) {
+                if (rSq[r] < dist && dist < rSq[r+1]) {
+                    psS32 n = buffer[r]->n;
+                    if (n == buffer[r]->nalloc) { // in case buffers already full, expand
+                        buffer[r] = psVectorRealloc(buffer[r], n*2);
+                        if (bufferMask[r] != NULL) {
+                            bufferMask[r] = psVectorRealloc(bufferMask[r], n*2);
+                        }
+                    }
+
+                    buffer[r]->data.F32[n] = inRow[col];
+                    buffer[r]->n = n+1;
+
+                    if (maskRow != NULL) {
+                        bufferMask[r]->data.PS_TYPE_MASK_DATA[n] = maskRow[col];
+                        bufferMask[r]->n = n+1;
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    psStats* myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    for (psS32 r = 0; r < numOut; r++) {
+        myStats = psVectorStats(myStats,buffer[r], NULL, bufferMask[r],maskVal);
+        (void)p_psGetStatValue(myStats,&statVal);
+        outData[r] = statVal;
+    }
+
+    psFree(myStats);
+
+    for (psS32 lcv = 0; lcv < numOut; lcv++) {
+        psFree(buffer[lcv]);
+        psFree(bufferMask[lcv]);
+    }
+    psFree(buffer);
+    psFree(bufferMask);
+    psFree(rSqVec);
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageExtraction.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageExtraction.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageExtraction.h	(revision 22271)
@@ -0,0 +1,198 @@
+
+/** @file  psImageExtraction.h
+*
+*  @brief Contains basic image extraction operations, as specified in the 
+*         PSLIB SDRS sections "Image Pixel Extractions" and "Image Structure
+*         Manipulation".
+*
+*  @ingroup Image
+*
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:24 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PSIMAGEEXTRACTION_H
+#define PSIMAGEEXTRACTION_H
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psStats.h"
+
+/// @addtogroup Image
+/// @{
+
+/* Cut direction flag.  Used with psImageCut function.
+ */
+typedef enum {
+    PS_CUT_X_POS,                      ///< Cut in the x dimension from left to right
+    PS_CUT_X_NEG,                      ///< Cut in the x dimension from rigth to left
+    PS_CUT_Y_POS,                      ///< Cut in the y dimension from bottom up
+    PS_CUT_Y_NEG,                      ///< Cut in the y dimension from top down.
+} psImageCutDirection;
+
+/** Create a subimage of the specified area.
+ *
+ *  Extracts a subimage starting at (col0,row0) to (col1-1,row1-1).  Note: 
+ *  the column col1 and row row1 are NOT included in the resulting subimage.
+ *  In the event that x1 or y1 are non-positive, they shall be interpreted as
+ *  being relative to the size of the parent image in that dimension.
+ *
+ *  If the entire specified subimage is not contained within the parent 
+ *  image, an error results and the return value will be NULL.
+ *
+ *  The resulting psImage does not create a copy of the underlying image 
+ *  data.  Future changes in the parent may be reflecting in the child
+ *  subimage and vis versa.
+ *
+ *  @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageSubset(
+    psImage* image,                    ///< Parent image.
+    psS32 col0,                          ///< starting column of subimage
+    psS32 row0,                          ///< starting row of subimage
+    psS32 col1,                          ///< exclusive end column of subimage.
+    psS32 row1                           ///< exclusive end row of subimage
+);
+
+/** Create a subimage of the specified area.
+ *
+ * Uses psLib memory allocation functions to create an image based on a larger
+ * one.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageSubsection(
+    psImage* image,                    ///< Parent image.
+    const char* section                ///< Subsection in the form '[x1:x2,y1:y2]'
+);
+
+/** Trim an image
+ *
+ *  Trim the specified image in-place, which involves shuffling the pixels 
+ *  around in memory.  The pixels in the region [col0:col1,row0:row1] shall consist
+ *  the output image.  The column col1 and row row1 are NOT included in the range.
+ *  In the event that x1 or y1 are non-positive, they shall be interpreted as
+ *  being relative to the size of the parent image in that dimension.
+ *
+ *  If the entire specified subimage is not contained within the parent 
+ *  image, an error results and the return value will be NULL.
+ *
+ *  N.B. If the input psImage is a child of another psImage, no pixel data 
+ *  will be trimmed, rather it equivalent to calling psImageSubset.  If the input 
+ *  psImage is, however, a parent psImage, any children will be obliterated,
+ *  i.e., freed from memory.
+ *
+ *  @return psImage*  trimmed image result
+ */
+psImage* psImageTrim(
+    psImage* image,                    ///< image to trim
+    psS32 col0,                          ///< column of trim region's left boundary
+    psS32 row0,                          ///< row of trim region's lower boundary
+    psS32 col1,                          ///< column of trim region's right boundary
+    psS32 row1                           ///< row of trim region's upper boundary
+);
+
+/** Extract pixels from rectlinear region to a vector (array of floats).
+ *
+ *  The output vector contains either col1-col0 or row1-row0 elements, based 
+ *  on the value of the direction: e.g., if direction is PS_CUT_X_POS, there 
+ *  are col1-col0 elements. The region to be  sliced  is defined by the 
+ *  lower-left corner, (col0,row0), and the upper-right corner, (col1,row1). 
+ *  Note that the row and column of the  upper right-hand corner  are NOT 
+ *  included in the region. In the event that col1 or row1 are negative, they
+ *  shall be interpreted as being relative to the size of the parent image in 
+ *  that dimension. The input region is collapsed in the direction perpendicular 
+ *  to that specified by direction, and each element of the output vectors is 
+ *  derived from the statistics of the pixels at that direction coordinate. The
+ *  statistic used to derive the output vector value is specified by stats. 
+ *  If mask is non-NULL, pixels for which the corresponding mask pixel 
+ *  matches maskVal are excluded from operations. If coords is not NULL, the 
+ *  calculated coordinates along the slice are returned in this vector. Only 
+ *  one of the statistics choices may be specified, otherwise the function 
+ *  must return an error.
+ *
+ *  This function is defined for the following types: psS8, psU16, psF32, psF64.
+ *
+ * @return psVector    the resulting vector
+ */
+psVector* psImageSlice(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* slicePositions,
+    ///< If not NULL, it is populated with the coordinate in the slice dimension
+    ///< coorsponding to the output vector's value of the same position in the
+    ///< vector.  This vector maybe resized and retyped as appropriate.
+    const psImage* input,              ///< the input image in which to perform the slice
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    psS32 col0,                        ///< the leftmost column of the slice region
+    psS32 row0,                        ///< the bottommost row of the slice region
+    psS32 col1,                        ///< exclusive end column of the slice region
+    psS32 row1,                        ///< exclusive end row of the slice region
+    psImageCutDirection direction,     ///< the slice dimension and direction
+    const psStats* stats               ///< the statistic to perform in slice operation
+);
+
+/** Extract pixels from an image along a line to a vector (array of floats).
+ *
+ *  The vector (xs,ys) - (xe,ye) forms the basis of the output vector. Pixels 
+ *  are considered in a rectangular region of width dw about this vector. The 
+ *  input region is collapsed in the perpendicular direction, and each element 
+ *  of the output vector represents pixel-sized boxes, where the value is 
+ *  derived from the statistics of the pixels interpolated along the 
+ *  perpendicular direction. The specific algorithm which must be used is 
+ *  described in the PSLib ADD (PSDC-430-006). The statistic used to derive 
+ *  the output vector value is specified by stats. Only one of the statistics 
+ *  choices may be specified, otherwise the function must return an error. 
+ *  This function must be defined for the following types: psS8, psU16, psF32, 
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    psVector* cutCols,                 ///< if not NULL, the calculated column values along the slice (output)
+    psVector* cutRows,                 ///< if not NULL, the calculated row values along the slice (output)
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    float startCol,                    ///< the column of the start of the cut line
+    float startRow,                    ///< the row of the start of the cut line
+    float endCol,                      ///< the column of the end of the cut line
+    float endRow,                      ///< the row of the end of the cut line
+    psU32 nSamples,                    ///< the number of samples along the cut
+    psImageInterpolateMode mode        ///< the interpolation method to use
+);
+
+/** Extract radial region data to a vector. A vector is constructed where each
+ *  vector elements is derived from the statistics of the pixels which land 
+ *  within one of a sequence of radii. The radii are centered on the image 
+ *  pixel coordinate x,y, and are defined by the sequence of values in the
+ *  vector radii. The specific algorithm which must be used is described in 
+ *  the PSLib ADD (PSDC-430-006). The statistic used to derive the output 
+ *  vector value is specified by stats. Only one of the statistics choices 
+ *  may be specified, otherwise the function must return an error. This 
+ *  function must be defined for the following types: psS8, psU16, psF32, 
+ *  psF64.
+ *
+ *  @return psVector    resulting vector
+ */
+psVector* psImageRadialCut(
+    psVector* out,                     ///< psVector to recycle, or NULL.
+    const psImage* input,              ///< the input image in which to perform the cut
+    const psImage* mask,               ///< the mask for the input image.
+    psU32 maskVal,                     ///< the mask value to apply to the mask
+    float centerCol,                   ///< the column of the center of the cut circle
+    float centerRow,                   ///< the row of the center of the cut circle
+    const psVector* radii,             ///< the radii of the cut circle
+    const psStats* stats               ///< the statistic to perform in operation
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageFFT.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageFFT.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageFFT.c	(revision 22271)
@@ -0,0 +1,455 @@
+/** @file  psImageFFT.c
+ *
+ *  @brief Contains FFT transform related functions for psImage.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-15 00:12:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <unistd.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+
+#include "psImageFFT.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psImageExtraction.h"
+#include "psImageIO.h"
+
+#include "psImageErrors.h"
+
+#define PS_FFTW_PLAN_RIGOR FFTW_ESTIMATE
+
+static psBool p_fftwWisdomImported = false;
+
+psImage* psImageFFT(psImage* out, const psImage* in, psFFTFlags direction)
+{
+    psU32 numCols;
+    psU32 numRows;
+    psElemType type;
+    fftwf_plan plan;
+
+    /* got good image data? */
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    if ( ((direction & PS_FFT_FORWARD) != 0) ) {
+        if ((direction & PS_FFT_REVERSE) != 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageFFT_FORWARD_REVERSE);
+            psFree(out);
+            return NULL;
+        }
+        if ((direction & PS_FFT_REAL_RESULT) != 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageFFT_REAL_FORWARD_NOTSUPPORTED);
+            psFree(out);
+            return NULL;
+        }
+    } else if ((direction & PS_FFT_REVERSE) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageFFT_NO_DIRECTION_OPTION);
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+
+    /* make sure the system-level wisdom information is imported. */
+    if (!p_fftwWisdomImported) {
+        fftwf_import_system_wisdom();
+        p_fftwWisdomImported = true;
+    }
+
+    numRows = in->numRows;
+    numCols = in->numCols;
+
+    // n.b. FFTW can perform a in-place transform at the same rate or faster than out-of-place.
+    psS32 sign = ((direction & PS_FFT_FORWARD) != 0) ? FFTW_FORWARD : FFTW_BACKWARD;
+
+    fftwf_complex* outBuffer = fftwf_malloc(numRows*numCols*sizeof(fftwf_complex));
+    p_psImageCopyToRawBuffer(outBuffer, in, PS_TYPE_C32);
+
+    plan = fftwf_plan_dft_2d(numRows, numCols,
+                             outBuffer,
+                             outBuffer,
+                             sign,
+                             PS_FFTW_PLAN_RIGOR);
+
+    /* check if a plan exists now -- if not, it is a real problem at this point */
+    if (plan == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_FFTW_PLAN_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    /* finally, call FFTW with the plan made above */
+    fftwf_execute(plan);
+
+    fftwf_destroy_plan(plan);
+
+    if (direction & PS_FFT_REAL_RESULT) {
+        // n.b., we do this instead of using fftwf_plan_dft_c2r because that
+        // plan requires a half-image, which would require a image reordering
+        // that is not as simple as performing a normal complex transform and
+        // then taking the real part of the result.  If performance here
+        // becomes an issue, the use of fftwf_plan_dft_r2c should be considered
+        // as well as fftwf_plan_dft_c2r.
+        out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32);
+        int index = 0;
+        for (int row=0; row < numRows; row++) {
+            psF32* outRow = out->data.F32[row];
+            for (int col=0; col < numCols; col++) {
+                outRow[col] = crealf(outBuffer[index++]); // take just the real part
+            }
+        }
+    } else {
+        out = psImageRecycle(out,numCols,numRows,PS_TYPE_C32);
+        int index = 0;
+        for (int row=0; row < numRows; row++) {
+            psC32* outRow = out->data.C32[row];
+            for (int col=0; col < numCols; col++) {
+                outRow[col] = outBuffer[index++]; // take just the real part
+            }
+        }
+        //        memcpy(out->rawDataBuffer, outBuffer, numRows*numCols*sizeof(fftwf_complex));
+    }
+
+    fftwf_free(outBuffer);
+
+    return out;
+
+}
+
+psImage* psImageReal(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    /* if not a complex number, this is logically just a copy then */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        return psImageCopy(out, in, type);
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outRow;
+        psC32* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = crealf(inRow[col]);
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outRow;
+        psC64* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = creal(inRow[col]);
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                typeStr);
+
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psImage* psImageImaginary(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    /* if not a complex image type, this is logically just zeroed image of same size */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        out = psImageRecycle(out, numCols, numRows, type);
+        memset(out->data.V[0], 0, PSELEMTYPE_SIZEOF(type) * numCols * numRows);
+        return out;
+    }
+
+    if (type == PS_TYPE_C32) {
+        psF32* outRow;
+        psC32* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = cimagf(inRow[col]);
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outRow;
+        psC64* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = cimag(inRow[col]);
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psImage* psImageComplex(psImage* out, const psImage* real, const psImage* imag)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (real == NULL || imag == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = real->type.type;
+    numCols = real->numCols;
+    numRows = real->numRows;
+
+    if (imag->type.type != type) {
+        char* typeStrReal;
+        char* typeStrImag;
+        PS_TYPE_NAME(typeStrReal,type);
+        PS_TYPE_NAME(typeStrImag,imag->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_REAL_IMAG_TYPE_MISMATCH,
+                typeStrReal,typeStrImag);
+        psFree(out);
+        return NULL;
+    }
+
+    if (imag->numCols != numCols || imag->numRows != numRows) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageFFT_REAL_IMAG_SIZE_MISMATCH,
+                numCols, numRows, imag->numCols, imag->numRows);
+        psFree(out);
+        return NULL;
+    }
+
+    if (type == PS_TYPE_F32) {
+        psC32* outRow;
+        psF32* realRow;
+        psF32* imagRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C32);
+
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C32[row];
+            realRow = real->data.F32[row];
+            imagRow = imag->data.F32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = realRow[col] + I * imagRow[col];
+            }
+        }
+    } else if (type == PS_TYPE_F64) {
+        psC64* outRow;
+        psF64* realRow;
+        psF64* imagRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C64[row];
+            realRow = real->data.F64[row];
+            imagRow = imag->data.F64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = realRow[col] + I * imagRow[col];
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_NONREAL_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+
+    return out;
+}
+
+psImage* psImageConjugate(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    /* if not a complex image, this is logically just a image copy */
+    if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {
+        return psImageCopy(out, in, type);
+    }
+
+    if (type == PS_TYPE_C32) {
+        psC32* outRow;
+        psC32* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = crealf(inRow[col]) - I * cimagf(inRow[col]);
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psC64* outRow;
+        psC64* inRow;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.C64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                outRow[col] = creal(inRow[col]) - I * cimag(inRow[col]);
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+}
+
+psImage* psImagePowerSpectrum(psImage* out, const psImage* in)
+{
+    psElemType type;
+    psU32 numCols;
+    psU32 numRows;
+
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    type = in->type.type;
+    numCols = in->numCols;
+    numRows = in->numRows;
+
+    if (type == PS_TYPE_C32) {
+        psF32* outRow;
+        psC32* inRow;
+        psF32 real;
+        psF32 imag;
+        psF32 numElementsSquared = numCols * numCols * numRows * numRows;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F32[row];
+            inRow = in->data.C32[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                real = crealf(inRow[col]);
+                imag = cimagf(inRow[col]);
+                outRow[col] = (real * real + imag * imag) / numElementsSquared;
+            }
+        }
+    } else if (type == PS_TYPE_C64) {
+        psF64* outRow;
+        psC64* inRow;
+        psF64 real;
+        psF64 imag;
+        psF64 numElementsSquared = numCols * numCols * numRows * numRows;
+
+        out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64);
+        for (psU32 row = 0; row < numRows; row++) {
+            outRow = out->data.F64[row];
+            inRow = in->data.C64[row];
+
+            for (psU32 col = 0; col < numCols; col++) {
+                real = creal(inRow[col]);
+                imag = cimag(inRow[col]);
+                outRow[col] = (real * real + imag * imag) / numElementsSquared;
+            }
+        }
+    } else {
+        char* typeStr;
+        PS_TYPE_NAME(typeStr,type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageFFT_NONCOMPLEX_NOTSUPPORTED,
+                typeStr);
+        psFree(out);
+        return NULL;
+    }
+
+    return out;
+
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageFFT.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageFFT.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageFFT.h	(revision 22271)
@@ -0,0 +1,87 @@
+/** @file  psImageFFT.h
+ *
+ *  @brief Contains FFT transform related functions for psImage
+ *
+ *  @ingroup Transform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_FFT_H
+#define PS_IMAGE_FFT_H
+
+#include "psImage.h"
+#include "psVectorFFT.h"               // for psFFTFlags
+
+/// @addtogroup Transform
+/// @{
+
+/** Forward and reverse FFT calculations.
+ *
+ *  This takes as input the image of interest (in) and the direction 
+ *  (direction), which is specified by an enumerated type psFftDirection.
+ *  The input image may be of type psF32 or psC32, the result is always 
+ *  psC32. If the input vector is psF32, the direction must be forward. 
+ *  
+ *  @return psImage* the FFT transformation result
+ */
+psImage* psImageFFT(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in,                 ///< the psImage to apply transform to
+    psFFTFlags direction               ///< the direction of the transform
+);
+
+/** extract the real portion of a complex image
+ * 
+ *  @return psImage*   real portion of the input image.
+ */
+psImage* psImageReal(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                  ///< the psImage to extract real portion from
+);
+
+/** extract the imaginary portion of a complex image
+ * 
+ *  @return psImage*   imaginary portion of the input image.
+ */
+psImage* psImageImaginary(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                  ///< the psImage to extract imaginary portion from
+);
+
+/** creates a complex image from separate real and imaginary plane images
+ * 
+ *  @return psImage*   resulting complex image
+ */
+psImage* psImageComplex(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* real,               ///< the real plane image
+    const psImage* imag                ///< the imaginary plane image
+);
+
+/** computes the complex conjugate of an image
+ * 
+ *  @return psImage*   the complex conjugate of the 'in' image
+ */
+psImage* psImageConjugate(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                  ///< the psImage to compute conjugate of
+);
+
+/** computes the power spectrum of an image
+ * 
+ *  @return psImage*   the power spectrum of the 'in' image
+ */
+psImage* psImagePowerSpectrum(
+    psImage* out,                       ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in                   ///< the psImage to power spectrum of
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageIO.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageIO.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageIO.c	(revision 22271)
@@ -0,0 +1,436 @@
+/** @file  psImageIO.c
+ *
+ *  @brief Contains image I/O routines.
+ *
+ *  This file defines the file input/output functions for the psImage structure.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-29 02:25:10 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <fitsio.h>
+#include <unistd.h>
+
+#include "psImageIO.h"
+#include "psError.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+
+#include "psImageErrors.h"
+
+psImage* psImageReadSection(psImage* output,
+                            psS32 col,
+                            psS32 row,
+                            psS32 numCols,
+                            psS32 numRows,
+                            psS32 z,
+                            char *extname,
+                            psS32 extnum,
+                            char *filename)
+{
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+    psS32 status = 0;           /* CFITSIO file vars */
+    psS32 nAxis = 0;
+    psS32 anynull = 0;
+    psS32 bitPix = 0;           /* Pixel type */
+    long nAxes[3];
+    long firstPixel[3];         /* lower-left corner of image subset */
+    long lastPixel[3];          /* upper-right corner of image subset */
+    long increment[3];          /* increment for image subset */
+    char fitsErr[80] = "";      /* CFITSIO error message string */
+    psS32 hduType = IMAGE_HDU;
+    psS32 fitsDatatype = 0;
+    psS32 datatype = 0;
+
+    psLogMsg(__func__,PS_LOG_WARN, "psImageReadSection is deprecated.  Consider using psFitsReadImage instead.");
+
+    if (filename == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageIO_FILENAME_NULL);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Open the FITS file */
+    (void)fits_open_file(&fptr, filename, READONLY, &status);
+    if (fptr == NULL || status != 0) {
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageIO_FILENAME_INVALID,
+                filename, fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* find the specified extension */
+    if (extname != NULL) {
+        if (fits_movnam_hdu(fptr, hduType, extname, 0, &status) != 0) {
+            fits_get_errstatus(status, fitsErr);
+            status = 0;
+            (void)fits_close_file(fptr, &status);
+            psError(PS_ERR_LOCATION_INVALID, true,
+                    PS_ERRORTEXT_psImageIO_EXTNAME_INVALID,
+                    extname, filename, fitsErr);
+            psFree(output);
+            return NULL;
+        }
+    } else {
+        if (fits_movabs_hdu(fptr, extnum + 1, &hduType, &status) != 0) {
+            fits_get_errstatus(status, fitsErr);
+            status = 0;
+            (void)fits_close_file(fptr, &status);
+            psError(PS_ERR_LOCATION_INVALID, true,
+                    PS_ERRORTEXT_psImageIO_EXTNUM_INVALID,
+                    extnum, filename, fitsErr);
+            psFree(output);
+            return NULL;
+        }
+    }
+
+    /* Get the data type 'bitPix' from the FITS image */
+    if (fits_get_img_equivtype(fptr, &bitPix, &status) != 0) {
+        fits_get_errstatus(status, fitsErr);
+        status = 0;
+        (void)fits_close_file(fptr, &status);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_DATATYPE_UNKNOWN,
+                filename, fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Get the dimensions 'nAxis' from the FITS image */
+    if (fits_get_img_dim(fptr, &nAxis, &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        status = 0;
+        (void)fits_close_file(fptr, &status);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_IMAGE_DIM_UNKNOWN,
+                filename, fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Validate the number of axis */
+    if ((nAxis < 2) || (nAxis > 3)) {
+        status = 0;
+        (void)fits_close_file(fptr, &status);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_IMAGE_DIMENSION_UNSUPPORTED,
+                nAxis);
+        psFree(output);
+        return NULL;
+    }
+
+    /* Get the Image size from the FITS file */
+    if (fits_get_img_size(fptr, nAxis, nAxes, &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        status = 0;
+        (void)fits_close_file(fptr, &status);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_IMAGE_SIZE_UNKNOWN,
+                filename, fitsErr);
+        psFree(output);
+        return NULL;
+    }
+
+    if (numCols < 1) {
+        numCols += nAxes[0] - col;
+    }
+    if (numRows < 1) {
+        numRows += nAxes[1] - row;
+    }
+
+    firstPixel[0] = col + 1;
+    firstPixel[1] = row + 1;
+    firstPixel[2] = z + 1;
+
+    lastPixel[0] = firstPixel[0] + numCols - 1;
+    lastPixel[1] = firstPixel[1] + numRows - 1;
+    lastPixel[2] = z + 1;
+
+    increment[0] = 1;
+    increment[1] = 1;
+    increment[2] = 1;
+
+    switch (bitPix) {
+    case BYTE_IMG:
+        datatype = PS_TYPE_U8;
+        fitsDatatype = TBYTE;
+        break;
+    case SBYTE_IMG:
+        datatype = PS_TYPE_S8;
+        fitsDatatype = TSBYTE;
+        break;
+    case USHORT_IMG:
+        datatype = PS_TYPE_U16;
+        fitsDatatype = TUSHORT;
+        break;
+    case SHORT_IMG:
+        datatype = PS_TYPE_S16;
+        fitsDatatype = TSHORT;
+        break;
+    case ULONG_IMG:
+        datatype = PS_TYPE_U32;
+        fitsDatatype = TUINT;
+        break;
+    case LONG_IMG:
+        datatype = PS_TYPE_S32;
+        fitsDatatype = TINT;
+        break;
+    case LONGLONG_IMG:
+        datatype = PS_TYPE_S64;
+        fitsDatatype = TLONGLONG;
+        break;
+    case FLOAT_IMG:
+        datatype = PS_TYPE_F32;
+        fitsDatatype = TFLOAT;
+        break;
+    case DOUBLE_IMG:
+        datatype = PS_TYPE_F64;
+        fitsDatatype = TDOUBLE;
+        break;
+    default:
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_FITS_TYPE_UNSUPPORTED,
+                bitPix, filename);
+        psFree(output);
+        return NULL;
+    }
+    output = psImageRecycle(output, numCols, numRows, datatype);
+    if (fits_read_subset(fptr, fitsDatatype, firstPixel, lastPixel, increment,
+                         NULL, output->data.V[0], &anynull, &status) != 0) {
+        psFree(output);
+        (void)fits_get_errstatus(status, fitsErr);
+        status = 0;
+        (void)fits_close_file(fptr, &status);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_READ_FAILED,
+                filename, fitsErr);
+        return NULL;
+    }
+
+    (void)fits_close_file(fptr, &status);
+
+    return output;
+}
+
+psBool psImageWriteSection(psImage* input,
+                           psS32 col0,
+                           psS32 row0,
+                           psS32 z,
+                           char *extname,
+                           psS32 extnum,
+                           char *filename)
+{
+    psS32 numCols = 0;
+    psS32 numRows = 0;
+
+    psS32 status = 0;             /* CFITSIO status */
+    fitsfile *fptr = NULL;      /* pointer to the FITS file */
+    long nAxes[3];              /* Image axis vars */
+    long firstPixel[3];         /* First Pixel to read */
+    long lastPixel[3];          /* Last Pixel to read */
+    char fitsErr[80];           /* FITSIO message string */
+    psS32 datatype = 0;           /* the datatype of the image */
+    psS32 bitPix = 0;             /* FITS bitPix value */
+    psS32 hduType = IMAGE_HDU;    /* the HDU type (image,table, etc.) */
+    double bscale = 1.0;
+    double bzero = 0.0;
+    psBool createNewHDU = false;
+
+    psLogMsg(__func__,PS_LOG_WARN, "psImageWriteSection is deprecated.  Consider using psFitsWriteImage instead.");
+
+    /* need a valid image to write */
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return false;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+    case PS_TYPE_U8:
+        bitPix = BYTE_IMG;
+        datatype = TBYTE;
+        break;
+    case PS_TYPE_S8:
+        bitPix = BYTE_IMG;
+        bzero = INT8_MIN;
+        datatype = TSBYTE;
+        break;
+    case PS_TYPE_U16:
+        bitPix = SHORT_IMG;
+        bzero = -1.0f * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+    case PS_TYPE_S16:
+        bitPix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+    case PS_TYPE_U32:
+        bitPix = LONG_IMG;
+        bzero = -1.0f * INT32_MIN;
+        datatype = TUINT;
+        break;
+    case PS_TYPE_S32:
+        bitPix = LONG_IMG;
+        datatype = TINT;
+        break;
+    case PS_TYPE_F32:
+        bitPix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+    case PS_TYPE_F64:
+        bitPix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImageIO_TYPE_UNSUPPORTED,
+                    typeStr);
+            return false;
+        }
+    }
+
+    /* Open the FITS file */
+    if (access(filename, F_OK) == 0) {     // file
+        // exists
+        (void)fits_open_file(&fptr, filename, READWRITE, &status);
+        if (fptr == NULL || status != 0) {
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageIO_FILENAME_INVALID,
+                    filename, fitsErr);
+            return false;
+        }
+
+        /* find the specified extension */
+        if (extname != NULL) {
+            if (fits_movnam_hdu(fptr, hduType, extname, 0, &status) != 0) {
+                fits_get_errstatus(status, fitsErr);
+                status = 0;
+                (void)fits_close_file(fptr, &status);
+                psError(PS_ERR_LOCATION_INVALID, true,
+                        PS_ERRORTEXT_psImageIO_EXTNAME_INVALID,
+                        extname, filename, fitsErr);
+                return false;
+            }
+        } else {
+            psS32 numHDUs = 0;
+
+            fits_get_num_hdus(fptr, &numHDUs, &status);
+            if (numHDUs < extnum) {
+                status = 0;
+                (void)fits_close_file(fptr, &status);
+                psError(PS_ERR_LOCATION_INVALID, true,
+                        PS_ERRORTEXT_psImageIO_WRITE_EXTNUM_INVALID,
+                        extnum, numHDUs);
+                return false;
+            } else if (numHDUs == extnum) {
+                createNewHDU = true;
+            } else if (fits_movabs_hdu(fptr, extnum + 1, &hduType, &status) != 0) {
+                fits_get_errstatus(status, fitsErr);
+                status = 0;
+                (void)fits_close_file(fptr, &status);
+                psError(PS_ERR_LOCATION_INVALID, true,
+                        PS_ERRORTEXT_psImageIO_EXTNUM_INVALID,
+                        extnum, filename, fitsErr);
+                return false;
+            }
+        }
+
+    } else {
+        // file does not exist
+
+        (void)fits_create_file(&fptr, filename, &status);
+        if (fptr == NULL || status != 0) {
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psImageIO_FILENAME_CREATE_FAILED,
+                    filename, fitsErr);
+            return false;
+        }
+        createNewHDU = true;
+    }
+
+    if (createNewHDU) {
+        /* create the mandatory image keywords */
+        nAxes[0] = col0 + numCols;
+        nAxes[1] = row0 + numRows;
+        nAxes[2] = z + 1;
+        if (fits_create_img(fptr, bitPix, 3, nAxes, &status) != 0) {
+            (void)fits_get_errstatus(status, fitsErr);
+            status = 0;
+            (void)fits_close_file(fptr, &status);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psImageIO_CREATE_HDU_FAILED,
+                    filename, fitsErr);
+            return false;
+        }
+        // set the bscale/bzero
+        fits_write_key_dbl(fptr, "BZERO", bzero, 12, "Pixel Value Offset", &status);
+        fits_write_key_dbl(fptr, "BSCALE", bscale, 12, "Pixel Value Scale", &status);
+        fits_set_bscale(fptr, bscale, bzero, &status);
+
+        if (extname != NULL) {
+            /* create the extension for the Primary HDU */
+            if (fits_write_key_str(fptr, "EXTNAME", extname, "Extension Name", &status) != 0) {
+                (void)fits_get_errstatus(status, fitsErr);
+                status = 0;
+                (void)fits_close_file(fptr, &status);
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psImageIO_CREATE_EXTENSION_FAILED,
+                        filename, fitsErr);
+                return false;
+            }
+        }
+    }
+
+    firstPixel[0] = col0 + 1;
+    firstPixel[1] = row0 + 1;
+    firstPixel[2] = z + 1;
+
+    lastPixel[0] = firstPixel[0] + numCols - 1;
+    lastPixel[1] = firstPixel[1] + numRows - 1;
+    lastPixel[2] = z + 1;
+
+    if (fits_write_subset(fptr, datatype, firstPixel, lastPixel, input->data.V[0], &status) != 0) {
+        (void)fits_get_errstatus(status, fitsErr);
+        status = 0;
+        (void)fits_close_file(fptr, &status);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psImageIO_WRITE_FAILED,
+                filename, fitsErr);
+        return false;
+    }
+
+    status = 0;
+    (void)fits_close_file(fptr, &status);
+
+    return true;
+}
+
+bool p_psImagePrint (FILE *f, psImage *a, char *name)
+{
+
+    fprintf (f, "matrix: %s\n", name);
+
+    for (int j = 0; j < a[0].numRows; j++) {
+        for (int i = 0; i < a[0].numCols; i++) {
+            fprintf (f, "%f  ", p_psImageGetElementF64(a, i, j));
+        }
+        fprintf (f, "\n");
+    }
+    fprintf (f, "\n");
+    return (true);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageIO.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageIO.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageIO.h	(revision 22271)
@@ -0,0 +1,106 @@
+
+/** @file  psImageIO.h
+ *
+ *  @brief Contains image input/output routines
+ *
+ *  This file defines the file input/output functions for the psImage structure.
+ *
+ *  @ingroup ImageIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-29 02:25:10 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGEIO_H
+#define PS_IMAGEIO_H
+
+#include "psImage.h"
+
+/// @addtogroup ImageIO
+/// @{
+
+/** Read an image or subimage from a FITS file specified by a filename.
+ *
+ *  return psImage* The image read from the specified file.  NULL
+ *                          signifies that a problem had occured.
+ */
+psImage* psImageReadSection(
+    psImage* output,
+    /**< the output psImage to recycle, or NULL if new psImage desired */
+
+    psS32 col0,
+    /**< the column index of the origin to start reading */
+
+    psS32 row0,
+    /**< the row index of the origin to start reading */
+
+    psS32 numCols,
+    /**< the number of desired columns to read */
+
+    psS32 numRows,
+    /**< the number of desired rows to read */
+
+    psS32 z,
+    /**< the z index to read if file is organized as a 3D image cube. */
+
+    char *extname,
+    /**< the image extension to read (this should match the EXTNAME keyword in
+        *   the extension If NULL, the extnum parameter is to be used instead
+        */
+
+    psS32 extnum,
+    /**< the image extension to read (0=PHU, 1=first extension, etc.)  This is
+        *   only used if extname is NULL
+        */
+
+    char *filename
+    /**< the filename of the FITS image file to read */
+);
+
+/** Read an image or subimage from a FITS file specified by a filename.
+ *
+ *  return psBool         TRUE is successful, otherwise FALSE.
+ */
+psBool psImageWriteSection(
+    psImage* input,
+    /**< the psImage to write */
+
+    psS32 col0,
+    /**< the column index of the origin to start writing */
+
+    psS32 row0,
+    /**< the row index of the origin to start writing */
+
+    psS32 z,
+    /**< the z index to start writing */
+
+    char *extname,
+    /**< the image extension to write (this should match the EXTNAME keyword in
+    *   the extension If NULL, the extnum parameter is to be used instead
+    */
+
+    psS32 extnum,
+    /**< the image extension to write (0=PHU, 1=first extension, etc.)  This is
+    *   only used if extname is NULL.
+    */
+
+    char *filename
+    /**< the filename of the FITS image file to write */
+);
+
+/** print image pixel values.
+ *
+ *  @return bool    TRUE is successful, otherwise FALSE.
+ */
+bool p_psImagePrint(
+    FILE *f,                           ///< Destination stream
+    psImage *a,                        ///< image to print
+    char *name                         ///< name of the image (for title)
+);
+
+/// @}
+
+#endif // PS_IMAGEIO_H
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageManip.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageManip.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageManip.c	(revision 22271)
@@ -0,0 +1,1090 @@
+/** @file  psImageManip.c
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-12 00:54:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <complex.h>
+#include <math.h>                          // for isfinite(), etc.
+#include <stdlib.h>
+#include <string.h>                        // for memcpy, etc.
+
+#include "psImageManip.h"
+
+#include "psError.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psMemory.h"
+#include "psImageExtraction.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+#include "psCoord.h"
+
+psS32 psImageClip(psImage* input,
+                  psF64 min,
+                  psF64 vmin,
+                  psF64 max,
+                  psF64 vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+
+    if (max < min) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN,
+                (double)min,(double)max);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipCase(type) \
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if ((psF64)inputRow[col] < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if ((psF64)inputRow[col] > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        #define psImageClipCaseComplex(type,absfcn)\
+    case PS_TYPE_##type: { \
+            if (vmin < PS_MIN_##type || vmin > PS_MAX_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmin",vmin, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            if (vmax > PS_MAX_##type || vmax < PS_MIN_##type) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImage_PARAMETER_OUTOF_TYPERANGE, \
+                        "vmax",vmax, PS_TYPE_##type##_NAME, \
+                        (psF64)PS_MIN_##type,(psF64)PS_MAX_##type); \
+            } \
+            for (psU32 row = 0;row<numRows;row++) { \
+                ps##type* inputRow = input->data.type[row]; \
+                for (psU32 col = 0; col < numCols; col++) { \
+                    if (absfcn(inputRow[col]) < min) { \
+                        inputRow[col] = (ps##type)vmin; \
+                        numClipped++; \
+                    } else if (absfcn(inputRow[col]) > max) { \
+                        inputRow[col] = (ps##type)vmax; \
+                        numClipped++; \
+                    } \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipCase(S8)
+        psImageClipCase(S16)
+        psImageClipCase(S32)            // Not a requirement
+        psImageClipCase(S64)            // Not a requirement
+        psImageClipCase(U8)
+        psImageClipCase(U16)
+        psImageClipCase(U32)            // Not a requirement
+        psImageClipCase(U64)            // Not a requirement
+        psImageClipCase(F32)
+        psImageClipCase(F64)
+        psImageClipCaseComplex(C32, cabsf)
+        psImageClipCaseComplex(C64, cabs)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psS32 psImageClipNaN(psImage* input,
+                     psF64 value)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+
+    if (input == NULL) {
+        return 0;
+    }
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    switch (input->type.type) {
+
+        #define psImageClipNaNCase(type) \
+    case PS_TYPE_##type: \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if (! isfinite(inputRow[col])) { \
+                    inputRow[col] = (ps##type)value; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+        break;
+
+        psImageClipNaNCase(F32)
+        psImageClipNaNCase(F64)
+        psImageClipNaNCase(C32)
+        psImageClipNaNCase(C64)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psS32 psImageOverlaySection(psImage* image,
+                            const psImage* overlay,
+                            psS32 col0,
+                            psS32 row0,
+                            const char *op)
+{
+    psU32 imageNumRows;
+    psU32 imageNumCols;
+    psU32 overlayNumRows;
+    psU32 overlayNumCols;
+    psU32 imageRowLimit;
+    psU32 imageColLimit;
+    psElemType type;
+    psU32 pixelsOverlaid = 0;
+
+    if (image == NULL || overlay == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return pixelsOverlaid;
+    }
+
+    if (op == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageManip_OPERATION_NULL);
+        return pixelsOverlaid;
+    }
+
+    type = image->type.type;
+
+    if (type != overlay->type.type) {
+        char* typeStr;
+        char* typeStrOverlay;
+        PS_TYPE_NAME(typeStr,type);
+        PS_TYPE_NAME(typeStrOverlay,overlay->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageManip_OVERLAY_TYPE_MISMATCH,
+                typeStrOverlay, typeStr);
+        return pixelsOverlaid;
+    }
+
+    imageNumRows = image->numRows;
+    imageNumCols = image->numCols;
+    overlayNumRows = overlay->numRows;
+    overlayNumCols = overlay->numCols;
+    imageRowLimit = row0 + overlayNumRows;
+    imageColLimit = col0 + overlayNumCols;
+
+    /* check to see if overlay is within the input image */
+    if ( row0 < 0 ||
+            col0 < 0 ||
+            imageRowLimit > imageNumRows ||
+            imageColLimit > imageNumCols) {
+
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_INVALID,
+                col0, imageColLimit, row0, imageRowLimit,
+                imageNumCols, imageNumRows);
+        return pixelsOverlaid;
+    }
+
+
+    #define psImageOverlayLoop(DATATYPE,OP) { \
+        for (int row=row0;row<imageRowLimit;row++) { \
+            ps##DATATYPE* imageRow = image->data.DATATYPE[row]; \
+            ps##DATATYPE* overlayRow = overlay->data.DATATYPE[row-row0]; \
+            for (int col=col0;col<imageColLimit;col++) { \
+                imageRow[col] OP overlayRow[col-col0]; \
+            } \
+        } \
+        pixelsOverlaid += (imageRowLimit - row0) * (imageColLimit - col0); \
+    }
+
+    #define psImageOverlayCase(DATATYPE) \
+case PS_TYPE_##DATATYPE: \
+    switch (*op) { \
+    case '+': \
+        psImageOverlayLoop(DATATYPE,+=); \
+        break; \
+    case '-': \
+        psImageOverlayLoop(DATATYPE,-=); \
+        break; \
+    case '*': \
+        psImageOverlayLoop(DATATYPE,*=); \
+        break; \
+    case '/': \
+        psImageOverlayLoop(DATATYPE,/=); \
+        break; \
+    case '=': \
+        psImageOverlayLoop(DATATYPE,=); \
+        break; \
+    default: \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                PS_ERRORTEXT_psImageManip_OVERLAY_OPERATOR_INVALID, \
+                op); \
+        return pixelsOverlaid; \
+    } \
+    break;
+
+    switch (type) {
+        psImageOverlayCase(U8);
+        psImageOverlayCase(U16);
+        psImageOverlayCase(U32);       // Not a requirement
+        psImageOverlayCase(U64);       // Not a requirement
+        psImageOverlayCase(S8);
+        psImageOverlayCase(S16);
+        psImageOverlayCase(S32);       // Not a requirement
+        psImageOverlayCase(S64);       // Not a requirement
+        psImageOverlayCase(F32);
+        psImageOverlayCase(F64);
+        psImageOverlayCase(C32);
+        psImageOverlayCase(C64);
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            return pixelsOverlaid;
+        }
+    }
+
+    return pixelsOverlaid;
+}
+
+psS32 psImageClipComplexRegion(psImage* input,
+                               psC64 min,
+                               psC64 vmin,
+                               psC64 max,
+                               psC64 vmax)
+{
+    psS32 numClipped = 0;
+    psU32 numRows;
+    psU32 numCols;
+    psF64 realMin = creal(min);
+    psF64 imagMin = cimag(min);
+    psF64 realMax = creal(max);
+    psF64 imagMax = cimag(max);
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return 0;
+    }
+
+    if (realMax < realMin) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN_REAL,
+                (double)realMin, (double)realMax);
+        return 0;
+    }
+    if (imagMax < imagMin) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_MAXMIN_IMAG,
+                (double)imagMin, (double)imagMax);
+        return 0;
+    }
+
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    #define psImageClipComplexRegionCase(type,realfcn,imagfcn) \
+case PS_TYPE_##type: { \
+        if (realfcn(vmin) < PS_MIN_##type || imagfcn(vmin) < PS_MIN_##type || \
+                realfcn(vmin) > PS_MAX_##type || imagfcn(vmin) > PS_MAX_##type ) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                    "vmin", creal(vmin), cimag(vmin), \
+                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
+            break; \
+        } \
+        if (realfcn(vmax) > PS_MAX_##type || imagfcn(vmax) > PS_MAX_##type || \
+                realfcn(vmax) < PS_MIN_##type || imagfcn(vmax) < PS_MIN_##type ) { \
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                    PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                    "vmax", creal(vmax), cimag(vmax), \
+                    PS_TYPE_##type##_NAME, PS_MIN_##type, PS_MAX_##type); \
+            break; \
+        } \
+        for (psU32 row = 0;row<numRows;row++) { \
+            ps##type* inputRow = input->data.type[row]; \
+            for (psU32 col = 0; col < numCols; col++) { \
+                if ( (realfcn(inputRow[col]) > realMax) || (imagfcn(inputRow[col]) > imagMax) ) { \
+                    inputRow[col] = (ps##type)vmax; \
+                    numClipped++; \
+                } else if ( (realfcn(inputRow[col]) < realMin) || (imagfcn(inputRow[col]) < imagMin) ){ \
+                    inputRow[col] = (ps##type)vmin; \
+                    numClipped++; \
+                } \
+            } \
+        } \
+    } \
+    break;
+
+    switch (input->type.type) {
+
+        psImageClipComplexRegionCase(C32, crealf, cimagf)
+        psImageClipComplexRegionCase(C64, creal, cimag)
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return numClipped;
+}
+
+psImage* psImageRebin(psImage* out,
+                      const psImage* in,
+                      const psImage* restrict mask,
+                      psMaskType maskVal,
+                      psU32 scale,
+                      const psStats* stats)
+{
+    psS32 inRows;
+    psS32 inCols;
+    psS32 outRows;
+    psS32 outCols;
+    psVector* vec;                     // vector to hold the values of a single bin.
+    psVector* maskVec = NULL;          // vector to hold the mask of a single bin.
+    psMaskType* maskData = NULL;
+    psStats* myStats;
+    double statVal;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (stats == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_STAT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (p_psGetStatValue(stats, &statVal) == false) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_BAD_STAT,
+                stats->options);
+        psFree(out);
+        return NULL;
+    }
+
+    vec = psVectorAlloc(scale * scale, in->type.type);
+
+    if (mask != NULL) {
+        if (mask->type.type != PS_TYPE_MASK) {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,mask->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_MASK_TYPE,
+                    typeStr, PS_TYPE_MASK_NAME);
+            psFree(out);
+            psFree(vec);
+            return NULL;
+        }
+        maskVec = psVectorAlloc(scale * scale, PS_TYPE_MASK);
+        maskData = maskVec->data.PS_TYPE_MASK_DATA;
+    }
+
+    myStats = psAlloc(sizeof(psStats));
+    *myStats = *stats;
+
+    // create output image.
+    inRows = in->numRows;
+    inCols = in->numCols;
+    outRows = (inRows + scale - 1) / scale;     // round-up for remainders
+    outCols = (inCols + scale - 1) / scale;     // round-up for remainders
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    #define PS_IMAGE_REBIN_CASE(type) \
+case PS_TYPE_##type: { \
+        ps##type* outRowData; \
+        ps##type* vecData = vec->data.type; \
+        psMaskType* inRowMask = NULL; \
+        for (psS32 row = 0; row < outRows; row++) { \
+            outRowData = out->data.type[row]; \
+            psS32 inCurrentRow = row*scale; \
+            psS32 inNextRow = (row+1)*scale; \
+            for (psS32 col = 0; col < outCols; col++) { \
+                psS32 inCurrentCol = col*scale; \
+                psS32 inNextCol = (col+1)*scale; \
+                psS32 n = 0; \
+                for (psS32 inRow = inCurrentRow; inRow < inNextRow && inRow < inRows; inRow++) { \
+                    ps##type* inRowData = in->data.type[inRow]; \
+                    if (mask != NULL) { \
+                        inRowMask = mask->data.PS_TYPE_MASK_DATA[inRow]; \
+                    } \
+                    for (psS32 inCol = inCurrentCol; inCol < inNextCol && inCol < inCols; inCol++) { \
+                        if (maskData != NULL) { \
+                            maskData[n] = inRowMask[inCol]; \
+                        } \
+                        vecData[n++] = inRowData[inCol]; \
+                    } \
+                } \
+                vec->n = n; \
+                myStats = psVectorStats(myStats,vec,NULL,maskVec,maskVal); \
+                p_psGetStatValue(myStats,&statVal); \
+                outRowData[col] = (ps##type)statVal; \
+            } \
+        } \
+    } \
+    break;
+
+    switch (in->type.type) {
+        //        PS_IMAGE_REBIN_CASE(U8);       Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(U16);
+        PS_IMAGE_REBIN_CASE(U32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(U64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S8);
+        //        PS_IMAGE_REBIN_CASE(S16);      Not valid since psVectorStats doesn't allow
+        PS_IMAGE_REBIN_CASE(S32);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(S64);      // Not a requirement
+        PS_IMAGE_REBIN_CASE(F32);
+        PS_IMAGE_REBIN_CASE(F64);
+        //        PS_IMAGE_REBIN_CASE(C32);      Not valid since psVectorStats doesn't allow
+        //        PS_IMAGE_REBIN_CASE(C64);      Not valid since psVectorStats doesn't allow
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    psFree(vec);
+    psFree(maskVec);
+    psFree(myStats);
+
+    return out;
+}
+
+psImage* psImageResample(psImage* out,
+                         const psImage* in,
+                         psS32 scale,
+                         psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    float invScale;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    if (scale < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_SCALE_NOT_POSITIVE,
+                scale);
+        psFree(out);
+        return NULL;
+    }
+
+    if (mode < PS_INTERPOLATE_FLAT || mode >= PS_INTERPOLATE_NUM_MODES) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        return NULL;
+    }
+
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows * scale;
+    outCols = in->numCols * scale;
+    invScale = 1.0f / (float)scale;
+
+    #define PSIMAGE_RESAMPLE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        out = psImageRecycle(out,outCols, outRows, PS_TYPE_##TYPE); \
+        for (psS32 row=0;row<outRows;row++) { \
+            ps##TYPE* rowData = out->data.TYPE[row]; \
+            float inRow = (float)row * invScale; \
+            for (psS32 col=0;col<outCols;col++) { \
+                rowData[col] = psImagePixelInterpolate(in,(float)col*invScale,inRow,NULL,0,0,mode); \
+            } \
+        }  \
+        break; \
+    }
+
+    switch (in->type.type) {
+        PSIMAGE_RESAMPLE_CASE(U8)
+        PSIMAGE_RESAMPLE_CASE(U16)
+        PSIMAGE_RESAMPLE_CASE(U32)
+        PSIMAGE_RESAMPLE_CASE(U64)
+        PSIMAGE_RESAMPLE_CASE(S8)
+        PSIMAGE_RESAMPLE_CASE(S16)
+        PSIMAGE_RESAMPLE_CASE(S32)
+        PSIMAGE_RESAMPLE_CASE(S64)
+        PSIMAGE_RESAMPLE_CASE(F32)
+        PSIMAGE_RESAMPLE_CASE(F64)
+        PSIMAGE_RESAMPLE_CASE(C32)
+        PSIMAGE_RESAMPLE_CASE(C64)
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,in->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageRoll(psImage* out,
+                     const psImage* in,
+                     psS32 dx,
+                     psS32 dy)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows;
+    outCols = in->numCols;
+    elementSize = PSELEMTYPE_SIZEOF(in->type.type);
+    out = psImageRecycle(out, outCols, outRows, in->type.type);
+
+    // make dx and dy between 0 and outCols or
+    // outRows, respectively
+    dx = dx % outCols;
+    dy = dy % outRows;
+    if (dx < 0) {
+        dx += outCols;
+    }
+    if (dy < 0) {
+        dy += outRows;
+    }
+
+    psS32 segment1Size = elementSize * (outCols - dx);
+    psS32 segment2Size = elementSize * dx;
+
+    for (psS32 row = 0; row < outRows; row++) {
+        psS32 inRowNumber = row + dy;
+
+        if (inRowNumber >= outRows) {
+            inRowNumber -= outRows;
+        }
+        psU8* inRow = in->data.U8[inRowNumber]; // use byte arithmetic for all types
+        psU8* outRow = out->data.U8[row];
+
+        memcpy(outRow, inRow + segment2Size, segment1Size);
+        memcpy(outRow + segment1Size, inRow, segment2Size);
+    }
+
+    return out;
+}
+
+psImage* psImageRotate(psImage* out,
+                       const psImage* in,
+                       float angle,
+                       psC64 unexposedValue,
+                       psImageInterpolateMode mode)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // put the angle in the range of 0...2PI.
+    angle = (float)((double)angle - (2.0*M_PI) * floor(angle / (2.0*M_PI)));
+
+    if (fabsf(angle - M_PI_2) < FLT_EPSILON) {
+        // perform 1/4 rotate counter-clockwise
+        psS32 numRows = in->numCols;
+        psS32 numCols = in->numRows;
+        psS32 lastCol = numCols - 1;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_LEFT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[lastCol-col][row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_LEFT_90(U8);
+            PSIMAGE_ROTATE_LEFT_90(U16);
+            PSIMAGE_ROTATE_LEFT_90(U32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(U64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S8);
+            PSIMAGE_ROTATE_LEFT_90(S16);
+            PSIMAGE_ROTATE_LEFT_90(S32);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(S64);    //  Not a requirement
+            PSIMAGE_ROTATE_LEFT_90(F32);
+            PSIMAGE_ROTATE_LEFT_90(F64);
+            PSIMAGE_ROTATE_LEFT_90(C32);
+            PSIMAGE_ROTATE_LEFT_90(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - M_PI) < FLT_EPSILON) {
+        // perform 1/2 rotate
+        psS32 numRows = in->numRows;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = in->numCols;
+        psS32 lastCol = numCols - 1;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_180_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                ps##TYPE* inRow = in->data.TYPE[lastRow-row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inRow[lastCol - col]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_180_CASE(U8);
+            PSIMAGE_ROTATE_180_CASE(U16);
+            PSIMAGE_ROTATE_180_CASE(U32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(U64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S8);
+            PSIMAGE_ROTATE_180_CASE(S16);
+            PSIMAGE_ROTATE_180_CASE(S32);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(S64);    // Not a requirement
+            PSIMAGE_ROTATE_180_CASE(F32);
+            PSIMAGE_ROTATE_180_CASE(F64);
+            PSIMAGE_ROTATE_180_CASE(C32);
+            PSIMAGE_ROTATE_180_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle - (M_PI+M_PI_2)) < FLT_EPSILON) {
+        // perform 1/4 rotate clockwise
+        psS32 numRows = in->numCols;
+        psS32 lastRow = numRows - 1;
+        psS32 numCols = in->numRows;
+        psElemType type = in->type.type;
+
+        out = psImageRecycle(out, numCols, numRows, type);
+
+        #define PSIMAGE_ROTATE_RIGHT_90(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    outRow[col] = inData[col][lastRow-row]; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (type) {
+            PSIMAGE_ROTATE_RIGHT_90(U8);
+            PSIMAGE_ROTATE_RIGHT_90(U16);
+            PSIMAGE_ROTATE_RIGHT_90(U32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(U64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S8);
+            PSIMAGE_ROTATE_RIGHT_90(S16);
+            PSIMAGE_ROTATE_RIGHT_90(S32);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(S64);     // Not a requirement
+            PSIMAGE_ROTATE_RIGHT_90(F32);
+            PSIMAGE_ROTATE_RIGHT_90(F64);
+            PSIMAGE_ROTATE_RIGHT_90(C32);
+            PSIMAGE_ROTATE_RIGHT_90(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+            }
+        }
+    } else if (fabsf(angle) < FLT_EPSILON) {
+        out = psImageCopy(out, in, in->type.type);
+    } else {
+        psElemType type = in->type.type;
+        psS32 numRows = in->numRows;
+        psS32 numCols = in->numCols;
+        float centerX = (float)(numCols) / 2.0f;
+        float centerY = (float)(numRows) / 2.0f;
+        double cosT = cosf(angle);
+        double sinT = sinf(angle);
+
+        // calculate the corners of the rotated image so we know the proper output image size.
+        // x' = x cos(t) + y sin(t); i.e, x' = (x-centerX)*cosT + (y-centerY)*sinT;
+        // y' = y cos(t) - x sin(t); i.e. y' = (y-centerY)*cosT - (x-centerX)*sinT;
+
+        psS32 outCols = ceil(abs(numCols * cosT) + abs(numRows * sinT)) + 1;
+        psS32 outRows = ceil(abs(numCols * sinT) + abs(numRows * cosT)) + 1;
+        float minX = (float)outCols / -2.0f;
+        psS32 intMinY = outRows / -2;
+
+        out = psImageRecycle(out, outCols, outRows, type);
+
+        /* optimized public domain rotation routine by Karl Lager
+         *
+         * float cosT,sinT;
+         * cosT = cos(t);
+         * sinT = sin(t);
+         * for (y = min_y; y <= max_y; y++) {
+         *     x' = min_x * cosT + y * sinT + x1';
+         *     y' = y * cosT - min_x * sinT + y1';
+         *     for (x = min_x; x <= max_x; x++) {
+         *         if (x', y') is in the bounds of the bitmap, get pixel
+         *            (x', y') and plot the pixel to (x, y) on screen.
+         *         x' += cosT;
+         *         y' -= sinT;
+         *     }
+         * }
+         */
+
+        // precalculate some figures that are used within loop
+        float minXTimesCosTPlusCenterX = minX * cosT + centerX;
+        float CenterYMinusminXTimesSinT = centerY - minX * sinT;
+
+        #define PSIMAGE_ROTATE_ARBITRARY_LOOP(TYPE,MODE) { \
+            if (creal(unexposedValue) < PS_MIN_##TYPE || \
+                    creal(unexposedValue) > PS_MAX_##TYPE || \
+                    cimag(unexposedValue) < PS_MIN_##TYPE || \
+                    cimag(unexposedValue) > PS_MAX_##TYPE) { \
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                        PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                        "unexposedValue", \
+                        creal(unexposedValue),cimag(unexposedValue), \
+                        PS_TYPE_##TYPE##_NAME,  \
+                        (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+                psFree(out); \
+                out = NULL; \
+                break; \
+            } \
+            float inX; \
+            float inY; \
+            ps##TYPE* outRow; \
+            for (psS32 y = 0; y < outRows; y++) { \
+                inX = minXTimesCosTPlusCenterX + (y+intMinY) * sinT; \
+                inY = CenterYMinusminXTimesSinT + (y+intMinY) * cosT; \
+                outRow = out->data.TYPE[y]; \
+                for (psS32 x = 0; x < outCols; x++) { \
+                    outRow[x] = p_psImagePixelInterpolate##MODE##_##TYPE(in,inX,inY,NULL,0,unexposedValue); \
+                    inX += cosT; \
+                    inY -= sinT; \
+                } \
+            } \
+        }
+
+        #define PSIMAGE_ROTATE_ARBITRARY_CASE(MODE) \
+    case PS_INTERPOLATE_##MODE: \
+        switch (type) { \
+        case PS_TYPE_U8: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U8,MODE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U16,MODE); \
+            break; \
+        case PS_TYPE_U32:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U32,MODE); \
+            break; \
+        case PS_TYPE_U64:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(U64,MODE); \
+            break;  \
+        case PS_TYPE_S8: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S8,MODE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S16,MODE); \
+            break; \
+        case PS_TYPE_S32:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S32,MODE); \
+            break; \
+        case PS_TYPE_S64:   /* Not a requirement */ \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(S64,MODE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F32,MODE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(F64,MODE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(C32,MODE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_ROTATE_ARBITRARY_LOOP(C64,MODE); \
+            break; \
+        default: { \
+                char* typeStr; \
+                PS_TYPE_NAME(typeStr,type); \
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
+                        typeStr); \
+                psFree(out); \
+                out = NULL; \
+            } \
+        } \
+        break;
+
+        switch (mode) {
+            PSIMAGE_ROTATE_ARBITRARY_CASE(FLAT);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR);
+            PSIMAGE_ROTATE_ARBITRARY_CASE(BILINEAR_VARIANCE);
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                    mode);
+            psFree(out);
+            out = NULL;
+        }
+    }
+
+    return out;
+}
+
+psImage* psImageShift(psImage* out,
+                      const psImage* in,
+                      float dx,
+                      float dy,
+                      psC64 unexposedValue,
+                      psImageInterpolateMode mode)
+{
+    psS32 outRows;
+    psS32 outCols;
+    psS32 elementSize;
+    psElemType type;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(out);
+        return NULL;
+    }
+    // create an output image of the same size
+    // and type
+    outRows = in->numRows;
+    outCols = in->numCols;
+    type = in->type.type;
+    elementSize = PSELEMTYPE_SIZEOF(type);
+    out = psImageRecycle(out, outCols, outRows, type);
+
+    #define PSIMAGE_SHIFT_CASE(MODE,TYPE) \
+case PS_TYPE_##TYPE: \
+    if (creal(unexposedValue) < PS_MIN_##TYPE || \
+            creal(unexposedValue) > PS_MAX_##TYPE || \
+            cimag(unexposedValue) < PS_MIN_##TYPE || \
+            cimag(unexposedValue) > PS_MAX_##TYPE) { \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+                PS_ERRORTEXT_psImageManip_CLIP_VALUE_INVALID, \
+                "unexposedValue", \
+                creal(unexposedValue),cimag(unexposedValue), \
+                PS_TYPE_##TYPE##_NAME,  \
+                (double)PS_MIN_##TYPE,(double)PS_MAX_##TYPE); \
+        psFree(out); \
+        out = NULL; \
+        break; \
+    } \
+    for (psS32 row=0;row<outRows;row++) { \
+        ps##TYPE* outRow = out->data.TYPE[row]; \
+        float y = dy+(float)row; \
+        for (psS32 col=0;col<outCols;col++) { \
+            outRow[col] = p_psImagePixelInterpolate##MODE##_##TYPE( \
+                          in,dx+(float)col,y,NULL,0,unexposedValue); \
+        } \
+    } \
+    break;
+
+    #define PSIMAGE_SHIFT_ARBITRARY_CASE(MODE) \
+case PS_INTERPOLATE_##MODE: \
+    switch (in->type.type) { \
+        PSIMAGE_SHIFT_CASE(MODE,U8);  \
+        PSIMAGE_SHIFT_CASE(MODE,U16); \
+        PSIMAGE_SHIFT_CASE(MODE,U32);     /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,U64);     /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,S8);  \
+        PSIMAGE_SHIFT_CASE(MODE,S16); \
+        PSIMAGE_SHIFT_CASE(MODE,S32);    /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,S64);    /* Not a requirement */ \
+        PSIMAGE_SHIFT_CASE(MODE,F32); \
+        PSIMAGE_SHIFT_CASE(MODE,F64); \
+        PSIMAGE_SHIFT_CASE(MODE,C32); \
+        PSIMAGE_SHIFT_CASE(MODE,C64); \
+        \
+    default: { \
+            char* typeStr; \
+            PS_TYPE_NAME(typeStr,type); \
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED, \
+                    typeStr); \
+            psFree(out); \
+            out = NULL; \
+        } \
+    } \
+    break;
+
+    switch (mode) {
+        PSIMAGE_SHIFT_ARBITRARY_CASE(FLAT);
+        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR);
+        PSIMAGE_SHIFT_ARBITRARY_CASE(BILINEAR_VARIANCE);
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImageManip_INTERPOLATION_MODE_UNSUPPORTED,
+                mode);
+        psFree(out);
+        out = NULL;
+    }
+
+    return out;
+}
+
+
+// XXX: implementation is awaiting working psPlaneTransform functions like
+// invert.  Also, the next SDRS should have a different signature.
+psImage* psImageTransform(psImage *output,
+                          psArray** blankPixels,
+                          const psImage *input,
+                          const psImage *inputMask,
+                          int inputMaskVal,
+                          const psPlaneTransform *outToIn,
+                          const psRegion region,
+                          const psPixels* pixels,
+                          psImageInterpolateMode mode,
+                          int exposedValue)
+{
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return NULL;
+    }
+
+    if (outToIn == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageManip_TRANSFORM_NULL);
+        return NULL;
+    }
+
+    // find the input image domain in the output image
+
+    // loop through the output image using the domain above and transform
+    // each output pixel to input coordinates and use psImagePixelInterpolate
+    // to determine the pixel value.
+
+
+    return NULL;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageManip.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageManip.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageManip.h	(revision 22271)
@@ -0,0 +1,223 @@
+/** @file  psImageManip.h
+ *
+ *  @brief Contains basic image pixel and geometry manipulation operations, as
+ *         specified in the PSLIB SDRS sections "Image Pixel Manipulations" and
+ *         "Image Geometry Manipulations".
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 02:29:39 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_MANIP_H
+#define PS_IMAGE_MANIP_H
+
+#include "psImage.h"
+#include "psCoord.h"
+#include "psStats.h"
+#include "psPixels.h"
+
+/// @addtogroup Image
+/// @{
+
+/** Clip image values outside of range to given values
+ *
+ *  All pixels with values less than min are set to the value vmin.  all pixels
+ *  with values greater than max are set to the value vmax. This function is
+ *  defined for psU8, psU16, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClip(
+    psImage* input,                    ///< the image to clip
+    psF64 min,                         ///< the minimum image value allowed
+    psF64 vmin,                        ///< the value pixels < min are set to
+    psF64 max,                         ///< the maximum image value allowed
+    psF64 vmax                         ///< the value pixels > max are set to
+);
+
+/** Clip image values outside of a specified complex region
+ *
+ *  All pixels outside of the rectangular region in complex space formed by
+ *  the min and max input parameters are set to the value vmax (if either
+ *  the real or imaginary portion exceeds the respective max values), or vmin.
+ *  This function is defined for psC32, and psC64 imagery only.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClipComplexRegion(
+    psImage* input,                    ///< the image to clip
+    psC64 min,                         ///< the minimum image value allowed
+    psC64 vmin,                        ///< the value pixels < min are set to
+    psC64 max,                         ///< the maximum image value allowed
+    psC64 vmax                         ///< the value pixels > max are set to
+);
+
+/** Clip NaN image pixels to given value.
+ *
+ *  Pixels with NaN, +Inf, or -Inf values are set to the specified value. This
+ *  function is defined for psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32     The number of clipped pixels
+ */
+psS32 psImageClipNaN(
+    psImage* input,                    ///< the image to clip
+    psF64 value                        ///< the value to set all NaN/Inf values to
+);
+
+/** Overlay subregion of image with another image
+ *
+ *  Replace the pixels in the image which correspond to the pixels in OVERLAY
+ *  with values derived from the IMAGE and OVERLAY based on the given operator
+ *  OP.  Valid operators are "=" (set image value to OVERLAY value), "+" (add
+ *  OVERLAY value to image value), "-" (subtract OVERLAY from image), "*"
+ *  (multiply OVERLAY times image), "/" (divide image by OVERLAY).  This
+ *  function is defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psS32         0 if success, non-zero if failed.
+ */
+psS32 psImageOverlaySection(
+    psImage* image,                    ///< target image
+    const psImage* overlay,            ///< the overlay image
+    psS32 col0,                        ///< the column to start overlay
+    psS32 row0,                        ///< the row to start overlay
+    const char *op                     ///< the operation to perform for overlay
+);
+
+/** Rebin image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are reduced by a factor of
+ *  1/scale.  The scale, always a positive number, is equal in each dimension and
+ *  specified the number of pixels used to define a new pixel in the output image.
+ *  The output image is generated from all input image pixels. This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psImage    new image formed by rebinning input image.
+ */
+psImage* psImageRebin(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    const psImage* mask,               ///< mask for input image.  If NULL, no masking is done.
+    psMaskType maskVal,                ///< the bits to check in mask.
+    psU32 scale,                       ///< the scale to rebin for each dimension
+    const psStats* stats
+    ///< the statistic to perform when rebinning.  Only one method should be set.
+);
+
+/** Resample image to new scale.
+ *
+ *  A new image is constructed in which the dimensions are increased by a
+ *  factor of scale. The scale, always a positive number, is equal in each
+ *  dimension. The output image is generated from all input image pixels.
+ *  Each pixel in the output image is derived by interpolating between
+ *  neighboring pixels using the specified interpolation method (mode).
+ *
+ *  @return psImage*    resampled image result
+ */
+psImage* psImageResample(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    psS32 scale,                       ///< resample scaling factor
+    psImageInterpolateMode mode        ///< the interpolation mode used in resampling
+);
+
+/** Rotate the input image by given angle, specified in degrees.
+ *
+ *  The output image must contain all of the pixels from the input image in
+ *  their new frame. Pixels in the output image which do not map to input
+ *  pixels should be set to exposed. The center of rotation is always the
+ *  center pixel of the image. The rotation is specified in the sense that a
+ *  positive angle is an anti-clockwise rotation. This function must be
+ *  defined for the following types: psU8, psU16, psS8, psS16, psF32, psF64,
+ *  psC32, psC64.
+ *
+ *  @return psImage*     the rotated image result.
+ */
+psImage* psImageRotate(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    float angle,                       ///< the rotation angle in radians.
+    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode used
+);
+
+/** Shift image by an arbitrary number of pixels (dx,dy) in either direction.
+ *
+ *  If the shift values are fractional, the output pixel values should
+ *  interpolate between the input pixel values. The output image has the same
+ *  dimensions as the input image. Pixels which fall off the edge of the
+ *  output image are lost. Newly exposed pixels are set to the value given by
+ *  exposed. This function must be defined for the following types: psU8,
+ *  psU16, psS8, psS16, psF32, psF64, psC32, psC64.
+ *
+ *  @return psImage*     the shifted image result.
+ */
+psImage* psImageShift(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    float dx,                          ///< the shift in x direction.
+    float dy,                          ///< the shift in y direction.
+    psC64 unexposedValue,              ///< the output image pixel values for non-imagery areas
+    psImageInterpolateMode mode        ///< the interpolation mode to use
+);
+
+/** Roll image by an integer number of pixels in either direction.
+ *
+ *  The output image is the same dimensions as the input image.  Edge pixels
+ *  wrap to the other side (no values are lost).  This function is
+ *  defined for psU8, psS8, psS16, psF32, psF64, psC32, and psC64.
+ *
+ *  @return psImage* the rolled version of the input image.
+ */
+psImage* psImageRoll(
+    psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
+    const psImage* in,                 ///< input image
+    psS32 dx,                          ///< number of pixels to roll in the x-dimension
+    psS32 dy                           ///< number of pixels to roll in the y-dimension
+);
+
+/** Transform the input image according the supplied transformation.
+ *
+ *  Transform the input image according the supplied transformation. The size
+ *  of the transformed image is deï¬ned by the supplied output image, if
+ *  non-NULL, or the region otherwise (size region.x1 - region.x0 by region.y1
+ *  region.y0, with out->x0 = region.x0 and out->y0 = region.y0). If the
+ *  inputMask is non-NULL, those pixels in the inputMask matching inputMaskVal
+ *  are to be ignored in the transformation. The inputMask must be of type
+ *  psU8, and of the same size as the input, otherwise the function shall
+ *  generate an error and return NULL. The transformation outToIn speciï¬es the
+ *  coordinates in the input image of a pixel in the output image â note that
+ *  this is the reverse of what might be naively expected, but it is what is
+ *  required in order to use psImagePixelInterpolate. If the pixels array is
+ *  non-NULL, it shall consist of psPixelCoords, and only those pixels in the
+ *  output image shall be transformed; otherwise, the entire image is
+ *  generated. The interpolation is performed using the speciï¬ed interpolation
+ *  mode. Where a pixel in the output image does not correspond to a pixel in
+ *  the input image (or all appropriate pixels in the input image are
+ *  masked), the value shall be set to exposed, and the pixel added to the
+ *  appropriate list of pixels (psPixels) in the array of blankPixels for
+ *  return to the user. This function must be capable of handling the following
+ *  types for the input (with corresponding types for the output): psF32, psF64.
+ 
+ *
+ *  @return psImage*    The transformed image.
+ */
+psImage* psImageTransform(
+    psImage *output,                   ///< psImage to recycle, or NULL
+    psArray** blankPixels,             ///<
+    const psImage *input,              ///< psImage to apply transform to
+    const psImage *inputMask,          ///< if not NULL, mask of input psImage
+    int inputMaskVal,                  ///< masking value for inputMask
+    const psPlaneTransform *outToIn,   ///< the transform to apply
+    const psRegion region,             ///<
+    const psPixels* pixels,            ///<
+    psImageInterpolateMode mode,       ///<
+    int exposedValue                   ///<
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageStats.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageStats.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageStats.c	(revision 22271)
@@ -0,0 +1,635 @@
+/** @file psImageStats.c
+ *  \brief Routines for calculating statistics on images.
+ *  @ingroup ImageStats
+ *
+ *  This file will hold the prototypes for procedures which calculate
+ *  statistic on images, histograms on images, and fit/evaluate Chebyshev
+ *  polynomials to images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.73 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+#include "psMemory.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psStats.h"
+#include "psImage.h"
+#include "psFunctions.h"
+#include "psImageStats.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+
+/// This routine must determine the various statistics for the image.
+/*****************************************************************************
+psImageStats(stats, in, mask, maskVal): this routine simply calls the
+psVectorStats() routine, which does the actual statistical calculation.  In
+order to do so, we create dummy psVectors and set their "data" pointer to that
+of the input psImages.
+ 
+XXX: use static psVectors
+ 
+XXX: optimize this.  2k vs 4k, sample mean, takes8 seconds on Gene's machine.
+Should take .2.
+ *****************************************************************************/
+psStats* psImageStats(psStats* stats,
+                      const psImage* in,
+                      const psImage* mask,
+                      psS32 maskVal)
+{
+    psVector *junkData = NULL;
+    psVector *junkMask = NULL;
+
+    PS_PTR_CHECK_NULL(stats, NULL);
+    PS_INT_CHECK_ZERO(stats->options, NULL);
+    PS_IMAGE_CHECK_NULL(in, NULL)
+    if (mask != NULL) {
+        PS_IMAGE_CHECK_TYPE(mask, PS_TYPE_U8, NULL);
+        PS_IMAGE_CHECK_SIZE_EQUAL(in, mask, NULL);
+    }
+
+    if (in->parent == NULL) {
+        // stuff the image data into a psVector struct.
+        junkData = (psVector *) psAlloc(sizeof(psVector));
+        junkData->type = in->type;
+        *(int*)&junkData->nalloc = in->numRows * in->numCols;
+        junkData->n = junkData->nalloc;
+        junkData->data.U8 = in->data.V[0];      // since psImage data is contiguous...
+    } else {
+        // image not necessarily contiguous
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        int rowSize = numCols * (PSELEMTYPE_SIZEOF(in->type.type));
+
+        junkData = psVectorAlloc(numRows*numCols, in->type.type);
+        junkData->n = junkData->nalloc;
+
+        psU8* data = junkData->data.U8;
+        for (int row = 0; row < numRows; row++) {
+            memcpy(data, in->data.V[row], rowSize);
+            data += rowSize;
+        }
+    }
+
+    if (mask != NULL) {
+        if (mask->parent == NULL) {
+            // stuff the mask data into a psVector struct.
+            junkMask = psAlloc(sizeof(psVector));
+            junkMask->type = mask->type;
+            *(int*)&junkMask->nalloc = mask->numRows * mask->numCols;
+            junkMask->n = junkMask->nalloc;
+            junkMask->data.U8 = mask->data.V[0];
+        } else {
+            // image not necessarily contiguous
+            int numRows = mask->numRows;
+            int numCols = mask->numCols;
+            int rowSize = numCols * (PSELEMTYPE_SIZEOF(mask->type.type));
+
+            junkMask = psVectorAlloc(numRows*numCols, mask->type.type);
+            junkMask->n = junkMask->nalloc;
+
+            psU8* data = junkMask->data.U8;
+            for (int row = 0; row < numRows; row++) {
+                memcpy(data, mask->data.V[row], rowSize);
+                data += rowSize;
+            }
+        }
+    }
+
+    stats = psVectorStats(stats, junkData, NULL, junkMask, maskVal);
+
+    psFree(junkMask);
+    psFree(junkData);
+    return (stats);
+}
+
+/*****************************************************************************
+NOTE: We assume that the psHistogram structure out has already been allocated
+and initialized.
+ *****************************************************************************/
+psHistogram* psImageHistogram(psHistogram* out,
+                              const psImage* in,
+                              const psImage* mask,
+                              psU32 maskVal)
+{
+    PS_PTR_CHECK_NULL(out, NULL);
+    PS_PTR_CHECK_NULL(in, NULL);
+    if (mask != NULL) {
+        PS_IMAGE_CHECK_TYPE(mask, PS_TYPE_U8, NULL);
+        PS_IMAGE_CHECK_SIZE_EQUAL(in, mask, NULL);
+    }
+    psVector* junkData = NULL;
+    psVector* junkMask = NULL;
+
+    if (in->parent == NULL) {
+        // stuff the image data into a psVector struct.
+        junkData = (psVector *) psAlloc(sizeof(psVector));
+        junkData->type = in->type;
+        *(int*)&junkData->nalloc = in->numRows * in->numCols;
+        junkData->n = junkData->nalloc;
+        junkData->data.U8 = in->data.V[0];      // since psImage data is contiguous...
+    } else {
+        // image not necessarily contiguous
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        int rowSize = numCols * (PSELEMTYPE_SIZEOF(in->type.type));
+
+        junkData = psVectorAlloc(numRows*numCols, in->type.type);
+        junkData->n = junkData->nalloc;
+
+        psU8* data = junkData->data.U8;
+        for (int row = 0; row < numRows; row++) {
+            memcpy(data, in->data.V[row], rowSize);
+            data += rowSize;
+        }
+    }
+
+    if (mask != NULL) {
+        if (mask->parent == NULL) {
+            // stuff the mask data into a psVector struct.
+            junkMask = psAlloc(sizeof(psVector));
+            junkMask->type = mask->type;
+            *(int*)&junkMask->nalloc = mask->numRows * mask->numCols;
+            junkMask->n = junkMask->nalloc;
+            junkMask->data.U8 = mask->data.V[0];
+        } else {
+            // image not necessarily contiguous
+            int numRows = mask->numRows;
+            int numCols = mask->numCols;
+            int rowSize = numCols * (PSELEMTYPE_SIZEOF(mask->type.type));
+
+            junkMask = psVectorAlloc(numRows*numCols, mask->type.type);
+            junkMask->n = junkMask->nalloc;
+
+            psU8* data = junkMask->data.U8;
+            for (int row = 0; row < numRows; row++) {
+                memcpy(data, mask->data.V[row], rowSize);
+                data += rowSize;
+            }
+        }
+    }
+
+    out = psVectorHistogram(out, junkData, NULL, junkMask, maskVal);
+
+    psFree(junkMask);
+    psFree(junkData);
+
+    return (out);
+}
+
+/*****************************************************************************
+calcScaleFactorsEval(n): The Chebyshev polynomials are defined over the
+interval [-1.0 : 1.0].  Images typically have sizes of 512x512 or more.  In
+order to use Chebyshev polynomials, we must scale the coordinates from
+0:512 to -1:1.  This routine takes as input an integer N and produces as
+output a vector of evenly spaced floating point values between -1.0:1.0.
+ 
+XXX: Use the p_psNormalizeVector here?
+ *****************************************************************************/
+double* calcScaleFactors(psS32 n)
+{
+    PS_INT_CHECK_NON_NEGATIVE(n, NULL);
+    psS32 i = 0;
+    double tmp = 0.0;
+    double *scalingFactors = (double *)psAlloc(n * sizeof(double));
+
+    for (i = 0; i < n; i++) {
+        tmp = (double)(n - i);
+        tmp = (M_PI * (tmp - 0.5)) / ((double)n);
+        scalingFactors[i] = cos(tmp);
+    }
+
+    return (scalingFactors);
+}
+
+// XXX: Use a static array of Chebyshev polynomials.
+psPolynomial1D **p_psCreateChebyshevPolys(psS32 maxChebyPoly)
+{
+    PS_INT_CHECK_POSITIVE(maxChebyPoly, NULL);
+    psPolynomial1D **chebPolys = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+
+    chebPolys = (psPolynomial1D **) psAlloc(maxChebyPoly * sizeof(psPolynomial1D *));
+    for (i = 0; i < maxChebyPoly; i++) {
+        chebPolys[i] = psPolynomial1DAlloc(i + 1, PS_POLYNOMIAL_ORD);
+    }
+
+    // Create the Chebyshev polynomials.
+    // Polynomial i has i-th order.
+    chebPolys[0]->coeff[0] = 1;
+    chebPolys[1]->coeff[1] = 1;
+    for (i = 2; i < maxChebyPoly; i++) {
+        for (j = 0; j < chebPolys[i - 1]->n; j++) {
+            chebPolys[i]->coeff[j + 1] = 2 * chebPolys[i - 1]->coeff[j];
+        }
+        for (j = 0; j < chebPolys[i - 2]->n; j++) {
+            chebPolys[i]->coeff[j] -= chebPolys[i - 2]->coeff[j];
+        }
+    }
+
+    return (chebPolys);
+}
+
+/*****************************************************************************
+psImageFitPolynomial(): This routine takes as input a 2-D image and produces
+as output the coefficients of the Chebyshev polynomials which match that
+input image.
+  Input:
+  Output:
+  Internal Data Structures:
+    chebPolys[i][j] 
+    sums[i][j]: This will contain the sum of 
+                input->data.F32[x][y] *
+                psPolynomial1DEval(
+chebPolys[i],
+(float) x) *
+                psPolynomial1DEval(
+chebPolys[j],
+(float) y, 
+);
+        over all pixels (x,y) in the image.
+  *****************************************************************************/
+psPolynomial2D* psImageFitPolynomial(psPolynomial2D* coeffs,
+                                     const psImage* input)
+{
+    PS_IMAGE_CHECK_NULL(input, NULL);
+    PS_IMAGE_CHECK_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unallowable image type.\n");
+    }
+    PS_POLY_CHECK_NULL(coeffs, NULL);
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    double **sums = NULL;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+
+    // Create the sums[][] data structure.  This
+    // will hold the LHS of
+    // equation
+    // 29 in the ADD: sums[k][l] = SUM {
+    // image(x,y) * Tk(x) * Tl(y) }
+    sums = (double **)psAlloc(coeffs->nX * sizeof(double *));
+    for (i = 0; i < coeffs->nX; i++) {
+        sums[i] = (double *)psAlloc(coeffs->nY * sizeof(double));
+    }
+    // We scale the pixel positions to values
+    // between -1.0 and 1.0
+    rScalingFactors = calcScaleFactors(input->numRows);
+    cScalingFactors = calcScaleFactors(input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly);
+
+    // Compute the sums[][] data structure.
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            sums[i][j] = 0.0;
+            for (x = 0; x < input->numRows; x++) {
+                for (y = 0; y < input->numCols; y++) {
+                    double pixel = 0.0;
+                    if (input->type.type == PS_TYPE_S8) {
+                        pixel = (double) input->data.S8[x][y];
+                    } else if (input->type.type == PS_TYPE_U16) {
+                        pixel = (double) input->data.U16[x][y];
+                    } else if (input->type.type == PS_TYPE_F32) {
+                        pixel = (double) input->data.F32[x][y];
+                    } else if (input->type.type == PS_TYPE_F64) {
+                        pixel = input->data.F64[x][y];
+                    }
+                    sums[i][j] += pixel * psPolynomial1DEval(chebPolys[i],rScalingFactors[x]) *
+                                  psPolynomial1DEval(chebPolys[j], cScalingFactors[y]);
+                }
+            }
+        }
+    }
+
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            coeffs->coeff[i][j] = sums[i][j];
+            coeffs->coeff[i][j] /= (double)(input->numRows * input->numCols);
+
+            if ((i != 0) && (j != 0)) {
+                coeffs->coeff[i][j] *= 4.0;
+            } else if ((i == 0) && (j == 0)) {
+                coeffs->coeff[i][j] *= 1.0;
+            } else {
+                coeffs->coeff[i][j] *= 2.0;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    for (i = 0; i < maxChebyPoly; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    // Free some data
+    for (i = 0; i < coeffs->nX; i++) {
+        psFree(sums[i]);
+    }
+    psFree(sums);
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+
+    return (coeffs);
+}
+
+
+
+
+/*****************************************************************************
+psImageFitPolynomial(): This routine takes as input a 2-D image and produces
+as output the coefficients of the Chebyshev polynomials which match that input
+image.  This is a TEST version of the code.  It is not used by anything.
+  Input:
+  Output:
+  Internal Data Structures:
+    chebPolys[i][j] 
+    sums[i][j]: This will contain the sum of 
+                input->data.F32[x][y] *
+                psPolynomial1DEval(
+chebPolys[i],
+(float) x) *
+                psPolynomial1DEval(
+chebPolys[j],
+(float) y, 
+);
+        over all pixels (x,y) in the image.
+  *****************************************************************************/
+psPolynomial2D* psImageFitPolynomialTest(psPolynomial2D* coeffs,
+        const psImage* input)
+{
+    PS_IMAGE_CHECK_NULL(input, NULL);
+    PS_IMAGE_CHECK_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unallowable image type.\n");
+    }
+    PS_POLY_CHECK_NULL(coeffs, NULL);
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    double **sums = NULL;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+    psImage *nodes = psImageAlloc(input->numCols, input->numRows, PS_TYPE_F64);
+
+    double min = -1.0;
+    double max = 1.0;
+    double bma = 0.5 * (max-min);  // 1
+    double bpa = 0.5 * (max+min);  // 0
+    // We must calculate the value of the image at the nodes where the
+    // Chebyshev polynomials are 0.
+    for (x = 0; x < input->numRows; x++) {
+        double xTmp = cos(M_PI * (0.5 + ((float) x)) / ((float) input->numRows));
+        double xNode = - ((xTmp + bma + bpa) - 1.0);
+        double xOrig = ((float) input->numRows) * (xNode - min) / (max - min);
+
+        for (y = 0; y < input->numCols; y++) {
+            double yTmp = cos(M_PI * (0.5 + ((float) y)) / ((float) input->numCols));
+            double yNode = - ((yTmp + bma + bpa) - 1.0);
+            double yOrig = ((float) input->numCols) * (yNode - min) / (max - min);
+
+            //            nodes->data.F64[x][y] = psImagePixelInterpolate(input, yNode, xNode, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+            //            nodes->data.F64[x][y] = psImagePixelInterpolate(input, yTmp, xTmp, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+            nodes->data.F64[x][y] = psImagePixelInterpolate(input, yOrig, xOrig, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        }
+    }
+
+    // Create the sums[][] data structure.  This
+    // will hold the LHS of
+    // equation
+    // 29 in the ADD: sums[k][l] = SUM {
+    // image(x,y) * Tk(x) * Tl(y) }
+    sums = (double **)psAlloc(coeffs->nX * sizeof(double *));
+    for (i = 0; i < coeffs->nX; i++) {
+        sums[i] = (double *)psAlloc(coeffs->nY * sizeof(double));
+    }
+    // We scale the pixel positions to values
+    // between -1.0 and 1.0
+    rScalingFactors = calcScaleFactors(input->numRows);
+    cScalingFactors = calcScaleFactors(input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly);
+
+    // Compute the sums[][] data structure.
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            sums[i][j] = 0.0;
+            for (x = 0; x < input->numRows; x++) {
+                for (y = 0; y < input->numCols; y++) {
+                    double pixel;
+                    /*
+                                        if (input->type.type == PS_TYPE_S8) {
+                                            pixel = (double) input->data.S8[x][y];
+                                        } else if (input->type.type == PS_TYPE_U16) {
+                                            pixel = (double) input->data.U16[x][y];
+                                        } else if (input->type.type == PS_TYPE_F32) {
+                                            pixel = (double) input->data.F32[x][y];
+                                        } else if (input->type.type == PS_TYPE_F64) {
+                                            pixel = input->data.F64[x][y];
+                                        }
+                    */
+                    pixel = nodes->data.F64[x][y];
+                    sums[i][j] += pixel * psPolynomial1DEval(chebPolys[i], rScalingFactors[x]) *
+                                  psPolynomial1DEval(chebPolys[j], cScalingFactors[y]);
+                }
+            }
+        }
+    }
+
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            coeffs->coeff[i][j] = sums[i][j];
+            coeffs->coeff[i][j] /= (double)(input->numRows * input->numCols);
+
+            if ((i != 0) && (j != 0)) {
+                coeffs->coeff[i][j] *= 4.0;
+            } else if ((i == 0) && (j == 0)) {
+                coeffs->coeff[i][j] *= 1.0;
+            } else {
+                coeffs->coeff[i][j] *= 2.0;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    for (i = 0; i < maxChebyPoly; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    // Free some data
+    for (i = 0; i < coeffs->nX; i++) {
+        psFree(sums[i]);
+    }
+    psFree(sums);
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+    psFree(nodes);
+
+    return (coeffs);
+}
+
+/*****************************************************************************
+XXX: Use static variables for Chebyshev polynomials and scaling factors. 
+ *****************************************************************************/
+psImage* p_psImageEvalPolynomialCheb(psImage* input,
+                                     const psPolynomial2D* coeffs)
+{
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+    float polySum = 0.0;
+
+    // We scale the pixel positions to values between -1.0 and 1.0
+    // Use static data structures here.
+    rScalingFactors = calcScaleFactors(input->numRows);
+    cScalingFactors = calcScaleFactors(input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly);
+
+    for (x = 0; x < input->numRows; x++) {
+        for (y = 0; y < input->numCols; y++) {
+            polySum = 0.0;
+            for (i = 0; i < coeffs->nX; i++) {
+                for (j = 0; j < coeffs->nY; j++) {
+                    polySum +=
+                        psPolynomial1DEval(chebPolys[i], rScalingFactors[x]) *
+                        psPolynomial1DEval(chebPolys[j], cScalingFactors[y]) *
+                        coeffs->coeff[i][j];
+                }
+            }
+
+            if (input->type.type == PS_TYPE_S8) {
+                input->data.S8[x][y] = (char) polySum;
+            } else if (input->type.type == PS_TYPE_U16) {
+                input->data.U16[x][y] = (short int) polySum;
+            } else if (input->type.type == PS_TYPE_F32) {
+                input->data.F32[x][y] = (float) polySum;
+            } else if (input->type.type == PS_TYPE_F64) {
+                input->data.F64[x][y] = polySum;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    // XXX: Use static data structures here.
+    for (i = 0; i < maxChebyPoly; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+
+    return input;
+}
+
+psImage* p_psImageEvalPolynomialOrd(psImage* input,
+                                    const psPolynomial2D* coeffs)
+{
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_ORD, NULL);
+
+    for (int row = 0; row < input->numRows ; row++) {
+        for (int col = 0; col < input->numCols ; col++) {
+            if (input->type.type == PS_TYPE_S8) {
+                input->data.S8[row][col] = (psS8) psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            } else if (input->type.type == PS_TYPE_U16) {
+                input->data.U16[row][col] = (psS16) psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            } else if (input->type.type == PS_TYPE_F32) {
+                input->data.F32[row][col] = psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            } else if (input->type.type == PS_TYPE_F64) {
+                input->data.F64[row][col] = (psF64) psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            }
+        }
+    }
+
+    return(input);
+}
+
+
+/*****************************************************************************
+XXX: I added normal polynomials to this routine.  Let IfA know, put it in the
+psLib SDR.
+ *****************************************************************************/
+psImage* psImageEvalPolynomial(psImage* input,
+                               const psPolynomial2D* coeffs)
+{
+    PS_IMAGE_CHECK_NULL(input, NULL);
+    PS_IMAGE_CHECK_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Unallowable image type.\n");
+    }
+    PS_POLY_CHECK_NULL(coeffs, NULL);
+
+    if (coeffs->type == PS_POLYNOMIAL_ORD) {
+        return(p_psImageEvalPolynomialOrd(input, coeffs));
+    } else if (coeffs->type == PS_POLYNOMIAL_CHEB) {
+        return(p_psImageEvalPolynomialCheb(input, coeffs));
+    }
+    printf("XXX: Error: wrong polynomial type\n");
+    // XXX: psError()
+    return(NULL);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageStats.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageStats.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/image/psImageStats.h	(revision 22271)
@@ -0,0 +1,89 @@
+/** @file psImageStats.h
+*  \brief Routines for calculating statistics on images.
+*  @ingroup ImageStats
+*
+*  This file will hold the prototypes for procedures which calculate
+*  statistic on images, histograms on images, and fit/evaluate Chebyshev
+*  polynomials to images.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-03-24 19:39:53 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#if !defined(PS_IMAGE_STATS_H)
+#define PS_IMAGE_STATS_H
+
+#include "psType.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psFunctions.h"
+
+/// @addtogroup ImageStats
+/// @{
+
+/** This routine must determine the various statistics for the image.
+ *
+ *  Determine statistics for image (or subimage). The statistics to be 
+ *  determined are specified by stats. The mask allows pixels to be excluded 
+ *  if their corresponding mask pixel value matches the value of maskVal. 
+ *  This function must be defined for the following types: psS8, psU16, psF32, 
+ *  psF64.
+ *
+ *  @return psStats*    the resulting statistics result(s)
+ */
+psStats* psImageStats(
+    psStats* stats,                    ///< defines statistics to be calculated
+    const psImage* in,                 ///< image (or subimage) to calculate stats
+    const psImage* mask,               ///< mask data for image (NULL ok)
+    psS32 maskVal                      ///< mask Mask for mask
+);
+
+/** Construct a histogram from an image (or subimage).
+ *
+ *  The histogram to generate is specified by psHistogram hist (see section 
+ *  4.3.2 in SDRS). This function must be defined for the following types: 
+ *  psS8, psU16, psF32, psF64.
+ *
+ *  @return psHistogram*     the resulting histogram
+ */
+psHistogram* psImageHistogram(
+    psHistogram* out,                  ///< input histogram description & target
+    const psImage* in,                 ///< Image data to be histogramed.
+    const psImage* mask,               ///< mask data for image (NULL ok)
+    psU32 maskVal                      ///< mask Mask for mask
+);
+
+/** Fit a 2-D polynomial surface to an image.
+ *
+ *  The input structure coeffs contains the desired order and terms of 
+ *  interest. This function must be defined for the following types: psS8, 
+ *  psU16, psF32, psF64.
+ *
+ *  @return psPolynomial2D*     fitted polynomial result
+ *
+ */
+psPolynomial2D* psImageFitPolynomial(
+    psPolynomial2D* coeffs,            ///< coefficient structure carries in desired terms & target
+    const psImage* input
+);
+
+/** Evaluate a 2-D polynomial surface for the image pixels.
+ *
+ *  Given the input polynomial coefficients, set the image pixel values on the 
+ *  basis of the polynomial function. This function must be defined for the 
+ *  following types: psS8, psU16, psF32, psF64.
+ *
+ *  @return psImage*    the resulting image
+ */
+psImage* psImageEvalPolynomial(
+    psImage* input,                    ///< input image
+    const psPolynomial2D* coeffs       ///< coefficient structure carries in desired terms
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageConvolve.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageConvolve.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageConvolve.c	(revision 22271)
@@ -0,0 +1,479 @@
+/*  @file  psImageConvolve.c
+ *
+ *  @brief Contains FFT transform related functions for psImage.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-15 00:12:08 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+
+#include "psImageConvolve.h"
+#include "psImageFFT.h"
+#include "psImageExtraction.h"
+#include "psBinaryOp.h"
+#include "psMemory.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psImageIO.h"
+
+#include "psImageErrors.h"
+
+#define FOURIER_PADDING 32 /* padding amount in every side of the image for fourier convolution */
+
+static void freeKernel(psKernel* ptr);
+
+psKernel* psKernelAlloc(psS32 xMin, psS32 xMax, psS32 yMin, psS32 yMax)
+{
+    psKernel* result;
+    psS32 numRows;
+    psS32 numCols;
+
+    // following is explicitly spelled out in the SDRS as a requirement
+    if (yMin > yMax) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "Specified yMin, %d, was greater than yMax, %d.  Values swapped.",
+                 yMin, yMax);
+
+        psS32 temp = yMin;
+        yMin = yMax;
+        yMax = temp;
+    }
+
+    // following is explicitly spelled out in the SDRS as a requirement
+    if (xMin > xMax) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "Specified xMin, %d, was greater than xMax, %d.  Values swapped.",
+                 xMin, xMax);
+
+        psS32 temp = xMin;
+        xMin = xMax;
+        xMax = temp;
+    }
+
+    numRows = yMax - yMin + 1;
+    numCols = xMax - xMin + 1;
+
+    result = psAlloc(sizeof(psKernel));
+    result->xMin = xMin;
+    result->xMax = xMax;
+    result->yMin = yMin;
+    result->yMax = yMax;
+    result->image = psImageAlloc(numCols,numRows,PS_TYPE_KERNEL);
+    memset(result->image->rawDataBuffer,0,numCols*numRows*PSELEMTYPE_SIZEOF(PS_TYPE_KERNEL));
+    result->p_kernelRows = psAlloc(sizeof(psKernelType*)*numRows);
+
+    psKernelType** kernelRows = result->p_kernelRows;
+    psKernelType** imageRows = result->image->data.PS_TYPE_KERNEL_DATA;
+    for (psS32 i = 0; i < numRows; i++) {
+        kernelRows[i] = imageRows[i] - xMin;
+    }
+    result->kernel = kernelRows - yMin;
+
+    psMemSetDeallocator(result,(psFreeFcn)freeKernel);
+
+    return result;
+}
+
+void freeKernel(psKernel* ptr)
+{
+    if (ptr != NULL) {
+        psFree(ptr->image);
+        psFree(ptr->p_kernelRows);
+    }
+}
+
+psKernel* psKernelGenerate(const psVector* tShifts,
+                           const psVector* xShifts,
+                           const psVector* yShifts,
+                           psBool relative)
+{
+    psS32 lastX;
+    psS32 lastY;
+    psS32 lastT;
+    psS32 x;
+    psS32 y;
+    psS32 t;
+    psS32 xMin = 0;
+    psS32 xMax = 0;
+    psS32 yMin = 0;
+    psS32 yMax = 0;
+    psS32 length = 0;
+    psKernelType normalizeTime = 1.0;  // fraction of total time for each shift clock
+    psKernel* result = NULL;
+    psKernelType** kernel = NULL;
+
+    // got non-NULL vectors?
+    if (tShifts == NULL || xShifts == NULL || yShifts == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageConvolve_SHIFT_NULL);
+        return NULL;
+    }
+
+    // types match?
+    if (xShifts->type.type != yShifts->type.type ||
+            tShifts->type.type != xShifts->type.type) {
+        char* typeXStr;
+        char* typeYStr;
+        char* typeTStr;
+        PS_TYPE_NAME(typeXStr,xShifts->type.type);
+        PS_TYPE_NAME(typeYStr,yShifts->type.type);
+        PS_TYPE_NAME(typeTStr,tShifts->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImageConvolve_SHIFT_TYPE_MISMATCH,
+                typeTStr, typeXStr, typeYStr);
+        return NULL;
+    }
+
+    // sizes match?
+    length = xShifts->n;
+    if (length != yShifts->n ||
+            length != tShifts->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Shift vectors can not be of different sizes.");
+        return NULL;
+    }
+
+    // if no shifts, the kernel is just a 1 at 0,0
+    if (length < 1) {
+        result = psKernelAlloc(0,0,0,0);
+        result->kernel[0][0] = 1;
+        return result;
+    }
+
+    #define KERNEL_GENERATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *tShiftData = tShifts->data.TYPE; \
+        ps##TYPE *xShiftData = xShifts->data.TYPE; \
+        ps##TYPE *yShiftData = yShifts->data.TYPE; \
+        lastX = xShiftData[length-1]; \
+        lastY = yShiftData[length-1]; \
+        lastT = tShiftData[length-1]; \
+        \
+        for (int lcv = 0; lcv < length; lcv++) { \
+            x = lastX - xShiftData[lcv]; \
+            y = lastY - yShiftData[lcv]; \
+            \
+            if (x < xMin) { \
+                xMin = x; \
+            } else if (x > xMax) { \
+                xMax = x; \
+            } \
+            if (y < yMin) { \
+                yMin = y; \
+            } else if (y > yMax) { \
+                yMax = y; \
+            } \
+        } \
+        \
+        normalizeTime = 1.0 / (psKernelType)(tShiftData[length-1]); \
+        result = psKernelAlloc(xMin,xMax,yMin,yMax); \
+        kernel = result->kernel; \
+        \
+        psS32 prevT = 0; \
+        for (int i = 0; i < length; i++) { \
+            t = tShiftData[i] - prevT; \
+            x = lastX - xShiftData[i]; \
+            y = lastY - yShiftData[i]; \
+            \
+            kernel[y][x] += (psKernelType)t / (psKernelType)lastT; \
+            prevT = tShiftData[i]; \
+        } \
+        break; \
+    }
+
+    #define RELATIVE_KERNEL_GENERATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *tShiftData = tShifts->data.TYPE; \
+        ps##TYPE *xShiftData = xShifts->data.TYPE; \
+        ps##TYPE *yShiftData = yShifts->data.TYPE; \
+        \
+        x = 0; \
+        y = 0; \
+        t = 0; \
+        \
+        for (int lcv = length-1; lcv >= 0; lcv--) { \
+            t += tShiftData[lcv]; \
+            \
+            if (x < xMin) { \
+                xMin = x; \
+            } else if (x > xMax) { \
+                xMax = x; \
+            } \
+            if (y < yMin) { \
+                yMin = y; \
+            } else if (y > yMax) { \
+                yMax = y; \
+            } \
+            x -= xShiftData[lcv]; \
+            y -= yShiftData[lcv]; \
+            \
+        } \
+        result = psKernelAlloc(xMin,xMax,yMin,yMax); \
+        kernel = result->kernel; \
+        \
+        normalizeTime = 1.0 / (psKernelType)t; \
+        x = 0; \
+        y = 0; \
+        for (psS32 i = length-1; i >= 0; i--) { \
+            kernel[y][x] += (psKernelType)(tShiftData[i]) * normalizeTime; \
+            x -= xShiftData[i]; \
+            y -= yShiftData[i]; \
+            \
+        } \
+        break; \
+    }
+
+    if (relative) {
+        switch (xShifts->type.type) {
+            RELATIVE_KERNEL_GENERATE_CASE(U8);
+            RELATIVE_KERNEL_GENERATE_CASE(U16);
+            RELATIVE_KERNEL_GENERATE_CASE(U32);
+            RELATIVE_KERNEL_GENERATE_CASE(U64);
+            RELATIVE_KERNEL_GENERATE_CASE(S8);
+            RELATIVE_KERNEL_GENERATE_CASE(S16);
+            RELATIVE_KERNEL_GENERATE_CASE(S32);
+            RELATIVE_KERNEL_GENERATE_CASE(S64);
+            RELATIVE_KERNEL_GENERATE_CASE(F32);
+            RELATIVE_KERNEL_GENERATE_CASE(F64);
+            RELATIVE_KERNEL_GENERATE_CASE(C32);
+            RELATIVE_KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
+        }
+    } else {
+        switch (xShifts->type.type) {
+            KERNEL_GENERATE_CASE(U8);
+            KERNEL_GENERATE_CASE(U16);
+            KERNEL_GENERATE_CASE(U32);
+            KERNEL_GENERATE_CASE(U64);
+            KERNEL_GENERATE_CASE(S8);
+            KERNEL_GENERATE_CASE(S16);
+            KERNEL_GENERATE_CASE(S32);
+            KERNEL_GENERATE_CASE(S64);
+            KERNEL_GENERATE_CASE(F32);
+            KERNEL_GENERATE_CASE(F64);
+            KERNEL_GENERATE_CASE(C32);
+            KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
+        }
+    }
+
+    return result;
+}
+
+psImage* psImageConvolve(psImage* out, const psImage* in, const psKernel* kernel, psBool direct)
+{
+    if (in == NULL) {
+        psFree(out);
+        return NULL;
+    }
+
+    if (kernel == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImageConvolve_KERNEL_NULL);
+        psFree(out);
+        return NULL;
+    }
+    psS32 xMin = kernel->xMin;
+    psS32 xMax = kernel->xMax;
+    psS32 yMin = kernel->yMin;
+    psS32 yMax = kernel->yMax;
+    psKernelType** kData = kernel->kernel;
+
+    // make the output image to the proper size and type
+    psS32 numRows = in->numRows;
+    psS32 numCols = in->numCols;
+
+
+
+    if (direct) {
+        // spatial convolution
+
+        #define SPATIAL_CONVOLVE_CASE(TYPE) \
+    case PS_TYPE_##TYPE: { \
+            ps##TYPE** inData = in->data.TYPE; \
+            out = psImageRecycle(out, numCols, numRows, PS_TYPE_##TYPE); \
+            for (psS32 row=0;row<numRows;row++) { \
+                ps##TYPE* outRow = out->data.TYPE[row]; \
+                for (psS32 col=0;col<numCols;col++) { \
+                    ps##TYPE pixel = 0.0; \
+                    for (psS32 kRow = yMin; kRow < yMax; kRow++) { \
+                        if (row-kRow >= 0 && row-kRow < numRows) { \
+                            for (psS32 kCol = xMin; kCol < xMax; kCol++) { \
+                                if (col-kCol >= 0 && col-kCol < numCols) { \
+                                    pixel += kData[kRow][kCol] * inData[row-kRow][col-kCol]; \
+                                } \
+                            } \
+                        } \
+                    } \
+                    outRow[col] = pixel; \
+                } \
+            } \
+        } \
+        break;
+
+        switch (in->type.type) {
+            SPATIAL_CONVOLVE_CASE(U8)
+            SPATIAL_CONVOLVE_CASE(U16)
+            SPATIAL_CONVOLVE_CASE(U32)
+            SPATIAL_CONVOLVE_CASE(U64)
+            SPATIAL_CONVOLVE_CASE(S8)
+            SPATIAL_CONVOLVE_CASE(S16)
+            SPATIAL_CONVOLVE_CASE(S32)
+            SPATIAL_CONVOLVE_CASE(S64)
+            SPATIAL_CONVOLVE_CASE(F32)
+            SPATIAL_CONVOLVE_CASE(F64)
+            SPATIAL_CONVOLVE_CASE(C32)
+            SPATIAL_CONVOLVE_CASE(C64)
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,in->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+                psFree(out);
+                return NULL;
+
+            }
+        }
+
+
+    } else {
+        // fourier convolution
+        psS32 paddedCols = numCols+2*FOURIER_PADDING;
+        psS32 paddedRows = numRows+2*FOURIER_PADDING;
+
+        // check to see if kernel is smaller, otherwise padding it up will fail.
+        psS32 kRows = kernel->image->numRows;
+        psS32 kCols = kernel->image->numCols;
+        if (kRows >= numRows || kCols >= numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psImageConvolve_KERNEL_TOO_LARGE,
+                    kCols,kRows,
+                    numCols, numRows);
+            psFree(out);
+            return NULL;
+        }
+
+        // pad the image
+        psImage* paddedImage = psImageAlloc(paddedCols,paddedRows,in->type.type);
+        psS32 elementSize = PSELEMTYPE_SIZEOF(in->type.type);
+
+        // zero out padded area on top and bottom
+        memset(paddedImage->data.U8[0],0,FOURIER_PADDING*paddedCols*elementSize);
+        memset(paddedImage->data.U8[FOURIER_PADDING+numRows-1],0,FOURIER_PADDING*paddedCols*elementSize);
+
+        // fill in the image-containing rows.
+        psS32 sidePaddingSize = FOURIER_PADDING*elementSize;
+        psS32 imageRowSize = numCols*elementSize;
+        psU8* paddedData = paddedImage->data.U8[FOURIER_PADDING];
+        for (psS32 row=0;row<numRows;row++) {
+            // zero out padded area on left edge.
+            memset(paddedData,0,sidePaddingSize);
+            paddedData += sidePaddingSize;
+            memcpy(paddedData,in->data.U8[row],imageRowSize);
+            paddedData += imageRowSize;
+            // zero out padded area on right edge.
+            memset(paddedData,0,sidePaddingSize);
+            paddedData += sidePaddingSize;
+        }
+
+        // pad the kernel to the same size of paddedImage
+        psImage* paddedKernel = psImageAlloc(paddedCols,paddedRows,PS_TYPE_KERNEL);
+        memset(paddedKernel->data.U8[0],0,sizeof(psKernelType)*numCols*numRows); // zero-out image
+        psS32 yMax = kernel->yMax;
+        psS32 xMax = kernel->xMax;
+        for (psS32 row = kernel->yMin; row <= yMax;row++) {
+            psS32 padRow = row;
+            if (padRow < 0) {
+                padRow += paddedRows;
+            }
+            psKernelType* padData = paddedKernel->data.PS_TYPE_KERNEL_DATA[padRow];
+            psKernelType* kernelRow = kernel->kernel[row];
+            for (psS32 col = kernel->xMin; col <= xMax; col++) {
+                if (col < 0) {
+                    padData[col+paddedCols] = kernelRow[col];
+                } else {
+                    padData[col] = kernelRow[col];
+                }
+            }
+        }
+
+        psImage* kernelFourier = psImageFFT(NULL, paddedKernel, PS_FFT_FORWARD);
+        if (kernelFourier == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psImageConvolve_KERNEL_FFT_FAILED);
+            psFree(out);
+            return NULL;
+        }
+
+        psImage* inFourier = psImageFFT(NULL, paddedImage, PS_FFT_FORWARD);
+        if (inFourier == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psImageConvolve_FFT_FAILED);
+            psFree(out);
+            return NULL;
+        }
+
+        // convolution in fourier domain is just a pixel-wise multiplication
+        for (int row = 0; row < paddedRows; row++) {
+            psC32* inRow = inFourier->data.C32[row];
+            psC32* kRow = kernelFourier->data.C32[row];
+            for (int col = 0; col < paddedCols; col++) {
+                inRow[col] *= kRow[col];
+            }
+        }
+
+        psImage* complexOut = psImageFFT(NULL, inFourier,
+                                         PS_FFT_REVERSE);
+        if (complexOut == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psImageConvolve_FFT_FAILED);
+            psFree(out);
+            return NULL;
+        }
+
+        // subset out the padded area now.
+        psImage* complexOutSansPad = psImageSubset(complexOut,
+                                     FOURIER_PADDING,FOURIER_PADDING,
+                                     FOURIER_PADDING+numCols,FOURIER_PADDING+numRows);
+
+        out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32);
+        float factor = 1.0f/(float)paddedCols/(float)paddedRows;
+        for (psS32 row = 0; row < numRows; row++) {
+            psF32* outRow = out->data.F32[row];
+            psC32* resultRow = complexOutSansPad->data.C32[row];
+            for (psS32 col = 0; col < numCols; col++) {
+                outRow[col] = crealf(resultRow[col])*factor;
+            }
+        }
+
+        psFree(complexOut); // frees complexOutSansPad, as it is a child.
+        psFree(kernelFourier);
+        psFree(inFourier);
+        psFree(paddedImage);
+        psFree(paddedKernel);
+
+    }
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageConvolve.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageConvolve.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageConvolve.h	(revision 22271)
@@ -0,0 +1,121 @@
+/** @file  psImageConvolve.h
+ *
+ *  @brief image convolution functionality
+ *
+ *  @ingroup Transform
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_IMAGE_CONVOLVE_H
+#define PS_IMAGE_CONVOLVE_H
+
+#include "psImage.h"
+#include "psVector.h"
+#include "psType.h"
+
+#define PS_TYPE_KERNEL PS_TYPE_F32     /**< the data member to use for kernel image */
+#define PS_TYPE_KERNEL_DATA F32        /**< the data member to use for kernel image */
+#define PS_TYPE_KERNEL_NAME "psF32"    /**< the data type for kernel as a string */
+
+typedef psF32 psKernelType;
+
+/** A convolution kernel */
+typedef struct
+{
+    psImage* image;                    ///< Kernel data, in the form of an image
+    psS32 xMin;                          ///< Most negative x index
+    psS32 yMin;                          ///< Most negative y index
+    psS32 xMax;                          ///< Most positive x index
+    psS32 yMax;                          ///< Most positive y index
+    psKernelType** kernel;             ///< Pointer to the kernel data
+    psKernelType** p_kernelRows;       ///< Pointer to the rows of the kernel data; not intended for user use.
+}
+psKernel;
+
+/** Allocates a convolution kernel of the given range
+ *
+ *  In order to perform a convolution, we need to define the convolution 
+ *  kernel. We need a more general object than a psImage so that we can 
+ *  incorporate the offset from the (0, 0) pixel to the (0, 0) value of the 
+ *  kernel. It might be convenient to allow both positive and negative 
+ *  indices to convey the positive and negative shifts. One might consider 
+ *  setting the x0 and y0 members of a psImage to the appropriate offsets, 
+ *  but this is not the purpose of these members, and doing so may affect the 
+ *  behavior of other psImage operations.
+ *
+ *  This construction allows the kernel member to use negative indices, while 
+ *  preserving the location of psMemBlocks relative to allocated memory.
+ *
+ *  The maximum extent of the kernel shifts shall be defined by the xMin, 
+ *  xMax, yMin and yMax members. Note that xMin and yMin, under normal 
+ *  circumstances, should be negative numbers. That is, 
+ *  myKernel->kernel[-3][-2] may be defined if yMin and xMin are equal to or 
+ *  more negative than -3 and -2, respectively.
+ *
+ *  In the event that one of the minimum values is greater than the 
+ *  corresponding maximum value, the function shall generate a warning, and 
+ *  the offending values shall be exchanged.
+ *
+ *  @return psKernel*          A new kernel object
+ */
+psKernel* psKernelAlloc(
+    psS32 xMin,                          ///< Most negative x index
+    psS32 xMax,                          ///< Most positive x index
+    psS32 yMin,                          ///< Most negative y index
+    psS32 yMax                           ///< Most positive y index
+);
+
+/** Generates a kernel given a list of shift values
+ *
+ *  Given a list of values (e.g., shifts made in the course of OT guiding), 
+ *  psKernelGenerate shall return the appropriate kernel.  The vectors xShifts 
+ *  and yShifts, which are a list of shifts relative to some starting point, 
+ *  will be supplied by the user. The elements of the vectors should be of an 
+ *  integer type; otherwise the values shall be truncated to integers. The 
+ *  output kernel shall be normalized such that the sum over the kernel is 
+ *  unity. 
+ *
+ *  If the vectors are not of the same number of elements, then the function 
+ *  shall generate a warning shall be generated, following which, the longer 
+ *  vector trimmed to the length of the shorter, and the function shall continue.
+ *
+ *  @return psKernel*    new Kernel object
+ */
+psKernel* psKernelGenerate(
+    const psVector* tShifts,           ///< list of time shifts
+    const psVector* xShifts,           ///< list of x-axis shifts
+    const psVector* yShifts,           ///< list of y-axis shifts
+    psBool relative
+);
+
+/** convolve an image with a kernel
+ *
+ *  Given an input image and the convolution kernel, psImageConvolve shall 
+ *  convolve the input image, in, with the kernel, kernel and return the 
+ *  convolved image, out.
+ * 
+ *  Two methods shall be available for the convolution: if direct is true, 
+ *  then the convolution shall be performed in real space (appropriate for 
+ *  small kernels); otherwise, the convolution shall be performed using Fast 
+ *  Fourier Transforms (FFTs; appropriate for larger kernels). The latter 
+ *  option involves padding the input image, copying the kernel into an image 
+ *  of the same size as the padded input image, performing an FFT on each, 
+ *  multiplying the FFTs, and performing an inverse FFT before trimming the 
+ *  image back to the original size.
+ *
+ *  @return psImage*  resulting image 
+ */
+psImage* psImageConvolve(
+    psImage* out,                      ///< a psImage to recycle.  If NULL, a new psImage is made.
+    const psImage* in,                 ///< the psImage to convolve
+    const psKernel* kernel,            ///< kernel to colvolve with
+    psBool direct                        ///< specifies method, true=direct convolution, false=fourier
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageStats.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageStats.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageStats.c	(revision 22271)
@@ -0,0 +1,635 @@
+/** @file psImageStats.c
+ *  \brief Routines for calculating statistics on images.
+ *  @ingroup ImageStats
+ *
+ *  This file will hold the prototypes for procedures which calculate
+ *  statistic on images, histograms on images, and fit/evaluate Chebyshev
+ *  polynomials to images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.73 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+#include "psMemory.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psStats.h"
+#include "psImage.h"
+#include "psFunctions.h"
+#include "psImageStats.h"
+#include "psConstants.h"
+#include "psImageErrors.h"
+
+/// This routine must determine the various statistics for the image.
+/*****************************************************************************
+psImageStats(stats, in, mask, maskVal): this routine simply calls the
+psVectorStats() routine, which does the actual statistical calculation.  In
+order to do so, we create dummy psVectors and set their "data" pointer to that
+of the input psImages.
+ 
+XXX: use static psVectors
+ 
+XXX: optimize this.  2k vs 4k, sample mean, takes8 seconds on Gene's machine.
+Should take .2.
+ *****************************************************************************/
+psStats* psImageStats(psStats* stats,
+                      const psImage* in,
+                      const psImage* mask,
+                      psS32 maskVal)
+{
+    psVector *junkData = NULL;
+    psVector *junkMask = NULL;
+
+    PS_PTR_CHECK_NULL(stats, NULL);
+    PS_INT_CHECK_ZERO(stats->options, NULL);
+    PS_IMAGE_CHECK_NULL(in, NULL)
+    if (mask != NULL) {
+        PS_IMAGE_CHECK_TYPE(mask, PS_TYPE_U8, NULL);
+        PS_IMAGE_CHECK_SIZE_EQUAL(in, mask, NULL);
+    }
+
+    if (in->parent == NULL) {
+        // stuff the image data into a psVector struct.
+        junkData = (psVector *) psAlloc(sizeof(psVector));
+        junkData->type = in->type;
+        *(int*)&junkData->nalloc = in->numRows * in->numCols;
+        junkData->n = junkData->nalloc;
+        junkData->data.U8 = in->data.V[0];      // since psImage data is contiguous...
+    } else {
+        // image not necessarily contiguous
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        int rowSize = numCols * (PSELEMTYPE_SIZEOF(in->type.type));
+
+        junkData = psVectorAlloc(numRows*numCols, in->type.type);
+        junkData->n = junkData->nalloc;
+
+        psU8* data = junkData->data.U8;
+        for (int row = 0; row < numRows; row++) {
+            memcpy(data, in->data.V[row], rowSize);
+            data += rowSize;
+        }
+    }
+
+    if (mask != NULL) {
+        if (mask->parent == NULL) {
+            // stuff the mask data into a psVector struct.
+            junkMask = psAlloc(sizeof(psVector));
+            junkMask->type = mask->type;
+            *(int*)&junkMask->nalloc = mask->numRows * mask->numCols;
+            junkMask->n = junkMask->nalloc;
+            junkMask->data.U8 = mask->data.V[0];
+        } else {
+            // image not necessarily contiguous
+            int numRows = mask->numRows;
+            int numCols = mask->numCols;
+            int rowSize = numCols * (PSELEMTYPE_SIZEOF(mask->type.type));
+
+            junkMask = psVectorAlloc(numRows*numCols, mask->type.type);
+            junkMask->n = junkMask->nalloc;
+
+            psU8* data = junkMask->data.U8;
+            for (int row = 0; row < numRows; row++) {
+                memcpy(data, mask->data.V[row], rowSize);
+                data += rowSize;
+            }
+        }
+    }
+
+    stats = psVectorStats(stats, junkData, NULL, junkMask, maskVal);
+
+    psFree(junkMask);
+    psFree(junkData);
+    return (stats);
+}
+
+/*****************************************************************************
+NOTE: We assume that the psHistogram structure out has already been allocated
+and initialized.
+ *****************************************************************************/
+psHistogram* psImageHistogram(psHistogram* out,
+                              const psImage* in,
+                              const psImage* mask,
+                              psU32 maskVal)
+{
+    PS_PTR_CHECK_NULL(out, NULL);
+    PS_PTR_CHECK_NULL(in, NULL);
+    if (mask != NULL) {
+        PS_IMAGE_CHECK_TYPE(mask, PS_TYPE_U8, NULL);
+        PS_IMAGE_CHECK_SIZE_EQUAL(in, mask, NULL);
+    }
+    psVector* junkData = NULL;
+    psVector* junkMask = NULL;
+
+    if (in->parent == NULL) {
+        // stuff the image data into a psVector struct.
+        junkData = (psVector *) psAlloc(sizeof(psVector));
+        junkData->type = in->type;
+        *(int*)&junkData->nalloc = in->numRows * in->numCols;
+        junkData->n = junkData->nalloc;
+        junkData->data.U8 = in->data.V[0];      // since psImage data is contiguous...
+    } else {
+        // image not necessarily contiguous
+        int numRows = in->numRows;
+        int numCols = in->numCols;
+        int rowSize = numCols * (PSELEMTYPE_SIZEOF(in->type.type));
+
+        junkData = psVectorAlloc(numRows*numCols, in->type.type);
+        junkData->n = junkData->nalloc;
+
+        psU8* data = junkData->data.U8;
+        for (int row = 0; row < numRows; row++) {
+            memcpy(data, in->data.V[row], rowSize);
+            data += rowSize;
+        }
+    }
+
+    if (mask != NULL) {
+        if (mask->parent == NULL) {
+            // stuff the mask data into a psVector struct.
+            junkMask = psAlloc(sizeof(psVector));
+            junkMask->type = mask->type;
+            *(int*)&junkMask->nalloc = mask->numRows * mask->numCols;
+            junkMask->n = junkMask->nalloc;
+            junkMask->data.U8 = mask->data.V[0];
+        } else {
+            // image not necessarily contiguous
+            int numRows = mask->numRows;
+            int numCols = mask->numCols;
+            int rowSize = numCols * (PSELEMTYPE_SIZEOF(mask->type.type));
+
+            junkMask = psVectorAlloc(numRows*numCols, mask->type.type);
+            junkMask->n = junkMask->nalloc;
+
+            psU8* data = junkMask->data.U8;
+            for (int row = 0; row < numRows; row++) {
+                memcpy(data, mask->data.V[row], rowSize);
+                data += rowSize;
+            }
+        }
+    }
+
+    out = psVectorHistogram(out, junkData, NULL, junkMask, maskVal);
+
+    psFree(junkMask);
+    psFree(junkData);
+
+    return (out);
+}
+
+/*****************************************************************************
+calcScaleFactorsEval(n): The Chebyshev polynomials are defined over the
+interval [-1.0 : 1.0].  Images typically have sizes of 512x512 or more.  In
+order to use Chebyshev polynomials, we must scale the coordinates from
+0:512 to -1:1.  This routine takes as input an integer N and produces as
+output a vector of evenly spaced floating point values between -1.0:1.0.
+ 
+XXX: Use the p_psNormalizeVector here?
+ *****************************************************************************/
+double* calcScaleFactors(psS32 n)
+{
+    PS_INT_CHECK_NON_NEGATIVE(n, NULL);
+    psS32 i = 0;
+    double tmp = 0.0;
+    double *scalingFactors = (double *)psAlloc(n * sizeof(double));
+
+    for (i = 0; i < n; i++) {
+        tmp = (double)(n - i);
+        tmp = (M_PI * (tmp - 0.5)) / ((double)n);
+        scalingFactors[i] = cos(tmp);
+    }
+
+    return (scalingFactors);
+}
+
+// XXX: Use a static array of Chebyshev polynomials.
+psPolynomial1D **p_psCreateChebyshevPolys(psS32 maxChebyPoly)
+{
+    PS_INT_CHECK_POSITIVE(maxChebyPoly, NULL);
+    psPolynomial1D **chebPolys = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+
+    chebPolys = (psPolynomial1D **) psAlloc(maxChebyPoly * sizeof(psPolynomial1D *));
+    for (i = 0; i < maxChebyPoly; i++) {
+        chebPolys[i] = psPolynomial1DAlloc(i + 1, PS_POLYNOMIAL_ORD);
+    }
+
+    // Create the Chebyshev polynomials.
+    // Polynomial i has i-th order.
+    chebPolys[0]->coeff[0] = 1;
+    chebPolys[1]->coeff[1] = 1;
+    for (i = 2; i < maxChebyPoly; i++) {
+        for (j = 0; j < chebPolys[i - 1]->n; j++) {
+            chebPolys[i]->coeff[j + 1] = 2 * chebPolys[i - 1]->coeff[j];
+        }
+        for (j = 0; j < chebPolys[i - 2]->n; j++) {
+            chebPolys[i]->coeff[j] -= chebPolys[i - 2]->coeff[j];
+        }
+    }
+
+    return (chebPolys);
+}
+
+/*****************************************************************************
+psImageFitPolynomial(): This routine takes as input a 2-D image and produces
+as output the coefficients of the Chebyshev polynomials which match that
+input image.
+  Input:
+  Output:
+  Internal Data Structures:
+    chebPolys[i][j] 
+    sums[i][j]: This will contain the sum of 
+                input->data.F32[x][y] *
+                psPolynomial1DEval(
+chebPolys[i],
+(float) x) *
+                psPolynomial1DEval(
+chebPolys[j],
+(float) y, 
+);
+        over all pixels (x,y) in the image.
+  *****************************************************************************/
+psPolynomial2D* psImageFitPolynomial(psPolynomial2D* coeffs,
+                                     const psImage* input)
+{
+    PS_IMAGE_CHECK_NULL(input, NULL);
+    PS_IMAGE_CHECK_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unallowable image type.\n");
+    }
+    PS_POLY_CHECK_NULL(coeffs, NULL);
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    double **sums = NULL;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+
+    // Create the sums[][] data structure.  This
+    // will hold the LHS of
+    // equation
+    // 29 in the ADD: sums[k][l] = SUM {
+    // image(x,y) * Tk(x) * Tl(y) }
+    sums = (double **)psAlloc(coeffs->nX * sizeof(double *));
+    for (i = 0; i < coeffs->nX; i++) {
+        sums[i] = (double *)psAlloc(coeffs->nY * sizeof(double));
+    }
+    // We scale the pixel positions to values
+    // between -1.0 and 1.0
+    rScalingFactors = calcScaleFactors(input->numRows);
+    cScalingFactors = calcScaleFactors(input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly);
+
+    // Compute the sums[][] data structure.
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            sums[i][j] = 0.0;
+            for (x = 0; x < input->numRows; x++) {
+                for (y = 0; y < input->numCols; y++) {
+                    double pixel = 0.0;
+                    if (input->type.type == PS_TYPE_S8) {
+                        pixel = (double) input->data.S8[x][y];
+                    } else if (input->type.type == PS_TYPE_U16) {
+                        pixel = (double) input->data.U16[x][y];
+                    } else if (input->type.type == PS_TYPE_F32) {
+                        pixel = (double) input->data.F32[x][y];
+                    } else if (input->type.type == PS_TYPE_F64) {
+                        pixel = input->data.F64[x][y];
+                    }
+                    sums[i][j] += pixel * psPolynomial1DEval(chebPolys[i],rScalingFactors[x]) *
+                                  psPolynomial1DEval(chebPolys[j], cScalingFactors[y]);
+                }
+            }
+        }
+    }
+
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            coeffs->coeff[i][j] = sums[i][j];
+            coeffs->coeff[i][j] /= (double)(input->numRows * input->numCols);
+
+            if ((i != 0) && (j != 0)) {
+                coeffs->coeff[i][j] *= 4.0;
+            } else if ((i == 0) && (j == 0)) {
+                coeffs->coeff[i][j] *= 1.0;
+            } else {
+                coeffs->coeff[i][j] *= 2.0;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    for (i = 0; i < maxChebyPoly; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    // Free some data
+    for (i = 0; i < coeffs->nX; i++) {
+        psFree(sums[i]);
+    }
+    psFree(sums);
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+
+    return (coeffs);
+}
+
+
+
+
+/*****************************************************************************
+psImageFitPolynomial(): This routine takes as input a 2-D image and produces
+as output the coefficients of the Chebyshev polynomials which match that input
+image.  This is a TEST version of the code.  It is not used by anything.
+  Input:
+  Output:
+  Internal Data Structures:
+    chebPolys[i][j] 
+    sums[i][j]: This will contain the sum of 
+                input->data.F32[x][y] *
+                psPolynomial1DEval(
+chebPolys[i],
+(float) x) *
+                psPolynomial1DEval(
+chebPolys[j],
+(float) y, 
+);
+        over all pixels (x,y) in the image.
+  *****************************************************************************/
+psPolynomial2D* psImageFitPolynomialTest(psPolynomial2D* coeffs,
+        const psImage* input)
+{
+    PS_IMAGE_CHECK_NULL(input, NULL);
+    PS_IMAGE_CHECK_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unallowable image type.\n");
+    }
+    PS_POLY_CHECK_NULL(coeffs, NULL);
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    double **sums = NULL;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+    psImage *nodes = psImageAlloc(input->numCols, input->numRows, PS_TYPE_F64);
+
+    double min = -1.0;
+    double max = 1.0;
+    double bma = 0.5 * (max-min);  // 1
+    double bpa = 0.5 * (max+min);  // 0
+    // We must calculate the value of the image at the nodes where the
+    // Chebyshev polynomials are 0.
+    for (x = 0; x < input->numRows; x++) {
+        double xTmp = cos(M_PI * (0.5 + ((float) x)) / ((float) input->numRows));
+        double xNode = - ((xTmp + bma + bpa) - 1.0);
+        double xOrig = ((float) input->numRows) * (xNode - min) / (max - min);
+
+        for (y = 0; y < input->numCols; y++) {
+            double yTmp = cos(M_PI * (0.5 + ((float) y)) / ((float) input->numCols));
+            double yNode = - ((yTmp + bma + bpa) - 1.0);
+            double yOrig = ((float) input->numCols) * (yNode - min) / (max - min);
+
+            //            nodes->data.F64[x][y] = psImagePixelInterpolate(input, yNode, xNode, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+            //            nodes->data.F64[x][y] = psImagePixelInterpolate(input, yTmp, xTmp, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+            nodes->data.F64[x][y] = psImagePixelInterpolate(input, yOrig, xOrig, NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+        }
+    }
+
+    // Create the sums[][] data structure.  This
+    // will hold the LHS of
+    // equation
+    // 29 in the ADD: sums[k][l] = SUM {
+    // image(x,y) * Tk(x) * Tl(y) }
+    sums = (double **)psAlloc(coeffs->nX * sizeof(double *));
+    for (i = 0; i < coeffs->nX; i++) {
+        sums[i] = (double *)psAlloc(coeffs->nY * sizeof(double));
+    }
+    // We scale the pixel positions to values
+    // between -1.0 and 1.0
+    rScalingFactors = calcScaleFactors(input->numRows);
+    cScalingFactors = calcScaleFactors(input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly);
+
+    // Compute the sums[][] data structure.
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            sums[i][j] = 0.0;
+            for (x = 0; x < input->numRows; x++) {
+                for (y = 0; y < input->numCols; y++) {
+                    double pixel;
+                    /*
+                                        if (input->type.type == PS_TYPE_S8) {
+                                            pixel = (double) input->data.S8[x][y];
+                                        } else if (input->type.type == PS_TYPE_U16) {
+                                            pixel = (double) input->data.U16[x][y];
+                                        } else if (input->type.type == PS_TYPE_F32) {
+                                            pixel = (double) input->data.F32[x][y];
+                                        } else if (input->type.type == PS_TYPE_F64) {
+                                            pixel = input->data.F64[x][y];
+                                        }
+                    */
+                    pixel = nodes->data.F64[x][y];
+                    sums[i][j] += pixel * psPolynomial1DEval(chebPolys[i], rScalingFactors[x]) *
+                                  psPolynomial1DEval(chebPolys[j], cScalingFactors[y]);
+                }
+            }
+        }
+    }
+
+    for (i = 0; i < coeffs->nX; i++) {
+        for (j = 0; j < coeffs->nY; j++) {
+            coeffs->coeff[i][j] = sums[i][j];
+            coeffs->coeff[i][j] /= (double)(input->numRows * input->numCols);
+
+            if ((i != 0) && (j != 0)) {
+                coeffs->coeff[i][j] *= 4.0;
+            } else if ((i == 0) && (j == 0)) {
+                coeffs->coeff[i][j] *= 1.0;
+            } else {
+                coeffs->coeff[i][j] *= 2.0;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    for (i = 0; i < maxChebyPoly; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    // Free some data
+    for (i = 0; i < coeffs->nX; i++) {
+        psFree(sums[i]);
+    }
+    psFree(sums);
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+    psFree(nodes);
+
+    return (coeffs);
+}
+
+/*****************************************************************************
+XXX: Use static variables for Chebyshev polynomials and scaling factors. 
+ *****************************************************************************/
+psImage* p_psImageEvalPolynomialCheb(psImage* input,
+                                     const psPolynomial2D* coeffs)
+{
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_CHEB, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 i = 0;
+    psS32 j = 0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+    double *cScalingFactors = NULL;
+    double *rScalingFactors = NULL;
+    float polySum = 0.0;
+
+    // We scale the pixel positions to values between -1.0 and 1.0
+    // Use static data structures here.
+    rScalingFactors = calcScaleFactors(input->numRows);
+    cScalingFactors = calcScaleFactors(input->numCols);
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = coeffs->nX;
+    if (coeffs->nY > coeffs->nX) {
+        maxChebyPoly = coeffs->nY;
+    }
+
+    chebPolys = p_psCreateChebyshevPolys(maxChebyPoly);
+
+    for (x = 0; x < input->numRows; x++) {
+        for (y = 0; y < input->numCols; y++) {
+            polySum = 0.0;
+            for (i = 0; i < coeffs->nX; i++) {
+                for (j = 0; j < coeffs->nY; j++) {
+                    polySum +=
+                        psPolynomial1DEval(chebPolys[i], rScalingFactors[x]) *
+                        psPolynomial1DEval(chebPolys[j], cScalingFactors[y]) *
+                        coeffs->coeff[i][j];
+                }
+            }
+
+            if (input->type.type == PS_TYPE_S8) {
+                input->data.S8[x][y] = (char) polySum;
+            } else if (input->type.type == PS_TYPE_U16) {
+                input->data.U16[x][y] = (short int) polySum;
+            } else if (input->type.type == PS_TYPE_F32) {
+                input->data.F32[x][y] = (float) polySum;
+            } else if (input->type.type == PS_TYPE_F64) {
+                input->data.F64[x][y] = polySum;
+            }
+        }
+    }
+
+    // Free the Chebyshev polynomials that were
+    // created in this routine.
+    // XXX: Use static data structures here.
+    for (i = 0; i < maxChebyPoly; i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+
+    psFree(cScalingFactors);
+    psFree(rScalingFactors);
+
+    return input;
+}
+
+psImage* p_psImageEvalPolynomialOrd(psImage* input,
+                                    const psPolynomial2D* coeffs)
+{
+    PS_POLY_CHECK_TYPE(coeffs, PS_POLYNOMIAL_ORD, NULL);
+
+    for (int row = 0; row < input->numRows ; row++) {
+        for (int col = 0; col < input->numCols ; col++) {
+            if (input->type.type == PS_TYPE_S8) {
+                input->data.S8[row][col] = (psS8) psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            } else if (input->type.type == PS_TYPE_U16) {
+                input->data.U16[row][col] = (psS16) psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            } else if (input->type.type == PS_TYPE_F32) {
+                input->data.F32[row][col] = psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            } else if (input->type.type == PS_TYPE_F64) {
+                input->data.F64[row][col] = (psF64) psPolynomial2DEval(coeffs, (psF32) row, (psF32) col);
+            }
+        }
+    }
+
+    return(input);
+}
+
+
+/*****************************************************************************
+XXX: I added normal polynomials to this routine.  Let IfA know, put it in the
+psLib SDR.
+ *****************************************************************************/
+psImage* psImageEvalPolynomial(psImage* input,
+                               const psPolynomial2D* coeffs)
+{
+    PS_IMAGE_CHECK_NULL(input, NULL);
+    PS_IMAGE_CHECK_EMPTY(input, NULL);
+    if ((input->type.type != PS_TYPE_S8) &&
+            (input->type.type != PS_TYPE_U16) &&
+            (input->type.type != PS_TYPE_F32) &&
+            (input->type.type != PS_TYPE_F64)) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Unallowable image type.\n");
+    }
+    PS_POLY_CHECK_NULL(coeffs, NULL);
+
+    if (coeffs->type == PS_POLYNOMIAL_ORD) {
+        return(p_psImageEvalPolynomialOrd(input, coeffs));
+    } else if (coeffs->type == PS_POLYNOMIAL_CHEB) {
+        return(p_psImageEvalPolynomialCheb(input, coeffs));
+    }
+    printf("XXX: Error: wrong polynomial type\n");
+    // XXX: psError()
+    return(NULL);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageStats.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageStats.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/imageops/psImageStats.h	(revision 22271)
@@ -0,0 +1,89 @@
+/** @file psImageStats.h
+*  \brief Routines for calculating statistics on images.
+*  @ingroup ImageStats
+*
+*  This file will hold the prototypes for procedures which calculate
+*  statistic on images, histograms on images, and fit/evaluate Chebyshev
+*  polynomials to images.
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-03-24 19:39:53 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#if !defined(PS_IMAGE_STATS_H)
+#define PS_IMAGE_STATS_H
+
+#include "psType.h"
+#include "psVector.h"
+#include "psImage.h"
+#include "psStats.h"
+#include "psFunctions.h"
+
+/// @addtogroup ImageStats
+/// @{
+
+/** This routine must determine the various statistics for the image.
+ *
+ *  Determine statistics for image (or subimage). The statistics to be 
+ *  determined are specified by stats. The mask allows pixels to be excluded 
+ *  if their corresponding mask pixel value matches the value of maskVal. 
+ *  This function must be defined for the following types: psS8, psU16, psF32, 
+ *  psF64.
+ *
+ *  @return psStats*    the resulting statistics result(s)
+ */
+psStats* psImageStats(
+    psStats* stats,                    ///< defines statistics to be calculated
+    const psImage* in,                 ///< image (or subimage) to calculate stats
+    const psImage* mask,               ///< mask data for image (NULL ok)
+    psS32 maskVal                      ///< mask Mask for mask
+);
+
+/** Construct a histogram from an image (or subimage).
+ *
+ *  The histogram to generate is specified by psHistogram hist (see section 
+ *  4.3.2 in SDRS). This function must be defined for the following types: 
+ *  psS8, psU16, psF32, psF64.
+ *
+ *  @return psHistogram*     the resulting histogram
+ */
+psHistogram* psImageHistogram(
+    psHistogram* out,                  ///< input histogram description & target
+    const psImage* in,                 ///< Image data to be histogramed.
+    const psImage* mask,               ///< mask data for image (NULL ok)
+    psU32 maskVal                      ///< mask Mask for mask
+);
+
+/** Fit a 2-D polynomial surface to an image.
+ *
+ *  The input structure coeffs contains the desired order and terms of 
+ *  interest. This function must be defined for the following types: psS8, 
+ *  psU16, psF32, psF64.
+ *
+ *  @return psPolynomial2D*     fitted polynomial result
+ *
+ */
+psPolynomial2D* psImageFitPolynomial(
+    psPolynomial2D* coeffs,            ///< coefficient structure carries in desired terms & target
+    const psImage* input
+);
+
+/** Evaluate a 2-D polynomial surface for the image pixels.
+ *
+ *  Given the input polynomial coefficients, set the image pixel values on the 
+ *  basis of the polynomial function. This function must be defined for the 
+ *  following types: psS8, psU16, psF32, psF64.
+ *
+ *  @return psImage*    the resulting image
+ */
+psImage* psImageEvalPolynomial(
+    psImage* input,                    ///< input image
+    const psPolynomial2D* coeffs       ///< coefficient structure carries in desired terms
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mainpage.dox
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mainpage.dox	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mainpage.dox	(revision 22271)
@@ -0,0 +1,172 @@
+/** @file mainpage.dox
+ *  @brief Main page for the Doxygen documentation
+ *
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-31 23:01:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/** @mainpage psLib Image Processing Library
+
+@section intro Introduction
+
+This library contains the Pan-STARRS Image Processing Pipeline (IPP) common library,
+psLib. The intended use is to provide a set of basic software functions in which
+can be used throughout the Pan-STARRS project to simplify programming tasks. Among
+the benefits are:
+ - Reuse of code
+ - Simplification of testing
+ - Streamlining of code
+ - Isolation of functionality that may be subject to change
+
+The capabilities provided by psLib are grouped into the following areas which are
+also reflected in the file system directory structure:
+ - System Utilities
+ - Data Collections
+ - Image Operations
+ - Data Manipulation
+ - Astronomy
+
+This list is sorted in hierarchical order: The later entries depend on the functions and
+data types of the earlier entries.
+
+The installed code for psLib consists of header files and binary libraries in
+the form of static and/or shared library, depending on the configuration options.
+To assist in using the library, a script "pslib-config" is supplied that
+provides the compiler and linker options.
+
+
+@section extinstall 3rd Party Libraries
+
+Before building psLib from source, several 3rd party software libraries must
+be installed.  These include:
+ - GNU Scientific Library (GSL)
+     - Available at http://www.gnu.org/software/gsl
+     - Compatibility tested with version 1.4
+ - Fastest Fourier Transform in the West (FFTW), version 3, with single-precison float support
+     - Available at http://www.fftw.org
+     - Compatibility tested with version is 3.0.1
+     - The '--enable-float' option is required in the configure step to enable single-precision float support.  Use 'configure --help' for more information of the configuration options for this library.
+ - C Flexible Image Transport System Input output (CFITSIO), version 2.480 or later
+     - Available at http://heasarc.gsfc.nasa.gov/docs/software/fitsio
+     - Compatibility tested with version 2.490, requires at least 2.480
+ - Gnome XML C parser and toolkit (libxml2)
+     - Available at http://www.xmlsoft.org
+     - Compatibility tested with version 2.6.12 and 2.6.16
+
+Though not required to build the base library, several 3rd party software
+libraries are needed for particular functionality.
+ - Doxygen Documentation System
+     - Required for creation of documentation
+     - Available at http://www.doxygen.org
+     - Compatibility tested with version 1.3.2 and 1.3.8
+     - The optional companion package, GraphViz (a.k.a., the 'dot' command), is recommended to enable header-file dependency diagrams
+ - MySQL
+     - Required for database functionality
+     - Available at http://dev.mysql.com
+     - Compatibility tested with version 4.1.10a, requires at least 4.1.2
+     - A server running locally is required for testing.
+ - Autoconf/Automake
+     - Required only if making from CVS
+     - Available at http://www.gnu.org/software/autoconf and http://www.gnu.org/software/automake
+     - Compatibility tested with version 2.59 (autoconf) and 1.9.2 (automake)
+
+We recommend using the particular versions listed as compatibility tested, as
+that is the only versions of the external libraries tested to work well with psLib.
+Though it is quite possible that later versions of the libraries listed will also
+work as well, care must be taken when upgrading these libraries to verify that its
+functionality is compatible with the tested version.
+
+
+@section obtain How to Obtain the Source
+
+Tested versions of psLib are put into a tar file and can be downloaded from:
+
+https://mhpcc.pan-starrs.org/code/releases
+
+Though MHPCC recommends using the released packaged tar files descibed above,
+both recent daily development snapshots and direct CVS access is available.
+The recent daily snapshots are available at:
+
+https://mhpcc.pan-starrs.org/code/snapshots
+
+These snapshots represents development code as found in CVS on the day
+specified in the filename.  This code set may not be functional, vary in
+testing, etc.  Reasonable efforts are made to ensure that the code does
+actually compile, but given the nature of code development, even that is not
+always the case.  Use snapshots with great caution.
+
+If one has a login account on mhpcc.pan-starrs.org, direct CVS access is
+possible.  Example of the commands required for direct CVS retrieval are
+as follows:
+<pre>
+$ cvs -d:ext:USERNAME@mhpcc.pan-starrs.org:/data/panstarrs/cvsroot co -r RELEASEBRANCH psLib
+</pre>
+where:
+  - USERNAME is your login name on the server
+  - RELEASEBRANCH is the desired release branch, e.g. rel5.
+
+
+@section build How to Build and Test the psLib library
+
+The source uses autoconf/automake to build control the build process.  If, and
+only if, the source came from CVS or a CVS snapshot, the configuration script
+will have to be made via:
+<pre>
+$ cd psLib
+$ make -f Makefile.cvs
+</pre>
+<i>This creates the configure script and Makefile templates.</i>
+
+The recommended steps to build the library are:
+<pre>
+$ cd psLib
+$ ./configure
+$ make
+$ make check
+$ make install
+</pre>
+<i>This builds, tests, and installs the library.</i>
+
+The configure step allows users to specify specific locations for the third
+party libraries (see "3rd Party Libraries" for a list), disabling of select
+functionality (e.g., database and perl module), installation location, etc.
+An enumeration of the options for configure, and the default values, can be
+obtained via "./configure --help".
+
+Though 'make check' compiles and runs the test suite, one can also use custom
+scripts to run all the tests and just a particular test.  To run the unit test
+suite, do the following:
+<pre>
+$ cd psLib/test
+$ ./FullUnitTest
+</pre>
+
+This has the advantage over 'make check' in that it does not stop when a test
+fails and it collects some very basic statistics on the results.  To run a
+particular test, do the following:
+<pre>
+$ runTest testfilename
+</pre>
+
+
+@section doc How to Create Code Documentation
+
+Both HTML and man page documentation may be generated from the inline
+documentation embedded in the code using the following commands:
+<pre>
+$ cd psLib
+$ make docs
+</pre>
+<i>This places documentation in PREFIX/docs/psLib, where PREFIX is set using
+the configure script.</i>
+
+Also, a prebuilt set of code documentation for both the releases and last
+CVS snapshot can be found at:
+
+https://mhpcc.pan-starrs.org/docs/
+
+*/
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psBinaryOp.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psBinaryOp.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psBinaryOp.c	(revision 22271)
@@ -0,0 +1,582 @@
+/** @file  psBinaryOp.c
+ *
+ *  @brief Provides binary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************
+ *  INCLUDE FILES                                                             *
+ ******************************************************************************/
+#include <string.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+/*****************************************************************************
+ *  FUNCTION IMPLEMENTATION - LOCAL                                          *
+ *****************************************************************************/
+
+// Conversion for degrees to radians
+#define D2R 0.01745329252111111  /* PI/180 */
+
+// Conversion for radians to degrees
+#define R2D 57.29577950924861   /* 180.0/PI */
+
+// Binary SCALAR_XXXX operations
+#define SCALAR_SCALAR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    o  = &((psScalar* )OUT)->data.TYPE;                                                                      \
+    i1 = &((psScalar* )IN1)->data.TYPE;                                                                      \
+    i2 = &((psScalar* )IN2)->data.TYPE;                                                                      \
+    *o = OP;                                                                                                 \
+}
+
+#define SCALAR_VECTOR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 npt = 0;                                                                                             \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    npt  = ((psVector* )IN2)->n;                                                                             \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = &((psScalar* )IN1)->data.TYPE;                                                                      \
+    i2 = ((psVector* )IN2)->data.TYPE;                                                                       \
+    for (i=0; i < npt; i++, o++, i2++) {                                                                     \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+#define SCALAR_IMAGE(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 numRows = 0;                                                                                         \
+    psS32 numCols = 0;                                                                                         \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    numRows = ((psImage* )IN2)->numRows;                                                                     \
+    numCols = ((psImage* )IN2)->numCols;                                                                     \
+    for(j = 0; j < numCols; j++) {                                                                           \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = &((psScalar* )IN1)->data.TYPE;                                                                  \
+        i2 = ((psImage* )IN2)->data.TYPE[j];                                                                 \
+        for(i = 0; i < numRows; i++, o++, i2++) {                                                            \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Binary VECTOR_XXXX operations
+#define VECTOR_SCALAR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 n1 = 0;                                                                                              \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n1  = ((psVector* )IN1)->n;                                                                              \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = ((psVector* )IN1)->data.TYPE;                                                                       \
+    i2 = &((psScalar* )IN2)->data.TYPE;                                                                      \
+    for (i=0; i < n1; i++, o++, i1++) {                                                                      \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+#define VECTOR_VECTOR(OUT,IN1,OP,IN2,TYPE)                                                                   \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 n1 = 0;                                                                                            \
+    psS32 n2 = 0;                                                                                            \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n1  = ((psVector* )IN1)->n;                                                                              \
+    n2  = ((psVector* )IN2)->n;                                                                              \
+    if(n1 != n2) {                                                                                           \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                         \
+                n1, n2);                                                                                     \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = ((psVector* )IN1)->data.TYPE;                                                                       \
+    i2 = ((psVector* )IN2)->data.TYPE;                                                                       \
+    for (i=0; i < n1; i++, o++, i1++, i2++) {                                                                \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+#define VECTOR_IMAGE(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 j = 0;                                                                                             \
+    psS32 n1 = 0;                                                                                            \
+    psS32 numRows2 = 0;                                                                                      \
+    psS32 numCols2 = 0;                                                                                      \
+    psDimen dim1 = 0;                                                                                        \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n1  = ((psVector* )IN1)->n;                                                                              \
+    dim1 = ((psVector* )IN1)->type.dimen;                                                                    \
+    numRows2 = ((psImage* )IN2)->numRows;                                                                    \
+    numCols2 = ((psImage* )IN2)->numCols;                                                                    \
+    \
+    if(dim1 == PS_DIMEN_VECTOR) { /* Regular vectors */                                             \
+        if(n1!=numRows2) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                         \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                           \
+                    n1, numRows2);                                                                           \
+            if (OUT != IN1 && OUT != IN2) {                                                                  \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        i1 = ((psVector* )IN1)->data.TYPE;                                                                   \
+        for(j = 0; j < numRows2; j++, i1++) {                                                                \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i2 = ((psImage* )IN2)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols2; i++, o++, i2++) {                                                       \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    } else {  /* Transposed vectors */                                                                       \
+        if(n1!=numCols2) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                 \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                     \
+                    n1, numCols2);                       \
+            if (OUT != IN1 && OUT != IN2) {                                                                  \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        for(j = 0; j < numRows2; j++) {                                                                      \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i1 = ((psVector* )IN1)->data.TYPE;                                                               \
+            i2 = ((psImage* )IN2)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols2; i++, o++, i1++, i2++) {                                                 \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Binary IMAGE_XXXX operations
+#define IMAGE_SCALAR(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 numRows = 0;                                                                                         \
+    psS32 numCols = 0;                                                                                         \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    numRows = ((psImage* )IN1)->numRows;                                                                     \
+    numCols = ((psImage* )IN1)->numCols;                                                                     \
+    for(j = 0; j < numRows; j++) {                                                                           \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = ((psImage* )IN1)->data.TYPE[j];                                                                 \
+        i2 = &((psScalar* )IN2)->data.TYPE;                                                                  \
+        for(i = 0; i < numCols; i++, o++, i1++) {                                                            \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+#define IMAGE_VECTOR(OUT,IN1,OP,IN2,TYPE)                                                                    \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 n2 = 0;                                                                                              \
+    psS32 numRows1 = 0;                                                                                        \
+    psS32 numCols1 = 0;                                                                                        \
+    psDimen dim2 = 0;                                                                                        \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    n2  = ((psVector* )IN2)->n;                                                                              \
+    dim2 = ((psVector* )IN2)->type.dimen;                                                                    \
+    numRows1 = ((psImage* )IN1)->numRows;                                                                    \
+    numCols1 = ((psImage* )IN1)->numCols;                                                                    \
+    \
+    if(dim2 == PS_DIMEN_VECTOR) { /* Regular vectors */                                             \
+        if(n2!=numRows1) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                         \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                     \
+                    n2, numRows1);                                                                           \
+            if (OUT != IN1 && OUT != IN2) {                                                                  \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        i2 = ((psVector* )IN2)->data.TYPE;                                                                   \
+        for(j = 0; j < numRows1; j++, i1++) {                                                                \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i1 = ((psImage* )IN1)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols1; i++, o++, i1++) {                                                       \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    } else {  /* Transposed vectors */                                                                       \
+        if(n2!=numCols1) {                                                                                   \
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                         \
+                    PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                     \
+                    n2, numCols1);                                                                           \
+            if (OUT != IN1) {                                                                                \
+                psFree(OUT);                                                                                 \
+            }                                                                                                \
+            return NULL;                                                                                     \
+        }                                                                                                    \
+        \
+        for(j = 0; j < numRows1; j++) {                                                                      \
+            o  = ((psImage* )OUT)->data.TYPE[j];                                                             \
+            i1 = ((psVector* )IN2)->data.TYPE;                                                               \
+            i2 = ((psImage* )IN1)->data.TYPE[j];                                                             \
+            for(i = 0; i < numCols1; i++, o++, i2++, i1++) {                                                 \
+                *o = OP;                                                                                     \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+#define IMAGE_IMAGE(OUT,IN1,OP,IN2,TYPE)                                                                     \
+{                                                                                                            \
+    psS32 i = 0;                                                                                               \
+    psS32 j = 0;                                                                                               \
+    psS32 numRows1 = 0;                                                                                        \
+    psS32 numCols1 = 0;                                                                                        \
+    psS32 numRows2 = 0;                                                                                        \
+    psS32 numCols2 = 0;                                                                                        \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    ps##TYPE *i2 = NULL;                                                                                     \
+    numRows1 = ((psImage* )IN1)->numRows;                                                                    \
+    numCols1 = ((psImage* )IN1)->numCols;                                                                    \
+    numRows2 = ((psImage* )IN2)->numRows;                                                                    \
+    numCols2 = ((psImage* )IN2)->numCols;                                                                    \
+    if(numRows1!=numRows2 || numCols1!=numCols2) {                                                           \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_IMAGE_SIZE_DIFFERS,                                                     \
+                numCols1, numRows1, numCols2, numRows2);                                                     \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    for(j = 0; j < numRows1; j++) {                                                                          \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = ((psImage* )IN1)->data.TYPE[j];                                                                 \
+        i2 = ((psImage* )IN2)->data.TYPE[j];                                                                 \
+        for(i = 0; i < numCols1; i++, o++, i1++, i2++) {                                                     \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Preprocessor macro function to create arithmetic function based on input type
+#define BINARY_TYPE(DIM1,DIM2,OUT,IN1,OP,IN2)                                                                \
+switch (IN1->type) {                                                                                         \
+case PS_TYPE_U8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_U16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_U64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,U64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S8:                                                                                             \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S8);                                                                        \
+    break;                                                                                                   \
+case PS_TYPE_S16:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S16);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_S64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,S64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_F32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,F32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_F64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,F64);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_C32:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,C32);                                                                       \
+    break;                                                                                                   \
+case PS_TYPE_C64:                                                                                            \
+    DIM1##_##DIM2(OUT,IN1,OP,IN2,C64);                                                                       \
+    break;                                                                                                   \
+default:                                                                                                     \
+    /* char* strType; \
+    PS_TYPE_NAME(strType,IN1->type);                                                                         \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                                 \
+            PS_ERRORTEXT_psMatrix_TYPE_MISMATCH,                                                             \
+            strType);  */                                                                                      \
+    if (OUT != IN1 && OUT != IN2) {                                                                          \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+// Preprocessor macro function to create arithmetic function operation name
+#define BINARY_OP(DIM1,DIM2,OUT,IN1,OP,IN2)                                                                  \
+if(!strncmp(OP, "=", 1)) {                                                                                   \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i2,IN2);                                                                  \
+} else if(!strncmp(OP, "+", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 + *i2,IN2);                                                            \
+} else if(!strncmp(OP, "-", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 - *i2,IN2);                                                            \
+} else if(!strncmp(OP, "*", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 * *i2,IN2);                                                            \
+} else if(!strncmp(OP, "/", 1)) {                                                                            \
+    BINARY_TYPE(DIM1,DIM2,OUT,IN1,*i1 / *i2,IN2);                                                            \
+} else if(!strncmp(OP, "^", 1)) {                                                                            \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN1->type)) {                                                                \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,cpow(*i1,*i2),IN2);                                                    \
+    } else {                                                                                                 \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,pow(*i1,*i2),IN2);                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "min", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN1->type)) {                                                                \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                            \
+                PS_ERRORTEXT_psMatrix_MIN_COMPLEX_SUPPORT);                                                  \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    } else {                                                                                                 \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,fmin(*i1,*i2),IN2);                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "max", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN1->type)) {                                                                \
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                            \
+                PS_ERRORTEXT_psMatrix_MAX_COMPLEX_SUPPORT);                                                  \
+        if (OUT != IN1 && OUT != IN2) {                                                                      \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    } else {                                                                                                 \
+        BINARY_TYPE(DIM1,DIM2,OUT,IN1,fmax(*i1,*i2),IN2);                                                    \
+    }                                                                                                        \
+} else {                                                                                                     \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                                \
+            PS_ERRORTEXT_psMatrix_OPERATION_UNSUPPORTED,                                                     \
+            OP);                                                                                             \
+    if (OUT != IN1 && OUT != IN2) {                                                                          \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+psPtr psBinaryOp(psPtr out, const psPtr in1, const char *op, const psPtr in2)
+{
+
+    psVector* input1 = (psVector* ) in1;
+    psVector* input2 = (psVector* ) in2;
+
+    #define psBinaryOp_EXIT { \
+                              if (out != in1 && out != in2) { \
+                              psFree(out); \
+                              } \
+                              return NULL; \
+                            }
+
+    PS_PTR_CHECK_NULL_GENERAL(input1, psBinaryOp_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(input2, psBinaryOp_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(op, psBinaryOp_EXIT);
+
+    PS_PTR_CHECK_TYPE_EQUAL_GENERAL(input1,input2, psBinaryOp_EXIT);
+
+    PS_PTR_CHECK_DIMEN_GENERAL_NOT(input1, PS_DIMEN_OTHER, psBinaryOp_EXIT);
+    PS_PTR_CHECK_DIMEN_GENERAL_NOT(input2, PS_DIMEN_OTHER, psBinaryOp_EXIT);
+
+    psType* psType1 = (psType*)in1;
+    psType* psType2 = (psType*)in2;
+    psDimen dim1 = psType1->dimen;
+    psDimen dim2 = psType2->dimen;
+    psElemType elType1 = psType1->type;
+    psElemType elType2 = psType2->type;
+
+    if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+        if (((psVector* ) in1)->n == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Vector contains zero elements");
+        }
+    } else if (dim1 == PS_DIMEN_IMAGE) {
+        if (((psImage* ) in1)->numCols == 0 || ((psImage* ) in1)->numRows == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image contains zero length row or cols");
+        }
+    }
+
+    if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+        if (((psVector* ) in2)->n == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Vector contains zero elements");
+        }
+    } else if (dim2 == PS_DIMEN_IMAGE) {
+        if (((psImage* ) in2)->numCols == 0 || ((psImage* ) in2)->numRows == 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image contains zero length row or cols");
+        }
+    }
+
+    if (dim1 == PS_DIMEN_SCALAR) {
+        if ( out != NULL && ((psType*)out)->dimen != dim2) {
+            if (out != in1 && out != in2) {
+                psFree(out);
+            }
+            out = NULL;
+        }
+        if (dim2 == PS_DIMEN_SCALAR) {
+            if (out == NULL || ((psScalar*)out)->type.type != elType1) {
+                if (out != in1 && out != in2) {
+                    psFree(out);
+                }
+                out = psScalarAlloc(0.0,elType1);
+            }
+            BINARY_OP(SCALAR, SCALAR, out, psType1, op, psType2);       // scalar op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            out = psVectorRecycle(out,((psVector*)in2)->n,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(SCALAR, VECTOR, out, psType1, op, psType2);       // scalar op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            out = psImageRecycle(out, ((psImage* ) in2)->numCols, ((psImage* ) in2)->numRows,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(SCALAR, IMAGE, out, psType1, op, psType2);        // scalar op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else if (dim1 == PS_DIMEN_VECTOR || dim1 == PS_DIMEN_TRANSV) {
+        if (dim2 == PS_DIMEN_SCALAR) {
+            out = psVectorRecycle(out,((psVector*)in1)->n,elType1);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(VECTOR, SCALAR, out, psType1, op, psType2);       // vector op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            out = psVectorRecycle(out,((psVector*)in2)->n,elType2);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(VECTOR, VECTOR, out, psType1, op, psType2);       // vector op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            out = psImageRecycle(out, ((psImage* ) in2)->numCols, ((psImage* ) in2)->numRows, elType2);
+            if (out == NULL) {
+                psError(PS_ERR_UNKNOWN, false,
+                        PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+                return NULL;
+            }
+            BINARY_OP(VECTOR, IMAGE, out, psType1, op, psType2);        // vector op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else if (dim1 == PS_DIMEN_IMAGE) {
+        out = psImageRecycle(out, ((psImage*)in1)->numCols, ((psImage*)in1)->numRows, elType1);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+            return NULL;
+        }
+        if (dim2 == PS_DIMEN_SCALAR) {
+            BINARY_OP(IMAGE, SCALAR, out, psType1, op, psType2);        // image op scalar
+        } else if (dim2 == PS_DIMEN_VECTOR || dim2 == PS_DIMEN_TRANSV) {
+            BINARY_OP(IMAGE, VECTOR, out, psType1, op, psType2);        // image op vector
+        } else if (dim2 == PS_DIMEN_IMAGE) {
+            BINARY_OP(IMAGE, IMAGE, out, psType1, op, psType2); // image op image
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                    "in2",dim2);
+            psBinaryOp_EXIT;
+        }
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                "in1",dim1);
+        psBinaryOp_EXIT;
+    }
+
+    // Automtically free psScalar types, since they are usually allocated in the argument list when this
+    // function is called, provided that the input is not the output.
+    if(psType1->dimen==PS_DIMEN_SCALAR && in1!=out) {
+        psFree(in1);
+    }
+
+    if(psType2->dimen==PS_DIMEN_SCALAR && in2!=out) {
+        psFree(in2);
+    }
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psBinaryOp.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psBinaryOp.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psBinaryOp.h	(revision 22271)
@@ -0,0 +1,67 @@
+
+/** @file  psBinaryOp.h
+ *
+ *  @brief Provides binary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSBINARY_OP_H
+#define PSBINARY_OP_H
+
+/// @addtogroup MatrixArithmetic
+/// @{
+
+/** Perform simple binary arithmetic with images or vectors
+ *
+ *  Performs addition, subtraction, multiplication, division, power, minumum, and maximum arithmetic
+ *  operations with images and vectors. Uses the form:
+ *
+ *      out = in1 op in2,
+ *
+ *      Where op is: "=", "+", "-", "*", "/", "^", "min", or "max"
+ *
+ *  This function only supports vector-vector or image-image operations.
+ *
+ *  @return  psType* : Pointer to either psImage or psVector.
+ */
+psType* psBinaryOp(
+    psPtr out,                         ///< Output type, either psImage or psVector.
+    const psPtr in1,   ///< First input, either psImage or psVector.
+    const char *op,   ///< Operator.
+    const psPtr in2   ///< Second input, either psImage or psVector.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psCompare.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psCompare.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psCompare.c	(revision 22271)
@@ -0,0 +1,119 @@
+
+/** @file psCompare.c
+ *  @brief Comparison functions for sorting routines
+ *  @ingroup Compare
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-17 19:26:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psCompare.h"
+
+#define COMPARE_NUMERIC_PTR(TYPE) \
+int psCompare##TYPE##Ptr(const void** a, const void** b) { \
+    return **((ps##TYPE**)a) - **((ps##TYPE**)b); \
+}
+
+#define COMPARE_NUMERIC_PTR_DESCENDING(TYPE) \
+int psCompareDescending##TYPE##Ptr(const void** a, const void** b) { \
+    return **((ps##TYPE**)b) - **((ps##TYPE**)a); \
+}
+
+#define COMPARE_NUMERIC(TYPE) \
+int psCompare##TYPE(const void* a, const void* b) { \
+    return *((ps##TYPE*)a) - *((ps##TYPE*)b); \
+}
+
+#define COMPARE_NUMERIC_DESCENDING(TYPE) \
+int psCompareDescending##TYPE(const void* a, const void* b) { \
+    return *((ps##TYPE*)b) - *((ps##TYPE*)a); \
+}
+
+COMPARE_NUMERIC_PTR(S8)
+COMPARE_NUMERIC_PTR(S16)
+COMPARE_NUMERIC_PTR(S32)
+COMPARE_NUMERIC_PTR(S64)
+COMPARE_NUMERIC_PTR(U8)
+COMPARE_NUMERIC_PTR(U16)
+COMPARE_NUMERIC_PTR(U32)
+COMPARE_NUMERIC_PTR(U64)
+
+int psCompareF32Ptr(const void** a, const void** b)
+{
+    psF32 diff = **((psF32**)a) - **((psF32**)b);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareF64Ptr(const void** a, const void** b)
+{
+    psF64 diff = **((psF64**)a) - **((psF64**)b);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC_PTR_DESCENDING(S8)
+COMPARE_NUMERIC_PTR_DESCENDING(S16)
+COMPARE_NUMERIC_PTR_DESCENDING(S32)
+COMPARE_NUMERIC_PTR_DESCENDING(S64)
+COMPARE_NUMERIC_PTR_DESCENDING(U8)
+COMPARE_NUMERIC_PTR_DESCENDING(U16)
+COMPARE_NUMERIC_PTR_DESCENDING(U32)
+COMPARE_NUMERIC_PTR_DESCENDING(U64)
+
+int psCompareDescendingF32Ptr(const void** a, const void** b)
+{
+    psF32 diff = **((psF32**)b) - **((psF32**)a);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareDescendingF64Ptr(const void** a, const void** b)
+{
+    psF64 diff = **((psF64**)b) - **((psF64**)a);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC(S8)
+COMPARE_NUMERIC(S16)
+COMPARE_NUMERIC(S32)
+COMPARE_NUMERIC(S64)
+COMPARE_NUMERIC(U8)
+COMPARE_NUMERIC(U16)
+COMPARE_NUMERIC(U32)
+COMPARE_NUMERIC(U64)
+
+int psCompareF32(const void* a, const void* b)
+{
+    psF32 diff = *((psF32*)a) - *((psF32*)b);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareF64(const void* a, const void* b)
+{
+    psF64 diff = *((psF64*)a) - *((psF64*)b);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
+
+COMPARE_NUMERIC_DESCENDING(S8)
+COMPARE_NUMERIC_DESCENDING(S16)
+COMPARE_NUMERIC_DESCENDING(S32)
+COMPARE_NUMERIC_DESCENDING(S64)
+COMPARE_NUMERIC_DESCENDING(U8)
+COMPARE_NUMERIC_DESCENDING(U16)
+COMPARE_NUMERIC_DESCENDING(U32)
+COMPARE_NUMERIC_DESCENDING(U64)
+
+int psCompareDescendingF32(const void* a, const void* b)
+{
+    psF32 diff = *((psF32*)b) - *((psF32*)a);
+    return (diff>FLT_EPSILON) ? 1 : ((diff<FLT_EPSILON) ? -1 :0);
+}
+
+int psCompareDescendingF64(const void* a, const void* b)
+{
+    psF64 diff = *((psF64*)b) - *((psF64*)a);
+    return (diff>DBL_EPSILON) ? 1 : ((diff<DBL_EPSILON) ? -1 :0);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psCompare.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psCompare.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psCompare.h	(revision 22271)
@@ -0,0 +1,508 @@
+/** @file psCompare.h
+ *  @brief Comparison functions for sorting routines
+ *
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @ingroup Compare
+ *
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:23 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_COMPARE_H)
+#define PS_COMPARE_H
+
+#include "psType.h"
+
+/** @addtogroup Compare
+*  @{
+*/
+
+/** A comparison function for sorting elements that are pointers to data,
+ *  e.g., for psList of pointers to numeric values.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+typedef int (*psComparePtrFcn) (
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** A comparison function for sorting.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+typedef int (*psCompareFcn) (
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+/** Compare function of psS8 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS16 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU8 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU16 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF32 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF64 data.  For use with psListSort.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS8 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS16 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU8 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU8Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU16 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU16Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingU32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psU64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingU64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF32 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingF32Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psF64 data.  For use with psListSort for descending ordering.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or lessg than the second. 
+ */
+int psCompareDescendingF64Ptr(
+    const void **a,                    ///< first comparison target
+    const void **b                     ///< second comparison target
+);
+
+/** Compare function of psS8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareS64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareU64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively less 
+ *                   than, equal to, or greater than the second. 
+ */
+int psCompareF64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psS64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingS64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU8 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU8(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU16 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU16(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psU64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingU64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF32 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingF32(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/** Compare function of psF64 data.
+ *
+ *  @return int      an integer less than, equal to, or greater than zero if 
+ *                   the first argument is considered to be respectively greater 
+ *                   than, equal to, or less than the second. 
+ */
+int psCompareDescendingF64(
+    const void *a,                     ///< first comparison target
+    const void *b                      ///< second comparison target
+);
+
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psConstants.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psConstants.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psConstants.h	(revision 22271)
@@ -0,0 +1,641 @@
+/** @file  psConstants.h
+ *
+ *  This file will hold definitions of various constants as well as common
+ *  macros used throughout psLib.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.66 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: Add parenthesis around all arguments so that these macros can be
+ *       called with complex expressions.
+ *
+ *  XXX: All functions which use the PS_CHECK macros must be scrutinized so
+ *  that we ensure that an argument which is expected to be output is
+ *  psFree'ed before reurning NULL.
+ *
+ */
+
+#include<math.h> // for M_PI
+
+/*****************************************************************************
+These constants are used by various functions in the psLib.
+ *****************************************************************************/
+#define PS_DETERMINE_BRACKET_STEP_SIZE 0.10
+#define PS_MAX_LMM_ITERATIONS 100
+#define PS_MAX_MINIMIZE_ITERATIONS 100
+#define PS_LEFT_SPLINE_DERIV 0.0
+#define PS_RIGHT_SPLINE_DERIV 0.0
+/*****************************************************************************
+These are common mathimatical constants used by various functions in the psLib.
+ *****************************************************************************/
+#ifndef M_PI
+#define M_PI   3.1415926535897932384626433832795029  /* pi */
+#define M_PI_2 1.5707963267948966192313216916397514  /* pi/2 */
+#define M_PI_4 0.7853981633974483096156608458198757  /* pi/4 */
+#define M_1_PI 0.3183098861837906715377675267450287  /* 1/pi */
+#define M_2_PI 0.6366197723675813430755350534900574  /* 2/pi */
+#endif
+
+#define DEG_TO_RAD(DEGREES) ((DEGREES) * M_PI / 180.0)
+#define MIN_TO_RAD(MINUTES) ((MINUTES) * M_PI / (180.0 * 60.0))
+#define SEC_TO_RAD(SECONDS) ((SECONDS) * M_PI / (180.0 * 60.0 * 60.0))
+#define RAD_TO_DEG(RADIANS) ((RADIANS) * 180.0 / M_PI)
+#define RAD_TO_MIN(RADIANS) ((RADIANS) * 180.0 * 60.0 / M_PI)
+#define RAD_TO_SEC(RADIANS) ((RADIANS) * 180.0 * 60.0 * 60.0 / M_PI)
+
+/*****************************************************************************
+ 
+*****************************************************************************/
+
+#define PS_INT_CHECK_EQUALS(NAME1, NAME2, RVAL) \
+if (NAME1 == NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_NON_EQUALS(NAME1, NAME2, RVAL) \
+if (NAME1 != NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are not equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_NON_NEGATIVE(NAME, RVAL) \
+if (NAME < 0) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is less than 0.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_POSITIVE(NAME, RVAL) \
+if (NAME < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0 or less.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_ZERO(NAME, RVAL) \
+if (NAME < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s is 0.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_INT_CHECK_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((int)NAME < LOWER || (int)NAME > UPPER) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %d, is out of range.  Must be between %d and %d.", \
+            #NAME,(int)NAME,LOWER,UPPER); \
+    return RVAL; \
+}
+
+// Produce an error if (NAME1 > NAME2)
+#define PS_INT_COMPARE(NAME1, NAME2, RVAL) \
+if (NAME1 > NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: (%s > %s) (%d %d).", \
+            #NAME1, #NAME2, NAME1, NAME2); \
+    return(RVAL); \
+}
+
+
+// Produce an error if ((NAME1 > NAME2)
+#define PS_FLOAT_COMPARE(NAME1, NAME2, RVAL) \
+if (NAME1 > NAME2) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: (%s > %s) (%f %f)", \
+            #NAME1, #NAME2, NAME1, NAME2); \
+    return(RVAL); \
+}
+
+#define PS_FLOAT_CHECK_NON_EQUAL(NAME1, NAME2, RVAL) \
+if (fabs(NAME2 - NAME1) < FLT_EPSILON) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s and %s are equal.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_FLOAT_CHECK_RANGE(NAME, LOWER, UPPER, RVAL) \
+if ((NAME) < (LOWER) || (NAME) > (UPPER)) { \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true, \
+            "Error: %s, %f, is out of range.  Must be between %f and %f.", \
+            #NAME, NAME, LOWER, UPPER); \
+    return RVAL; \
+}
+
+/*****************************************************************************
+Macros which take a generic psLib type and determine if it is NULL, or has
+the wrong type.
+*****************************************************************************/
+#define PS_PTR_CHECK_NULL(NAME, RVAL) PS_PTR_CHECK_NULL_GENERAL(NAME, return RVAL)
+#define PS_PTR_CHECK_NULL_GENERAL(NAME, CLEANUP) \
+if (NAME == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: %s is NULL.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_PTR_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_PTR_CHECK_DIMEN(NAME, DIMEN, RVAL) PS_PTR_CHECK_DIMEN_GENERAL(NAME, DIMEN, return RVAL)
+#define PS_PTR_CHECK_DIMEN_GENERAL(NAME, DIMEN, CLEANUP) \
+if (NAME->type.dimen != DIMEN) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect dimensionality.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_PTR_CHECK_DIMEN_GENERAL_NOT(NAME, DIMEN, CLEANUP) \
+if (NAME->type.dimen == DIMEN) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: %s has incorrect dimensionality.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+
+#define PS_PTR_CHECK_SIZE_EQUAL(PTR1, PTR2, RVAL) \
+if (PTR1->n != PTR2->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "ptr %s has size %d, ptr %s has size %d.", \
+            #PTR1, PTR1->n, #PTR2, PTR2->n); \
+    return(RVAL); \
+}
+
+#define PS_PTR_CHECK_TYPE_EQUAL(PTR1, PTR2, RVAL) PS_PTR_CHECK_TYPE_EQUAL_GENERAL(PTR1, PTR2, return RVAL)
+
+#define PS_PTR_CHECK_TYPE_EQUAL_GENERAL(PTR1, PTR2, CLEANUP) \
+if (PTR1->type.type != PTR2->type.type) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "ptr %s has type %d, ptr %s has type %d.", \
+            #PTR1, PTR1->type.type, #PTR2, PTR2->type.type); \
+    CLEANUP; \
+}
+
+
+/*****************************************************************************
+    PS_VECTOR macros:
+ *****************************************************************************/
+#define PS_VECTOR_CHECK_NULL(NAME, RVAL) PS_VECTOR_CHECK_NULL_GENERAL(NAME, return RVAL)
+#define PS_VECTOR_CHECK_NULL_GENERAL(NAME, CLEANUP) \
+if (NAME == NULL || NAME->data.U8 == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psVector %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_VECTOR_CHECK_EMPTY(NAME, RVAL) PS_VECTOR_CHECK_EMPTY_GENERAL(NAME, return RVAL)
+#define PS_VECTOR_CHECK_EMPTY_GENERAL(NAME, CLEANUP) \
+if (NAME->n < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psVector %s has no elements.", \
+            #NAME); \
+    CLEANUP; \
+} \
+
+#define PS_VECTOR_CHECK_TYPE_F32_OR_F64(NAME, RVAL) \
+if ((NAME->type.type != PS_TYPE_F32) && (NAME->type.type != PS_TYPE_F64)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "psVector %s: bad type(%d)", \
+            #NAME, NAME->type.type); \
+    return(RVAL); \
+} \
+
+#define PS_VECTOR_CHECK_TYPE_S16_S32_F32(NAME, RVAL) \
+if ((NAME->type.type != PS_TYPE_S16) && (NAME->type.type != PS_TYPE_S32) && (NAME->type.type != PS_TYPE_F32)) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "psVector %s: bad type(%d)", \
+            #NAME, NAME->type.type); \
+    return(RVAL); \
+} \
+
+#define PS_VECTOR_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psVector %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_CHECK_SIZE_EQUAL(VEC1, VEC2, RVAL) \
+if (VEC1->n != VEC2->n) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %d, psVector %s has size %d.", \
+            #VEC1, VEC1->n, #VEC2, VEC2->n); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_CHECK_TYPE_EQUAL(VEC1, VEC2, RVAL) \
+if (VEC1->type.type != VEC2->type.type) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %d, psVector %s has size %d.", \
+            #VEC1, VEC1->type.type, #VEC2, VEC2->type.type); \
+    return(RVAL); \
+}
+
+#define PS_VECTOR_F64_TO_F32(X64, X32) \
+psVector *X32 = psVectorAlloc(X64->n, PS_TYPE_F32); \
+for (int i=0;i<X64->n;i++) { \
+    X32->data.F32[i] = (float) X64->data.F64[i]; \
+} \
+
+#define PS_VECTOR_F32_TO_F64(X32, X64) \
+psVector *X64 = psVectorAlloc(X32->n, PS_TYPE_F64); \
+for (int i=0;i<X32->n;i++) { \
+    X64->data.F64[i] = (float) X32->data.F32[i]; \
+} \
+
+#define PS_VECTOR_PRINT_F32(NAME) \
+for (int my_i=0;my_i<NAME->n;my_i++) { \
+    printf("%s->data.F32[%d] is %f\n", #NAME, my_i, NAME->data.F32[my_i]); \
+} \
+printf("\n"); \
+
+
+#define PS_VECTOR_CONVERT_F64_TO_F32_STATIC(OLD, NEW_PTR32, NEW_STATIC32) \
+if (OLD->type.type == PS_TYPE_F32) { \
+    NEW_PTR32 = (psVector *) OLD; \
+} else if (OLD->type.type == PS_TYPE_F64) { \
+    NEW_STATIC32 = psVectorRecycle(NEW_STATIC32, OLD->n, PS_TYPE_F32); \
+    p_psMemSetPersistent(NEW_STATIC32, true); \
+    p_psMemSetPersistent(NEW_STATIC32->data.U8, true); \
+    for (i=0; i < OLD->n ; i++) { \
+        NEW_STATIC32->data.F32[i] = (float) OLD->data.F64[i]; \
+    } \
+    NEW_PTR32 = NEW_STATIC32; \
+} \
+
+#define PS_VECTOR_CONVERT_F32_TO_F64_STATIC(OLD, NEW_PTR64, NEW_STATIC64) \
+if (OLD->type.type == PS_TYPE_F64) { \
+    NEW_PTR64 = (psVector *) OLD; \
+} else if (OLD->type.type == PS_TYPE_F32) { \
+    NEW_STATIC64 = psVectorRecycle(NEW_STATIC64, OLD->n, PS_TYPE_F64); \
+    p_psMemSetPersistent(NEW_STATIC64, true); \
+    p_psMemSetPersistent(NEW_STATIC64->data.U8, true); \
+    for (i=0; i < OLD->n ; i++) { \
+        NEW_STATIC64->data.F64[i] = (double) OLD->data.F32[i]; \
+    } \
+    NEW_PTR64 = NEW_STATIC64; \
+} \
+
+#define PS_VECTOR_GEN_YERR_STATIC_F32(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F32); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F32[i] = 1.0; \
+} \
+
+#define PS_VECTOR_GEN_YERR_STATIC_F64(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F64); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F64[i] = 1.0; \
+} \
+
+#define PS_VECTOR_GEN_X_INDEX_STATIC_F32(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F32); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F32[i] = (float) i; \
+} \
+
+#define PS_VECTOR_GEN_X_INDEX_STATIC_F64(VEC, N) \
+VEC = psVectorRecycle(VEC, N, PS_TYPE_F64); \
+p_psMemSetPersistent(VEC, true); \
+p_psMemSetPersistent(VEC->data.U8, true); \
+for (int i=0;i<N;i++) { \
+    VEC->data.F64[i] = (float) i; \
+} \
+
+#define PS_VECTOR_GEN_STATIC_RECYCLED(NAME, SIZE, TYPE) \
+static psVector *NAME = NULL; \
+NAME = psVectorRecycle(NAME, SIZE, TYPE); \
+p_psMemSetPersistent(NAME, true); \
+p_psMemSetPersistent(NAME->data.U8, true); \
+
+#define PS_VECTOR_DECLARE_ALLOC_STATIC(NAME, SIZE, TYPE) \
+static psVector *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psVectorAlloc(SIZE, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_VECTOR_SET_U8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U8[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_U16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U16[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_U32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U32[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_U64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.U64[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S8[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S16[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S32[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_S64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.S64[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_F64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.F64[i] = VALUE; \
+}\
+
+#define PS_VECTOR_SET_F32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->n ; i++) { \
+    (NAME)->data.F32[i] = VALUE; \
+}\
+
+
+/*****************************************************************************
+    PS_POLY macros:
+*****************************************************************************/
+#define PS_POLY_CHECK_NULL(NAME, RVAL) \
+if (NAME == NULL || NAME->coeff == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: polynomial %s or its coeffs is NULL.", \
+            #NAME); \
+    return(RVAL); \
+} \
+
+#define PS_POLY_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: polynomial %s has wrong type.", #NAME); \
+    return(RVAL); \
+} \
+
+// The following macros declare and allocate a static polynomial of the
+// specified order and type.
+
+#define PS_POLY_1D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial1D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial1DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+    p_psMemSetPersistent(NAME->coeff, true); \
+    p_psMemSetPersistent(NAME->coeffErr, true); \
+    p_psMemSetPersistent(NAME->mask, true); \
+} \
+
+#define PS_POLY_2D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial2D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial2DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_3D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial3D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial3DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_4D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial4D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial4DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_1D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial1D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial1DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_2D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial2D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial2DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_3D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial3D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial3DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+#define PS_POLY_4D_D_DECLARE_ALLOC_STATIC(NAME, ORDER, TYPE) \
+static psPolynomial4D *NAME = NULL; \
+if (NAME == NULL) { \
+    NAME = psPolynomial4DAlloc(ORDER, TYPE); \
+    p_psMemSetPersistent(NAME, true); \
+} \
+
+/*****************************************************************************
+    PS_IMAGE macros:
+*****************************************************************************/
+#define PS_IMAGE_CHECK_NULL(NAME, RVAL) PS_IMAGE_CHECK_NULL_GENERAL(NAME, return RVAL)
+#define PS_IMAGE_CHECK_NULL_GENERAL(NAME, CLEANUP) \
+if (NAME == NULL || NAME->data.V == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psImage %s or its data is NULL.", \
+            #NAME); \
+    CLEANUP; \
+}
+
+#define PS_IMAGE_CHECK_EMPTY(NAME, RVAL) PS_IMAGE_CHECK_EMPTY_GENERAL(NAME, return RVAL)
+#define PS_IMAGE_CHECK_EMPTY_GENERAL(NAME, CLEANUP) \
+if (NAME->numCols < 1 || NAME->numRows < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImage %s has zero rows or columns (%dx%d).", \
+            #NAME, NAME->numCols, NAME->numRows); \
+    CLEANUP; \
+}
+
+#define PS_IMAGE_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psImage %s has incorrect type.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_CHECK_SIZE_EQUAL(NAME1, NAME2, RVAL) \
+if ((NAME1->numCols != NAME2->numCols) || \
+        (NAME1->numRows != NAME2->numRows)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImages %s and %s are not the same size.", \
+            #NAME1, #NAME2); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_CHECK_SIZE(NAME1, NUM_COLS, NUM_ROWS, RVAL) \
+if ((NAME1->numCols != NUM_COLS) || \
+        (NAME1->numRows != NUM_ROWS)) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psImages %s is not the correct size.", \
+            #NAME1); \
+    return(RVAL); \
+}
+
+#define PS_IMAGE_PRINT_F32(NAME) \
+printf("======== printing %s ========\n", #NAME); \
+for (int i = 0 ; i < NAME->numRows ; i++) { \
+    for (int j = 0 ; j < NAME->numCols ; j++) { \
+        printf("%.2f ", NAME->data.F32[i][j]); \
+    } \
+    printf("\n"); \
+}\
+
+#define PS_IMAGE_SET_U8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U8[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_U16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U16[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_U32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U32[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_U64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.U64[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S8(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S8[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S16(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S16[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S32[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_S64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.S64[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_F32(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.F32[i][j] = (VALUE); \
+    } \
+}\
+
+#define PS_IMAGE_SET_F64(NAME, VALUE) \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        (NAME)->data.F64[i][j] = (VALUE); \
+    } \
+}\
+
+/*****************************************************************************
+    PS_READOUT macros:
+*****************************************************************************/
+#define PS_READOUT_CHECK_NULL(NAME, RVAL) \
+if (NAME == NULL || NAME->image == NULL) { \
+    psError(PS_ERR_BAD_PARAMETER_NULL, true, \
+            "Unallowable operation: psReadout %s or its data is NULL.", \
+            #NAME); \
+    return(RVAL); \
+}
+
+#define PS_READOUT_CHECK_EMPTY(NAME, RVAL) \
+if (NAME->image->numCols < 1 || NAME->image->numRows < 1) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "Unallowable operation: psReadout %s or its data is NULL.", #NAME); \
+    return(RVAL); \
+}
+
+#define PS_READOUT_CHECK_TYPE(NAME, TYPE, RVAL) \
+if (NAME->image->type.type != TYPE) { \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+            "Unallowable operation: psImage %s has incorrect type.", #NAME); \
+    return(RVAL); \
+}
+
+/*****************************************************************************
+    Misc. macros:
+ *****************************************************************************/
+#define PS_MAX(A, B) \
+(((A) > (B)) ? (A) : (B)) \
+
+#define PS_MIN(A, B) \
+(((A) < (B)) ? (A) : (B)) \
+
+#define PS_SQR(A) \
+((A) * (A)) \
+
+#ifdef DARWIN
+#define PS_SQRT_F32(A) ((float) sqrt(A))
+#else
+#define PS_SQRT_F32(A) (sqrtf(A))
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psMatrix.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psMatrix.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psMatrix.c	(revision 22271)
@@ -0,0 +1,640 @@
+/** @file  psMatrix.c
+ *
+ *  @brief Provides functions for linear algebra operations on psImages and psVectors.
+ *
+ *  Functions are provided to:
+ *      Transpose a psImage
+ *      Compute LUD
+ *      Solve LUD
+ *      Matrix inversion
+ *      Calculate determinant
+ *      Matrix addition
+ *      Matrix subtraction
+ *      Matrix multiplication
+ *      Calculate Eigenvectors
+ *      Convert matrix to vector
+ *      Convert vector to matrix
+ *
+ *  These functions treat psImages as if they were matrices, therefore there is no psMatrix.
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.28.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <string.h>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_permutation.h>
+#include <gsl/gsl_eigen.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psMatrix.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/** Preprocessor macro to generate error for image dimensionality not set to PS_DIMEN_IMAGE */
+#define PS_CHECK_DIMEN_AND_TYPE(NAME, PS_DIMEN, CLEANUP)                                             \
+if (NAME->type.dimen != PS_DIMEN) {                                                                 \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                        \
+            "Invalid operation. %s has incorrect dimensionality %d.", #NAME, PS_DIMEN);             \
+    CLEANUP;                                                                                  \
+} else if(NAME->type.type!=PS_TYPE_F64 && NAME->type.type!=PS_TYPE_F32) {                           \
+    psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                        \
+            "Invalid operation. %s not PS_TYPE_F64.", #NAME);                                       \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to check that input is not equal to output */
+#define PS_CHECK_POINTERS(NAME1, NAME2, CLEANUP)                                                     \
+if (NAME1 == NAME2) {                                                                               \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                       \
+            "Invalid operation: Pointer to %s is same as %s.", #NAME1, #NAME2);                     \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to check that an image is square */
+#define PS_CHECK_SQUARE(NAME, CLEANUP)                                                               \
+if (NAME->numCols != NAME->numRows) {                                                               \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: %s not square array.", #NAME);     \
+    CLEANUP;                                                                                  \
+}
+
+/** Preprocessor macro to initalize a GSL matrix. */
+#define PS_GSL_MATRIX_INITIALIZE(LHS_NAME, RHS_NAME)                                                \
+LHS_NAME.size1 = numRows;                                                                           \
+LHS_NAME.size2 = numCols;                                                                           \
+LHS_NAME.tda   = numCols;                                                                           \
+LHS_NAME.data  = RHS_NAME;
+
+
+/*****************************************************************************/
+/* FILE STATIC FUNCTIONS                                                     */
+/*****************************************************************************/
+
+static void  psVectorToGslVector(gsl_vector *outGslVector, const psVector *inVector);
+static void gslVectorToPsVector(psVector *outVector, gsl_vector *inGslVector);
+static void  psImageToGslMatrix(gsl_matrix *outGslMatrix, const psImage *inImage);
+static void gslMatrixToPsImage(psImage *outImage, gsl_matrix *inGslMatrix);
+
+/** Static function to copy psF32 or psF64 vector data to a GSL vector */
+static void  psVectorToGslVector(gsl_vector *outGslVector, const psVector *inVector)
+{
+    psU32 i = 0;
+    psU32 n = 0;
+
+
+    n = inVector->n;
+    for(i=0; i<n; i++) {
+        if(inVector->type.type == PS_TYPE_F32) {
+            outGslVector->data[i] = (psF64)inVector->data.F32[i];
+        } else {
+            outGslVector->data[i] = inVector->data.F64[i];
+        }
+    }
+}
+
+/** Static function to copy GSL vector data to a psF32 or psF64 vector */
+static void gslVectorToPsVector(psVector *outVector, gsl_vector *inGslVector)
+{
+    psU32 i = 0;
+    psU32 n = 0;
+
+
+    n = outVector->n;
+    for(i=0; i<n; i++) {
+        if(outVector->type.type == PS_TYPE_F32) {
+            outVector->data.F32[i] = (psF32)inGslVector->data[i];
+        } else {
+            outVector->data.F64[i] = inGslVector->data[i];
+        }
+    }
+}
+
+/** Static function to copy psF32 or psF64 image data to a GSL matrix */
+static void  psImageToGslMatrix(gsl_matrix *outGslMatrix, const psImage *inImage)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psU32 numRows = 0;
+    psU32 numCols = 0;
+
+
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+    for(i=0; i<numRows; i++) {
+        for(j=0; j<numCols; j++) {
+            if(inImage->type.type == PS_TYPE_F32) {
+                outGslMatrix->data[i*numCols+j] = (psF64)inImage->data.F32[i][j];
+            } else {
+                outGslMatrix->data[i*numCols+j] = inImage->data.F64[i][j];
+            }
+        }
+    }
+}
+
+/** Static function to copy GSL matrix data to a psF32 or psF64 image */
+static void gslMatrixToPsImage(psImage *outImage, gsl_matrix *inGslMatrix)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psU32 numRows = 0;
+    psU32 numCols = 0;
+
+
+    numRows = outImage->numRows;
+    numCols = outImage->numCols;
+    for(i=0; i<numRows; i++) {
+        for(j=0; j<numCols; j++) {
+            if(outImage->type.type == PS_TYPE_F32) {
+                outImage->data.F32[i][j] = (psF32)inGslMatrix->data[i*numCols+j];
+            } else {
+                outImage->data.F64[i][j] = inGslMatrix->data[i*numCols+j];
+            }
+        }
+    }
+}
+
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psImage* psMatrixLUD(psImage* outImage, psVector** outPerm, psImage* inImage)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *lu = NULL;
+    gsl_permutation perm;
+
+
+    #define psMatrixLUD_EXIT {psFree(outImage); return NULL;}
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, psMatrixLUD_EXIT);
+    PS_CHECK_POINTERS(inImage, outImage, psMatrixLUD_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, psMatrixLUD_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(outPerm, psMatrixLUD_EXIT);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    PS_CHECK_SQUARE(inImage, psMatrixLUD_EXIT);
+    PS_CHECK_SQUARE(outImage, psMatrixLUD_EXIT);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Initialize GSL data
+    perm.size = numCols;
+    if (sizeof(size_t) == 4) {
+        *outPerm = psVectorRecycle(*outPerm, numCols, PS_TYPE_S32);
+    } else if (sizeof(size_t) == 8) {
+        *outPerm = psVectorRecycle(*outPerm, numCols, PS_TYPE_S64);
+    } else {
+        psError(PS_ERR_UNKNOWN, true,
+                "Failed to allocate the permutation vector; "
+                "could not determine the cooresponding data type.");
+        psMatrixLUD_EXIT;
+    }
+
+    (*outPerm)->n = numCols;
+    perm.data = (psPtr)((*outPerm)->data.U8);
+    lu = gsl_matrix_alloc(numRows, numCols);
+
+    // Copy psImage data into GSL matrix data
+    psImageToGslMatrix(lu, inImage);
+
+    // Calculate LU decomposition
+    gsl_linalg_LU_decomp(lu, &perm, &signum);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, lu);
+
+    // Free GSL data
+    gsl_matrix_free(lu);
+
+    return outImage;
+}
+
+psVector* psMatrixLUSolve(psVector* outVector, const psImage* inImage, const psVector* inVector,
+                          const psVector* inPerm)
+{
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *lu;
+    gsl_permutation perm;
+    gsl_vector *b = NULL;
+    gsl_vector *x = NULL;
+
+    #define LUSOLVE_CLEANUP {psFree(outVector); return NULL;}
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, LUSOLVE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, LUSOLVE_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, LUSOLVE_CLEANUP);
+    PS_VECTOR_CHECK_NULL_GENERAL(inVector, LUSOLVE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_VECTOR, LUSOLVE_CLEANUP);
+    PS_VECTOR_CHECK_NULL_GENERAL(inPerm, LUSOLVE_CLEANUP);
+
+    outVector = psVectorRecycle(outVector, inImage->numRows, inImage->type.type);
+
+    PS_CHECK_POINTERS(outVector, inVector, LUSOLVE_CLEANUP);
+    PS_CHECK_POINTERS(inVector, inPerm, LUSOLVE_CLEANUP);
+    PS_CHECK_POINTERS(outVector, inPerm, LUSOLVE_CLEANUP);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Initialize GSL data
+    lu = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, inImage);
+    b = gsl_vector_alloc(inVector->n);
+    psVectorToGslVector(b, inVector);
+    x = gsl_vector_alloc(inVector->n);
+
+    outVector->n = numCols;
+    perm.size = inPerm->n;
+    perm.data = (psPtr)(inPerm->data.U8);
+
+    // Solve for {x} in equation: {b} = [A]{x}
+    gsl_linalg_LU_solve(lu, &perm, b, x);
+
+    // Copy GSL vector data to psVector data
+    gslVectorToPsVector(outVector, x);
+
+    // Free GSL data
+    gsl_vector_free(b);
+    gsl_vector_free(x);
+    gsl_matrix_free(lu);
+
+    return outVector;
+}
+
+psImage* psMatrixInvert(psImage* outImage, const psImage* inImage, psF32 *det)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_matrix *inv = NULL;
+    gsl_matrix *lu = NULL;
+    gsl_permutation *perm = NULL;
+
+    #define INVERT_CLEANUP { psFree(outImage); return NULL; }
+    // Error checks
+    PS_PTR_CHECK_NULL_GENERAL(det, INVERT_CLEANUP);
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, INVERT_CLEANUP);
+    PS_CHECK_POINTERS(inImage, outImage, INVERT_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, INVERT_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, INVERT_CLEANUP);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    PS_CHECK_SQUARE(inImage, INVERT_CLEANUP);
+    PS_CHECK_SQUARE(outImage, INVERT_CLEANUP);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Initialize GSL data
+    perm = gsl_permutation_alloc(numRows);
+    lu = gsl_matrix_alloc(numRows, numCols);
+    inv = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, inImage);
+
+    // Invert data and calculate determinant
+    gsl_linalg_LU_decomp(lu, perm, &signum);
+    gsl_linalg_LU_invert(lu, perm, inv);
+    *det = (float)gsl_linalg_LU_det(lu, signum);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, inv);
+
+    // Free GSL structs
+    gsl_permutation_free(perm);
+    gsl_matrix_free(lu);
+    gsl_matrix_free(inv);
+
+    return outImage;
+}
+
+psF32 *psMatrixDeterminant(const psImage* inImage)
+{
+    psS32 signum = 0;
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    psF32 *det = NULL;
+    gsl_matrix *lu = NULL;
+    gsl_permutation *perm = NULL;
+
+    #define DETERMINANT_EXIT { return NULL; }
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, DETERMINANT_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, DETERMINANT_EXIT);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, DETERMINANT_EXIT);
+    PS_CHECK_SQUARE(inImage, DETERMINANT_EXIT);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    // Allocate GSL structs
+    perm = gsl_permutation_alloc(numRows);
+    lu = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(lu, inImage);
+
+    // Calculate determinant
+    det = (psF32*)psAlloc(sizeof(psF32));
+    gsl_linalg_LU_decomp(lu, perm, &signum);
+    *det = (psF32)gsl_linalg_LU_det(lu, signum);
+
+    // Free GSL structs
+    gsl_permutation_free(perm);
+    gsl_matrix_free(lu);
+
+    return det;
+}
+
+psImage* psMatrixMultiply(psImage* outImage, psImage* inImage1, psImage* inImage2)
+{
+    gsl_matrix *m1 = NULL;
+    gsl_matrix *m2 = NULL;
+    gsl_matrix *m3 = NULL;
+
+    #define MULTIPLY_CLEANUP { psFree(outImage); return NULL; }
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage1, MULTIPLY_CLEANUP);
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage2, MULTIPLY_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage1, MULTIPLY_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage2, MULTIPLY_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage1, PS_DIMEN_IMAGE, MULTIPLY_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage2, PS_DIMEN_IMAGE, MULTIPLY_CLEANUP);
+    PS_CHECK_POINTERS(inImage1, outImage, MULTIPLY_CLEANUP);
+    PS_CHECK_POINTERS(inImage1, inImage2, MULTIPLY_CLEANUP);
+
+    if (inImage1->numRows != inImage2->numCols) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: number of rows of inImage1 != number of cols of inImage2.");
+        MULTIPLY_CLEANUP;
+    }
+    if (inImage1->type.type != inImage2->type.type) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid operation: data types of inImage1 and inImage2 must match.");
+        MULTIPLY_CLEANUP;
+    }
+
+    outImage = psImageRecycle(outImage, inImage1->numCols, inImage2->numRows, inImage2->type.type);
+
+    // Initialize GSL data
+    m1 = gsl_matrix_alloc(inImage1->numRows, inImage1->numCols);
+    psImageToGslMatrix(m1, inImage1);
+    m2 = gsl_matrix_alloc(inImage2->numRows, inImage2->numCols);
+    psImageToGslMatrix(m2, inImage2);
+    m3 = gsl_matrix_alloc(outImage->numRows, outImage->numCols);
+
+    // Perform multiplication
+    gsl_linalg_matmult(m1, m2, m3);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, m3);
+
+    // Free GSL structs
+    gsl_matrix_free(m1);
+    gsl_matrix_free(m2);
+    gsl_matrix_free(m3);
+
+    return outImage;
+}
+
+psImage* psMatrixTranspose(psImage* outImage, const psImage* inImage)
+{
+    psU32 i = 0;
+    psU32 j = 0;
+    psS32 numRowsIn = 0;
+    psS32 numColsIn = 0;
+    psS32 numRowsOut = 0;
+    psS32 numColsOut = 0;
+
+    #define TRANSPOSE_CLEANUP { psFree(outImage); return NULL; }
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, TRANSPOSE_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, TRANSPOSE_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, TRANSPOSE_CLEANUP);
+    PS_CHECK_POINTERS(inImage, outImage, TRANSPOSE_CLEANUP);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    // Initialize data
+    numRowsIn = inImage->numRows;
+    numColsIn = inImage->numCols;
+    numRowsOut = outImage->numRows;
+    numColsOut = outImage->numCols;
+
+    if(numRowsIn!=numColsOut && numRowsOut!=numColsIn) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMatrix_TRANSPOSE_MISMATCH);
+        TRANSPOSE_CLEANUP;
+    }
+
+    if(outImage->type.type == PS_TYPE_F32) {
+        for(i=0; i<numRowsOut; i++) {
+            for(j=0; j<numColsOut; j++) {
+                outImage->data.F32[i][j] = inImage->data.F32[j][i];
+            }
+        }
+    } else {
+        for(i=0; i<numRowsOut; i++) {
+            for(j=0; j<numColsOut; j++) {
+                outImage->data.F64[i][j] = inImage->data.F64[j][i];
+            }
+        }
+    }
+
+    return outImage;
+}
+
+psImage* psMatrixEigenvectors(psImage* outImage, const psImage* inImage)
+{
+    psS32 numRows = 0;
+    psS32 numCols = 0;
+    gsl_vector *eVals = NULL;
+    gsl_eigen_symmv_workspace *w = NULL;
+    gsl_matrix *out = NULL;
+    gsl_matrix *in = NULL;
+
+    #define EIGENVECTORS_CLEANUP { psFree(outImage); return NULL; }
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, EIGENVECTORS_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, EIGENVECTORS_CLEANUP);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, EIGENVECTORS_CLEANUP);
+    PS_CHECK_POINTERS(inImage, outImage, EIGENVECTORS_CLEANUP);
+
+    outImage = psImageRecycle(outImage, inImage->numCols, inImage->numRows, inImage->type.type);
+
+    // Initialize data
+    numRows = inImage->numRows;
+    numCols = inImage->numCols;
+
+    in = gsl_matrix_alloc(numRows, numCols);
+    psImageToGslMatrix(in, inImage);
+    out = gsl_matrix_alloc(numRows, numCols);
+
+    // Allocate GSL structs
+    eVals = gsl_vector_alloc(numRows);
+    w = gsl_eigen_symmv_alloc(numRows);
+
+    // Non-square matrices not allowed
+    PS_CHECK_SQUARE(inImage, EIGENVECTORS_CLEANUP);
+    PS_CHECK_SQUARE(outImage, EIGENVECTORS_CLEANUP);
+
+    // Calculate Eigenvalues and Eigenvectors...Eigenvalues not currently used
+    gsl_eigen_symmv(in, eVals, out, w);
+
+    // Copy GSL matrix data to psImage data
+    gslMatrixToPsImage(outImage, out);
+
+    // Free GSL structs
+    gsl_matrix_free(in);
+    gsl_matrix_free(out);
+    gsl_eigen_symmv_free(w);
+    gsl_vector_free(eVals);
+
+    return outImage;
+}
+
+psVector* psMatrixToVector(psVector* outVector, const psImage* inImage)
+{
+    psS32 size = 0;
+
+    #define psMatrixToVector_EXIT {psFree(outVector); return NULL;}
+
+    // Error checks
+    PS_IMAGE_CHECK_NULL_GENERAL(inImage, psMatrixToVector_EXIT);
+    PS_CHECK_DIMEN_AND_TYPE(inImage, PS_DIMEN_IMAGE, psMatrixToVector_EXIT);
+    PS_IMAGE_CHECK_EMPTY_GENERAL(inImage, psMatrixToVector_EXIT);
+
+    if (inImage->numRows == 1) {
+        // Create transposed row vector
+        outVector = psVectorRecycle(outVector, inImage->numCols, inImage->type.type);
+        outVector->type.dimen = PS_DIMEN_TRANSV;
+    } else if (inImage->numCols == 1) {
+        // Create non-transposed column vector
+        outVector = psVectorRecycle(outVector, inImage->numRows, inImage->type.type);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Image does not have dim with 1 col or 1 row: (%d x %d).",
+                inImage->numRows, inImage->numCols);
+        psMatrixToVector_EXIT;
+    }
+
+    // More checks
+    if (outVector->type.dimen == PS_DIMEN_VECTOR) {
+        PS_CHECK_DIMEN_AND_TYPE(outVector, PS_DIMEN_VECTOR, psMatrixToVector_EXIT);
+
+        if (outVector->n == 0) {
+            outVector->n = inImage->numRows;
+        }
+
+        if (outVector->n != inImage->numRows) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    inImage->numRows, outVector->n);
+            psMatrixToVector_EXIT;
+        }
+
+        size = PSELEMTYPE_SIZEOF(inImage->type.type) * inImage->numRows;
+
+    } else if (outVector->type.dimen == PS_DIMEN_TRANSV) {
+        PS_CHECK_DIMEN_AND_TYPE(outVector, PS_DIMEN_TRANSV, psMatrixToVector_EXIT);
+
+        if (outVector->n == 0) {
+            outVector->n = inImage->numCols;
+        }
+
+        if (outVector->n != inImage->numCols) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    inImage->numCols, outVector->n);
+            psMatrixToVector_EXIT;
+        }
+
+        size = PSELEMTYPE_SIZEOF(inImage->type.type) * inImage->numCols;
+    }
+
+    memcpy(outVector->data.U8, inImage->data.U8[0], size);
+
+    return outVector;
+}
+
+psImage* psVectorToMatrix(psImage* outImage, const psVector* inVector)
+{
+    psS32 size = 0;
+
+    #define VECTORTOMATRIX_CLEANUP {psFree(outImage); return NULL; }
+    // Error checks
+    PS_VECTOR_CHECK_NULL_GENERAL(inVector, VECTORTOMATRIX_CLEANUP);
+
+    if (inVector->type.dimen == PS_DIMEN_VECTOR) {
+        PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_VECTOR, VECTORTOMATRIX_CLEANUP);
+        PS_VECTOR_CHECK_EMPTY_GENERAL(inVector, VECTORTOMATRIX_CLEANUP);
+
+        outImage = psImageRecycle(outImage, 1, inVector->n, inVector->type.type);
+
+        // More checks for PS_DIMEN_VECTOR
+        if (outImage->numCols > 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image has more than 1 column: numCols = %d.",
+                    outImage->numCols);
+            VECTORTOMATRIX_CLEANUP;
+        } else if (outImage->numRows != inVector->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    outImage->numRows, inVector->n);
+            VECTORTOMATRIX_CLEANUP;
+        }
+
+        size = PSELEMTYPE_SIZEOF(outImage->type.type) * outImage->numRows;
+
+    } else if (inVector->type.dimen == PS_DIMEN_TRANSV) {
+        PS_CHECK_DIMEN_AND_TYPE(inVector, PS_DIMEN_TRANSV, VECTORTOMATRIX_CLEANUP);
+        PS_VECTOR_CHECK_EMPTY_GENERAL(inVector, VECTORTOMATRIX_CLEANUP);
+        outImage = psImageRecycle(outImage, inVector->n, 1, inVector->type.type);
+        // More checks for PS_DIMEN_TRANSV
+        if (outImage->numRows > 1) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image has more than 1 row: numRows = %d.",
+                    outImage->numRows);
+            VECTORTOMATRIX_CLEANUP;
+        } else if (outImage->numCols != inVector->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    "Image and vector sizes differ: (%d vs %d).",
+                    outImage->numCols, inVector->n);
+            VECTORTOMATRIX_CLEANUP;
+        }
+
+        size = PSELEMTYPE_SIZEOF(outImage->type.type) * outImage->numCols;
+    }
+
+    PS_IMAGE_CHECK_NULL_GENERAL(outImage, VECTORTOMATRIX_CLEANUP);
+    PS_CHECK_DIMEN_AND_TYPE(outImage, PS_DIMEN_IMAGE, VECTORTOMATRIX_CLEANUP);
+
+    memcpy(outImage->data.U8[0], inVector->data.U8, size);
+
+    return outImage;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psMatrix.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psMatrix.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psMatrix.h	(revision 22271)
@@ -0,0 +1,166 @@
+
+/** @file  psMatrix.h
+ *
+ *  @brief Provides functions for linear algebra operations on psImages and psVectors.
+ *
+ *  Functions are provided to:
+ *      Transpose a psImage
+ *      Compute LUD
+ *      Solve LUD
+ *      Matrix inversion
+ *      Calculate determinant
+ *      Matrix multiplication
+ *      Calculate Eigenvectors
+ *      Convert matrix to vector
+ *      Convert vector to matrix
+ *
+ *  These functions treat psImages as if they were matrices, therefore there is no psMatrix. These functions
+ *  operate only with the psF64 data type.
+ *
+ *  @ingroup Matrix
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.15.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSMATRIX_H
+#define PSMATRIX_H
+
+/// @addtogroup Matrix
+/// @{
+
+/** LU Decomposition of psImage matrix.
+ *
+ *  Performs a LU decomposition on a psImage matrix and returns the LU matrix. If the user specifies NULL for
+ *  the outImage or outPerm arguments, then they will be automatically created. The input image must
+ *  be square. This function operates only with the psF64 data type. Input and output arguments should not be
+ *  the same. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to LU decomposed psImage.
+ */
+psImage* psMatrixLUD(
+    psImage* outImage,                 ///< Image to return, or NULL.
+    psVector** outPerm,                ///< Output permutation vector used by psMatrixLUSolve.
+    psImage* inImage                   ///< Image to decompose.
+);
+
+/** LU Solution of psImage matrix.
+ *
+ *  Solves for and returns the psVector, {x} in the equation [A]{x} = {b}. If the user specifies NULL as the
+ *  outVector argument, then it will automatically be created. The input image must be square. This function
+ *  operates only with the psF64 data type. Input and output arguments should not be the same. GSL indexes
+ *  the top row as the zero row, not the bottom.
+ *
+ *  @return  psVector* : Pointer to psVector solution of matrix equation.
+ */
+psVector* psMatrixLUSolve(
+    psVector* outVector,               ///< Vector to return, or NULL.
+    const psImage* luImage,            ///< LU-decomposed matrix.
+    const psVector* inVector,          ///< Vector right-hand-side of equation.
+    const psVector* inPerm             ///< Permutation vector resulting from psMatrixLUD function.
+);
+
+/** Invert psImage matrix.
+ *
+ *  Inverts a psImage matrix and returns the determinant as an option through the argument list. If the user
+ *  specifies NULL as the outImage argument, then it will automatically be created. The input image must be
+ *  square. This function operates only with the psF64 data type. Input and output arguments should not be
+ *  the same. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to inverted psImage.
+ */
+psImage* psMatrixInvert(
+    psImage* outImage,                 ///< Image to return, or NULL for in-place substitution.
+    const psImage* inImage,            ///< Image to be inverted
+    psF32 *det                         ///< Determinant to return, or NULL
+);
+
+/** Calculate psImage matrix determinant.
+ *
+ *  Calculates the determinant of a psImage matrix and returns the single precision floating point result. The
+ *  input image must be square. This function operates only with the psF64 data type. GSL indexes the top row
+ *  as the zero row, not the bottom.
+ *
+ *  @return  float: Determinant from psImage.
+ */
+psF32 *psMatrixDeterminant(
+    const psImage* inMatrix        ///< Image used to calculate determinant.
+);
+
+/** Performs psImage matrix multiplication.
+ *
+ *  Performs a classical matrix multiplication involving row and column operations. Input images must be square
+ *  and the same size. If the user specifies NULL as the outImage argument, then it will automatically be
+ *  created. This function operates only with the psF64 data type. GSL indexes the top row as the
+ *  zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to resulting psImage.
+ */
+psImage* psMatrixMultiply(
+    psImage* outImage,                 ///< Matrix to return, or NULL.
+    psImage* inImage1,                 ///< First input image.
+    psImage* inImage2                  ///< Second input image.
+);
+
+/** Transpose matrix.
+ *
+ *  Performs psImage matrix transpose by substituting existing rows for columns. The input image must be
+ *  square. If the user specifies NULL as the outImage argument, then it will automaticallty be created.
+ *  This function operates only with the psF64 data type. GSL indexes the top row as the zero
+ *  row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to transposed psImage.
+ */
+psImage* psMatrixTranspose(
+    psImage* outImage,                 ///< Image to return, or NULL
+    const psImage* inImage             ///< Image to transpose
+);
+
+/** Calculate matrix eigenvectors.
+ *
+ *  Calculates the eigenvectors for a matrix. The input image must be symmetric and square. If the user
+ *  specifies NULL as the outImage argument, then it will automatically be created. This function operates
+ *  only with the psF64 data type. GSL indexes the top row as the zero row, not the bottom.
+ *
+ *  @return  psImage* : Pointer to matrix of Eigenvectors.
+ */
+psImage* psMatrixEigenvectors(
+    psImage* outImage,                 ///< Eigenvectors to return, or NULL.
+    const psImage* inImage  ///< Input image.
+);
+
+/** Convert matrix to vector.
+ *
+ *  Converts a 1-d psImage matrix into a vector. If the user specifies NULL as the outVector argument, then it
+ *  will automatically be created based on the input image (PS_DIMEN_VECTOR for an input image with 1 col or
+ *  PS_DIMENT_TRANSV for an input image with 1 row). Either the number of rows or the number of colums of the
+ *  input matrix must be 1. This function operates only  with the psF64 data type.
+ *
+ *  @return  psVector* : Pointer to psVector.
+ */
+psVector* psMatrixToVector(
+    psVector* outVector,               ///< Vector to return, or NULL.
+    const psImage* inImage             ///< Image to convert.
+);
+
+/** Convert vector to matrix.
+ *
+ *  Converts a vector into a psImage matrix. If the dimensionality of the vector is PS_DIMEN_VECTOR, then the
+ *  resulting psImage is a 1d column. If the dimensionality of the vector is PS_DIMEN_TRANSV, then the
+ *  resulting psImage is a 1d row. If the user specifies NULL as the outImage argument,  then it will
+ *  automatically be created. This function operates only with the psF64 data type.
+ *
+ *  @return  psVector* : Pointer to psIamge.
+ */
+psImage* psVectorToMatrix(
+    psImage* outImage,                 ///< Matrix to return, or NULL.
+    const psVector* inVector           ///< Vector to convert.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psMinimize.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psMinimize.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psMinimize.c	(revision 22271)
@@ -0,0 +1,2203 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain functions to minimize an arbitrary function at
+ *  a data point, fit an arbitrary function to a set of data points, and
+ *  fit a 1-D polynomial to a set of data points.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.116 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: must follow coding name standards on local functions.
+ *
+ */
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include <stdio.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMinimize.h"
+#include "psStats.h"
+#include "psImageIO.h"
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+#define PS_SEG psLib
+#define PS_PWD dataManip
+#define PS_FILE psMinimize
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+static psMinimizeChi2PowellFunc Chi2PowellFunc = NULL;
+static psVector *myValue;
+static psVector *myError;
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/******************************************************************************
+p_psBuildSums1D(x, polyOrder, sums): this routine calculates the powers of
+input parameter "x" between 0 and input parameter polyOrder.  The result is
+returned as a psVector sums.
+ 
+XXX: Use a static vector.
+ *****************************************************************************/
+static void buildSums1D(psF64 x,
+                        psS32 polyOrder,
+                        psVector* sums)
+{
+    psS32 i = 0;
+    psF64 xSum = 0.0;
+
+    if (sums == NULL) {
+        sums = psVectorAlloc(polyOrder, PS_TYPE_F64);
+    }
+    if (polyOrder > sums->n) {
+        sums = psVectorRealloc(sums, polyOrder);
+    }
+
+    xSum = 1.0;
+    for (i = 0; i <= polyOrder; i++) {
+        sums->data.F64[i] = xSum;
+        xSum *= x;
+    }
+}
+
+/*****************************************************************************
+CalculateSecondDerivs(): Given a set of x/y vectors corresponding to a
+tabulated function at n points, this routine calculates the second
+derivatives of the interpolating cubic splines at those n points.
+ 
+The first and second derivatives at the endpoints, undefined in the SDR, are
+here defined to be 0.0.  They can be modified via ypo and yp1.
+ 
+This routine assumes that vectors x and y are of the appropriate types/sizes
+(F32).
+ 
+XXX: This algorithm is derived from the Numerical Recipes.
+XXX: use recycled vectors for internal data.
+XXX: do an F64 version?
+ *****************************************************************************/
+static psF32 *calculateSecondDerivs(const psVector* x,        ///< Ordinates (or NULL to just use the indices)
+                                    const psVector* y)        ///< Coordinates
+{
+    psTrace(".psLib.dataManip.calculateSecondDerivs", 4,
+            "---- calculateSecondDerivs() begin ----\n");
+
+    psS32 i;
+    psS32 k;
+    psF32 sig;
+    psF32 p;
+    psS32 n = y->n;
+    psF32 *u = (psF32 *) psAlloc(n * sizeof(psF32));
+    psF32 *derivs2 = (psF32 *) psAlloc(n * sizeof(psF32));
+    psF32 *X = (psF32 *) & (x->data.F32[0]);
+    psF32 *Y = (psF32 *) & (y->data.F32[0]);
+    psF32 qn;
+
+    // XXX: The second derivatives at the endpoints, undefined in the SDR,
+    // are set in psConstants.h: PS_LEFT_SPLINE_DERIV, PS_RIGHT_SPLINE_DERIV.
+    derivs2[0] = -0.5;
+    u[0]= (3.0/(X[1]-X[0])) * ((Y[1]-Y[0])/(X[1]-X[0]) - PS_LEFT_SPLINE_DERIV);
+
+    for (i=1;i<=(n-2);i++) {
+        sig = (X[i] - X[i-1]) / (X[i+1] - X[i-1]);
+        p = sig * derivs2[i-1] + 2.0;
+        derivs2[i] = (sig - 1.0) / p;
+        u[i] = ((Y[i+1] - Y[i])/(X[i+1]-X[i])) - ((Y[i]-Y[i-1])/(X[i]-X[i-1]));
+        u[i] = ((6.0 * u[i] / (X[i+1] - X[i-1])) - (sig * u[i-1])) / p;
+
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "X[%d] is %f\n", i, X[i]);
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "Y[%d] is %f\n", i, Y[i]);
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "u[%d] is %f\n", i, u[i]);
+    }
+
+    qn = 0.5;
+    u[n-1] = (3.0/(X[n-1]-X[n-2])) * (PS_RIGHT_SPLINE_DERIV - (Y[n-1]-Y[n-2])/(X[n-1]-X[n-2]));
+    derivs2[n-1] = (u[n-1] - (qn * u[n-2])) / ((qn * derivs2[n-2]) + 1.0);
+
+    for (k=(n-2);k>=0;k--) {
+        derivs2[k] = derivs2[k] * derivs2[k+1] + u[k];
+
+        psTrace(".psLib.dataManip.calculateSecondDerivs", 6,
+                "derivs2[%d] is %f\n", k, derivs2[k]);
+    }
+
+    psFree(u);
+    psTrace(".psLib.dataManip.calculateSecondDerivs", 4,
+            "---- calculateSecondDerivs() end ----\n");
+    return(derivs2);
+}
+
+/******************************************************************************
+p_psNRSpline1DEval(): This routine does NR-style evaluation of cubic splines.
+It takes advantage of the 2nd derivatives of the cubic splines, which are
+stored in the psSPline1D data structure, and computes the interpolated value
+directly, without computing (or using) the interpolating cubic spline
+polynomial.
+ 
+This routine is here mostly for a sanity check on the psLib function
+evalSpline() which computes the interpolated value based on the cubic spline
+polynomials which are stored in psSpline1D.
+ 
+XXX: This is F32 only
+ 
+XXX: spline->knots must be psF32
+ *****************************************************************************/
+/*
+psF32 p_psNRSpline1DEval(psSpline1D *spline,
+                         const psVector* x,
+                         const psVector* y,
+                         psF32 X)
+{
+    PS_PTR_CHECK_NULL(spline, NAN);
+    PS_INT_CHECK_NON_NEGATIVE(spline->n, NAN);
+    PS_VECTOR_CHECK_NULL(spline->domains, NAN);
+    PS_PTR_CHECK_NULL(spline->p_psDeriv2, NAN);
+    PS_VECTOR_CHECK_NULL(x, NAN);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_NULL(y, NAN);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NULL);
+ 
+    psS32 n;
+    psS32 klo;
+    psS32 khi;
+    psF32 H;
+    psF32 A;
+    psF32 B;
+    psF32 C;
+    psF32 D;
+    psF32 Y;
+ 
+    n = spline->n;
+    klo = p_psVectorBinDisect32(spline->knots->data.F32, (spline->n)+1, X);
+    if (klo < 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: psMinimize.c: p_psNRSpline1DEval(): p_psVectorBinDisect32 returned an error (%d).\n", klo);
+        return(NAN);
+    }
+    khi = klo + 1;
+    H = spline->knots->data.F32[khi] - spline->knots->data.F32[klo];
+    A = (spline->knots->data.F32[khi] - X) / H;
+    B = (X - spline->knots->data.F32[klo]) / H;
+    C = ((A*A*A)-A) * (H*H/6.0);
+    D = ((B*B*B)-B) * (H*H/6.0);
+ 
+    Y = (A * y->data.F32[klo]) +
+        (B * y->data.F32[khi]) +
+        (C * (spline->p_psDeriv2)[klo]) +
+        (D * (spline->p_psDeriv2)[khi]);
+ 
+    return(Y);
+}
+*/
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+/*****************************************************************************
+psVectorFitSpline1D(): given a psSpline1D data structure and a set of x/y
+vectors, this routine generates the linear or cublic splines which satisfy
+those data points.
+ 
+The formula for calculating the spline polynomials is derived from Numerical
+Recipes in C.  The basic idea is that the polynomial is
+ (1)     y = (A * y[0]) +
+ (2)         (B * y[1]) +
+ (3)         ((((A*A*A)-A) * mySpline->p_psDeriv2[0]) * H^2)/6.0 +
+ (4)         ((((B*B*B)-B) * mySpline->p_psDeriv2[1]) * H^2)/6.0
+Where:
+ H = x[1]-x[0]
+ A = (x[1]-x)/H
+ B = (x-x[0])/H
+The bulk of the code in this routine is the expansion of the above equation
+into a polynomial in terms of x, and then saving the coefficients of the
+powers of x in the spline polynomials.  This gets pretty complicated.
+ 
+XXX: usage of yErr is not specified in IfA documentation.
+ 
+XXX: Is the x argument redundant?  What do we do if the x argument is
+supplied, but does not equal the knots specified in mySpline?
+ 
+XXX: can psSpline be NULL?
+ 
+XXX: reimplement this assuming that mySpline is NULL?
+ 
+XXX: What happens if X is NULL, then an index vector is generated for X, but
+that index vector lies outside the range vectors in mySpline?
+ 
+XXX: Assumes mySpline->knots is psF32.  Must add psU32 and psF64.
+ *****************************************************************************/
+psSpline1D *psVectorFitSpline1D(psSpline1D *mySpline,     ///< The spline which will be generated.
+                                const psVector* x,        ///< Ordinates (or NULL to just use the indices)
+                                const psVector* y,        ///< Coordinates
+                                const psVector* yErr)     ///< Errors in coordinates, or NULL
+{
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(y, NULL);
+    if (mySpline != NULL) {
+        PS_VECTOR_CHECK_TYPE(mySpline->knots, PS_TYPE_F32, NULL);
+    }
+
+    psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
+            "---- psVectorFitSpline1D() begin ----\n");
+    psS32 numSplines = (y->n)-1;
+    psF32 tmp;
+    psF32 H;
+    psS32 i;
+    psF32 slope;
+    psVector *x32 = NULL;
+    psVector *y32 = NULL;
+    psVector *yErr32 = NULL;
+    static psVector *x32Static = NULL;
+    static psVector *y32Static = NULL;
+    static psVector *yErr32Static = NULL;
+
+    PS_VECTOR_CONVERT_F64_TO_F32_STATIC(y, y32, y32Static);
+
+    // If yErr==NULL, set all errors equal.
+    if (yErr == NULL) {
+        PS_VECTOR_GEN_YERR_STATIC_F32(yErr32Static, y->n);
+        yErr32 = yErr32Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(yErr, NULL);
+        PS_VECTOR_CONVERT_F64_TO_F32_STATIC(yErr, yErr32, yErr32Static);
+    }
+
+    // If x==NULL, create an x32 vector with x values set to (0:n).
+    if (x == NULL) {
+        PS_VECTOR_GEN_X_INDEX_STATIC_F32(x32Static, y->n);
+        x32 = x32Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+        PS_VECTOR_CONVERT_F64_TO_F32_STATIC(x, x32, x32Static);
+    }
+    PS_VECTOR_CHECK_SIZE_EQUAL(x32, y32, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(yErr32, y32, NULL);
+
+    /*
+        XXX:
+        This can not be implemented until SDR states what order spline should be
+        created.
+        Should we error if mySpline is not NULL?
+        Should we error if mySPline is not NULL?
+    */
+    if (mySpline == NULL) {
+        mySpline = psSpline1DAllocGeneric(x32, 3);
+    }
+    PS_PTR_CHECK_NULL(mySpline, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(mySpline->n, NULL);
+
+    if (y32->n != (1 + mySpline->n)) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "data size / spline size mismatch (%d %d)\n",
+                y32->n, mySpline->n);
+        return(NULL);
+    }
+
+    // If these are linear splines, which means their polynomials will have
+    // two coefficients, then we do the simple calculation.
+    if (2 == (mySpline->spline[0])->n) {
+        for (i=0;i<mySpline->n;i++) {
+            slope = (y32->data.F32[i+1] - y32->data.F32[i]) /
+                    (mySpline->knots->data.F32[i+1] - mySpline->knots->data.F32[i]);
+            (mySpline->spline[i])->coeff[0] = y32->data.F32[i] -
+                                              (slope * mySpline->knots->data.F32[i]);
+
+            (mySpline->spline[i])->coeff[1] = slope;
+            psTrace(".psLib.dataManip.psMinimize.psVectorFitSpline1D", 4,
+                    "---- mySpline %d coeffs are (%f, %f)\n", i,
+                    (mySpline->spline[i])->coeff[0],
+                    (mySpline->spline[i])->coeff[1]);
+        }
+        psTrace(".psLib.dataManip.psMinimize.psVectorFitSpline1D", 4,
+                "---- Exiting psVectorFitSpline1D()()\n");
+        return((psSpline1D *) mySpline);
+    }
+
+    // Check if these are cubic splines (n==4).  If not, psError.
+    if (4 != (mySpline->spline[0])->n) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                "Don't know how to generate %d-order splines.",
+                (mySpline->spline[0])->n-1);
+        return(NULL);
+    }
+
+    // If we get here, then we know these are cubic splines.  We first
+    // generate the second derivatives at each data point.
+    mySpline->p_psDeriv2 = calculateSecondDerivs(x32, y32);
+    for (i=0;i<y32->n;i++)
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "Second deriv[%d] is %f\n", i, mySpline->p_psDeriv2[i]);
+
+    // We generate the coefficients of the spline polynomials.  I can't
+    // concisely explain how this code works.  See above function comments
+    // and Numerical Recipes in C.
+    for (i=0;i<numSplines;i++) {
+        H = x32->data.F32[i+1] - x32->data.F32[i];
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
+                "x data (%f - %f) (%f)\n",
+                x32->data.F32[i],
+                x32->data.F32[i+1], H);
+        //
+        // ******** Calculate 0-order term ********
+        //
+        // From (1)
+        (mySpline->spline[i])->coeff[0] = (y32->data.F32[i] * x32->data.F32[i+1]/H);
+        // From (2)
+        ((mySpline->spline[i])->coeff[0])-= ((y32->data.F32[i+1] * x32->data.F32[i])/H);
+        // From (3)
+        tmp = (x32->data.F32[i+1] * x32->data.F32[i+1] * x32->data.F32[i+1]) / (H * H * H);
+        tmp-= (x32->data.F32[i+1] / H);
+        tmp*= (mySpline->p_psDeriv2)[i] * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[0])+= tmp;
+        // From (4)
+        tmp = -(x32->data.F32[i] * x32->data.F32[i] * x32->data.F32[i]) / (H * H * H);
+        tmp+= (x32->data.F32[i] / H);
+        tmp*= (mySpline->p_psDeriv2)[i+1] * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[0])+= tmp;
+
+        //
+        // ******** Calculate 1-order term ********
+        //
+        // From (1)
+        (mySpline->spline[i])->coeff[1] = -(y32->data.F32[i]) / H;
+        // From (2)
+        ((mySpline->spline[i])->coeff[1])+= (y32->data.F32[i+1] / H);
+        // From (3)
+        tmp = -3.0 * (x32->data.F32[i+1] * x32->data.F32[i+1]) / (H * H * H);
+        tmp+= (1.0 / H);
+        tmp*= ((mySpline->p_psDeriv2)[i]) * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[1])+= tmp;
+        // From (4)
+        tmp = 3.0 * (x32->data.F32[i] * x32->data.F32[i]) / (H * H * H);
+        tmp-= (1.0 / H);
+        tmp*= ((mySpline->p_psDeriv2)[i+1]) * H * H / 6.0;
+        ((mySpline->spline[i])->coeff[1])+= tmp;
+
+        //
+        // ******** Calculate 2-order term ********
+        //
+        // From (3)
+        (mySpline->spline[i])->coeff[2] = ((mySpline->p_psDeriv2)[i]) * 3.0 * x32->data.F32[i+1] / (6.0 * H);
+        // From (4)
+        ((mySpline->spline[i])->coeff[2])-= (((mySpline->p_psDeriv2)[i+1]) * 3.0 * x32->data.F32[i] / (6.0 * H));
+
+        //
+        // ******** Calculate 3-order term ********
+        //
+        // From (3)
+        (mySpline->spline[i])->coeff[3] = -((mySpline->p_psDeriv2)[i]) / (6.0 * H);
+        // From (4)
+        ((mySpline->spline[i])->coeff[3])+=  ((mySpline->p_psDeriv2)[i+1]) / (6.0 * H);
+
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[0] is %f\n", i, (mySpline->spline[i])->coeff[0]);
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[1] is %f\n", i, (mySpline->spline[i])->coeff[1]);
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[2] is %f\n", i, (mySpline->spline[i])->coeff[2]);
+        psTrace(".psLib.dataManip.psVectorFitSpline1D", 6,
+                "(mySpline->spline[%d])->coeff[3] is %f\n", i, (mySpline->spline[i])->coeff[3]);
+
+    }
+
+    psTrace(".psLib.dataManip.psVectorFitSpline1D", 4,
+            "---- psVectorFitSpline1D() end ----\n");
+    return(mySpline);
+}
+
+
+/******************************************************************************
+XXX: We assume unnormalized gaussians.
+ *****************************************************************************/
+psVector *psMinimizeLMChi2Gauss1D(psImage *deriv,
+                                  const psVector *params,
+                                  const psArray *coords)
+{
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(params, NULL);
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss1D() begin ----\n");
+    psF32 x;
+    psS32 i;
+    psF32 mean = params->data.F32[0];
+    psF32 stdev = params->data.F32[1];
+    psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
+
+    psTrace(".psLib.dataManip.psMinimize", 6,
+            "(mean, stdev) is (%f, %f)\n", mean, stdev);
+
+    if (deriv == NULL) {
+        deriv = psImageAlloc(params->n, coords->n, PS_TYPE_F32);
+    } else {
+        PS_IMAGE_CHECK_SIZE(deriv, params->n, coords->n, NULL);
+        PS_IMAGE_CHECK_TYPE(deriv, PS_TYPE_F32, NULL);
+    }
+
+    for (i=0;i<coords->n;i++) {
+        x = ((psVector *) (coords->data[i]))->data.F32[0];
+        out->data.F32[i] = psGaussian(x, mean, stdev, false);
+    }
+
+    for (i=0;i<coords->n;i++) {
+        x = ((psVector *) (coords->data[i]))->data.F32[0];
+        psF32 tmp = (x - mean) * psGaussian(x, mean, stdev, false);
+        deriv->data.F32[i][0] = tmp / (stdev * stdev);
+        tmp = (x - mean) * (x - mean) *
+              psGaussian(x, mean, stdev, 0);
+        deriv->data.F32[i][1] = tmp / (stdev * stdev * stdev);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss1D() end ----\n");
+    return(out);
+}
+
+/*
+XXX: from bug 230:
+ 
+We first perform a rotation:
+u = - (x-x0)*cos(theta) + (y-y0)*sin(theta)
+v = (x-x0)*cos(theta) + (y-y0)*sin(theta)
+ 
+Here u is the major axis, and v is the minor axis, x0,y0 is the centre, and
+theta is the position angle.
+ 
+Then the flux is
+ 
+flux = norm * exp(-( u*u/2.0/sigmau/sigmau + v*v/2.0/sigmav/sigmav)
+)/2.0/pi/sigmau/sigmav
+ 
+Here sigmau and sigmav are the widths of the major and minor axes.
+ 
+The "norm" parameter in the equation above corresponds to the normalisation.
+ 
+Suggest order:
+ 
+norm
+x0
+y0
+sigma_u
+sigma_v
+theta
+*/
+
+psVector *psMinimizeLMChi2Gauss2D(psImage *deriv,
+                                  const psVector *params,
+                                  const psArray *coords)
+{
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(params, NULL);
+
+    psF64 normalization = params->data.F32[0];
+    psF64 x0 = params->data.F32[1];
+    psF64 y0 = params->data.F32[2];
+    psF64 sigmaX = params->data.F32[3];
+    psF64 sigmaY = params->data.F32[4];
+    psF64 theta = params->data.F32[5];
+    psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
+
+    if (deriv == NULL) {
+        deriv = psImageAlloc(params->n, coords->n, PS_TYPE_F32);
+    } else {
+        PS_IMAGE_CHECK_SIZE(deriv, 6, coords->n, NULL);
+        PS_IMAGE_CHECK_TYPE(deriv, PS_TYPE_F32, NULL);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss2D() begin ----\n");
+
+    for (psS32 i=0;i<coords->n;i++) {
+        psF64 x = ((psVector *) coords->data[i])->data.F32[0];
+        psF64 y = ((psVector *) coords->data[i])->data.F32[0];
+
+        psF64 u = - (x-x0)*cos(theta) + (y-y0)*sin(theta);
+        psF64 v = (x-x0)*cos(theta) + (y-y0)*sin(theta);
+
+        psF64 flux = normalization * exp(-( u*u/(2.0 * sigmaX * sigmaX) +
+                                            v*v/(2.0 * sigmaY * sigmaY)))/
+                     (2.0 * M_PI * sigmaX * sigmaY);
+        out->data.F32[i] = flux;
+
+        // XXX: Calculate these correctly.
+        deriv->data.F32[i][0] = 0.0;
+        deriv->data.F32[i][1] = 0.0;
+        deriv->data.F32[i][2] = 0.0;
+        deriv->data.F32[i][3] = 0.0;
+        deriv->data.F32[i][4] = 0.0;
+        deriv->data.F32[i][5] = 0.0;
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2Gauss2D() end ----\n");
+    return(out);
+}
+
+psF64 p_psImageGetElementF64(psImage *a, int i, int j);
+
+// XXX EAM this is my re-implementation of MinLM
+psBool psMinimizeLMChi2(psMinimization *min,
+                        psImage *covar,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *x,
+                        const psVector *y,
+                        const psVector *yErr,
+                        psMinimizeLMChi2Func func)
+{
+    PS_PTR_CHECK_NULL(min, NULL);
+    PS_VECTOR_CHECK_NULL(params, NULL);
+    PS_VECTOR_CHECK_EMPTY(params, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_EMPTY(y, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
+    PS_PTR_CHECK_NULL(func, NULL);
+
+    // this function has test and current values for several things
+    // the current best value is in lower case
+    // the next guess value is in upper case
+
+    // allocate internal arrays (current vs Guess)
+    psImage *alpha   = psImageAlloc (params->n, params->n, PS_TYPE_F64);
+    psImage *Alpha   = psImageAlloc (params->n, params->n, PS_TYPE_F64);
+    psVector *beta   = psVectorAlloc (params->n, PS_TYPE_F64);
+    psVector *Beta   = psVectorAlloc (params->n, PS_TYPE_F64);
+    psVector *Params = psVectorAlloc (params->n, PS_TYPE_F64);
+    psVector *dy     = NULL;
+    psF64 chisq = 0.0;
+    psF64 Chisq = 0.0;
+    psF64 lambda = 0.001;
+
+    // the initial guess on params is provided by the user
+    Params = psVectorCopy (Params, params, PS_TYPE_F32);
+
+    // the user provides the error or NULL.  we need to convert
+    // to appropriate weights
+    dy = psVectorAlloc (y->n, PS_TYPE_F32);
+    if (yErr != NULL) {
+        for (int i = 0; i < dy->n; i++) {
+            dy->data.F32[i] = 1.0 / PS_SQR (yErr->data.F32[i]);
+        }
+    } else {
+        for (int i = 0; i < dy->n; i++) {
+            dy->data.F32[i] = 1.0;
+        }
+    }
+
+    // calculate initial alpha and beta, set chisq (min->value)
+    min->value = p_psMinLM_SetABX (alpha, beta, params, x, y, dy, func);
+    # ifndef PS_NO_TRACE
+    // dump some useful info if trace is defined
+    if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
+        p_psImagePrint  (psTraceGetDestination(), alpha, "alpha guess");
+        p_psVectorPrint (psTraceGetDestination(), beta, "beta guess");
+        p_psVectorPrint (psTraceGetDestination(), params, "params guess");
+    }
+    # endif /* PS_NO_TRACE */
+
+
+    // iterate until the tolerance is reached, or give up
+    while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
+
+        // set a new guess for Alpha, Beta, Params
+        p_psMinLM_GuessABP (Alpha, Beta, Params, alpha, beta, params, lambda);
+
+        # ifndef PS_NO_TRACE
+        // dump some useful info if trace is defined
+        if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
+            p_psImagePrint  (psTraceGetDestination(), Alpha, "alpha guess");
+            p_psVectorPrint (psTraceGetDestination(), Beta, "beta guess");
+            p_psVectorPrint (psTraceGetDestination(), Params, "params guess");
+        }
+        # endif /* PS_NO_TRACE */
+
+        // calculate Chisq for new guess, update Alpha & Beta
+        Chisq = p_psMinLM_SetABX (Alpha, Beta, Params, x, y, dy, func);
+        psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
+
+        // accept new guess (if improvement), or increase lambda
+        if (Chisq < min->value) {
+            min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
+            min->value = Chisq;
+            alpha  = psImageCopy (alpha, Alpha, PS_TYPE_F64);
+            beta   = psVectorCopy (beta, Beta, PS_TYPE_F64);
+            params = psVectorCopy (params, Params, PS_TYPE_F32);
+            lambda *= 0.1;
+        } else {
+            lambda *= 10.0;
+        }
+        min->iter ++;
+    }
+    psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
+
+    // free the internal temporary data
+    psFree (alpha);
+    psFree (Alpha);
+    psFree (beta);
+    psFree (Beta);
+    psFree (Params);
+    psFree (dy);
+    return (true);
+}
+
+// XXX EAM: this needs to respect the mask on params
+// XXX EAM: check not NULL on alpha, beta, params
+// alpha, beta, params are already allocated
+psF64 p_psMinLM_SetABX (psImage  *alpha,
+                        psVector *beta,
+                        psVector *params,
+                        const psArray  *x,
+                        const psVector *y,
+                        const psVector *dy,
+                        psMinimizeLMChi2Func func)
+{
+
+    psF64 chisq;
+    psF64 delta;
+    psF64 weight;
+    psF64 ymodel;
+    psVector *deriv = psVectorAlloc (params->n, PS_TYPE_F32);
+
+    // zero alpha and beta for summing below
+    for (int j = 0; j < params->n; j++) {
+        for (int k = 0; k < params->n; k++) {
+            alpha->data.F64[j][k] = 0;
+        }
+        beta->data.F64[j] = 0;
+    }
+    chisq = 0.0;
+
+    // calculate chisq, alpha, beta
+    for (int i = 0; i < y->n; i++) {
+        ymodel = func (deriv, params, (psVector *) x->data[i]);
+
+        delta = ymodel - y->data.F32[i];
+        chisq += PS_SQR (delta) * dy->data.F32[i];
+
+        for (int j = 0; j < params->n; j++) {
+            weight = deriv->data.F32[j] * dy->data.F32[i];
+            for (int k = 0; k <= j; k++) {
+                alpha->data.F64[j][k] += weight * deriv->data.F32[k];
+            }
+            beta->data.F64[j] += weight * delta;
+        }
+    }
+
+    // calculate lower-left half of alpha
+    for (int j = 1; j < params->n; j++) {
+        for (int k = 0; k < j; k++) {
+            alpha->data.F64[k][j] = alpha->data.F64[j][k];
+        }
+    }
+    psFree (deriv);
+    return (chisq);
+}
+
+// XXX EAM : can we use static copies of LUv, LUm, A?
+psBool p_psMinLM_GuessABP (psImage  *Alpha,
+                           psVector *Beta,
+                           psVector *Params,
+                           psImage  *alpha,
+                           psVector *beta,
+                           psVector *params,
+                           psF64 lambda)
+{
+
+    # define USE_LU_DECOMP 1
+    # if (USE_LU_DECOMP)
+        psVector *LUv = NULL;
+    psImage  *LUm = NULL;
+    psImage  *A   = NULL;
+    psF32    det;
+
+    // LU decomposition version
+    psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using LUD version");
+
+    // set new guess values (creates matrix A)
+    A = psImageCopy (NULL, alpha, PS_TYPE_F64);
+    for (int j = 0; j < params->n; j++) {
+        A->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
+    }
+
+    // solve A*beta = Beta (Alpha = 1/A)
+    // these operations do not modify the input values (creates LUm, LUv)
+    LUm   = psMatrixLUD (NULL, &LUv, A);
+    Beta  = psMatrixLUSolve (Beta, LUm, beta, LUv);
+    Alpha = psMatrixInvert (Alpha, A, &det);
+
+    # else
+        // gauss-jordan version
+        psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using Gauss-J version");
+
+    // set new guess values (creates matrix A)
+    Beta = psVectorCopy (Beta, beta, PS_TYPE_F64);
+    Alpha = psImageCopy (Alpha, alpha, PS_TYPE_F64);
+    for (int j = 0; j < params->n; j++) {
+        Alpha->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
+    }
+
+    psGaussJordan (Alpha, Beta);
+    # endif
+
+    // apply beta to get new params values
+    for (int j = 0; j < params->n; j++) {
+        Params->data.F32[j] = params->data.F32[j] - Beta->data.F64[j];
+    }
+
+    # if (USE_LU_DECOMP)
+        psFree (A);
+    psFree (LUm);
+    psFree (LUv);
+    # endif
+
+    return true;
+}
+
+// XXX" put this in constants.h
+# define PS_SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
+
+// XXX EAM : temporary gauss-jordan solver based on gene's
+// version based on the Numerical Recipes version
+bool psGaussJordan (psImage *a, psVector *b)
+{
+
+    int *indxc,*indxr,*ipiv;
+    int Nx, icol, irow;
+    int i, j, k, l, ll;
+    float big, dum, pivinv;
+    psF64 *vector;
+    psF64 **matrix;
+
+    Nx = a->numCols;
+    matrix = a->data.F64;
+    vector = b->data.F64;
+
+    indxc = psAlloc (Nx*sizeof(int));
+    indxr = psAlloc (Nx*sizeof(int));
+    ipiv  = psAlloc (Nx*sizeof(int));
+    for (j = 0; j < Nx; j++) {
+        ipiv[j] = 0;
+    }
+
+    irow = icol = 0;
+    big = fabs(matrix[0][0]);
+
+    for (i = 0; i < Nx; i++) {
+        big = 0.0;
+        for (j = 0; j < Nx; j++) {
+            if (!isfinite(matrix[i][j])) {
+                // XXX EAM: this should use the psError stack
+                fprintf (stderr, "GAUSSJ: NaN\n");
+                goto fescape;
+            }
+            if (ipiv[j] != 1) {
+                for (k = 0; k < Nx; k++) {
+                    if (ipiv[k] == 0) {
+                        if (fabs (matrix[j][k]) >= big) {
+                            big  = fabs (matrix[j][k]);
+                            irow = j;
+                            icol = k;
+                        }
+                    } else {
+                        if (ipiv[k] > 1) {
+                            // XXX EAM: this should use the psError stack
+                            fprintf (stderr, "GAUSSJ: Singular Matrix! (1)\n");
+                            goto fescape;
+                        }
+                    }
+                }
+            }
+        }
+        ipiv[icol]++;
+        if (irow != icol) {
+            for (l = 0; l < Nx; l++) {
+                PS_SWAP (matrix[irow][l], matrix[icol][l]);
+            }
+            PS_SWAP (vector[irow], vector[icol]);
+        }
+        indxr[i] = irow;
+        indxc[i] = icol;
+        if (matrix[icol][icol] == 0.0) {
+            // XXX EAM: this should use the psError stack
+            fprintf (stderr, "GAUSSJ: Singular Matrix! (2)\n");
+            goto fescape;
+        }
+        pivinv = 1.0 / matrix[icol][icol];
+        matrix[icol][icol] = 1.0;
+        for (l = 0; l < Nx; l++) {
+            matrix[icol][l] *= pivinv;
+        }
+        vector[icol] *= pivinv;
+
+        for (ll = 0; ll < Nx; ll++) {
+            if (ll != icol) {
+                dum = matrix[ll][icol];
+                matrix[ll][icol] = 0.0;
+                for (l = 0; l < Nx; l++) {
+                    matrix[ll][l] -= matrix[icol][l]*dum;
+                }
+                vector[ll] -= vector[icol]*dum;
+            }
+        }
+    }
+
+    for (l = Nx - 1; l >= 0; l--) {
+        if (indxr[l] != indxc[l]) {
+            for (k = 0; k < Nx; k++) {
+                PS_SWAP (matrix[k][indxr[l]], matrix[k][indxc[l]]);
+            }
+        }
+    }
+    psFree (ipiv);
+    psFree (indxr);
+    psFree (indxc);
+    return (true);
+
+fescape:
+    psFree (ipiv);
+    psFree (indxr);
+    psFree (indxc);
+    return (false);
+}
+
+/******************************************************************************
+psMinimizeLMChi2():  This routine will take an procedure which calculates
+an arbitrary function and it's derivative and minimize the chi-squared match
+between that function at the specified coords and the specified value at
+those coords.
+ 
+XXX: Do this:
+ After checking that all entries in the paramMask are 1 or 0, when
+ forming the A matrix from alpha, try this:
+ 
+     A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i];
+ 
+XXX: This is very different from what is specified in the SDR.  Must
+coordinate with IfA on new SDR.
+ 
+XXX: Do vector/image recycles.
+ 
+XXX: probably yErr will be part of the SDR.
+ 
+XXX: This must work for both F32 and F64.  F32 is currently implemented.
+     Note: since the LUD routines are only implemented in F64, then we
+     will have to convert all F32 input vectors to F64 regardless.  So,
+     the F64 port might be.
+ 
+XXX: Must update the covar matrix.
+ *****************************************************************************/
+psBool psMinimizeLMChi2Old(psMinimization *min,
+                           psImage *covar,
+                           psVector *params,
+                           const psVector *paramMask,
+                           const psArray *x,
+                           const psVector *y,
+                           const psVector *yErr,
+                           psMinimizeLMChi2Func func)
+{
+    PS_PTR_CHECK_NULL(min, NULL);
+    PS_VECTOR_CHECK_NULL(params, NULL);
+    PS_VECTOR_CHECK_EMPTY(params, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_EMPTY(y, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
+    PS_PTR_CHECK_NULL(func, NULL);
+
+    if (paramMask != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(params, paramMask, NULL);
+    }
+    if (yErr != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(y, yErr, NULL);
+    }
+    if (covar != NULL) {
+        PS_IMAGE_CHECK_SIZE(covar, params->n, params->n, NULL);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2() begin ----\n");
+    psS32 numData = y->n;
+    psS32 numParams = params->n;
+    psS32 i;
+    psS32 j;
+    psS32 k;
+    psS32 l;
+    psS32 n;
+    psS32 p;
+    psVector *beta = psVectorAlloc(numParams, PS_TYPE_F64);
+    psVector *perm = NULL;
+
+    psVector *paramDeltasF64 = psVectorAlloc(numParams, PS_TYPE_F64);
+    psVector *origParams = psVectorAlloc(numParams, PS_TYPE_F32);
+    psVector *newParams = psVectorAlloc(numParams, PS_TYPE_F32);
+
+    psImage *alpha = psImageAlloc(numParams, numParams, PS_TYPE_F32);
+    psImage *A = psImageAlloc(numParams, numParams, PS_TYPE_F64);
+    psImage *aOut = psImageAlloc(numParams, numParams, PS_TYPE_F64);
+    psImage *deriv = psImageAlloc(numParams, numData, PS_TYPE_F32);
+    psVector *currValueVec = NULL;
+    psVector *newValueVec = NULL;
+    psF32 currChi2 = 0.0;
+    psF32 newChi2 = 0.0;
+    psF32 lamda = 0.00005;  // XXX EAM : this starting value is VERY small (lamda is mis-spelt)
+    lamda = 0.05;  // XXX EAM : this starting value is quite large (lamda is mis-spelt)
+
+    psTrace(".psLib.dataManip.psMinimize", 6,
+            "min->maxIter is %d\n", min->maxIter);
+    psTrace(".psLib.dataManip.psMinimize", 6,
+            "min->tol is %f\n", min->tol);
+
+    for (p=0;p<numParams;p++) {
+        origParams->data.F32[p] = params->data.F32[p];
+    }
+
+    min->lastDelta = PS_MAX_F32;
+    min->iter = 0;
+
+    while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "------------------------------------------------------\n");
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "Iteration %d.  Delta is %f\n", min->iter, min->lastDelta);
+
+        //
+        // Calculate the current values and chi-squared of the function.
+        //
+        currChi2 = 0.0;
+        // currValueVec = func(deriv, params, x);
+
+        // XXX EAM: use BinaryOp ?
+        // t1 = BinaryOp (NULL, currValueVec, "-", y);
+        // t1 = BinaryOp (t1, t1, "*", t1);
+
+        // XXX EAM: this ignores yErr
+        for (n=0;n<numData;n++) {
+            currChi2+= (currValueVec->data.F32[n] - y->data.F32[n]) *
+                       (currValueVec->data.F32[n] - y->data.F32[n]);
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "data[%d], chi2 calculation+= (%f - %f)^2\n", n,
+                    currValueVec->data.F32[n], y->data.F32[n]);
+        }
+
+        // XXX EAM: this is just for tracing
+        for (p=0;p<numParams;p++) {
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "params->data.F32[%d] is %f.\n", p, params->data.F32[p]);
+        }
+        psTrace(".psLib.dataManip.psMinimize", 6,
+                "Current chi-squared is (%f)\n", currChi2);
+
+        //
+        // Mask elements of the derivative for each data point.
+        // XXX EAM : is this necessary?  probably not...
+        for (p=0;p<numParams;p++) {
+            if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
+                for (n=0;n<numData;n++) {
+                    deriv->data.F32[n][p] = 0.0;
+                }
+            }
+        }
+
+        //
+        // Calculate the BETA vector.
+        // XXX EAM: I think this is wrong
+        for (p=0;p<numParams;p++) {
+            if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
+                continue;
+            }
+            beta->data.F64[p] = 0.0;
+            for (n=0;n<numData;n++) {
+                (beta->data.F64[p])+=
+                    (y->data.F32[n] - currValueVec->data.F32[n]) *
+                    deriv->data.F32[n][p];
+            }
+            // XXX: multiply by -1 here?
+            (beta->data.F64[p])*= -1.0;
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "beta->data.F64[%d] is %f.\n", p, beta->data.F64[p]);
+        }
+        psFree(currValueVec);
+
+        //
+        // Calculate the ALPHA matrix.
+        // XXX EAM: also wrong? (missing yErr)
+        for (k=0;k<numParams;k++) {
+            for (l=0;l<numParams;l++) {
+                alpha->data.F32[k][l] = 0.0;
+                for (n=0;n<numData;n++) {
+                    alpha->data.F32[k][l]+= deriv->data.F32[n][k] *
+                                            deriv->data.F32[n][l];
+                }
+            }
+        }
+
+        //
+        // Calculate the matrix A.
+        //
+        for (j=0;j<numParams;j++) {
+            for (k=0;k<numParams;k++) {
+                if (j == k) {
+                    A->data.F64[j][k] =
+                        (psF64) ((1.0 + lamda) * alpha->data.F32[j][k]);
+                } else {
+                    A->data.F64[j][k] = (psF64) alpha->data.F32[j][k];
+                }
+            }
+        }
+        for (j=0;j<numParams;j++) {
+            psTrace(".psLib.dataManip.psMinimize", 6, "Matrix A[][]:\n");
+            for (k=0;k<numParams;k++) {
+                psTrace(".psLib.dataManip.psMinimize", 6, "%f ", A->data.F64[j][k]);
+            }
+            psTrace(".psLib.dataManip.psMinimize", 6, "Matrix A[][]:\n");
+        }
+
+        //
+        // Solve A * alpha = Beta
+        //
+        // XXX: How do we know if these functions were successful?
+        //
+        aOut = psMatrixLUD(aOut, &perm, A);
+        paramDeltasF64 = psMatrixLUSolve(paramDeltasF64, aOut, beta, perm);
+
+        //
+        // Mask any masked parameters.
+        //
+        for (i=0;i<numParams;i++) {
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "paramDeltasF64->data.F64[%d] is %f.\n", i, paramDeltasF64->data.F64[i]);
+            if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) {
+                newParams->data.F32[i] = origParams->data.F32[i];
+            } else {
+                newParams->data.F32[i] = params->data.F32[i] -
+                                         (psF32) paramDeltasF64->data.F64[i];
+            }
+        }
+
+        psTrace(".psLib.dataManip.psMinimize", 6,
+                "Calling func() with new parameters:\n");
+        for (i=0;i<numParams;i++) {
+            psTrace(".psLib.dataManip.psMinimize", 6,
+                    "newParams->data.F32[%d] is %f.\n", i, newParams->data.F32[i]);
+        }
+
+
+        //
+        // Calculate new function values.
+        //
+        newChi2 = 0.0;
+        // newValueVec = func(deriv, newParams, x);
+        for (n=0;n<numData;n++) {
+            newChi2+= (newValueVec->data.F32[n] - y->data.F32[n]) *
+                      (newValueVec->data.F32[n] - y->data.F32[n]);
+
+        }
+        psFree(newValueVec);
+
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "old/new chi-squareds are (%f, %f)\n", currChi2, newChi2);
+
+        //
+        // If the new chi-squared is lower, then keep it.
+        //
+        if (currChi2 > newChi2) {
+            min->lastDelta = (currChi2 - newChi2)/currChi2;
+            min->value = newChi2;
+
+            // We already masked params.
+            for (i=0;i<numParams;i++) {
+                params->data.F32[i] = (psF32) newParams->data.F32[i];
+            }
+            lamda*= 0.1;
+            psTrace(".psLib.dataManip.psMinimize", 4, "*** Reducing lamda by factor of 10\n");
+        } else {
+            lamda*= 10.0;
+            psTrace(".psLib.dataManip.psMinimize", 4, "*** Increasing lamda by factor of 10\n");
+        }
+        psTrace(".psLib.dataManip.psMinimize", 4,
+                "lamda is %f\n", lamda);
+        min->iter++;
+    }
+    psFree(beta);
+    psFree(perm);
+    psFree(paramDeltasF64);
+    psFree(origParams);
+    psFree(newParams);
+    psFree(alpha);
+    psFree(A);
+    psFree(aOut);
+    psFree(deriv);
+
+    if ((min->iter < min->maxIter) ||
+            (min->lastDelta <= min->tol)) {
+        return(true);
+    }
+
+    psTrace(".psLib.dataManip.psMinimize", 4,
+            "---- psMinimizeLMChi2() end (false) ----\n");
+    return(false);
+}
+
+/******************************************************************************
+vectorFitPolynomial1DCheb():  This routine will fit a Chebyshev polynomial of
+degree myPoly to the data points (x, y) and return the coefficients of that
+polynomial.
+ 
+XXX: yErr is currently ignored.
+*****************************************************************************/
+static psPolynomial1D *vectorFitPolynomial1DCheby(psPolynomial1D* myPoly,
+        const psVector* x,
+        const psVector* y,
+        const psVector* yErr)
+{
+    psS32 j;
+    psS32 k;
+    psS32 n = x->n;
+    psF64 fac;
+    psF64 sum;
+    PS_VECTOR_GEN_STATIC_RECYCLED(f, n, PS_TYPE_F64);
+    psScalar *fScalar;
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F64;
+
+    // XXX: These assignments appear too simple to warrant code and
+    // variable declarations.  I retain them here to maintain coherence
+    // with the NR code.
+    psF64 min = -1.0;
+    psF64 max = 1.0;
+    psF64 bma = 0.5 * (max-min);  // 1
+    psF64 bpa = 0.5 * (max+min);  // 0
+
+    // In this loop, we first calculate the values of X for which the
+    // Chebyshev polynomials are zero (see NR, section 5.4).  Then we
+    // calculate the value of the function we are fitting the Chebyshev
+    // polynomials to at those values of X.  This is a bit tricky since
+    // we don't know that function.  So, we instead do 3-order LaGrange
+    // interpolation at the point X for the psVectors x,y for which we
+    // are fitting this ChebyShev polynomial to.
+
+    for (psS32 i=0;i<n;i++) {
+        // NR 5.8.4
+        psF64 Y = cos(M_PI * (0.5 + ((psF32) i)) / ((psF32) n));
+        psF64 X = (Y + bma + bpa) - 1.0;
+        tmpScalar.data.F64 = X;
+
+        // We interpolate against are tabluated x,y vectors to determine the
+        // function value at X.
+        fScalar = p_psVectorInterpolate((psVector *) x,
+                                        (psVector *) y,
+                                        3,
+                                        &tmpScalar);
+
+        f->data.F64[i] = fScalar->data.F64;
+        psFree(fScalar);
+
+        psTrace(".psLib.dataManip.vectorFitPolynomial1DCheby", 6,
+                "(x, X, y, f(X)) is (%f, %f, %f, %f)\n",
+                x->data.F64[i], X, y->data.F64[i], f->data.F64[i]);
+    }
+
+    // We have the values for f() at the zero points, we now calculate the
+    // coefficients of the Chebyshev polynomial: NR 5.8.7.
+
+    fac = 2.0/((psF32) n);
+    // XXX: is this loop bound correct?
+    for (j=0;j<myPoly->n;j++) {
+        sum = 0.0;
+        for (k=0;k<n;k++) {
+            sum+= f->data.F64[k] *
+                  cos(M_PI * ((psF32) j) * (0.5 + ((psF32) k)) / ((psF32) n));
+        }
+
+        myPoly->coeff[j] = fac * sum;
+    }
+
+    return(myPoly);
+}
+
+/******************************************************************************
+VectorFitPolynomial1DOrd():  This routine will fit an ordinary polynomial of
+degree myPoly to the data points (x, y) and return the coefficients of that
+polynomial.
+ 
+XXX: Use private name?
+XXX: Use recycled vectors.
+ *****************************************************************************/
+static psPolynomial1D* vectorFitPolynomial1DOrd(psPolynomial1D* myPoly,
+        const psVector* x,
+        const psVector* y,
+        const psVector* yErr)
+{
+    psS32 polyOrder = myPoly->n;
+    psImage* A = NULL;
+    psImage* ALUD = NULL;
+    psVector* B = NULL;
+    psVector* outPerm = NULL;
+    psVector* X = NULL;         // NOTE: do we need this?
+    psVector* coeffs = NULL;
+    psS32 i = 0;
+    psS32 j = 0;
+    psS32 k = 0;
+    psVector* xSums = NULL;
+
+    psTrace(".psLib.dataManip.vectorFitPolynomial1DOrd", 4,
+            "---- vectorFitPolynomial1DOrd() begin ----\n");
+    // printf("VectorFitPolynomial1D()\n");
+    // for (i=0;i<x->n;i++) {
+    // printf("(x, y, yErr) is (%f, %f, %f)\n", x->data.F64[i], y->data.F64[i], yErr->data.F64[i]);
+    // }
+
+    A = psImageAlloc(polyOrder, polyOrder, PS_TYPE_F64);
+    ALUD = psImageAlloc(polyOrder, polyOrder, PS_TYPE_F64);
+
+    B = psVectorAlloc(polyOrder, PS_TYPE_F64);
+    coeffs = psVectorAlloc(polyOrder, PS_TYPE_F64);
+    X = psVectorAlloc(x->n, PS_TYPE_F64);
+    xSums = psVectorAlloc(1 + 2 * polyOrder, PS_TYPE_F64);
+
+    // Initialize data structures.
+    for (i = 0; i < polyOrder; i++) {
+        B->data.F64[i] = 0.0;
+        coeffs->data.F64[i] = 0.0;
+        for (j = 0; j < polyOrder; j++) {
+            A->data.F64[i][j] = 0.0;
+            ALUD->data.F64[i][j] = 0.0;
+        }
+    }
+    for (i = 0; i < X->n; i++) {
+        X->data.F64[i] = x->data.F64[i];
+    }
+
+    // Build the B and A data structs.
+    if (yErr == NULL) {
+        for (i = 0; i < X->n; i++) {
+            buildSums1D(X->data.F64[i], 2 * polyOrder, xSums);
+
+            for (k = 0; k < polyOrder; k++) {
+                B->data.F64[k] += y->data.F64[i] * xSums->data.F64[k];
+            }
+
+            for (k = 0; k < polyOrder; k++) {
+                for (j = 0; j < polyOrder; j++) {
+                    A->data.F64[k][j] += xSums->data.F64[k + j];
+                }
+            }
+        }
+    } else {
+        for (i = 0; i < X->n; i++) {
+            buildSums1D(X->data.F64[i], 2 * polyOrder, xSums);
+
+            for (k = 0; k < polyOrder; k++) {
+                B->data.F64[k] += y->data.F64[i] * xSums->data.F64[k] /
+                                  yErr->data.F64[i];
+            }
+
+            for (k = 0; k < polyOrder; k++) {
+                for (j = 0; j < polyOrder; j++) {
+                    A->data.F64[k][j] += xSums->data.F64[k + j] /
+                                         yErr->data.F64[i];
+                }
+            }
+        }
+    }
+
+    // XXX: How do we know if these routines were successful?
+    ALUD = psMatrixLUD(ALUD, &outPerm, A);
+    coeffs = psMatrixLUSolve(coeffs, ALUD, B, outPerm);
+
+    for (k = 0; k < polyOrder; k++) {
+        myPoly->coeff[k] = coeffs->data.F64[k];
+        // printf("myPoly->coeff[%d] is %f\n", k, myPoly->coeff[k]);
+    }
+
+    psFree(A);
+    psFree(ALUD);
+    psFree(B);
+    psFree(coeffs);
+    psFree(X);
+    psFree(outPerm);
+    psFree(xSums);
+
+    psTrace(".psLib.dataManip.vectorFitPolynomial1DOrd", 4,
+            "---- vectorFitPolynomial1DOrd() begin ----\n");
+    return (myPoly);
+}
+
+/******************************************************************************
+psVectorFitPolynomial1D():  This routine must fit a polynomial of degree
+myPoly to the data points (x, y) and return the coefficients of that
+polynomial.
+ 
+XXX: type F32 is done via vector conversion only.
+ *****************************************************************************/
+psPolynomial1D* psVectorFitPolynomial1D(psPolynomial1D* myPoly,
+                                        const psVector* x,
+                                        const psVector* y,
+                                        const psVector* yErr)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(myPoly->n, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_EMPTY(y, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(y, NULL);
+
+    psS32 i;
+    psVector *x64 = NULL;
+    psVector *y64 = NULL;
+    psVector *yErr64 = NULL;
+    static psVector *x64Static = NULL;
+    static psVector *y64Static = NULL;
+    static psVector *yErr64Static = NULL;
+
+    PS_VECTOR_CONVERT_F32_TO_F64_STATIC(y, y64, y64Static);
+    // If yErr==NULL, set all errors equal.
+    if (yErr == NULL) {
+        PS_VECTOR_GEN_YERR_STATIC_F64(yErr64Static, y->n);
+        yErr64 = yErr64Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(yErr, NULL);
+        PS_VECTOR_CONVERT_F32_TO_F64_STATIC(yErr, yErr64, yErr64Static);
+    }
+
+    // If x==NULL, create an x64 vector with x values set to (0:n).
+    if (x == NULL) {
+        PS_VECTOR_GEN_X_INDEX_STATIC_F64(x64Static, y->n);
+        if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+            p_psNormalizeVectorRangeF64(x64Static, -1.0, 1.0);
+        }
+        x64 = x64Static;
+    } else {
+        PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+        PS_VECTOR_CONVERT_F32_TO_F64_STATIC(x, x64, x64Static);
+        if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+            p_psNormalizeVectorRangeF64(x64, -1.0, 1.0);
+        }
+    }
+    PS_VECTOR_CHECK_SIZE_EQUAL(x64, y64, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(yErr64, y64, NULL);
+
+    // Call the appropriate vector fitting routine.
+    psPolynomial1D *rc = NULL;
+    if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        rc = vectorFitPolynomial1DCheby(myPoly, x64, y64, yErr64);
+    } else if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        rc = vectorFitPolynomial1DOrd(myPoly, x64, y64, yErr64);
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "unknown polynomial type.\n");
+        return(NULL);
+    }
+    if (rc == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Could not fit a polynomial to the data.  Returning NULL.\n");
+        return(NULL);
+    }
+
+    return(myPoly);
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+psMinimization *psMinimizationAlloc(psS32 maxIter,
+                                    psF32 tol)
+{
+    PS_INT_CHECK_NON_NEGATIVE(maxIter, NULL);
+
+    psMinimization *min = psAlloc(sizeof(psMinimization));
+    min->maxIter = maxIter;
+    min->tol = tol;
+    min->value = 0.0;
+    min->iter = 0;
+    min->lastDelta = tol + 1;
+
+    return(min);
+}
+
+// This macro takes as input the vector BASE and adds a multiple of the vector
+// LINE to it.  We assume BASEMASK is non-null.
+#define PS_VECTOR_ADD_MULTIPLE(BASE, BASEMASK, LINE, OUT, MUL) \
+for (psS32 i=0;i<BASE->n;i++) { \
+    if (BASEMASK->data.U8[i] == 0) { \
+        OUT->data.F32[i] = BASE->data.F32[i] + (MUL * LINE->data.F32[i]); \
+    } else { \
+        OUT->data.F32[i] = BASE->data.F32[i]; \
+    } \
+} \
+
+#define PS_VECTOR_F32_CHECK_ZERO_VECTOR(IN, BOOL_VAR) \
+BOOL_VAR = true; \
+for (psS32 i=0;i<IN->n;i++) { \
+    if (fabs(IN->data.F32[i]) >= FLT_EPSILON) { \
+        BOOL_VAR = false; \
+        break; \
+    } \
+} \
+
+#define PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(IN, INMASK, BOOL_VAR) \
+BOOL_VAR = true; \
+for (psS32 i=0;i<IN->n;i++) { \
+    if ((INMASK->data.U8[i] == 0) && (fabs(IN->data.F32[i]) >= FLT_EPSILON)) { \
+        BOOL_VAR = false; \
+        break; \
+    } \
+} \
+
+
+/******************************************************************************
+p_psDetermineBracket():  This routine takes as input an arbitrary function,
+and the parameter to vary, and the line along which it must vary.  This
+function produces as output a bracket [a, b, c] such that
+f(param + b * line) < f(param + a * line)
+f(param + b * line) < f(param + c * line)
+a < b < c
+ 
+Algorithm:
+ 
+XXX completely ad hoc:
+start with the user-supplied starting parameter and
+call that b.  Calculate a/c as a fractional amount smaller/larger than b.
+Repeat this process until a local minimum is found.
+ 
+XXX:
+new algorithm:
+start at x=0, expand in one direction until the function
+decreases.  Then you have two points in the bracket.  Keep going until it
+increases, or x is too large.  If thst does not work, expand in the other
+direction.
+ 
+XXX:
+This is F32 only.
+ 
+XXX:
+output bracket vector should be an input as well.
+*****************************************************************************/
+psVector *p_psDetermineBracket(psVector *params,
+                               psVector *line,
+                               const psVector *paramMask,
+                               const psArray *coords,
+                               psMinimizePowellFunc func)
+{
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psS32 iter = 100;
+    psF32 aDir = 0.0;
+    psF32 cDir = 0.0;
+    psF32 new_aDir = 0.0;
+    psF32 new_cDir = 0.0;
+    psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
+    psF32 stepSize = PS_DETERMINE_BRACKET_STEP_SIZE;
+    psVector *tmp = NULL;
+    psBool boolLineIsNull = true;
+
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() begin ----\n");
+
+    // If the line vector is zero, then return NULL.
+    PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
+    if (boolLineIsNull == true) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 2,
+                "p_psDetermineBracket() called with zero line vector.\n");
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                "---- p_psDetermineBracket() end (NULL) ----\n");
+        psFree(bracket);
+        return(NULL);
+    }
+
+    tmp = psVectorAlloc(params->n, PS_TYPE_F32);
+
+    b = 0;
+    a = -stepSize;
+    c = stepSize;
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
+    fa = func(tmp, coords);
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
+    fb = func(tmp, coords);
+
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+    fc = func(tmp, coords);
+
+    if (fa < fb) {
+        aDir = -1;
+    } else {
+        aDir = 1;
+    }
+
+    if (fc < fb) {
+        cDir = -1;
+    } else {
+        cDir = 1;
+    }
+
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+            "(a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc);
+
+    while (iter > 0) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "psDetermineBracket(): iteration %d\n", iter);
+        if ((fb < fa) && (fb < fc)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                    "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+        stepSize*= (1.0 + stepSize);
+        a =- stepSize;
+        c =+ stepSize;
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, a);
+        fa = func(tmp, coords);
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+        fc = func(tmp, coords);
+
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "Iter(%d): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
+
+        if (fa < fb) {
+            new_aDir = -1;
+        } else {
+            new_aDir = 1;
+        }
+
+        if (fc < fb) {
+            new_cDir = -1;
+        } else {
+            new_cDir = 1;
+        }
+        if ((new_aDir == 1) && (aDir == -1)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                    "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+
+        if ((new_cDir == 1) && (cDir == -1)) {
+            bracket->data.F32[0] = a;
+            bracket->data.F32[1] = b;
+            bracket->data.F32[2] = c;
+            psFree(tmp);
+            psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                    "---- p_psDetermineBracket() end ----\n");
+            return(bracket);
+        }
+        aDir = new_aDir;
+        cDir = new_cDir;
+        iter--;
+    }
+    psFree(tmp);
+    psFree(bracket);
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() end (NULL) ----\n");
+    return(NULL);
+}
+
+
+#define RETURN_FINAL_BRACKET(d) \
+if (a < c) { \
+    bracket->data.F32[0] = a; \
+    bracket->data.F32[1] = b; \
+    bracket->data.F32[2] = c; \
+} else { \
+    bracket->data.F32[0] = c; \
+    bracket->data.F32[1] = b; \
+    bracket->data.F32[2] = a; \
+} \
+psTrace(".psLib.dataManip.p_psDetermineBracket", 4, \
+        "---- p_psDetermineBracket() end ----\n"); \
+psTrace(".psLib.dataManip.p_psDetermineBracket", 4, "Final bracket (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", a, b, c, fa, fb, fc); \
+return(bracket); \
+
+#define PS_DETERMINE_BRACKET_MAX_ITERATIONS 100
+psVector *p_psDetermineBracket2(psVector *params,
+                                psVector *line,
+                                const psVector *paramMask,
+                                const psArray *coords,
+                                psMinimizePowellFunc func)
+{
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psS32 iter = 0;
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmp, params->n, PS_TYPE_F32);
+    psBool boolLineIsNull = true;
+    psF32 prevMin = 0.0;
+    psS32 countMin = 0;
+
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() begin ----\n");
+
+    // If the line vector is zero, then return NULL.
+    PS_VECTOR_WITH_MASK_F32_CHECK_ZERO_VECTOR(params, paramMask, boolLineIsNull);
+    if (boolLineIsNull == true) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 2,
+                "p_psDetermineBracket() called with zero line vector.\n");
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+                "---- p_psDetermineBracket() end (NULL) ----\n");
+        return(NULL);
+    }
+
+    // We determine in what x-direction does the function decrease.
+    a = 0.0;
+    fa = func(params, coords);
+    b = 0.5;
+    iter = 0;
+    do {
+        b*= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, b);
+        fb = func(tmp, coords);
+    } while ((fabs(fb - fa) < FLT_EPSILON) && (iter++ < 100));
+
+    if (fb > fa) {
+        a = b;
+        fa = fb;
+        b = 0.0;
+        fb = func(params, coords);
+    }
+    c = b;
+
+    // At this point we have (a, b) and we know that (fa >= fb).  Initially, c=b;
+    // We keep stretching b out further from "a" until (fc > previous fc).  If
+    // that happens, then we have our bracket.
+    psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);
+    iter = 0;
+    while (iter < PS_DETERMINE_BRACKET_MAX_ITERATIONS) {
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "psDetermineBracket(): iterationA %d\n", iter);
+        c+= (1.0 + PS_DETERMINE_BRACKET_STEP_SIZE) * (c - a);
+
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmp, c);
+        fc = func(tmp, coords);
+
+        psTrace(".psLib.dataManip.p_psDetermineBracket", 6,
+                "Iteration(%d) (bracket): (a, b, c) is (%f %f %f) (fa, fb, fc) is (%f %f %f)\n", iter, a, b, c, fa, fb, fc);
+
+        if ((fb < fa) && (fb < fc)) {
+            RETURN_FINAL_BRACKET();
+        } else {
+            b = c;
+            fb = fc;
+        }
+
+        // This code maintains a count of how many times the minimum fc has
+        // stayed the same.  If it gets too high, we exit this loop.
+        if (fc == prevMin) {
+            countMin++;
+        } else {
+            countMin = 0;
+        }
+        prevMin = fc;
+        if (countMin == 10) {
+            RETURN_FINAL_BRACKET();
+        }
+
+        iter++;
+    }
+
+    psFree(bracket);
+    psTrace(".psLib.dataManip.p_psDetermineBracket", 4,
+            "---- p_psDetermineBracket() end (NULL) (BAD) ----\n");
+    return(NULL);
+}
+
+/******************************************************************************
+This routine takes as input a possibly multi-dimensional function, along
+with an initial guess at the parameters of that function and vector "line"
+of the same size as the parameter vector.  It will minimize the function
+along that vector and returns the offset along that vector at which the
+minimum is determined.
+ 
+XXX: This routine is not very efficient in terms of total evaluations of the
+function.
+XXX: This is F32 only
+XXX: Since this is an internal function, many of the parameter checks are
+     redundant.
+XXX: Don't modify the psMinimization argument.
+ *****************************************************************************/
+#define PS_LINEMIN_MAX_ITERATIONS 30
+psF32 p_psLineMin(psMinimization *min,
+                  psVector *params,
+                  psVector *line,
+                  const psVector *paramMask,
+                  const psArray *coords,
+                  psMinimizePowellFunc func)
+{
+    PS_PTR_CHECK_NULL(min, NAN);
+    PS_VECTOR_CHECK_NULL(params, NAN);
+    PS_VECTOR_CHECK_EMPTY(params, NAN);
+    PS_VECTOR_CHECK_TYPE(params, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_NULL(line, NAN);
+    PS_VECTOR_CHECK_EMPTY(line, NAN);
+    PS_VECTOR_CHECK_TYPE(line, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_NULL(paramMask, NAN);
+    PS_VECTOR_CHECK_EMPTY(paramMask, NAN);
+    PS_VECTOR_CHECK_TYPE(paramMask, PS_TYPE_U8, NAN);
+    PS_PTR_CHECK_NULL(coords, NAN);
+    PS_PTR_CHECK_NULL(func, NAN);
+    psVector *bracket;
+    psF32 a = 0.0;
+    psF32 b = 0.0;
+    psF32 c = 0.0;
+    psF32 n = 0.0;
+    psF32 fa = 0.0;
+    psF32 fb = 0.0;
+    psF32 fc = 0.0;
+    psF32 fn = 0.0;
+    psF32 mul = 0.0;
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpa, params->n, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpb, params->n, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpc, params->n, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(tmpn, params->n, PS_TYPE_F32);
+    psS32 i = 0;
+    psS32 boolLineIsNull = true;
+    psS32 numIterations = 0;
+
+    psTrace(".psLib.dataManip.p_psLineMin", 4, "---- p_psLineMin() begin ----\n");
+    PS_VECTOR_F32_CHECK_ZERO_VECTOR(line, boolLineIsNull);
+
+    if (boolLineIsNull == true) {
+        min->value = func(params, coords);
+        psTrace(".psLib.dataManip.p_psLineMin", 2,
+                "p_psLineMin() called with zero line vector.  Return 0.0.  Function value is %f\n", min->value);
+        return(0.0);
+    }
+
+    for (i=0;i<params->n;i++) {
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "(params, paramMask, line)[%d] is (%f %d %f)\n", i,
+                params->data.F32[i],
+                paramMask->data.U8[i],
+                line->data.F32[i]);
+    }
+
+    bracket = p_psDetermineBracket2(params, line, paramMask, coords, func);
+    if (bracket == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                "Could not bracket minimum.  Returning NAN.\n");
+        return(NAN);
+    }
+    numIterations = 0;
+    while (numIterations < PS_LINEMIN_MAX_ITERATIONS) {
+        numIterations++;
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "p_psLineMin(): iteration %d\n", numIterations);
+
+        a = bracket->data.F32[0];
+        b = bracket->data.F32[1];
+        c = bracket->data.F32[2];
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpa, a);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpb, b);
+        PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, tmpc, c);
+        fa = func(tmpa, coords);
+        fb = func(tmpb, coords);
+        fc = func(tmpc, coords);
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "LineMin: f(%f %f %f) is (%f %f %f)\n", a, b, c, fa, fb, fc);
+
+        // We determine which is the biggest segment in [a,b,c] then split
+        // that with the point n.
+        if ((b-a) > (c-b)) {
+            // This is the golden section formula
+            n = a + (0.69 * (b-a));
+            for (i=0;i<params->n;i++) {
+                tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
+            }
+            fn = func(tmpn, coords);
+
+            if (fn > fb) {
+                // a = n, b = b, c = c
+                bracket->data.F32[0] = n;
+            } else {
+                // a = a, b = n, c = b
+                bracket->data.F32[1] = n;
+                bracket->data.F32[2] = b;
+            }
+        } else {
+            n = b + (0.69 * (c-b));
+            for (i=0;i<params->n;i++) {
+                tmpn->data.F32[i] = params->data.F32[i] + (n * line->data.F32[i]);
+            }
+            fn = func(tmpn, coords);
+
+            if (fn > fb) {
+                // a = a, b = b, c = n
+                bracket->data.F32[2] = n;
+            } else {
+                // a = b, b = n, c = c
+                bracket->data.F32[0] = b;
+                bracket->data.F32[1] = n;
+            }
+        }
+        psTrace(".psLib.dataManip.p_psLineMin", 6,
+                "LineMin: new bracket is (%f %f %f)\n", bracket->data.F32[0], bracket->data.F32[1], bracket->data.F32[2]);
+
+        mul = bracket->data.F32[1];
+        if ((fabs(a-b) < min->tol) && (fabs(b-c) < min->tol)) {
+            PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
+            min->value = func(params, coords);
+            psFree(bracket);
+            psTrace(".psLib.dataManip.p_psLineMin", 4,
+                    "---- p_psLineMin() end.a (%f) (%f) ----\n", mul, min->value);
+            return(mul);
+        }
+    }
+
+    mul = bracket->data.F32[1];
+    PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
+    min->value = func(params, coords);
+    psTrace(".psLib.dataManip.p_psLineMin", 4,
+            "---- p_psLineMin() end.b (%f) %f ----\n", mul, min->value);
+
+    psFree(bracket);
+    return(mul);
+}
+
+
+/******************************************************************************
+This routine must minimize a possibly multi-dimensional function.  The
+function to be minimized "func" is:
+    psF32 func(psVector *params, psArray *coords)
+The "params" are the parameters of the function which are varied.  The data
+points at which the function is varied are in the argument "coords" which is
+a psArray of psVectors: each vector represents a different coordinate.
+ 
+XXX: We do not use Brent's method.
+ 
+XXX: The SDR is silent about data types.  F32 is implemented here.
+ 
+XXX: Check for F32 types?
+ *****************************************************************************/
+#define PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS 20
+#define PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE 0.01
+
+psBool psMinimizePowell(psMinimization *min,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *coords,
+                        psMinimizePowellFunc func)
+{
+    PS_PTR_CHECK_NULL(min, NULL);
+    PS_VECTOR_CHECK_NULL(params, NULL);
+    PS_VECTOR_CHECK_EMPTY(params, NULL);
+    PS_VECTOR_CHECK_TYPE(params, PS_TYPE_F32, NULL);
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(func, NULL);
+    psS32 numDims = params->n;
+    PS_VECTOR_GEN_STATIC_RECYCLED(pQP, numDims, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(u, numDims, PS_TYPE_F32);
+    PS_VECTOR_GEN_STATIC_RECYCLED(Q, numDims, PS_TYPE_F32);
+    psS32 i = 0;
+    psS32 j = 0;
+    psVector *myParamMask = NULL;
+    psMinimization dummyMin;
+    psF32 mul = 0.0;
+    psF32 baseFuncVal = 0.0;
+    psF32 currFuncVal = 0.0;
+    psS32 biggestIter = 0;
+    psF32 biggestDiff = 0.0;
+    psS32 iterationNumber = 0;
+
+    psTrace(".psLib.dataManip.psMinimizePowell", 4,
+            "---- psMinimizePowell() begin ----\n");
+    psTrace(".psLib.dataManip.psMinimizePowell", 6,
+            "min->maxIter is %d\n", min->maxIter);
+    psTrace(".psLib.dataManip.psMinimizePowell", 6,
+            "min->tol is %f\n", min->tol);
+
+    if (paramMask == NULL) {
+        myParamMask = psVectorRecycle(myParamMask, params->n, PS_TYPE_U8);
+        p_psMemSetPersistent(myParamMask, true);
+        p_psMemSetPersistent(myParamMask->data.U8, true);
+        for (i=0;i<myParamMask->n;i++) {
+            myParamMask->data.U8[i] = 0;
+        }
+    } else {
+        myParamMask = (psVector *) paramMask;
+    }
+    PS_VECTOR_CHECK_SIZE_EQUAL(params, myParamMask, NULL);
+
+    // 1: Set v[i] to be the unit vectors for each dimension in params
+    psArray *v = psArrayAlloc(numDims);
+    for (i=0;i<numDims;i++) {
+        (v->data[i]) = (psVector *) psVectorAlloc(numDims, PS_TYPE_F32);
+        for (j=0;j<numDims;j++) {
+            if (i == j) {
+                ((psVector *) (v->data[i]))->data.F32[j] = 1.0;
+            } else {
+                ((psVector *) (v->data[i]))->data.F32[j] = 0.0;
+            }
+        }
+    }
+
+    // 2: Set Q to be the initial params (P in the ADD)
+    for (i=0;i<numDims;i++) {
+        Q->data.F32[i] = params->data.F32[i];
+    }
+
+    while (iterationNumber < min->maxIter) {
+        iterationNumber++;
+        psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                "psMinimizePowell() iteration %d\n", iterationNumber);
+
+        // 3: For each dimension in params, move Q only in the vector v[i] to
+        //    minimize the function.
+
+        baseFuncVal = func(Q, coords);
+        currFuncVal = baseFuncVal;
+        psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                "Current function value is %f\n", currFuncVal);
+
+        biggestDiff = 0;
+        biggestIter = 0;
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                dummyMin.maxIter = PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS;
+                dummyMin.tol = PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE;
+                mul = p_psLineMin(&dummyMin,
+                                  Q,
+                                  ((psVector *) v->data[i]),
+                                  myParamMask,
+                                  coords,
+                                  func);
+                if (isnan(mul)) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "Could not perform line minimization.  Returning FALSE.\n");
+                    psFree(v);
+                    return(false);
+                }
+                psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                        "LineMin along dimension %d has multiple %f\n", i, mul);
+
+                if (fabs(dummyMin.value - currFuncVal) > biggestDiff) {
+                    biggestDiff = fabs(dummyMin.value - currFuncVal);
+                    biggestIter = i;
+                }
+                currFuncVal = dummyMin.value;
+            }
+        }
+        psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                "New function value is %f\n", currFuncVal);
+
+        // 4: Set the vector u = Q - P
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                u->data.F32[i] = Q->data.F32[i] - params->data.F32[i];
+
+                psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                        "u[i]=Q[i]-P[i] (%f = %f - %f)\n", u->data.F32[i],
+                        Q->data.F32[i],
+                        params->data.F32[i]);
+
+            } else {
+                u->data.F32[i] = 0.0;
+            }
+        }
+
+        // 5: Move Q only in the direction u, and minimize the function.
+        for (i=0;i<numDims;i++) {
+            psTrace(".psLib.dataManip.psMinimizePowell", 6,
+                    "u[i] is %f\n", u->data.F32[i]);
+        }
+
+        mul = p_psLineMin(&dummyMin, params, u, myParamMask, coords, func);
+        if (isnan(mul)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Could not perform line minimization.  Returning FALSE.\n");
+            psFree(v);
+            return(false);
+        }
+
+        // 6:
+        if (dummyMin.value > currFuncVal) {
+            psFree(v);
+            min->iter = iterationNumber;
+            // XXX: Ensure that currFuncVal is the correct value to use here.
+            min->value = currFuncVal;
+            psTrace(".psLib.dataManip.psMinimizePowell", 4,
+                    "---- psMinimizePowell() end (1)(true) ----\n");
+            return(true);
+        }
+
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                pQP->data.F32[i] = (2 * Q->data.F32[i]) - params->data.F32[i];
+            } else {
+                pQP->data.F32[i] = params->data.F32[i];
+            }
+        }
+        psF32 fqp = func(pQP, coords);
+        psF32 term1 = (baseFuncVal - currFuncVal) - biggestDiff;
+        term1*= term1;
+        term1*= 2.0 * (baseFuncVal - (2.0 * currFuncVal) + fqp);
+        psF32 term2 = baseFuncVal - fqp;
+        term2*= term2 * biggestDiff;
+        if (term1 < term2) {
+            for (i=0;i<numDims;i++) {
+                if (myParamMask->data.U8[i] == 0) {
+                    ((psVector *) v->data[biggestIter])->data.F32[i] = u->data.F32[i];
+                }
+            }
+        }
+
+        // 7: Set P to Q
+        for (i=0;i<numDims;i++) {
+            if (myParamMask->data.U8[i] == 0) {
+                params->data.F32[i] = Q->data.F32[i];
+            }
+        }
+
+        // 8: Go to step 3 until the change is less than some tolerance.
+        if (fabs(baseFuncVal - currFuncVal) <= min->tol) {
+            psFree(v);
+            // XXX: Ensure that currFuncVal is the correct value to use here.
+            min->value = currFuncVal;
+            min->iter = iterationNumber;
+            psTrace(".psLib.dataManip.psMinimizePowell", 4,
+                    "---- psMinimizePowell() end (2) (true) ----\n");
+            return(true);
+        }
+    }
+
+    psFree(v);
+    min->iter = iterationNumber;
+    psTrace(".psLib.dataManip.psMinimizePowell", 4,
+            "---- psMinimizePowell() end (0) (false) ----\n");
+    return(false);
+}
+
+
+/******************************************************************************
+XXX: We assume unnormalized gaussians.
+XXX: Currently, yErr is ignored.
+ *****************************************************************************/
+psVector *psMinimizePowellChi2Gauss1D(const psVector *params,
+                                      const psArray *coords)
+{
+    PS_PTR_CHECK_NULL(coords, NULL);
+    PS_PTR_CHECK_NULL(params, NULL);
+
+    psF32 x;
+    psS32 i;
+    psF32 mean = params->data.F32[0];
+    psF32 stdev = params->data.F32[1];
+    psVector *out = psVectorAlloc(coords->n, PS_TYPE_F32);
+
+    for (i=0;i<coords->n;i++) {
+        x = ((psVector *) (coords->data[i]))->data.F32[0];
+        out->data.F32[i] = psGaussian(x, mean, stdev, false);
+    }
+
+    return(out);
+}
+
+/******************************************************************************
+This routine is to be used with the psMinimizeChi2Powell() function below.
+and the psMinimizePowell() function above.
+ 
+The basic idea is calculate chi-squared for a set of params/coords/errors.
+This functions uses global variables to receive the function pointer, the
+data values, and the data errors.
+XXX: This is F32 only
+ *****************************************************************************/
+static psF32 myPowellChi2Func(const psVector *params,
+                              const psArray *coords)
+{
+    psTrace(".psLib.dataManip.myPowellChi2Func", 4,
+            "---- myPowellChi2Func() begin ----\n");
+    PS_VECTOR_CHECK_NULL(params, NAN);
+    PS_VECTOR_CHECK_EMPTY(params, NAN);
+    PS_VECTOR_CHECK_NULL(myValue, NAN);
+    PS_VECTOR_CHECK_EMPTY(myValue, NAN);
+    PS_PTR_CHECK_NULL(coords, NAN);
+
+    psF32 chi2 = 0.0;
+    psF32 d;
+    psS32 i;
+    psVector *tmp;
+
+    tmp = Chi2PowellFunc(params, coords);
+    if (myError == NULL) {
+        for (i=0;i<coords->n;i++) {
+            d = (tmp->data.F32[i] - myValue->data.F32[i]);
+            chi2+= d * d;
+        }
+    } else {
+        for (i=0;i<coords->n;i++) {
+            d = (tmp->data.F32[i] - myValue->data.F32[i]) / myError->data.F32[i];
+            chi2+= d * d;
+        }
+    }
+    psFree(tmp);
+    psTrace(".psLib.dataManip.myPowellChi2Func", 4,
+            "---- myPowellChi2Func() end (chi2 is %f) ----\n", chi2);
+    return(chi2);
+}
+
+
+/******************************************************************************
+This routine must minimize the chi-squared match of a set of data points and
+values for a possibly multi-dimensional function.
+ 
+The basic idea is to use the psMinimizePowell() function defined above.  In
+order to do so, we defined above a function myPowellChi2Func() which takes
+the "func" function and returns chi-squared over the params/coords/values.
+We then use that function myPowellChi2Func() in the call to
+psMinimizePowell().
+ *****************************************************************************/
+psBool psMinimizeChi2Powell(psMinimization *min,
+                            psVector *params,
+                            const psVector *paramMask,
+                            const psArray *coords,
+                            const psVector *value,
+                            const psVector *error,
+                            psMinimizeChi2PowellFunc func)
+{
+    myValue = (psVector *) value;
+    myError = (psVector *) error;
+
+    Chi2PowellFunc = func;
+
+    return(psMinimizePowell(min, params, paramMask, coords, myPowellChi2Func));
+}
+
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psMinimize.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psMinimize.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psMinimize.h	(revision 22271)
@@ -0,0 +1,145 @@
+/** @file  psMinimize.c
+ *  \brief basic minimization functions
+ *  @ingroup Math
+ *
+ *  This file will contain function prototypes for various minimization,
+ *  chi-squared minimization, and 1-D polynomial fitting routines.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-05 22:23:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PS_MINIMIZE_H)
+#define PS_MINIMIZE_H
+
+/** \file psMinimize.h
+ *  \brief minimization operations
+ *  \ingroup Stats
+ */
+/** \addtogroup Stats
+ *  \{
+ */
+
+#include "psVector.h"
+#include "psMemory.h"
+#include "psArray.h"
+#include "psImage.h"
+#include "psMatrix.h"
+#include "psFunctions.h"
+#include "psStats.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psConstants.h"
+
+typedef struct
+{
+    psS32 maxIter;                     ///< Convergence limit
+    psF32 tol;                         ///< Error Tolerance
+    psF32 value;                       ///< Value of function at minimum
+    psS32 iter;                        ///< Number of iterations to date
+    psF32 lastDelta;                   ///< The last difference for the fit
+}
+psMinimization;
+
+psMinimization *psMinimizationAlloc(psS32 maxIter,   ///< Number of minimization iterations to perform.
+                                    psF32 tol        ///< Requested error tolerance
+                                   );
+
+/** Derive a polynomial fit.
+ *
+ *  psVectorFitPolynomial1d returns the polynomial that best fits the 
+ *  observations. The input parameters are a polynomial that specifies the 
+ *  fit order, myPoly, which will be altered and returned with the best-fit 
+ *  coefficients; and the observations, x, y and yErr. The independent 
+ *  variable list, x may be NULL, in which case the vector index is used. 
+ *  The dependent variable error, yErr may be null, in which case the solution 
+ *  is determined in the assumption that all data errors are equal. This 
+ *  function must be valid only for types psF32, psF64.
+ *
+ *  @return psPolynomial1D*    polynomial fit
+ */
+psPolynomial1D* psVectorFitPolynomial1D(
+    psPolynomial1D* myPoly,            ///< Polynomial to fit
+    const psVector* x,                 ///< Ordinates (or NULL to just use the indices)
+    const psVector* y,                 ///< Coordinates
+    const psVector* yErr               ///< Errors in coordinates, or NULL
+);
+
+psSpline1D *psVectorFitSpline1D(psSpline1D *mySpline,     ///< The spline which will be generated.
+                                const psVector* x,        ///< Ordinates (or NULL to just use the indices)
+                                const psVector* y,        ///< Coordinates
+                                const psVector* yErr      ///< Errors in coordinates, or NULL
+                               );
+
+// XXX: Add Doxygen comments here.
+typedef
+psF64 (*psMinimizeLMChi2Func)(psVector *deriv,
+                              psVector *params,
+                              psVector *x);
+
+psBool psMinimizeLMChi2(psMinimization *min,
+                        psImage *covar,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *x,
+                        const psVector *y,
+                        const psVector *yErr,
+                        psMinimizeLMChi2Func func);
+
+psBool p_psMinLM_GuessABP (psImage  *Alpha,
+                           psVector *Beta,
+                           psVector *Params,
+                           psImage  *alpha,
+                           psVector *beta,
+                           psVector *params,
+                           psF64 lambda);
+
+psF64 p_psMinLM_SetABX (psImage  *alpha,
+                        psVector *beta,
+                        psVector *params,
+                        const psArray  *x,
+                        const psVector *y,
+                        const psVector *dy,
+                        psMinimizeLMChi2Func func);
+
+typedef
+psF32 (*psMinimizePowellFunc)(const psVector *params,
+                              const psArray *coords);
+
+psBool psMinimizePowell(psMinimization *min,
+                        psVector *params,
+                        const psVector *paramMask,
+                        const psArray *coords,
+                        psMinimizePowellFunc func);
+
+psVector *psMinimizeLMChi2Gauss1D(psImage *deriv,
+                                  const psVector *params,
+                                  const psArray *coords);
+
+psVector *psMinimizePowellChi2Gauss1D(const psVector *params,
+                                      const psArray *coords);
+
+typedef
+psVector *(*psMinimizeChi2PowellFunc)(const psVector *params,
+                                      const psArray *coords);
+
+psBool psMinimizeChi2Powell(psMinimization *min,
+                            psVector *params,
+                            const psVector *paramMask,
+                            const psArray *coords,
+                            const psVector *value,
+                            const psVector *error,
+                            psMinimizeChi2PowellFunc func);
+
+// XXX EAM : psGaussJordan provided as an alternate to LU Decomp for psMinimizeLMChi2
+bool psGaussJordan (psImage *a, psVector *b);
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psPolynomial.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psPolynomial.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psPolynomial.c	(revision 22271)
@@ -0,0 +1,2179 @@
+/** @file  psFunctions.c
+ *
+ *  @brief Contains basic function allocation, deallocation, and evaluation
+ *         routines.
+ *
+ *  This file will hold the functions for allocated, freeing, and evaluating
+ *  polynomials.  It also contains a Gaussian functions.
+ *
+ *  @version $Revision: 1.101 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: What happens if the polyEval functions are called with data of the wrong
+ *       type?
+ *  XXX: Should the "coeffErr[]" be used as well?  Bug ???.  Ignore coeffErr
+ *
+ *  XXX: In the various polyAlloc(n) functions, n is really the order of the
+ *  polynomial plus 1.  To create a 2nd-order polynomial, n == 3.
+ *
+ *  XXX: potential bug: for a multi-dimensional polynomial with order (m, n)
+ *  the functions in this file currently do not ignore many of the
+ *  coefficients in the coeff matrix that ...
+ *
+ */
+/*****************************************************************************/
+/*  INCLUDE FILES                                                            */
+/*****************************************************************************/
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psFunctions.h"
+#include "psConstants.h"
+
+#include "psDataManipErrors.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+static void polynomial1DFree(psPolynomial1D* myPoly);
+static void polynomial2DFree(psPolynomial2D* myPoly);
+static void polynomial3DFree(psPolynomial3D* myPoly);
+static void polynomial4DFree(psPolynomial4D* myPoly);
+static void dPolynomial1DFree(psDPolynomial1D* myPoly);
+static void dPolynomial2DFree(psDPolynomial2D* myPoly);
+static void dPolynomial3DFree(psDPolynomial3D* myPoly);
+static void dPolynomial4DFree(psDPolynomial4D* myPoly);
+static void spline1DFree(psSpline1D *tmpSpline);
+static psS32 vectorBinDisectF32(psF32 *bins,psS32 numBins,psF32 x);
+static psS32 vectorBinDisectS32(psS32 *bins,psS32 numBins,psS32 x);
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+static void spline1DFree(psSpline1D *tmpSpline)
+{
+    psS32 i;
+
+    if (tmpSpline == NULL) {
+        return;
+    }
+
+    if (tmpSpline->spline != NULL) {
+        for (i=0;i<tmpSpline->n;i++) {
+            psFree((tmpSpline->spline)[i]);
+        }
+        psFree(tmpSpline->spline);
+    }
+
+    if (tmpSpline->p_psDeriv2 != NULL) {
+        psFree(tmpSpline->p_psDeriv2);
+    }
+    psFree(tmpSpline->knots);
+
+    return;
+}
+
+static void polynomial1DFree(psPolynomial1D* myPoly)
+{
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial2DFree(psPolynomial2D* myPoly)
+{
+    psS32 x = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial3DFree(psPolynomial3D* myPoly)
+{
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        for (y = 0; y < myPoly->nY; y++) {
+            psFree(myPoly->coeff[x][y]);
+            psFree(myPoly->coeffErr[x][y]);
+            psFree(myPoly->mask[x][y]);
+        }
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial4DFree(psPolynomial4D* myPoly)
+{
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (w = 0; w < myPoly->nW; w++) {
+        for (x = 0; x < myPoly->nX; x++) {
+            for (y = 0; y < myPoly->nY; y++) {
+                psFree(myPoly->coeff[w][x][y]);
+                psFree(myPoly->coeffErr[w][x][y]);
+                psFree(myPoly->mask[w][x][y]);
+            }
+            psFree(myPoly->coeff[w][x]);
+            psFree(myPoly->coeffErr[w][x]);
+            psFree(myPoly->mask[w][x]);
+        }
+        psFree(myPoly->coeff[w]);
+        psFree(myPoly->coeffErr[w]);
+        psFree(myPoly->mask[w]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial1DFree(psDPolynomial1D* myPoly)
+{
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial2DFree(psDPolynomial2D* myPoly)
+{
+    //printf("dPolynomial2DFree(): HMMM: myPoly->nX is %d\n", myPoly->nX);
+    //printf("dPolynomial2DFree(): HMMM: myPoly->nY is %d\n", myPoly->nY);
+    for (psS32 x = 0; x < myPoly->nX; x++) {
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial3DFree(psDPolynomial3D* myPoly)
+{
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        for (y = 0; y < myPoly->nY; y++) {
+            psFree(myPoly->coeff[x][y]);
+            psFree(myPoly->coeffErr[x][y]);
+            psFree(myPoly->mask[x][y]);
+        }
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial4DFree(psDPolynomial4D* myPoly)
+{
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (w = 0; w < myPoly->nW; w++) {
+        for (x = 0; x < myPoly->nX; x++) {
+            for (y = 0; y < myPoly->nY; y++) {
+                psFree(myPoly->coeff[w][x][y]);
+                psFree(myPoly->coeffErr[w][x][y]);
+                psFree(myPoly->mask[w][x][y]);
+            }
+            psFree(myPoly->coeff[w][x]);
+            psFree(myPoly->coeffErr[w][x]);
+            psFree(myPoly->mask[w][x]);
+        }
+        psFree(myPoly->coeff[w]);
+        psFree(myPoly->coeffErr[w]);
+        psFree(myPoly->mask[w]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+/*****************************************************************************
+createChebyshevPolys(n): this routine takes as input the required order n,
+and returns as output as a pointer to an array of n psPolynomial1D
+structures, corresponding to the first n Chebyshev polynomials.
+ 
+XXX: The output should be static since the Chebyshev polynomials might be
+used frequently and the data structure created here does not contain the
+outer coefficients of the Chebyshev polynomials.
+ *****************************************************************************/
+static psPolynomial1D **createChebyshevPolys(psS32 maxChebyPoly)
+{
+    PS_INT_CHECK_NON_NEGATIVE(maxChebyPoly, NULL);
+
+    psPolynomial1D **chebPolys = NULL;
+
+    chebPolys = (psPolynomial1D **) psAlloc(maxChebyPoly * sizeof(psPolynomial1D *));
+    for (psS32 i = 0; i < maxChebyPoly; i++) {
+        chebPolys[i] = psPolynomial1DAlloc(i + 1, PS_POLYNOMIAL_ORD);
+    }
+
+    // Create the Chebyshev polynomials.
+    // Polynomial i has i-th order.
+    chebPolys[0]->coeff[0] = 1;
+
+    // XXX: Bug 296
+    if (maxChebyPoly > 1) {
+        chebPolys[1]->coeff[1] = 1;
+    }
+    for (psS32 i = 2; i < maxChebyPoly; i++) {
+        for (psS32 j = 0; j < chebPolys[i - 1]->n; j++) {
+            chebPolys[i]->coeff[j + 1] = 2 * chebPolys[i - 1]->coeff[j];
+        }
+        for (psS32 j = 0; j < chebPolys[i - 2]->n; j++) {
+            chebPolys[i]->coeff[j] -= chebPolys[i - 2]->coeff[j];
+        }
+    }
+
+    return (chebPolys);
+}
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF32 ordPolynomial1DEval(psF32 x, const psPolynomial1D* myPoly)
+{
+    psS32 loop_x = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+
+    psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+            "---- Calling ordPolynomial1DEval(%f)\n", x);
+    psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+            "Polynomial order is %d\n", myPoly->n);
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+                "Polynomial coeff[%d] is %f\n", loop_x, myPoly->coeff[loop_x]);
+    }
+
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        if (myPoly->mask[loop_x] == 0) {
+            psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 10,
+                    "polysum+= sum*coeff [%f+= (%f * %f)\n", polySum, xSum, myPoly->coeff[loop_x]);
+            polySum += xSum * myPoly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+// XXX: We assume that x is scaled between -1.0 and 1.0;
+static psF32 chebPolynomial1DEval(psF32 x, const psPolynomial1D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    psVector *d;
+    psS32 n;
+    psS32 i;
+    psF32 tmp;
+
+    n = myPoly->n;
+    d = psVectorAlloc(n, PS_TYPE_F32);
+    if(myPoly->mask[n-1] == 0) {
+        d->data.F32[n-1] = myPoly->coeff[n-1];
+    } else {
+        d->data.F32[n-1] = 0.0;
+    }
+    d->data.F32[n-2] = (2.0 * x * d->data.F32[n-1]);
+    if(myPoly->mask[n-2] == 0) {
+        d->data.F32[n-2] += myPoly->coeff[n-2];
+    }
+    for (i=n-3;i>=1;i--) {
+        d->data.F32[i] = (2.0 * x * d->data.F32[i+1]) -
+                         (d->data.F32[i+2]);
+        if(myPoly->mask[i] == 0) {
+            d->data.F32[i] += myPoly->coeff[i];
+        }
+    }
+
+    tmp = (x * d->data.F32[1]) -
+          (d->data.F32[2]);
+    if(myPoly->mask[0] == 0) {
+        tmp += (0.5 * myPoly->coeff[0]);
+    }
+    psFree(d);
+    return(tmp);
+
+    /*
+
+    psS32 n;
+    psS32 i;
+    psF32 tmp;
+    psPolynomial1D **chebPolys = NULL;
+
+    n = myPoly->n;
+    chebPolys = createChebyshevPolys(n);
+
+    tmp = 0.0;
+    for (i=0;i<myPoly->n;i++) {
+        tmp+= (myPoly->coeff[i] * psPolynomial1DEval(x, chebPolys[i]));
+    }
+    tmp-= (myPoly->coeff[0]/2.0);
+
+
+    return(tmp);
+    */
+}
+
+static psF32 ordPolynomial2DEval(psF32 x,
+                                 psF32 y,
+                                 const psPolynomial2D* myPoly)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += ySum * myPoly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial2DEval(psF32 x, psF32 y, const psPolynomial2D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += myPoly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF32 ordPolynomial3DEval(psF32 x, psF32 y, psF32 z, const psPolynomial3D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+    psF32 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += zSum * myPoly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial3DEval(psF32 x, psF32 y, psF32 z, const psPolynomial3D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += myPoly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF32 ordPolynomial4DEval(psF32 w, psF32 x, psF32 y, psF32 z, const psPolynomial4D* myPoly)
+{
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF32 polySum = 0.0;
+    psF32 wSum = 1.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+    psF32 zSum = 1.0;
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        xSum = wSum;
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            ySum = xSum;
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                zSum = ySum;
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += zSum * myPoly->coeff[loop_w][loop_x][loop_y][loop_z];
+                    }
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+        wSum *= w;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial4DEval(psF32 w, psF32 x, psF32 y, psF32 z, const psPolynomial4D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(w, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nW;
+    if (myPoly->nX > maxChebyPoly) {
+        maxChebyPoly = myPoly->nX;
+    }
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += myPoly->coeff[loop_w][loop_x][loop_y][loop_z] *
+                                   psPolynomial1DEval(chebPolys[loop_w], w) *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF64 dOrdPolynomial1DEval(psF64 x, const psDPolynomial1D* myPoly)
+{
+    psS32 loop_x = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        if (myPoly->mask[loop_x] == 0) {
+            polySum += xSum * myPoly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+static psF64 dChebPolynomial1DEval(psF64 x, const psDPolynomial1D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    psVector *d;
+    psS32 n;
+    psS32 i;
+    psF64 tmp;
+
+    n = myPoly->n;
+    d = psVectorAlloc(n, PS_TYPE_F64);
+    if(myPoly->mask[n-1] == 0) {
+        d->data.F64[n-1] = myPoly->coeff[n-1];
+    } else {
+        d->data.F64[n-1] = 0.0;
+    }
+    d->data.F64[n-2] = (2.0 * x * d->data.F64[n-1]);
+    if(myPoly->mask[n-2] == 0) {
+        d->data.F64[n-2] += myPoly->coeff[n-2];
+    }
+    for (i=n-3;i>=1;i--) {
+        d->data.F64[i] = (2.0 * x * d->data.F64[i+1]) -
+                         (d->data.F64[i+2]);
+        if(myPoly->mask[i] == 0) {
+            d->data.F64[i] += myPoly->coeff[i];
+        }
+    }
+
+    tmp = (x * d->data.F64[1]) -
+          (d->data.F64[2]);
+    if(myPoly->mask[0] == 0) {
+        tmp += (0.5 * myPoly->coeff[0]);
+    }
+
+    psFree(d);
+    return(tmp);
+}
+
+static psF64 dOrdPolynomial2DEval(psF64 x, psF64 y, const psDPolynomial2D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += ySum * myPoly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial2DEval(psF64 x, psF64 y, const psDPolynomial2D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += myPoly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 dOrdPolynomial3DEval(psF64 x, psF64 y, psF64 z, const psDPolynomial3D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += zSum * myPoly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial3DEval(psF64 x, psF64 y, psF64 z, const psDPolynomial3D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += myPoly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 dOrdPolynomial4DEval(psF64 w, psF64 x, psF64 y, psF64 z, const psDPolynomial4D* myPoly)
+{
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 wSum = 1.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        xSum = wSum;
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            ySum = xSum;
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                zSum = ySum;
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += zSum * myPoly->coeff[loop_w][loop_x][loop_y][loop_z];
+                    }
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+        wSum *= w;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial4DEval(psF64 w, psF64 x, psF64 y, psF64 z, const psDPolynomial4D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(w, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nW;
+    if (myPoly->nX > maxChebyPoly) {
+        maxChebyPoly = myPoly->nX;
+    }
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += myPoly->coeff[loop_w][loop_x][loop_y][loop_z] *
+                                   psPolynomial1DEval(chebPolys[loop_w], w) *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+
+/*****************************************************************************
+fullInterpolate1DF32(): This routine will take as input n-element floating
+point arrays domain and range, and the x value, assumed to lie with the
+domain vector.  It produces as output the (n-1)-order LaGrange interpolated
+value of x.
+ 
+XXX: do we error check for non-distinct domain values?
+ *****************************************************************************/
+#define FUNC_MACRO_FULL_INTERPOLATE_1D(TYPE) \
+static psF32 fullInterpolate1D##TYPE(ps##TYPE *domain, \
+                                     ps##TYPE *range, \
+                                     psS32 n, \
+                                     ps##TYPE x) \
+{ \
+    \
+    psS32 i; \
+    psS32 m; \
+    static psVector *p = NULL; \
+    p = psVectorRecycle(p, n, PS_TYPE_##TYPE); \
+    p_psMemSetPersistent(p, true); \
+    p_psMemSetPersistent(p->data.TYPE, true); \
+    \
+    psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 4, \
+            "---- fullInterpolate1D##TYPE() begin (%d-order at x=%f) (%d data points)----\n", n-1, x, n); \
+    \
+    for (i=0;i<n;i++) { \
+        psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                "domain/range is (%f %f)\n", domain[i], range[i]); \
+    } \
+    \
+    for (i=0;i<n;i++) { \
+        p->data.TYPE[i] = range[i]; \
+        psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                "p->data.TYPE[%d] is %f\n", i, p->data.TYPE[i]); \
+        \
+    } \
+    \
+    /* From NR, during each iteration of the m loop, we are computing the \
+       p_{i ... i+m} terms. \
+    */ \
+    for (m=1;m<n;m++) { \
+        for (i=0;i<n-m;i++) { \
+            /* From NR: we are computing P_{i ... i+m} \
+             */ \
+            p->data.TYPE[i] = (((x-domain[i+m]) * p->data.TYPE[i]) + \
+                               ((domain[i]-x) * p->data.TYPE[i+1])) / \
+                              (domain[i] - domain[i+m]); \
+            /*printf("((%f-%f * %f) + (%f-%f * %f)) / (%f - %f)\n", x, domain[i+m], p->data.TYPE[i], domain[i], x, p->data.TYPE[i+1], domain[i], domain[i+m]); \
+             */ \
+            psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                    "p->data.TYPE[%d] is %f\n", i, p->data.TYPE[i]); \
+        } \
+    } \
+    psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 4, \
+            "---- fullInterpolate1D##TYPE() end ----\n"); \
+    \
+    return(p->data.TYPE[0]); \
+} \
+
+/*
+FUNC_MACRO_FULL_INTERPOLATE_1D(U8)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U16)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U32)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U64)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S8)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S16)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S32)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S64)
+FUNC_MACRO_FULL_INTERPOLATE_1D(F64)
+*/
+FUNC_MACRO_FULL_INTERPOLATE_1D(F32)
+
+
+/*****************************************************************************
+interpolate1DF32(): this is the base 1-D flat memory routine to perform
+LaGrange interpolation.
+ *****************************************************************************/
+static psF32 interpolate1DF32(psF32 *domain,
+                              psF32 *range,
+                              psS32 n,
+                              psS32 order,
+                              psF32 x)
+{
+    PS_PTR_CHECK_NULL(domain, NAN)
+    PS_PTR_CHECK_NULL(range, NAN)
+    // XXX: Check valid values for n, order, and x?
+
+    psS32 binNum;
+    psS32 numIntPoints = order+1;
+    psS32 origin;
+
+    psTrace(".psLib.dataManip.psFunctions.interpolate1DF32", 4,
+            "---- interpolate1DF32() begin ----\n");
+
+    binNum = vectorBinDisectF32(domain, n, x);
+
+    if (0 == numIntPoints%2) {
+        origin = binNum - ((numIntPoints/2) - 1);
+    } else {
+        origin = binNum - (numIntPoints/2);
+        if ((x-domain[binNum]) > (domain[binNum+1]-x)) {
+            // x is closer to binNum+1.
+            origin = 1 + (binNum - (numIntPoints/2));
+        }
+    }
+    if (origin < 0) {
+        origin = 0;
+    }
+    if ((origin + numIntPoints) > n) {
+        origin = n - numIntPoints;
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.interpolate1DF32", 4,
+            "---- interpolate1DF32() end ----\n");
+    return(fullInterpolate1DF32(&domain[origin], &range[origin], order+1, x));
+}
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - PUBLIC                                         */
+/*****************************************************************************/
+
+/*****************************************************************************
+    Evaluate a non-normalized Gaussian with the given mean and sigma at the
+    given coordianate.  Note that this is not a Gaussian deviate.  The
+    evaluated Gaussian is: \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f]
+ *****************************************************************************/
+psF32 psGaussian(psF32 x, psF32 mean, psF32 sigma, psBool normal)
+{
+    psF32 tmp = 1.0;
+
+    psTrace(".psLib.dataManip.psFunctions.psGaussian", 4,
+            "---- psGaussian() begin ----\n");
+
+    if (normal == true) {
+        tmp = 1.0 / PS_SQRT_F32(2.0 * M_PI * (sigma * sigma));
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.psGaussian", 4,
+            "---- psGaussian() end ----\n");
+    return(tmp * exp(-((x - mean) * (x - mean)) / (2.0 * sigma * sigma)));
+}
+
+/*****************************************************************************
+    p_psGaussianDev()
+ This private routine (formerly a psLib API routine) creates a psVector of the
+ specified size and type F32 and fills it with a random Gaussian distribution
+ of numbers with the specified mean and sigma.  This routine makes use of the
+ GSL routines for generating both uniformly distributed numbers and the
+ Gaussian distribution as well.
+ 
+XXX: There is no way to seed the random generator.
+ *****************************************************************************/
+psVector* p_psGaussianDev(psF32 mean, psF32 sigma, psS32 Npts)
+{
+    PS_INT_CHECK_NON_NEGATIVE(Npts, NULL);
+
+    psVector* gauss = NULL;
+    const gsl_rng_type *T = NULL;
+    gsl_rng *r = NULL;
+    psS32 i = 0;
+
+
+    gauss = psVectorAlloc(Npts, PS_TYPE_F32);
+    gauss->n = Npts;
+    gsl_rng_env_setup();
+    T = gsl_rng_default;
+    r = gsl_rng_alloc(T);
+
+    for (i = 0; i < Npts; i++) {
+        gauss->data.F32[i] = mean + gsl_ran_gaussian(r, sigma);
+    }
+
+    // XXX: Should I free r, T as well?  This is a memory leak.
+    return(gauss);
+}
+
+/*****************************************************************************
+    This routine must allocate memory for the polynomial structures.
+ *****************************************************************************/
+psPolynomial1D* psPolynomial1DAlloc(psS32 n,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+
+    psS32 i = 0;
+    psPolynomial1D* newPoly = NULL;
+
+    newPoly = (psPolynomial1D* ) psAlloc(sizeof(psPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->n = n;
+    newPoly->coeff = (psF32 *)psAlloc(n * sizeof(psF32));
+    newPoly->coeffErr = (psF32 *)psAlloc(n * sizeof(psF32));
+    newPoly->mask = (psU8 *)psAlloc(n * sizeof(psU8));
+    for (i = 0; i < n; i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->mask[i] = 0;
+    }
+
+    return(newPoly);
+}
+
+psPolynomial2D* psPolynomial2DAlloc(psS32 nX, psS32 nY,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psPolynomial2D* newPoly = NULL;
+
+    newPoly = (psPolynomial2D* ) psAlloc(sizeof(psPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = (psF32 **)psAlloc(nX * sizeof(psF32 *));
+    newPoly->coeffErr = (psF32 **)psAlloc(nX * sizeof(psF32 *));
+    newPoly->mask = (psU8 **)psAlloc(nX * sizeof(psU8 *));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF32 *)psAlloc(nY * sizeof(psF32));
+        newPoly->coeffErr[x] = (psF32 *)psAlloc(nY * sizeof(psF32));
+        newPoly->mask[x] = (psU8 *)psAlloc(nY * sizeof(psU8));
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->mask[x][y] = 0;
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial3D* psPolynomial3DAlloc(psS32 nX, psS32 nY, psS32 nZ,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psPolynomial3D* newPoly = NULL;
+
+    newPoly = (psPolynomial3D* ) psAlloc(sizeof(psPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+    newPoly->coeffErr = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+    newPoly->mask = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+        newPoly->coeffErr[x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+        newPoly->mask[x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+            newPoly->coeffErr[x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+            newPoly->mask[x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+        }
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            for (z = 0; z < nZ; z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->mask[x][y][z] = 0;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial4D* psPolynomial4DAlloc(psS32 nW, psS32 nX, psS32 nY, psS32 nZ,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nW, NULL);
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psPolynomial4D* newPoly = NULL;
+
+    newPoly = (psPolynomial4D* ) psAlloc(sizeof(psPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nW = nW;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF32 ****)psAlloc(nW * sizeof(psF32 ***));
+    newPoly->coeffErr = (psF32 ****)psAlloc(nW * sizeof(psF32 ***));
+    newPoly->mask = (psU8 ****)psAlloc(nW * sizeof(psU8 ***));
+    for (w = 0; w < nW; w++) {
+        newPoly->coeff[w] = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+        newPoly->coeffErr[w] = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+        newPoly->mask[w] = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+        for (x = 0; x < nX; x++) {
+            newPoly->coeff[w][x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+            newPoly->coeffErr[w][x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+            newPoly->mask[w][x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+            for (y = 0; y < nY; y++) {
+                newPoly->coeff[w][x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+                newPoly->coeffErr[w][x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+                newPoly->mask[w][x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+            }
+        }
+    }
+    for (w = 0; w < nW; w++) {
+        for (x = 0; x < nX; x++) {
+            for (y = 0; y < nY; y++) {
+                for (z = 0; z < nZ; z++) {
+                    newPoly->coeff[w][x][y][z] = 0.0;
+                    newPoly->coeffErr[w][x][y][z] = 0.0;
+                    newPoly->mask[w][x][y][z] = 0;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psF32 psPolynomial1DEval(const psPolynomial1D* myPoly, psF32 x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial1DEval(x, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial1DEval(x, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial1DEvalVector(const psPolynomial1D *myPoly,
+                                   const psVector *x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+
+    tmp = psVectorAlloc(x->n, PS_TYPE_F32);
+    for (psS32 i=0;i<x->n;i++) {
+        tmp->data.F32[i] = psPolynomial1DEval(myPoly, x->data.F32[i]);
+    }
+
+    return(tmp);
+}
+
+psF32 psPolynomial2DEval(const psPolynomial2D* myPoly, psF32 x, psF32 y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial2DEval(x, y, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial2DEval(x, y, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial2DEvalVector(const psPolynomial2D *myPoly,
+                                   const psVector *x,
+                                   const psVector *y)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the length of the output vector to by the minimum of the x,y vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    // Create output vector to return
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate the polynomial at the specified points
+    for (psS32 i=0; i<vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial2DEval(myPoly,x->data.F32[i],y->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF32 psPolynomial3DEval(const psPolynomial3D* myPoly, psF32 x, psF32 y, psF32 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial3DEval(x, y, z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial3DEval(x, y, z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial3DEvalVector(const psPolynomial3D *myPoly,
+                                   const psVector *x,
+                                   const psVector *y,
+                                   const psVector *z)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the length of output vector from min of the input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial3DEval(myPoly,
+                                              x->data.F32[i],
+                                              y->data.F32[i],
+                                              z->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF32 psPolynomial4DEval(const psPolynomial4D* myPoly, psF32 w, psF32 x, psF32 y, psF32 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial4DEval(w,x,y,z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial4DEval(w,x,y,z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial4DEvalVector(const psPolynomial4D *myPoly,
+                                   const psVector *w,
+                                   const psVector *x,
+                                   const psVector *y,
+                                   const psVector *z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(w, NULL);
+    PS_VECTOR_CHECK_TYPE(w, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=w->n;
+
+    // Determine output vector size from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (x->n < vecLen) {
+        vecLen = x->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial4DEval(myPoly,
+                                              w->data.F32[i],
+                                              x->data.F32[i],
+                                              y->data.F32[i],
+                                              z->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+psDPolynomial1D* psDPolynomial1DAlloc(psS32 n,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+
+    psS32 i = 0;
+    psDPolynomial1D* newPoly = NULL;
+
+    newPoly = (psDPolynomial1D* ) psAlloc(sizeof(psDPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->n = n;
+    newPoly->coeff = (psF64 *)psAlloc(n * sizeof(psF64));
+    newPoly->coeffErr = (psF64 *)psAlloc(n * sizeof(psF64));
+    newPoly->mask = (psU8 *)psAlloc(n * sizeof(psU8));
+    for (i = 0; i < n; i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->mask[i] = 0;
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial2D* psDPolynomial2DAlloc(psS32 nX, psS32 nY,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psDPolynomial2D* newPoly = NULL;
+
+    newPoly = (psDPolynomial2D* ) psAlloc(sizeof(psDPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = (psF64 **)psAlloc(nX * sizeof(psF64 *));
+    newPoly->coeffErr = (psF64 **)psAlloc(nX * sizeof(psF64 *));
+    newPoly->mask = (psU8 **)psAlloc(nX * sizeof(psU8 *));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF64 *)psAlloc(nY * sizeof(psF64));
+        newPoly->coeffErr[x] = (psF64 *)psAlloc(nY * sizeof(psF64));
+        newPoly->mask[x] = (psU8 *)psAlloc(nY * sizeof(psU8));
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->mask[x][y] = 0;
+        }
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial3D* psDPolynomial3DAlloc(psS32 nX, psS32 nY, psS32 nZ,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psDPolynomial3D* newPoly = NULL;
+
+    newPoly = (psDPolynomial3D* ) psAlloc(sizeof(psDPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+    newPoly->coeffErr = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+    newPoly->mask = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+        newPoly->coeffErr[x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+        newPoly->mask[x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+            newPoly->coeffErr[x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+            newPoly->mask[x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+        }
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            for (z = 0; z < nZ; z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->mask[x][y][z] = 0;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial4D* psDPolynomial4DAlloc(psS32 nW, psS32 nX, psS32 nY, psS32 nZ,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nW, NULL);
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psDPolynomial4D* newPoly = NULL;
+
+    newPoly = (psDPolynomial4D* ) psAlloc(sizeof(psDPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nW = nW;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF64 ****)psAlloc(nW * sizeof(psF64 ***));
+    newPoly->coeffErr = (psF64 ****)psAlloc(nW * sizeof(psF64 ***));
+    newPoly->mask = (psU8 ****)psAlloc(nW * sizeof(psU8 ***));
+    for (w = 0; w < nW; w++) {
+        newPoly->coeff[w] = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+        newPoly->coeffErr[w] = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+        newPoly->mask[w] = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+        for (x = 0; x < nX; x++) {
+            newPoly->coeff[w][x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+            newPoly->coeffErr[w][x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+            newPoly->mask[w][x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+            for (y = 0; y < nY; y++) {
+                newPoly->coeff[w][x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+                newPoly->coeffErr[w][x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+                newPoly->mask[w][x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+            }
+        }
+    }
+    for (w = 0; w < nW; w++) {
+        for (x = 0; x < nX; x++) {
+            for (y = 0; y < nY; y++) {
+                for (z = 0; z < nZ; z++) {
+                    newPoly->coeff[w][x][y][z] = 0.0;
+                    newPoly->coeffErr[w][x][y][z] = 0.0;
+                    newPoly->mask[w][x][y][z] = 0;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+
+psF64 psDPolynomial1DEval(const psDPolynomial1D* myPoly, psF64 x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial1DEval(x, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial1DEval(x, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial1DEvalVector(const psDPolynomial1D *myPoly,
+                                    const psVector *x)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+
+    tmp = psVectorAlloc(x->n, PS_TYPE_F64);
+    for (psS32 i=0;i<x->n;i++) {
+        tmp->data.F64[i] = psDPolynomial1DEval(myPoly,
+                                               x->data.F64[i]);
+    }
+
+    return(tmp);
+}
+
+
+psF64 psDPolynomial2DEval(const psDPolynomial2D* myPoly,
+                          psF64 x,
+                          psF64 y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial2DEval(x, y, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial2DEval(x, y, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial2DEvalVector(const psDPolynomial2D *myPoly,
+                                    const psVector *x,
+                                    const psVector *y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the output vector length from minimum length of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate the polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial2DEval(myPoly,x->data.F64[i],y->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+psF64 psDPolynomial3DEval(const psDPolynomial3D* myPoly,
+                          psF64 x,
+                          psF64 y,
+                          psF64 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial3DEval(x, y, z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial3DEval(x, y, z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial3DEvalVector(const psDPolynomial3D *myPoly,
+                                    const psVector *x,
+                                    const psVector *y,
+                                    const psVector *z)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the size of output vector from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial3DEval(myPoly,
+                                               x->data.F64[i],
+                                               y->data.F64[i],
+                                               z->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF64 psDPolynomial4DEval(const psDPolynomial4D* myPoly,
+                          psF64 w,
+                          psF64 x,
+                          psF64 y,
+                          psF64 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial4DEval(w,x,y,z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial4DEval(w,x,y,z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial4DEvalVector(const psDPolynomial4D *myPoly,
+                                    const psVector *w,
+                                    const psVector *x,
+                                    const psVector *y,
+                                    const psVector *z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(w, NULL);
+    PS_VECTOR_CHECK_TYPE(w, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=w->n;
+
+    // Determine the output vector size from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (x->n < vecLen) {
+        vecLen = x->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate the polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial4DEval(myPoly,
+                                               w->data.F64[i],
+                                               x->data.F64[i],
+                                               y->data.F64[i],
+                                               z->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+
+
+//typedef struct {
+//    psS32 n;
+//    psPolynomial1D **spline;
+//    psF32 *p_psDeriv2;
+//    psVector *knots;
+//} psSpline1D;
+
+/*****************************************************************************
+    NOTE: "n" specifies the number of spline polynomials.  Therefore, there
+    must exist n+1 points in "knots".
+ 
+XXX: Ensure that domain[i+1] != domain[i]
+ 
+XXX: What should be the defualty type for knots be?  psF32 is assumed.
+ *****************************************************************************/
+psSpline1D *psSpline1DAlloc(psS32 numSplines,
+                            psS32 order,
+                            psF32 min,
+                            psF32 max)
+{
+    PS_INT_CHECK_NON_NEGATIVE(numSplines, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+    PS_FLOAT_CHECK_NON_EQUAL(max, min, NULL);
+
+    psSpline1D *tmp = NULL;
+    psS32 i;
+    psF32 tmpDomain;
+    psF32 width;
+
+    tmp = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+    tmp->n = numSplines;
+
+    tmp->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (i=0;i<numSplines;i++) {
+        (tmp->spline)[i] = psPolynomial1DAlloc(order+1, PS_POLYNOMIAL_ORD);
+    }
+
+    // This should be set by the psVectorFitSpline1D()
+    tmp->p_psDeriv2 = NULL;
+
+    tmp->knots = psVectorAlloc(numSplines+1, PS_TYPE_F32);
+    width = (max - min) / ((psF32) numSplines);
+
+    tmp->knots->data.F32[0] = min;
+    tmpDomain = min+width;
+    for (i=1;i<numSplines+1;i++) {
+        tmp->knots->data.F32[i] = tmpDomain;
+        tmpDomain+= width;
+    }
+    tmp->knots->data.F32[numSplines] = max;
+
+    psMemSetDeallocator(tmp,(psFreeFcn)spline1DFree);
+    return(tmp);
+}
+
+
+/*****************************************************************************
+XXX: What should be the defualty type for knots be?  psF32 is assumed.
+ *****************************************************************************/
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds,
+                                   psS32 order)
+{
+    PS_VECTOR_CHECK_NULL(bounds, NULL);
+    PS_VECTOR_CHECK_EMPTY(bounds, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+
+    psSpline1D *tmp = NULL;
+    psS32 i;
+    psS32 numSplines;
+
+    tmp = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+
+    numSplines = bounds->n - 1;
+    tmp->n = numSplines;
+
+    tmp->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (i=0;i<numSplines;i++) {
+        (tmp->spline)[i] = psPolynomial1DAlloc(order+1, PS_POLYNOMIAL_ORD);
+    }
+
+    // This should be set by the psVectorFitSpline1D()
+    tmp->p_psDeriv2 = NULL;
+
+    tmp->knots = psVectorAlloc(bounds->n, PS_TYPE_F32);
+
+    for (i=0;i<bounds->n;i++) {
+        tmp->knots->data.F32[i] = bounds->data.F32[i];
+        if (i<(bounds->n-1)) {
+            if (FLT_EPSILON >= fabs(bounds->data.F32[i+1]-bounds->data.F32[i])) {
+                psError(PS_ERR_UNKNOWN, true, "data points must be distinct\n");
+            }
+        }
+    }
+
+    psMemSetDeallocator(tmp,(psFreeFcn)spline1DFree);
+    return(tmp);
+}
+
+/*****************************************************************************
+vectorBinDisectF32(): This is a macro for a private function which takes as
+input a vector an array of data as well as a single value for that data.  The
+input vector values are assumed to be non-decreasing (v[i-1] <= v[i] for all
+i).  This routine does a binary disection of the vector and returns "i" such
+that (v[i] <= x <= v[i+1).  If x lies outside the range of v[], then this
+routine prints a warning message and returns (-2 or -1).
+ *****************************************************************************/
+#define FUNC_MACRO_VECTOR_BIN_DISECT(TYPE) \
+static psS32 vectorBinDisect##TYPE(ps##TYPE *bins, \
+                                   psS32 numBins, \
+                                   ps##TYPE x) \
+{ \
+    psS32 min; \
+    psS32 max; \
+    psS32 mid; \
+    \
+    psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+            "---- Calling vectorBinDisect##TYPE(%f)\n", x); \
+    \
+    if (x < bins[0]) { \
+        psLogMsg(__func__, PS_LOG_WARN, \
+                 "vectorBinDisect%s(): ordinate %f is outside vector range (%f - %f).", \
+                 #TYPE, x, bins[0], bins[numBins-1]); \
+        return(-2); \
+    } \
+    \
+    if (x > bins[numBins-1]) { \
+        psLogMsg(__func__, PS_LOG_WARN, \
+                 "vectorBinDisect%s(): ordinate %f is outside vector range (%f - %f).", \
+                 #TYPE, x, bins[0], bins[numBins-1]); \
+        return(-1); \
+    } \
+    \
+    min = 0; \
+    max = numBins-2; \
+    mid = ((max+1)-min)/2; \
+    \
+    while (min != max) { \
+        psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+                "(min, mid, max) is (%d, %d, %d): (x, bins) is (%f, %f)\n", \
+                min, mid, max, x, bins[mid]); \
+        \
+        if (x == bins[mid]) { \
+            psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+                    "---- Exiting vectorBinDisect##TYPE(): bin %d\n", mid); \
+            return(mid); \
+        } else if (x < bins[mid]) { \
+            max = mid-1; \
+        } else { \
+            min = mid; \
+        } \
+        mid = ((max+1)+min)/2; \
+    } \
+    \
+    psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+            "---- Exiting vectorBinDisect##TYPE(): bin %d\n", min); \
+    return(min); \
+} \
+
+FUNC_MACRO_VECTOR_BIN_DISECT(S8)
+FUNC_MACRO_VECTOR_BIN_DISECT(S16)
+FUNC_MACRO_VECTOR_BIN_DISECT(S32)
+FUNC_MACRO_VECTOR_BIN_DISECT(S64)
+FUNC_MACRO_VECTOR_BIN_DISECT(U8)
+FUNC_MACRO_VECTOR_BIN_DISECT(U16)
+FUNC_MACRO_VECTOR_BIN_DISECT(U32)
+FUNC_MACRO_VECTOR_BIN_DISECT(U64)
+FUNC_MACRO_VECTOR_BIN_DISECT(F32)
+FUNC_MACRO_VECTOR_BIN_DISECT(F64)
+
+/*****************************************************************************
+p_psVectorBinDisect(): A wrapper to the above p_psVectorBinDisect().
+ *****************************************************************************/
+psS32 p_psVectorBinDisect(psVector *bins,
+                          psScalar *x)
+{
+    PS_VECTOR_CHECK_NULL(bins, -4);
+    PS_VECTOR_CHECK_EMPTY(bins, -4);
+    PS_PTR_CHECK_NULL(x, -6);
+    PS_PTR_CHECK_TYPE_EQUAL(x, bins, -3);
+    char* strType;
+
+    switch (x->type.type) {
+    case PS_TYPE_U8:
+        return(vectorBinDisectU8(bins->data.U8, bins->n, x->data.U8));
+    case PS_TYPE_U16:
+        return(vectorBinDisectU16(bins->data.U16, bins->n, x->data.U16));
+    case PS_TYPE_U32:
+        return(vectorBinDisectU32(bins->data.U32, bins->n, x->data.U32));
+    case PS_TYPE_U64:
+        return(vectorBinDisectU64(bins->data.U64, bins->n, x->data.U64));
+    case PS_TYPE_S8:
+        return(vectorBinDisectS8(bins->data.S8, bins->n, x->data.S8));
+    case PS_TYPE_S16:
+        return(vectorBinDisectS16(bins->data.S16, bins->n, x->data.S16));
+    case PS_TYPE_S32:
+        return(vectorBinDisectS32(bins->data.S32, bins->n, x->data.S32));
+    case PS_TYPE_S64:
+        return(vectorBinDisectS64(bins->data.S64, bins->n, x->data.S64));
+    case PS_TYPE_F32:
+        return(vectorBinDisectF32(bins->data.F32, bins->n, x->data.F32));
+    case PS_TYPE_F64:
+        return(vectorBinDisectF64(bins->data.F64, bins->n, x->data.F64));
+    case PS_TYPE_C32:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    case PS_TYPE_C64:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    case PS_TYPE_BOOL:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    }
+    return(-3);
+}
+
+/*****************************************************************************
+p_psVectorInterpolate(): This routine will take as input psVectors domain and
+range, and the x value, assumed to lie with the domain vector.  It produces
+as output the LaGrange interpolated value of a polynomial of the specified
+order around the point x.
+ 
+XXX: This stuff does not currently work with a mask.
+ 
+XXX: add another psScalar argument for the result.
+ 
+XXX: The VectorCopy routines seg fault when I declare range32 as static.
+ *****************************************************************************/
+psScalar *p_psVectorInterpolate(psVector *domain,
+                                psVector *range,
+                                psS32 order,
+                                psScalar *x)
+{
+    PS_VECTOR_CHECK_NULL(domain, NULL);
+    PS_VECTOR_CHECK_NULL(range, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(domain, range, NULL);
+    PS_PTR_CHECK_TYPE_EQUAL(domain, range, NULL);
+    PS_PTR_CHECK_TYPE_EQUAL(domain, x, NULL);
+
+    psVector *range32 = NULL;
+    psVector *domain32 = NULL;
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "---- p_psVectorInterpolate() begin ----\n");
+
+    if (order > (domain->n - 1)) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFunctions_NOT_ENOUGH_DATAPOINTS,
+                order);
+        return(NULL);
+    }
+
+    if (x->type.type == PS_TYPE_F32) {
+        psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+                "---- p_psVectorInterpolate() end ----\n");
+        return(psScalarAlloc(interpolate1DF32(domain->data.F32,
+                                              range->data.F32,
+                                              domain->n,
+                                              order,
+                                              x->data.F32), PS_TYPE_F32));
+    } else if (x->type.type == PS_TYPE_F64) {
+        // XXX: use recycled vectors here.
+        range32 = psVectorCopy(range32, range, PS_TYPE_F32);
+        domain32 = psVectorCopy(domain32, domain, PS_TYPE_F32);
+
+        psScalar *tmpScalar = psScalarAlloc((psF64)
+                                            interpolate1DF32(domain32->data.F32,
+                                                             range32->data.F32,
+                                                             domain32->n,
+                                                             order,
+                                                             (psF32) x->data.F64), PS_TYPE_F64);
+        psFree(range32);
+        psFree(domain32);
+
+        psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+                "---- p_psVectorInterpolate() end ----\n");
+        // XXX: Convert data type to F64?
+        return(tmpScalar);
+
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "return(NULL)\n");
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "---- p_psVectorInterpolate() end ----\n");
+
+    return(NULL);
+}
+
+
+/*****************************************************************************
+psSpline1DEval(): this routine takes an existing spline of arbitrary order
+and an independent x value.  Each determines which spline that x corresponds
+to by doing a bracket disection on the knots of the spline data structure
+(vectorBinDisectF32()).  Then it evaluates the spline at that x location
+by a call to the 1D polynomial functions.
+ 
+XXX: The spline eval functions require input and output to be F32.  however
+     the spline fit functions require F32 and F64.
+ 
+XXX: This only works if spline0>knots if psF32.  Must add support for psU32 and
+psF64.
+ *****************************************************************************/
+psF32 psSpline1DEval(
+    const psSpline1D *spline,
+    psF32 x
+)
+{
+    PS_PTR_CHECK_NULL(spline, NAN);
+    PS_INT_CHECK_NON_NEGATIVE(spline->n, NAN);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NAN);
+
+    psS32 binNum;
+    psS32 n;
+
+    n = spline->n;
+    //XXX    binNum = vectorBinDisectF32(spline->domains, (spline->n)+1, x);
+    binNum = vectorBinDisectF32(spline->knots->data.F32, (spline->n)+1, x);
+    if (binNum < 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "psSpline1DEval(): x ordinate (%f) is outside the spline range (%f - %f).",
+                 x, spline->knots->data.F32[0],
+                 spline->knots->data.F32[n-1]);
+
+        if (x < spline->knots->data.F32[0]) {
+            return(psPolynomial1DEval(spline->spline[0],
+                                      x));
+        } else if (x > spline->knots->data.F32[n-1]) {
+            return(psPolynomial1DEval(spline->spline[n-1],
+                                      x));
+        }
+    }
+
+    return(psPolynomial1DEval(spline->spline[binNum],
+                              x));
+}
+
+// XXX: The spline eval functions require input and output to be F32.
+// however the spline fit functions require F32 and F64.
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x
+)
+{
+    PS_PTR_CHECK_NULL(spline, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NULL);
+
+    psS32 i;
+    psVector *tmpVector;
+
+    tmpVector = psVectorAlloc(x->n, PS_TYPE_F32);
+    if (x->type.type == PS_TYPE_F32) {
+        for (i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(
+                                         spline,
+                                         x->data.F32[i]
+                                     );
+        }
+    } else if (x->type.type == PS_TYPE_F64) {
+        for (i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(
+                                         spline,
+                                         (psF32) x->data.F64[i]
+                                     );
+        }
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return(NULL);
+    }
+
+    return(tmpVector);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psPolynomial.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psPolynomial.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psPolynomial.h	(revision 22271)
@@ -0,0 +1,440 @@
+/** @file psFunctions.h
+ *  @brief Standard Mathematical Functions.
+ *  @ingroup Stats
+ *
+ *  This file will hold the prototypes for procedures which allocate, free,
+ *  and evaluate various polynomials.  Those polynomial structures are also
+ *  defined here.
+ *
+ *  @ingroup Stats
+ *
+ *  @author Someone at IfA
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.44 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-31 23:01:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_FUNCTIONS_H)
+#define PS_FUNCTIONS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+
+/** \addtogroup Stats
+ *  \{
+ */
+
+/** Evaluate a non-normalized Gaussian with the given mean and sigma at the
+ *  given coordianate.  
+ *
+ *  Note that this is not a Gaussian deviate.  The evaluated Gaussian is: 
+ *        \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f] 
+ *
+ *  @return psF32      value on the gaussian curve given the input parameters
+ */
+psF32 psGaussian(
+    psF32 x,                           ///< Value at which to evaluate
+    psF32 mean,                        ///< Mean for the Gaussian
+    psF32 stddev,                      ///< Standard deviation for the Gaussian
+    psBool normal                        ///< Indicates whether result should be normalized
+);
+
+/** Produce a vector of random numbers from a Gaussian distribution with
+ *  the specified mean and sigma 
+ *  
+ *  @return psVector*    vector of random numbers
+ *  
+ */
+psVector* p_psGaussianDev(
+    psF32 mean,                        ///< The mean of the Gaussian
+    psF32 sigma,                       ///< The sigma of the Gaussian
+    psS32 Npts                           ///< The size of the vector
+);
+
+typedef enum {
+    PS_POLYNOMIAL_ORD,                 ///< Ordinary Polynomial
+    PS_POLYNOMIAL_CHEB                 ///< Chebyshev Polynomial
+} psPolynomialType;
+
+/** One-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 n;                             ///< Number of terms
+    psF32 *coeff;                      ///< Coefficients
+    psF32 *coeffErr;                   ///< Error in coefficients
+    psU8 *mask;                        ///< Coefficient mask
+}
+psPolynomial1D;
+
+/** Two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psF32 **coeff;                     ///< Coefficients
+    psF32 **coeffErr;                  ///< Error in coefficients
+    psU8 **mask;                       ///< Coefficients mask
+}
+psPolynomial2D;
+
+/** Three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF32 ***coeff;                    ///< Coefficients
+    psF32 ***coeffErr;                 ///< Error in coefficients
+    psU8 ***mask;                      ///< Coefficients mask
+}
+psPolynomial3D;
+
+/** Four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nW;                            ///< Number of terms in w
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF32 ****coeff;                   ///< Coefficients
+    psF32 ****coeffErr;                ///< Error in coefficients
+    psU8 ****mask;                     ///< Coefficients mask
+}
+psPolynomial4D;
+
+
+/** Allocates a psPolynomial1D structure with n terms
+ *
+ *  @return  psPolynomial1D*    new 1-D polynomial struct
+ */
+psPolynomial1D* psPolynomial1DAlloc(
+    psS32 n,                              ///< Number of terms
+    psPolynomialType type               ///< Polynomial Type
+);
+
+/** Allocates a 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new 2-D polynomial struct
+ */
+psPolynomial2D* psPolynomial2DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new 3-D polynomial struct
+ */
+psPolynomial3D* psPolynomial3DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new 4-D polynomial struct
+ */
+psPolynomial4D* psPolynomial4DAlloc(
+    psS32 nW,                            ///< Number of terms in w
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Evaluates a 1-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial1DEval(
+    const psPolynomial1D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x                           ///< location at which to evaluate
+);
+
+/** Evaluates a 2-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial2DEval(
+    const psPolynomial2D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y                           ///< y location at which to evaluate
+);
+
+/** Evaluates a 3-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial3DEval(
+    const psPolynomial3D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y,                           ///< y location at which to evaluate
+    psF32 z                           ///< z location at which to evaluate
+);
+
+/** Evaluates a 4-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial4DEval(
+    const psPolynomial4D* myPoly,       ///< Coefficients for the polynomial
+    psF32 w,                           ///< w location at which to evaluate
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y,                           ///< y location at which to evaluate
+    psF32 z                           ///< z location at which to evaluate
+);
+
+psVector *psPolynomial1DEvalVector(
+    const psPolynomial1D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x             ///< x locations at which to evaluate
+);
+
+psVector *psPolynomial2DEvalVector(
+    const psPolynomial2D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y             ///< y locations at which to evaluate
+);
+
+psVector *psPolynomial3DEvalVector(
+    const psPolynomial3D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+psVector *psPolynomial4DEvalVector(
+    const psPolynomial4D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *w,             ///< w locations at which to evaluate
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+/*****************************************************************************/
+
+/* Double-precision polynomials, mainly for use in astrometry */
+
+/** Double-precision one-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 n;                             ///< Number of terms
+    psF64 *coeff;                     ///< Coefficients
+    psF64 *coeffErr;                  ///< Error in coefficients
+    psU8 *mask;                        ///< Coefficient mask
+}
+psDPolynomial1D;
+
+/** Double-precision two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psF64 **coeff;                    ///< Coefficients
+    psF64 **coeffErr;                 ///< Error in coefficients
+    psU8 **mask;                       ///< Coefficients mask
+}
+psDPolynomial2D;
+
+/** Double-precision three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF64 ***coeff;                   ///< Coefficients
+    psF64 ***coeffErr;                ///< Error in coefficients
+    psU8 ***mask;                      ///< Coefficient mask
+}
+psDPolynomial3D;
+
+/** Double-precision four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nW;                            ///< Number of terms in w
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF64 ****coeff;                  ///< Coefficients
+    psF64 ****coeffErr;               ///< Error in coefficients
+    psU8 ****mask;                     ///< Coefficients mask
+}
+psDPolynomial4D;
+
+/** Allocates a double-precision 1-D polynomial structure with n terms
+ *
+ *  @return  psPolynomial1D*    new double-precision 1-D polynomial struct
+ */
+psDPolynomial1D* psDPolynomial1DAlloc(
+    psS32 n,                             ///< Number of terms
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new double-precision 2-D polynomial struct
+ */
+psDPolynomial2D* psDPolynomial2DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new double-precision 3-D polynomial struct
+ */
+psDPolynomial3D* psDPolynomial3DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new double-precision 4-D polynomial struct
+ */
+psDPolynomial4D* psDPolynomial4DAlloc(
+    psS32 nW,                            ///< Number of terms in w
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Evaluates a double-precision 1-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial1DEval(
+    const psDPolynomial1D* myPoly,      ///< Coefficients for the polynomial
+    psF64 x                          ///< Value at which to evaluate
+);
+
+/** Evaluates a double-precision 2-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial2DEval(
+    const psDPolynomial2D* myPoly,       ///< Coefficients for the polynomial
+    psF64 x,                           ///< Value x at which to evaluate
+    psF64 y            ///< Value y at which to evaluate
+);
+
+/** Evaluates a double-precision 3-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial3DEval(
+    const psDPolynomial3D* myPoly,      ///< Coefficients for the polynomial
+    psF64 x,                          ///< Value x at which to evaluate
+    psF64 y,                          ///< Value y at which to evaluate
+    psF64 z     ///< Value z at which to evaluate
+);
+
+/** Evaluates a double-precision 4-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial4DEval(
+    const psDPolynomial4D* myPoly,      ///< Coefficients for the polynomial
+    psF64 w,                          ///< Value w at which to evaluate
+    psF64 x,                          ///< Value x at which to evaluate
+    psF64 y,                          ///< Value y at which to evaluate
+    psF64 z     ///< Value z at which to evaluate
+);
+
+psVector *psDPolynomial1DEvalVector(
+    const psDPolynomial1D *myPoly, ///< Coefficients for the polynomial
+    const psVector *x             ///< x locations at which to evaluate
+);
+
+psVector *psDPolynomial2DEvalVector(
+    const psDPolynomial2D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y             ///< y locations at which to evaluate
+);
+
+psVector *psDPolynomial3DEvalVector(
+    const psDPolynomial3D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+psVector *psDPolynomial4DEvalVector(
+    const psDPolynomial4D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *w,             ///< w locations at which to evaluate
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+
+
+typedef struct
+{
+    psS32 n;                       ///< The number of spline polynomials
+    psPolynomial1D **spline;       ///< An array of n pointers to the spline polynomials
+    psF32 *p_psDeriv2;             ///< For cubic splines, the second derivative at each domain point.  Size is n+1.
+    psF32 *domains;                ///< The boundaries between each spline piece.  Size is n+1.
+    psVector *knots;               ///< The boundaries between each spline piece.  Size is n+1.
+}
+psSpline1D;
+
+psSpline1D *psSpline1DAlloc(psS32 n,
+                            psS32 order,
+                            psF32 min,
+                            psF32 max);
+
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds,
+                                   psS32 order);
+
+psF32 psSpline1DEval(
+    const psSpline1D *spline,
+    psF32 x
+);
+
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x
+);
+
+psS32 p_psVectorBinDisect(psVector *bins,
+                          psScalar *x);
+
+psScalar *p_psVectorInterpolate(psVector *domain,
+                                psVector *range,
+                                psS32 order,
+                                psScalar *x);
+
+#if 0
+psF32 p_psNRSpline1DEval(psSpline1D *spline,
+                         const psVector* x,
+                         const psVector* y,
+                         psF32 X);
+#endif
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psRandom.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psRandom.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psRandom.c	(revision 22271)
@@ -0,0 +1,140 @@
+/** @file psRandom.c
+*  \brief Random Number Generators
+*  \ingroup Math
+*
+*  This file will hold the functions which allocate, free,
+*  and evaluate random number Generators.
+*
+*  @ingroup Math
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:23 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+#include <time.h>
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+#include "psMemory.h"
+#include "psRandom.h"
+#include "psScalar.h"
+#include "psError.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+psU64 p_psRandomGetSystemSeed()
+{
+    FILE*  fd;
+    psU64  seedVal = 0;
+    time_t timeVal;
+
+    fd = fopen("/dev/urandom","r");
+    if(fd == NULL) {
+        // Read system clock to get seed
+        seedVal = (psU64)time(&timeVal);
+    } else {
+        // Read urandom to get seed
+        fread(&seedVal, sizeof(psU64),1,fd);
+        // Close file
+        fclose(fd);
+    }
+
+    // Send log message of the system seed value used
+    psLogMsg(__func__,PS_LOG_INFO,"System random seed value used  seed = %llX hex",seedVal);
+
+    return seedVal;
+}
+
+psRandom *psRandomAlloc(psRandomType type,
+                        psU64 seed)
+{
+    gsl_rng   *r      = NULL;
+    psRandom  *myRNG  = NULL;
+
+    switch (type) {
+    case PS_RANDOM_TAUS:
+        myRNG = (psRandom*)psAlloc(sizeof(psRandom));
+        r = gsl_rng_alloc(gsl_rng_taus);
+        myRNG->gsl = r;
+        if(seed == 0) {
+            gsl_rng_set(myRNG->gsl,p_psRandomGetSystemSeed());
+        }
+        break;
+
+    default:
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_UNKNOWN_RANDFOM_NUMBER_GENERATOR_TYPE);
+        break;
+    }
+    return(myRNG);
+}
+
+void psRandomReset(psRandom *rand,
+                   psU64 seed)
+{
+    // Check null psRandom
+    if(rand==NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+    } else {
+        // Check seed value to see if system seed should be used
+        if(seed == 0) {
+            gsl_rng_set(rand->gsl,p_psRandomGetSystemSeed());
+        } else {
+            gsl_rng_set(rand->gsl, seed);
+        }
+    }
+}
+
+psF64 psRandomUniform(const psRandom *r)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+        return(0);
+    } else {
+        return(gsl_rng_uniform(r->gsl));
+    }
+}
+
+psF64 psRandomGaussian(const psRandom *r)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+        return(0);
+    } else {
+        // XXX: What should sigma be?
+        return(gsl_ran_gaussian(r->gsl, 1.0));
+    }
+}
+
+psF64 psRandomPoisson(const psRandom *r, psF64 mean)
+{
+    // Check null psRandom variable
+    if(r == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                true,
+                PS_ERRORTEXT_psRandom_NULL_RANDOM_VAR);
+        return(0);
+    } else {
+        return((psF64) gsl_ran_poisson(r->gsl, mean));
+    }
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psRandom.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psRandom.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psRandom.h	(revision 22271)
@@ -0,0 +1,59 @@
+/** @file psRandom.h
+*  \brief Random Number Generators
+*  \ingroup Math
+*
+*  This file will hold the prototypes for procedures which allocate, free,
+*  and evaluate random number Generators.
+*
+*  @ingroup Math
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-02-17 19:26:23 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#if !defined(PS_RANDOM_H)
+#define PS_RANDOM_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+/** \addtogroup Math
+ *  \{
+ */
+
+typedef enum {
+    PS_RANDOM_TAUS    ///< A maximally equidistributed combined Tausworthe generator.
+} psRandomType;
+
+typedef struct
+{
+    psRandomType type; ///< The type of RNG
+    gsl_rng *gsl; ///< The RNG itself
+}
+psRandom;
+
+psRandom *psRandomAlloc(psRandomType type,
+                        psU64 seed);
+
+void psRandomReset(psRandom *rand,
+                   psU64 seed);
+
+psF64 psRandomUniform(const psRandom *r);
+psF64 psRandomGaussian(const psRandom *r);
+psF64 psRandomPoisson(const psRandom *r, psF64 mean);
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psSpline.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psSpline.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psSpline.c	(revision 22271)
@@ -0,0 +1,2179 @@
+/** @file  psFunctions.c
+ *
+ *  @brief Contains basic function allocation, deallocation, and evaluation
+ *         routines.
+ *
+ *  This file will hold the functions for allocated, freeing, and evaluating
+ *  polynomials.  It also contains a Gaussian functions.
+ *
+ *  @version $Revision: 1.101 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-11 22:02:16 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: What happens if the polyEval functions are called with data of the wrong
+ *       type?
+ *  XXX: Should the "coeffErr[]" be used as well?  Bug ???.  Ignore coeffErr
+ *
+ *  XXX: In the various polyAlloc(n) functions, n is really the order of the
+ *  polynomial plus 1.  To create a 2nd-order polynomial, n == 3.
+ *
+ *  XXX: potential bug: for a multi-dimensional polynomial with order (m, n)
+ *  the functions in this file currently do not ignore many of the
+ *  coefficients in the coeff matrix that ...
+ *
+ */
+/*****************************************************************************/
+/*  INCLUDE FILES                                                            */
+/*****************************************************************************/
+#include <gsl/gsl_rng.h>
+#include <gsl/gsl_randist.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psTrace.h"
+#include "psError.h"
+#include "psLogMsg.h"
+#include "psFunctions.h"
+#include "psConstants.h"
+
+#include "psDataManipErrors.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+static void polynomial1DFree(psPolynomial1D* myPoly);
+static void polynomial2DFree(psPolynomial2D* myPoly);
+static void polynomial3DFree(psPolynomial3D* myPoly);
+static void polynomial4DFree(psPolynomial4D* myPoly);
+static void dPolynomial1DFree(psDPolynomial1D* myPoly);
+static void dPolynomial2DFree(psDPolynomial2D* myPoly);
+static void dPolynomial3DFree(psDPolynomial3D* myPoly);
+static void dPolynomial4DFree(psDPolynomial4D* myPoly);
+static void spline1DFree(psSpline1D *tmpSpline);
+static psS32 vectorBinDisectF32(psF32 *bins,psS32 numBins,psF32 x);
+static psS32 vectorBinDisectS32(psS32 *bins,psS32 numBins,psS32 x);
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+static void spline1DFree(psSpline1D *tmpSpline)
+{
+    psS32 i;
+
+    if (tmpSpline == NULL) {
+        return;
+    }
+
+    if (tmpSpline->spline != NULL) {
+        for (i=0;i<tmpSpline->n;i++) {
+            psFree((tmpSpline->spline)[i]);
+        }
+        psFree(tmpSpline->spline);
+    }
+
+    if (tmpSpline->p_psDeriv2 != NULL) {
+        psFree(tmpSpline->p_psDeriv2);
+    }
+    psFree(tmpSpline->knots);
+
+    return;
+}
+
+static void polynomial1DFree(psPolynomial1D* myPoly)
+{
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial2DFree(psPolynomial2D* myPoly)
+{
+    psS32 x = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial3DFree(psPolynomial3D* myPoly)
+{
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        for (y = 0; y < myPoly->nY; y++) {
+            psFree(myPoly->coeff[x][y]);
+            psFree(myPoly->coeffErr[x][y]);
+            psFree(myPoly->mask[x][y]);
+        }
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void polynomial4DFree(psPolynomial4D* myPoly)
+{
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (w = 0; w < myPoly->nW; w++) {
+        for (x = 0; x < myPoly->nX; x++) {
+            for (y = 0; y < myPoly->nY; y++) {
+                psFree(myPoly->coeff[w][x][y]);
+                psFree(myPoly->coeffErr[w][x][y]);
+                psFree(myPoly->mask[w][x][y]);
+            }
+            psFree(myPoly->coeff[w][x]);
+            psFree(myPoly->coeffErr[w][x]);
+            psFree(myPoly->mask[w][x]);
+        }
+        psFree(myPoly->coeff[w]);
+        psFree(myPoly->coeffErr[w]);
+        psFree(myPoly->mask[w]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial1DFree(psDPolynomial1D* myPoly)
+{
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial2DFree(psDPolynomial2D* myPoly)
+{
+    //printf("dPolynomial2DFree(): HMMM: myPoly->nX is %d\n", myPoly->nX);
+    //printf("dPolynomial2DFree(): HMMM: myPoly->nY is %d\n", myPoly->nY);
+    for (psS32 x = 0; x < myPoly->nX; x++) {
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial3DFree(psDPolynomial3D* myPoly)
+{
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (x = 0; x < myPoly->nX; x++) {
+        for (y = 0; y < myPoly->nY; y++) {
+            psFree(myPoly->coeff[x][y]);
+            psFree(myPoly->coeffErr[x][y]);
+            psFree(myPoly->mask[x][y]);
+        }
+        psFree(myPoly->coeff[x]);
+        psFree(myPoly->coeffErr[x]);
+        psFree(myPoly->mask[x]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+static void dPolynomial4DFree(psDPolynomial4D* myPoly)
+{
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+
+    for (w = 0; w < myPoly->nW; w++) {
+        for (x = 0; x < myPoly->nX; x++) {
+            for (y = 0; y < myPoly->nY; y++) {
+                psFree(myPoly->coeff[w][x][y]);
+                psFree(myPoly->coeffErr[w][x][y]);
+                psFree(myPoly->mask[w][x][y]);
+            }
+            psFree(myPoly->coeff[w][x]);
+            psFree(myPoly->coeffErr[w][x]);
+            psFree(myPoly->mask[w][x]);
+        }
+        psFree(myPoly->coeff[w]);
+        psFree(myPoly->coeffErr[w]);
+        psFree(myPoly->mask[w]);
+    }
+
+    psFree(myPoly->coeff);
+    psFree(myPoly->coeffErr);
+    psFree(myPoly->mask);
+}
+
+/*****************************************************************************
+createChebyshevPolys(n): this routine takes as input the required order n,
+and returns as output as a pointer to an array of n psPolynomial1D
+structures, corresponding to the first n Chebyshev polynomials.
+ 
+XXX: The output should be static since the Chebyshev polynomials might be
+used frequently and the data structure created here does not contain the
+outer coefficients of the Chebyshev polynomials.
+ *****************************************************************************/
+static psPolynomial1D **createChebyshevPolys(psS32 maxChebyPoly)
+{
+    PS_INT_CHECK_NON_NEGATIVE(maxChebyPoly, NULL);
+
+    psPolynomial1D **chebPolys = NULL;
+
+    chebPolys = (psPolynomial1D **) psAlloc(maxChebyPoly * sizeof(psPolynomial1D *));
+    for (psS32 i = 0; i < maxChebyPoly; i++) {
+        chebPolys[i] = psPolynomial1DAlloc(i + 1, PS_POLYNOMIAL_ORD);
+    }
+
+    // Create the Chebyshev polynomials.
+    // Polynomial i has i-th order.
+    chebPolys[0]->coeff[0] = 1;
+
+    // XXX: Bug 296
+    if (maxChebyPoly > 1) {
+        chebPolys[1]->coeff[1] = 1;
+    }
+    for (psS32 i = 2; i < maxChebyPoly; i++) {
+        for (psS32 j = 0; j < chebPolys[i - 1]->n; j++) {
+            chebPolys[i]->coeff[j + 1] = 2 * chebPolys[i - 1]->coeff[j];
+        }
+        for (psS32 j = 0; j < chebPolys[i - 2]->n; j++) {
+            chebPolys[i]->coeff[j] -= chebPolys[i - 2]->coeff[j];
+        }
+    }
+
+    return (chebPolys);
+}
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF32 ordPolynomial1DEval(psF32 x, const psPolynomial1D* myPoly)
+{
+    psS32 loop_x = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+
+    psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+            "---- Calling ordPolynomial1DEval(%f)\n", x);
+    psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+            "Polynomial order is %d\n", myPoly->n);
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 4,
+                "Polynomial coeff[%d] is %f\n", loop_x, myPoly->coeff[loop_x]);
+    }
+
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        if (myPoly->mask[loop_x] == 0) {
+            psTrace(".psLib.dataManip.psFunctions.ordPolynomial1DEval", 10,
+                    "polysum+= sum*coeff [%f+= (%f * %f)\n", polySum, xSum, myPoly->coeff[loop_x]);
+            polySum += xSum * myPoly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+// XXX: We assume that x is scaled between -1.0 and 1.0;
+static psF32 chebPolynomial1DEval(psF32 x, const psPolynomial1D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    psVector *d;
+    psS32 n;
+    psS32 i;
+    psF32 tmp;
+
+    n = myPoly->n;
+    d = psVectorAlloc(n, PS_TYPE_F32);
+    if(myPoly->mask[n-1] == 0) {
+        d->data.F32[n-1] = myPoly->coeff[n-1];
+    } else {
+        d->data.F32[n-1] = 0.0;
+    }
+    d->data.F32[n-2] = (2.0 * x * d->data.F32[n-1]);
+    if(myPoly->mask[n-2] == 0) {
+        d->data.F32[n-2] += myPoly->coeff[n-2];
+    }
+    for (i=n-3;i>=1;i--) {
+        d->data.F32[i] = (2.0 * x * d->data.F32[i+1]) -
+                         (d->data.F32[i+2]);
+        if(myPoly->mask[i] == 0) {
+            d->data.F32[i] += myPoly->coeff[i];
+        }
+    }
+
+    tmp = (x * d->data.F32[1]) -
+          (d->data.F32[2]);
+    if(myPoly->mask[0] == 0) {
+        tmp += (0.5 * myPoly->coeff[0]);
+    }
+    psFree(d);
+    return(tmp);
+
+    /*
+
+    psS32 n;
+    psS32 i;
+    psF32 tmp;
+    psPolynomial1D **chebPolys = NULL;
+
+    n = myPoly->n;
+    chebPolys = createChebyshevPolys(n);
+
+    tmp = 0.0;
+    for (i=0;i<myPoly->n;i++) {
+        tmp+= (myPoly->coeff[i] * psPolynomial1DEval(x, chebPolys[i]));
+    }
+    tmp-= (myPoly->coeff[0]/2.0);
+
+
+    return(tmp);
+    */
+}
+
+static psF32 ordPolynomial2DEval(psF32 x,
+                                 psF32 y,
+                                 const psPolynomial2D* myPoly)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += ySum * myPoly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial2DEval(psF32 x, psF32 y, const psPolynomial2D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += myPoly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF32 ordPolynomial3DEval(psF32 x, psF32 y, psF32 z, const psPolynomial3D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF32 polySum = 0.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+    psF32 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += zSum * myPoly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial3DEval(psF32 x, psF32 y, psF32 z, const psPolynomial3D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += myPoly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF32 ordPolynomial4DEval(psF32 w, psF32 x, psF32 y, psF32 z, const psPolynomial4D* myPoly)
+{
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF32 polySum = 0.0;
+    psF32 wSum = 1.0;
+    psF32 xSum = 1.0;
+    psF32 ySum = 1.0;
+    psF32 zSum = 1.0;
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        xSum = wSum;
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            ySum = xSum;
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                zSum = ySum;
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += zSum * myPoly->coeff[loop_w][loop_x][loop_y][loop_z];
+                    }
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+        wSum *= w;
+    }
+
+    return(polySum);
+}
+
+static psF32 chebPolynomial4DEval(psF32 w, psF32 x, psF32 y, psF32 z, const psPolynomial4D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(w, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF32 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nW;
+    if (myPoly->nX > maxChebyPoly) {
+        maxChebyPoly = myPoly->nX;
+    }
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += myPoly->coeff[loop_w][loop_x][loop_y][loop_z] *
+                                   psPolynomial1DEval(chebPolys[loop_w], w) *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+/*****************************************************************************
+    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
+ *****************************************************************************/
+static psF64 dOrdPolynomial1DEval(psF64 x, const psDPolynomial1D* myPoly)
+{
+    psS32 loop_x = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->n; loop_x++) {
+        if (myPoly->mask[loop_x] == 0) {
+            polySum += xSum * myPoly->coeff[loop_x];
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+// XXX: You can do this without having to psAlloc() vector d.
+// XXX: How does the mask vector effect Crenshaw's formula?
+static psF64 dChebPolynomial1DEval(psF64 x, const psDPolynomial1D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    psVector *d;
+    psS32 n;
+    psS32 i;
+    psF64 tmp;
+
+    n = myPoly->n;
+    d = psVectorAlloc(n, PS_TYPE_F64);
+    if(myPoly->mask[n-1] == 0) {
+        d->data.F64[n-1] = myPoly->coeff[n-1];
+    } else {
+        d->data.F64[n-1] = 0.0;
+    }
+    d->data.F64[n-2] = (2.0 * x * d->data.F64[n-1]);
+    if(myPoly->mask[n-2] == 0) {
+        d->data.F64[n-2] += myPoly->coeff[n-2];
+    }
+    for (i=n-3;i>=1;i--) {
+        d->data.F64[i] = (2.0 * x * d->data.F64[i+1]) -
+                         (d->data.F64[i+2]);
+        if(myPoly->mask[i] == 0) {
+            d->data.F64[i] += myPoly->coeff[i];
+        }
+    }
+
+    tmp = (x * d->data.F64[1]) -
+          (d->data.F64[2]);
+    if(myPoly->mask[0] == 0) {
+        tmp += (0.5 * myPoly->coeff[0]);
+    }
+
+    psFree(d);
+    return(tmp);
+}
+
+static psF64 dOrdPolynomial2DEval(psF64 x, psF64 y, const psDPolynomial2D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += ySum * myPoly->coeff[loop_x][loop_y];
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial2DEval(psF64 x, psF64 y, const psDPolynomial2D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            if (myPoly->mask[loop_x][loop_y] == 0) {
+                polySum += myPoly->coeff[loop_x][loop_y] *
+                           psPolynomial1DEval(chebPolys[loop_x], x) *
+                           psPolynomial1DEval(chebPolys[loop_y], y);
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 dOrdPolynomial3DEval(psF64 x, psF64 y, psF64 z, const psDPolynomial3D* myPoly)
+{
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        ySum = xSum;
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            zSum = ySum;
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += zSum * myPoly->coeff[loop_x][loop_y][loop_z];
+                }
+                zSum *= z;
+            }
+            ySum *= y;
+        }
+        xSum *= x;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial3DEval(psF64 x, psF64 y, psF64 z, const psDPolynomial3D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nX;
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+        for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+            for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                if (myPoly->mask[loop_x][loop_y][loop_z] == 0) {
+                    polySum += myPoly->coeff[loop_x][loop_y][loop_z] *
+                               psPolynomial1DEval(chebPolys[loop_x], x) *
+                               psPolynomial1DEval(chebPolys[loop_y], y) *
+                               psPolynomial1DEval(chebPolys[loop_z], z);
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+static psF64 dOrdPolynomial4DEval(psF64 w, psF64 x, psF64 y, psF64 z, const psDPolynomial4D* myPoly)
+{
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psF64 polySum = 0.0;
+    psF64 wSum = 1.0;
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    psF64 zSum = 1.0;
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        xSum = wSum;
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            ySum = xSum;
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                zSum = ySum;
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += zSum * myPoly->coeff[loop_w][loop_x][loop_y][loop_z];
+                    }
+                    zSum *= z;
+                }
+                ySum *= y;
+            }
+            xSum *= x;
+        }
+        wSum *= w;
+    }
+
+    return(polySum);
+}
+
+static psF64 dChebPolynomial4DEval(psF64 w, psF64 x, psF64 y, psF64 z, const psDPolynomial4D* myPoly)
+{
+    PS_FLOAT_CHECK_RANGE(w, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(x, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(y, -1.0, 1.0, 0.0);
+    PS_FLOAT_CHECK_RANGE(z, -1.0, 1.0, 0.0);
+    psS32 loop_w = 0;
+    psS32 loop_x = 0;
+    psS32 loop_y = 0;
+    psS32 loop_z = 0;
+    psS32 i = 0;
+    psF64 polySum = 0.0;
+    psPolynomial1D* *chebPolys = NULL;
+    psS32 maxChebyPoly = 0;
+
+    // Determine how many Chebyshev polynomials
+    // are needed, then create them.
+    maxChebyPoly = myPoly->nW;
+    if (myPoly->nX > maxChebyPoly) {
+        maxChebyPoly = myPoly->nX;
+    }
+    if (myPoly->nY > maxChebyPoly) {
+        maxChebyPoly = myPoly->nY;
+    }
+    if (myPoly->nZ > maxChebyPoly) {
+        maxChebyPoly = myPoly->nZ;
+    }
+    chebPolys = createChebyshevPolys(maxChebyPoly);
+
+    for (loop_w = 0; loop_w < myPoly->nW; loop_w++) {
+        for (loop_x = 0; loop_x < myPoly->nX; loop_x++) {
+            for (loop_y = 0; loop_y < myPoly->nY; loop_y++) {
+                for (loop_z = 0; loop_z < myPoly->nZ; loop_z++) {
+                    if (myPoly->mask[loop_w][loop_x][loop_y][loop_z] == 0) {
+                        polySum += myPoly->coeff[loop_w][loop_x][loop_y][loop_z] *
+                                   psPolynomial1DEval(chebPolys[loop_w], w) *
+                                   psPolynomial1DEval(chebPolys[loop_x], x) *
+                                   psPolynomial1DEval(chebPolys[loop_y], y) *
+                                   psPolynomial1DEval(chebPolys[loop_z], z);
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0;i<maxChebyPoly;i++) {
+        psFree(chebPolys[i]);
+    }
+    psFree(chebPolys);
+    return(polySum);
+}
+
+
+/*****************************************************************************
+fullInterpolate1DF32(): This routine will take as input n-element floating
+point arrays domain and range, and the x value, assumed to lie with the
+domain vector.  It produces as output the (n-1)-order LaGrange interpolated
+value of x.
+ 
+XXX: do we error check for non-distinct domain values?
+ *****************************************************************************/
+#define FUNC_MACRO_FULL_INTERPOLATE_1D(TYPE) \
+static psF32 fullInterpolate1D##TYPE(ps##TYPE *domain, \
+                                     ps##TYPE *range, \
+                                     psS32 n, \
+                                     ps##TYPE x) \
+{ \
+    \
+    psS32 i; \
+    psS32 m; \
+    static psVector *p = NULL; \
+    p = psVectorRecycle(p, n, PS_TYPE_##TYPE); \
+    p_psMemSetPersistent(p, true); \
+    p_psMemSetPersistent(p->data.TYPE, true); \
+    \
+    psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 4, \
+            "---- fullInterpolate1D##TYPE() begin (%d-order at x=%f) (%d data points)----\n", n-1, x, n); \
+    \
+    for (i=0;i<n;i++) { \
+        psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                "domain/range is (%f %f)\n", domain[i], range[i]); \
+    } \
+    \
+    for (i=0;i<n;i++) { \
+        p->data.TYPE[i] = range[i]; \
+        psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                "p->data.TYPE[%d] is %f\n", i, p->data.TYPE[i]); \
+        \
+    } \
+    \
+    /* From NR, during each iteration of the m loop, we are computing the \
+       p_{i ... i+m} terms. \
+    */ \
+    for (m=1;m<n;m++) { \
+        for (i=0;i<n-m;i++) { \
+            /* From NR: we are computing P_{i ... i+m} \
+             */ \
+            p->data.TYPE[i] = (((x-domain[i+m]) * p->data.TYPE[i]) + \
+                               ((domain[i]-x) * p->data.TYPE[i+1])) / \
+                              (domain[i] - domain[i+m]); \
+            /*printf("((%f-%f * %f) + (%f-%f * %f)) / (%f - %f)\n", x, domain[i+m], p->data.TYPE[i], domain[i], x, p->data.TYPE[i+1], domain[i], domain[i+m]); \
+             */ \
+            psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 6, \
+                    "p->data.TYPE[%d] is %f\n", i, p->data.TYPE[i]); \
+        } \
+    } \
+    psTrace(".psLib.dataManip.psFunctions.fullInterpolate1D##TYPE", 4, \
+            "---- fullInterpolate1D##TYPE() end ----\n"); \
+    \
+    return(p->data.TYPE[0]); \
+} \
+
+/*
+FUNC_MACRO_FULL_INTERPOLATE_1D(U8)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U16)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U32)
+FUNC_MACRO_FULL_INTERPOLATE_1D(U64)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S8)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S16)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S32)
+FUNC_MACRO_FULL_INTERPOLATE_1D(S64)
+FUNC_MACRO_FULL_INTERPOLATE_1D(F64)
+*/
+FUNC_MACRO_FULL_INTERPOLATE_1D(F32)
+
+
+/*****************************************************************************
+interpolate1DF32(): this is the base 1-D flat memory routine to perform
+LaGrange interpolation.
+ *****************************************************************************/
+static psF32 interpolate1DF32(psF32 *domain,
+                              psF32 *range,
+                              psS32 n,
+                              psS32 order,
+                              psF32 x)
+{
+    PS_PTR_CHECK_NULL(domain, NAN)
+    PS_PTR_CHECK_NULL(range, NAN)
+    // XXX: Check valid values for n, order, and x?
+
+    psS32 binNum;
+    psS32 numIntPoints = order+1;
+    psS32 origin;
+
+    psTrace(".psLib.dataManip.psFunctions.interpolate1DF32", 4,
+            "---- interpolate1DF32() begin ----\n");
+
+    binNum = vectorBinDisectF32(domain, n, x);
+
+    if (0 == numIntPoints%2) {
+        origin = binNum - ((numIntPoints/2) - 1);
+    } else {
+        origin = binNum - (numIntPoints/2);
+        if ((x-domain[binNum]) > (domain[binNum+1]-x)) {
+            // x is closer to binNum+1.
+            origin = 1 + (binNum - (numIntPoints/2));
+        }
+    }
+    if (origin < 0) {
+        origin = 0;
+    }
+    if ((origin + numIntPoints) > n) {
+        origin = n - numIntPoints;
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.interpolate1DF32", 4,
+            "---- interpolate1DF32() end ----\n");
+    return(fullInterpolate1DF32(&domain[origin], &range[origin], order+1, x));
+}
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - PUBLIC                                         */
+/*****************************************************************************/
+
+/*****************************************************************************
+    Evaluate a non-normalized Gaussian with the given mean and sigma at the
+    given coordianate.  Note that this is not a Gaussian deviate.  The
+    evaluated Gaussian is: \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f]
+ *****************************************************************************/
+psF32 psGaussian(psF32 x, psF32 mean, psF32 sigma, psBool normal)
+{
+    psF32 tmp = 1.0;
+
+    psTrace(".psLib.dataManip.psFunctions.psGaussian", 4,
+            "---- psGaussian() begin ----\n");
+
+    if (normal == true) {
+        tmp = 1.0 / PS_SQRT_F32(2.0 * M_PI * (sigma * sigma));
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.psGaussian", 4,
+            "---- psGaussian() end ----\n");
+    return(tmp * exp(-((x - mean) * (x - mean)) / (2.0 * sigma * sigma)));
+}
+
+/*****************************************************************************
+    p_psGaussianDev()
+ This private routine (formerly a psLib API routine) creates a psVector of the
+ specified size and type F32 and fills it with a random Gaussian distribution
+ of numbers with the specified mean and sigma.  This routine makes use of the
+ GSL routines for generating both uniformly distributed numbers and the
+ Gaussian distribution as well.
+ 
+XXX: There is no way to seed the random generator.
+ *****************************************************************************/
+psVector* p_psGaussianDev(psF32 mean, psF32 sigma, psS32 Npts)
+{
+    PS_INT_CHECK_NON_NEGATIVE(Npts, NULL);
+
+    psVector* gauss = NULL;
+    const gsl_rng_type *T = NULL;
+    gsl_rng *r = NULL;
+    psS32 i = 0;
+
+
+    gauss = psVectorAlloc(Npts, PS_TYPE_F32);
+    gauss->n = Npts;
+    gsl_rng_env_setup();
+    T = gsl_rng_default;
+    r = gsl_rng_alloc(T);
+
+    for (i = 0; i < Npts; i++) {
+        gauss->data.F32[i] = mean + gsl_ran_gaussian(r, sigma);
+    }
+
+    // XXX: Should I free r, T as well?  This is a memory leak.
+    return(gauss);
+}
+
+/*****************************************************************************
+    This routine must allocate memory for the polynomial structures.
+ *****************************************************************************/
+psPolynomial1D* psPolynomial1DAlloc(psS32 n,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+
+    psS32 i = 0;
+    psPolynomial1D* newPoly = NULL;
+
+    newPoly = (psPolynomial1D* ) psAlloc(sizeof(psPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->n = n;
+    newPoly->coeff = (psF32 *)psAlloc(n * sizeof(psF32));
+    newPoly->coeffErr = (psF32 *)psAlloc(n * sizeof(psF32));
+    newPoly->mask = (psU8 *)psAlloc(n * sizeof(psU8));
+    for (i = 0; i < n; i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->mask[i] = 0;
+    }
+
+    return(newPoly);
+}
+
+psPolynomial2D* psPolynomial2DAlloc(psS32 nX, psS32 nY,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psPolynomial2D* newPoly = NULL;
+
+    newPoly = (psPolynomial2D* ) psAlloc(sizeof(psPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = (psF32 **)psAlloc(nX * sizeof(psF32 *));
+    newPoly->coeffErr = (psF32 **)psAlloc(nX * sizeof(psF32 *));
+    newPoly->mask = (psU8 **)psAlloc(nX * sizeof(psU8 *));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF32 *)psAlloc(nY * sizeof(psF32));
+        newPoly->coeffErr[x] = (psF32 *)psAlloc(nY * sizeof(psF32));
+        newPoly->mask[x] = (psU8 *)psAlloc(nY * sizeof(psU8));
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->mask[x][y] = 0;
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial3D* psPolynomial3DAlloc(psS32 nX, psS32 nY, psS32 nZ,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psPolynomial3D* newPoly = NULL;
+
+    newPoly = (psPolynomial3D* ) psAlloc(sizeof(psPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+    newPoly->coeffErr = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+    newPoly->mask = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+        newPoly->coeffErr[x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+        newPoly->mask[x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+            newPoly->coeffErr[x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+            newPoly->mask[x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+        }
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            for (z = 0; z < nZ; z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->mask[x][y][z] = 0;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psPolynomial4D* psPolynomial4DAlloc(psS32 nW, psS32 nX, psS32 nY, psS32 nZ,
+                                    psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nW, NULL);
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psPolynomial4D* newPoly = NULL;
+
+    newPoly = (psPolynomial4D* ) psAlloc(sizeof(psPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) polynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nW = nW;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF32 ****)psAlloc(nW * sizeof(psF32 ***));
+    newPoly->coeffErr = (psF32 ****)psAlloc(nW * sizeof(psF32 ***));
+    newPoly->mask = (psU8 ****)psAlloc(nW * sizeof(psU8 ***));
+    for (w = 0; w < nW; w++) {
+        newPoly->coeff[w] = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+        newPoly->coeffErr[w] = (psF32 ***)psAlloc(nX * sizeof(psF32 **));
+        newPoly->mask[w] = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+        for (x = 0; x < nX; x++) {
+            newPoly->coeff[w][x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+            newPoly->coeffErr[w][x] = (psF32 **)psAlloc(nY * sizeof(psF32 *));
+            newPoly->mask[w][x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+            for (y = 0; y < nY; y++) {
+                newPoly->coeff[w][x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+                newPoly->coeffErr[w][x][y] = (psF32 *)psAlloc(nZ * sizeof(psF32));
+                newPoly->mask[w][x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+            }
+        }
+    }
+    for (w = 0; w < nW; w++) {
+        for (x = 0; x < nX; x++) {
+            for (y = 0; y < nY; y++) {
+                for (z = 0; z < nZ; z++) {
+                    newPoly->coeff[w][x][y][z] = 0.0;
+                    newPoly->coeffErr[w][x][y][z] = 0.0;
+                    newPoly->mask[w][x][y][z] = 0;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psF32 psPolynomial1DEval(const psPolynomial1D* myPoly, psF32 x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial1DEval(x, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial1DEval(x, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial1DEvalVector(const psPolynomial1D *myPoly,
+                                   const psVector *x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+
+    tmp = psVectorAlloc(x->n, PS_TYPE_F32);
+    for (psS32 i=0;i<x->n;i++) {
+        tmp->data.F32[i] = psPolynomial1DEval(myPoly, x->data.F32[i]);
+    }
+
+    return(tmp);
+}
+
+psF32 psPolynomial2DEval(const psPolynomial2D* myPoly, psF32 x, psF32 y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial2DEval(x, y, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial2DEval(x, y, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial2DEvalVector(const psPolynomial2D *myPoly,
+                                   const psVector *x,
+                                   const psVector *y)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the length of the output vector to by the minimum of the x,y vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    // Create output vector to return
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate the polynomial at the specified points
+    for (psS32 i=0; i<vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial2DEval(myPoly,x->data.F32[i],y->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF32 psPolynomial3DEval(const psPolynomial3D* myPoly, psF32 x, psF32 y, psF32 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial3DEval(x, y, z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial3DEval(x, y, z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial3DEvalVector(const psPolynomial3D *myPoly,
+                                   const psVector *x,
+                                   const psVector *y,
+                                   const psVector *z)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the length of output vector from min of the input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial3DEval(myPoly,
+                                              x->data.F32[i],
+                                              y->data.F32[i],
+                                              z->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF32 psPolynomial4DEval(const psPolynomial4D* myPoly, psF32 w, psF32 x, psF32 y, psF32 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(ordPolynomial4DEval(w,x,y,z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(chebPolynomial4DEval(w,x,y,z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psPolynomial4DEvalVector(const psPolynomial4D *myPoly,
+                                   const psVector *w,
+                                   const psVector *x,
+                                   const psVector *y,
+                                   const psVector *z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(w, NULL);
+    PS_VECTOR_CHECK_TYPE(w, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F32, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F32, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=w->n;
+
+    // Determine output vector size from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (x->n < vecLen) {
+        vecLen = x->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F32[i] = psPolynomial4DEval(myPoly,
+                                              w->data.F32[i],
+                                              x->data.F32[i],
+                                              y->data.F32[i],
+                                              z->data.F32[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+psDPolynomial1D* psDPolynomial1DAlloc(psS32 n,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+
+    psS32 i = 0;
+    psDPolynomial1D* newPoly = NULL;
+
+    newPoly = (psDPolynomial1D* ) psAlloc(sizeof(psDPolynomial1D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial1DFree);
+
+    newPoly->type = type;
+    newPoly->n = n;
+    newPoly->coeff = (psF64 *)psAlloc(n * sizeof(psF64));
+    newPoly->coeffErr = (psF64 *)psAlloc(n * sizeof(psF64));
+    newPoly->mask = (psU8 *)psAlloc(n * sizeof(psU8));
+    for (i = 0; i < n; i++) {
+        newPoly->coeff[i] = 0.0;
+        newPoly->coeffErr[i] = 0.0;
+        newPoly->mask[i] = 0;
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial2D* psDPolynomial2DAlloc(psS32 nX, psS32 nY,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psDPolynomial2D* newPoly = NULL;
+
+    newPoly = (psDPolynomial2D* ) psAlloc(sizeof(psDPolynomial2D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial2DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+
+    newPoly->coeff = (psF64 **)psAlloc(nX * sizeof(psF64 *));
+    newPoly->coeffErr = (psF64 **)psAlloc(nX * sizeof(psF64 *));
+    newPoly->mask = (psU8 **)psAlloc(nX * sizeof(psU8 *));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF64 *)psAlloc(nY * sizeof(psF64));
+        newPoly->coeffErr[x] = (psF64 *)psAlloc(nY * sizeof(psF64));
+        newPoly->mask[x] = (psU8 *)psAlloc(nY * sizeof(psU8));
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = 0.0;
+            newPoly->coeffErr[x][y] = 0.0;
+            newPoly->mask[x][y] = 0;
+        }
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial3D* psDPolynomial3DAlloc(psS32 nX, psS32 nY, psS32 nZ,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psDPolynomial3D* newPoly = NULL;
+
+    newPoly = (psDPolynomial3D* ) psAlloc(sizeof(psDPolynomial3D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial3DFree);
+
+    newPoly->type = type;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+    newPoly->coeffErr = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+    newPoly->mask = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+    for (x = 0; x < nX; x++) {
+        newPoly->coeff[x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+        newPoly->coeffErr[x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+        newPoly->mask[x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+        for (y = 0; y < nY; y++) {
+            newPoly->coeff[x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+            newPoly->coeffErr[x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+            newPoly->mask[x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+        }
+    }
+    for (x = 0; x < nX; x++) {
+        for (y = 0; y < nY; y++) {
+            for (z = 0; z < nZ; z++) {
+                newPoly->coeff[x][y][z] = 0.0;
+                newPoly->coeffErr[x][y][z] = 0.0;
+                newPoly->mask[x][y][z] = 0;
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+psDPolynomial4D* psDPolynomial4DAlloc(psS32 nW, psS32 nX, psS32 nY, psS32 nZ,
+                                      psPolynomialType type)
+{
+    PS_INT_CHECK_POSITIVE(nW, NULL);
+    PS_INT_CHECK_POSITIVE(nX, NULL);
+    PS_INT_CHECK_POSITIVE(nY, NULL);
+    PS_INT_CHECK_POSITIVE(nZ, NULL);
+
+    psS32 w = 0;
+    psS32 x = 0;
+    psS32 y = 0;
+    psS32 z = 0;
+    psDPolynomial4D* newPoly = NULL;
+
+    newPoly = (psDPolynomial4D* ) psAlloc(sizeof(psDPolynomial4D));
+    psMemSetDeallocator(newPoly, (psFreeFcn) dPolynomial4DFree);
+
+    newPoly->type = type;
+    newPoly->nW = nW;
+    newPoly->nX = nX;
+    newPoly->nY = nY;
+    newPoly->nZ = nZ;
+
+    newPoly->coeff = (psF64 ****)psAlloc(nW * sizeof(psF64 ***));
+    newPoly->coeffErr = (psF64 ****)psAlloc(nW * sizeof(psF64 ***));
+    newPoly->mask = (psU8 ****)psAlloc(nW * sizeof(psU8 ***));
+    for (w = 0; w < nW; w++) {
+        newPoly->coeff[w] = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+        newPoly->coeffErr[w] = (psF64 ***)psAlloc(nX * sizeof(psF64 **));
+        newPoly->mask[w] = (psU8 ***)psAlloc(nX * sizeof(psU8 **));
+        for (x = 0; x < nX; x++) {
+            newPoly->coeff[w][x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+            newPoly->coeffErr[w][x] = (psF64 **)psAlloc(nY * sizeof(psF64 *));
+            newPoly->mask[w][x] = (psU8 **)psAlloc(nY * sizeof(psU8 *));
+            for (y = 0; y < nY; y++) {
+                newPoly->coeff[w][x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+                newPoly->coeffErr[w][x][y] = (psF64 *)psAlloc(nZ * sizeof(psF64));
+                newPoly->mask[w][x][y] = (psU8 *)psAlloc(nZ * sizeof(psU8));
+            }
+        }
+    }
+    for (w = 0; w < nW; w++) {
+        for (x = 0; x < nX; x++) {
+            for (y = 0; y < nY; y++) {
+                for (z = 0; z < nZ; z++) {
+                    newPoly->coeff[w][x][y][z] = 0.0;
+                    newPoly->coeffErr[w][x][y][z] = 0.0;
+                    newPoly->mask[w][x][y][z] = 0;
+                }
+            }
+        }
+    }
+
+    return(newPoly);
+}
+
+
+psF64 psDPolynomial1DEval(const psDPolynomial1D* myPoly, psF64 x)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial1DEval(x, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial1DEval(x, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial1DEvalVector(const psDPolynomial1D *myPoly,
+                                    const psVector *x)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+
+    tmp = psVectorAlloc(x->n, PS_TYPE_F64);
+    for (psS32 i=0;i<x->n;i++) {
+        tmp->data.F64[i] = psDPolynomial1DEval(myPoly,
+                                               x->data.F64[i]);
+    }
+
+    return(tmp);
+}
+
+
+psF64 psDPolynomial2DEval(const psDPolynomial2D* myPoly,
+                          psF64 x,
+                          psF64 y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial2DEval(x, y, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial2DEval(x, y, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial2DEvalVector(const psDPolynomial2D *myPoly,
+                                    const psVector *x,
+                                    const psVector *y)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the output vector length from minimum length of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate the polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial2DEval(myPoly,x->data.F64[i],y->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+psF64 psDPolynomial3DEval(const psDPolynomial3D* myPoly,
+                          psF64 x,
+                          psF64 y,
+                          psF64 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial3DEval(x, y, z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial3DEval(x, y, z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial3DEvalVector(const psDPolynomial3D *myPoly,
+                                    const psVector *x,
+                                    const psVector *y,
+                                    const psVector *z)
+
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=x->n;
+
+    // Determine the size of output vector from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial3DEval(myPoly,
+                                               x->data.F64[i],
+                                               y->data.F64[i],
+                                               z->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+psF64 psDPolynomial4DEval(const psDPolynomial4D* myPoly,
+                          psF64 w,
+                          psF64 x,
+                          psF64 y,
+                          psF64 z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+
+    if (myPoly->type == PS_POLYNOMIAL_ORD) {
+        return(dOrdPolynomial4DEval(w,x,y,z, myPoly));
+    } else if (myPoly->type == PS_POLYNOMIAL_CHEB) {
+        return(dChebPolynomial4DEval(w,x,y,z, myPoly));
+    } else {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psFunctions_INVALID_POLYNOMIAL_TYPE,
+                myPoly->type);
+    }
+    return(NAN);
+}
+
+psVector *psDPolynomial4DEvalVector(const psDPolynomial4D *myPoly,
+                                    const psVector *w,
+                                    const psVector *x,
+                                    const psVector *y,
+                                    const psVector *z)
+{
+    PS_POLY_CHECK_NULL(myPoly, NULL);
+    PS_VECTOR_CHECK_NULL(w, NULL);
+    PS_VECTOR_CHECK_TYPE(w, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE(x, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(y, NULL);
+    PS_VECTOR_CHECK_TYPE(y, PS_TYPE_F64, NULL);
+    PS_VECTOR_CHECK_NULL(z, NULL);
+    PS_VECTOR_CHECK_TYPE(z, PS_TYPE_F64, NULL);
+
+    psVector *tmp;
+    psS32 vecLen=w->n;
+
+    // Determine the output vector size from min of input vectors
+    if (y->n < vecLen) {
+        vecLen = y->n;
+    }
+    if (x->n < vecLen) {
+        vecLen = x->n;
+    }
+    if (z->n < vecLen) {
+        vecLen = z->n;
+    }
+
+    // Allocate output vector
+    tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
+
+    // Evaluate the polynomial
+    for (psS32 i = 0; i < vecLen; i++) {
+        tmp->data.F64[i] = psDPolynomial4DEval(myPoly,
+                                               w->data.F64[i],
+                                               x->data.F64[i],
+                                               y->data.F64[i],
+                                               z->data.F64[i]);
+    }
+
+    // Return output vector
+    return(tmp);
+}
+
+
+
+
+//typedef struct {
+//    psS32 n;
+//    psPolynomial1D **spline;
+//    psF32 *p_psDeriv2;
+//    psVector *knots;
+//} psSpline1D;
+
+/*****************************************************************************
+    NOTE: "n" specifies the number of spline polynomials.  Therefore, there
+    must exist n+1 points in "knots".
+ 
+XXX: Ensure that domain[i+1] != domain[i]
+ 
+XXX: What should be the defualty type for knots be?  psF32 is assumed.
+ *****************************************************************************/
+psSpline1D *psSpline1DAlloc(psS32 numSplines,
+                            psS32 order,
+                            psF32 min,
+                            psF32 max)
+{
+    PS_INT_CHECK_NON_NEGATIVE(numSplines, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+    PS_FLOAT_CHECK_NON_EQUAL(max, min, NULL);
+
+    psSpline1D *tmp = NULL;
+    psS32 i;
+    psF32 tmpDomain;
+    psF32 width;
+
+    tmp = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+    tmp->n = numSplines;
+
+    tmp->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (i=0;i<numSplines;i++) {
+        (tmp->spline)[i] = psPolynomial1DAlloc(order+1, PS_POLYNOMIAL_ORD);
+    }
+
+    // This should be set by the psVectorFitSpline1D()
+    tmp->p_psDeriv2 = NULL;
+
+    tmp->knots = psVectorAlloc(numSplines+1, PS_TYPE_F32);
+    width = (max - min) / ((psF32) numSplines);
+
+    tmp->knots->data.F32[0] = min;
+    tmpDomain = min+width;
+    for (i=1;i<numSplines+1;i++) {
+        tmp->knots->data.F32[i] = tmpDomain;
+        tmpDomain+= width;
+    }
+    tmp->knots->data.F32[numSplines] = max;
+
+    psMemSetDeallocator(tmp,(psFreeFcn)spline1DFree);
+    return(tmp);
+}
+
+
+/*****************************************************************************
+XXX: What should be the defualty type for knots be?  psF32 is assumed.
+ *****************************************************************************/
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds,
+                                   psS32 order)
+{
+    PS_VECTOR_CHECK_NULL(bounds, NULL);
+    PS_VECTOR_CHECK_EMPTY(bounds, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+
+    psSpline1D *tmp = NULL;
+    psS32 i;
+    psS32 numSplines;
+
+    tmp = (psSpline1D *) psAlloc(sizeof(psSpline1D));
+
+    numSplines = bounds->n - 1;
+    tmp->n = numSplines;
+
+    tmp->spline = (psPolynomial1D **) psAlloc(numSplines * sizeof(psPolynomial1D *));
+    for (i=0;i<numSplines;i++) {
+        (tmp->spline)[i] = psPolynomial1DAlloc(order+1, PS_POLYNOMIAL_ORD);
+    }
+
+    // This should be set by the psVectorFitSpline1D()
+    tmp->p_psDeriv2 = NULL;
+
+    tmp->knots = psVectorAlloc(bounds->n, PS_TYPE_F32);
+
+    for (i=0;i<bounds->n;i++) {
+        tmp->knots->data.F32[i] = bounds->data.F32[i];
+        if (i<(bounds->n-1)) {
+            if (FLT_EPSILON >= fabs(bounds->data.F32[i+1]-bounds->data.F32[i])) {
+                psError(PS_ERR_UNKNOWN, true, "data points must be distinct\n");
+            }
+        }
+    }
+
+    psMemSetDeallocator(tmp,(psFreeFcn)spline1DFree);
+    return(tmp);
+}
+
+/*****************************************************************************
+vectorBinDisectF32(): This is a macro for a private function which takes as
+input a vector an array of data as well as a single value for that data.  The
+input vector values are assumed to be non-decreasing (v[i-1] <= v[i] for all
+i).  This routine does a binary disection of the vector and returns "i" such
+that (v[i] <= x <= v[i+1).  If x lies outside the range of v[], then this
+routine prints a warning message and returns (-2 or -1).
+ *****************************************************************************/
+#define FUNC_MACRO_VECTOR_BIN_DISECT(TYPE) \
+static psS32 vectorBinDisect##TYPE(ps##TYPE *bins, \
+                                   psS32 numBins, \
+                                   ps##TYPE x) \
+{ \
+    psS32 min; \
+    psS32 max; \
+    psS32 mid; \
+    \
+    psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+            "---- Calling vectorBinDisect##TYPE(%f)\n", x); \
+    \
+    if (x < bins[0]) { \
+        psLogMsg(__func__, PS_LOG_WARN, \
+                 "vectorBinDisect%s(): ordinate %f is outside vector range (%f - %f).", \
+                 #TYPE, x, bins[0], bins[numBins-1]); \
+        return(-2); \
+    } \
+    \
+    if (x > bins[numBins-1]) { \
+        psLogMsg(__func__, PS_LOG_WARN, \
+                 "vectorBinDisect%s(): ordinate %f is outside vector range (%f - %f).", \
+                 #TYPE, x, bins[0], bins[numBins-1]); \
+        return(-1); \
+    } \
+    \
+    min = 0; \
+    max = numBins-2; \
+    mid = ((max+1)-min)/2; \
+    \
+    while (min != max) { \
+        psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+                "(min, mid, max) is (%d, %d, %d): (x, bins) is (%f, %f)\n", \
+                min, mid, max, x, bins[mid]); \
+        \
+        if (x == bins[mid]) { \
+            psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+                    "---- Exiting vectorBinDisect##TYPE(): bin %d\n", mid); \
+            return(mid); \
+        } else if (x < bins[mid]) { \
+            max = mid-1; \
+        } else { \
+            min = mid; \
+        } \
+        mid = ((max+1)+min)/2; \
+    } \
+    \
+    psTrace(".psLib.dataManip.psFunctions.vectorBinDisect##TYPE", 4, \
+            "---- Exiting vectorBinDisect##TYPE(): bin %d\n", min); \
+    return(min); \
+} \
+
+FUNC_MACRO_VECTOR_BIN_DISECT(S8)
+FUNC_MACRO_VECTOR_BIN_DISECT(S16)
+FUNC_MACRO_VECTOR_BIN_DISECT(S32)
+FUNC_MACRO_VECTOR_BIN_DISECT(S64)
+FUNC_MACRO_VECTOR_BIN_DISECT(U8)
+FUNC_MACRO_VECTOR_BIN_DISECT(U16)
+FUNC_MACRO_VECTOR_BIN_DISECT(U32)
+FUNC_MACRO_VECTOR_BIN_DISECT(U64)
+FUNC_MACRO_VECTOR_BIN_DISECT(F32)
+FUNC_MACRO_VECTOR_BIN_DISECT(F64)
+
+/*****************************************************************************
+p_psVectorBinDisect(): A wrapper to the above p_psVectorBinDisect().
+ *****************************************************************************/
+psS32 p_psVectorBinDisect(psVector *bins,
+                          psScalar *x)
+{
+    PS_VECTOR_CHECK_NULL(bins, -4);
+    PS_VECTOR_CHECK_EMPTY(bins, -4);
+    PS_PTR_CHECK_NULL(x, -6);
+    PS_PTR_CHECK_TYPE_EQUAL(x, bins, -3);
+    char* strType;
+
+    switch (x->type.type) {
+    case PS_TYPE_U8:
+        return(vectorBinDisectU8(bins->data.U8, bins->n, x->data.U8));
+    case PS_TYPE_U16:
+        return(vectorBinDisectU16(bins->data.U16, bins->n, x->data.U16));
+    case PS_TYPE_U32:
+        return(vectorBinDisectU32(bins->data.U32, bins->n, x->data.U32));
+    case PS_TYPE_U64:
+        return(vectorBinDisectU64(bins->data.U64, bins->n, x->data.U64));
+    case PS_TYPE_S8:
+        return(vectorBinDisectS8(bins->data.S8, bins->n, x->data.S8));
+    case PS_TYPE_S16:
+        return(vectorBinDisectS16(bins->data.S16, bins->n, x->data.S16));
+    case PS_TYPE_S32:
+        return(vectorBinDisectS32(bins->data.S32, bins->n, x->data.S32));
+    case PS_TYPE_S64:
+        return(vectorBinDisectS64(bins->data.S64, bins->n, x->data.S64));
+    case PS_TYPE_F32:
+        return(vectorBinDisectF32(bins->data.F32, bins->n, x->data.F32));
+    case PS_TYPE_F64:
+        return(vectorBinDisectF64(bins->data.F64, bins->n, x->data.F64));
+    case PS_TYPE_C32:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    case PS_TYPE_C64:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    case PS_TYPE_BOOL:
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return 0;
+    }
+    return(-3);
+}
+
+/*****************************************************************************
+p_psVectorInterpolate(): This routine will take as input psVectors domain and
+range, and the x value, assumed to lie with the domain vector.  It produces
+as output the LaGrange interpolated value of a polynomial of the specified
+order around the point x.
+ 
+XXX: This stuff does not currently work with a mask.
+ 
+XXX: add another psScalar argument for the result.
+ 
+XXX: The VectorCopy routines seg fault when I declare range32 as static.
+ *****************************************************************************/
+psScalar *p_psVectorInterpolate(psVector *domain,
+                                psVector *range,
+                                psS32 order,
+                                psScalar *x)
+{
+    PS_VECTOR_CHECK_NULL(domain, NULL);
+    PS_VECTOR_CHECK_NULL(range, NULL);
+    PS_PTR_CHECK_NULL(x, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(order, NULL);
+    PS_VECTOR_CHECK_SIZE_EQUAL(domain, range, NULL);
+    PS_PTR_CHECK_TYPE_EQUAL(domain, range, NULL);
+    PS_PTR_CHECK_TYPE_EQUAL(domain, x, NULL);
+
+    psVector *range32 = NULL;
+    psVector *domain32 = NULL;
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "---- p_psVectorInterpolate() begin ----\n");
+
+    if (order > (domain->n - 1)) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psFunctions_NOT_ENOUGH_DATAPOINTS,
+                order);
+        return(NULL);
+    }
+
+    if (x->type.type == PS_TYPE_F32) {
+        psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+                "---- p_psVectorInterpolate() end ----\n");
+        return(psScalarAlloc(interpolate1DF32(domain->data.F32,
+                                              range->data.F32,
+                                              domain->n,
+                                              order,
+                                              x->data.F32), PS_TYPE_F32));
+    } else if (x->type.type == PS_TYPE_F64) {
+        // XXX: use recycled vectors here.
+        range32 = psVectorCopy(range32, range, PS_TYPE_F32);
+        domain32 = psVectorCopy(domain32, domain, PS_TYPE_F32);
+
+        psScalar *tmpScalar = psScalarAlloc((psF64)
+                                            interpolate1DF32(domain32->data.F32,
+                                                             range32->data.F32,
+                                                             domain32->n,
+                                                             order,
+                                                             (psF32) x->data.F64), PS_TYPE_F64);
+        psFree(range32);
+        psFree(domain32);
+
+        psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+                "---- p_psVectorInterpolate() end ----\n");
+        // XXX: Convert data type to F64?
+        return(tmpScalar);
+
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+    }
+
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "return(NULL)\n");
+    psTrace(".psLib.dataManip.psFunctions.p_psVectorInterpolate", 4,
+            "---- p_psVectorInterpolate() end ----\n");
+
+    return(NULL);
+}
+
+
+/*****************************************************************************
+psSpline1DEval(): this routine takes an existing spline of arbitrary order
+and an independent x value.  Each determines which spline that x corresponds
+to by doing a bracket disection on the knots of the spline data structure
+(vectorBinDisectF32()).  Then it evaluates the spline at that x location
+by a call to the 1D polynomial functions.
+ 
+XXX: The spline eval functions require input and output to be F32.  however
+     the spline fit functions require F32 and F64.
+ 
+XXX: This only works if spline0>knots if psF32.  Must add support for psU32 and
+psF64.
+ *****************************************************************************/
+psF32 psSpline1DEval(
+    const psSpline1D *spline,
+    psF32 x
+)
+{
+    PS_PTR_CHECK_NULL(spline, NAN);
+    PS_INT_CHECK_NON_NEGATIVE(spline->n, NAN);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NAN);
+
+    psS32 binNum;
+    psS32 n;
+
+    n = spline->n;
+    //XXX    binNum = vectorBinDisectF32(spline->domains, (spline->n)+1, x);
+    binNum = vectorBinDisectF32(spline->knots->data.F32, (spline->n)+1, x);
+    if (binNum < 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "psSpline1DEval(): x ordinate (%f) is outside the spline range (%f - %f).",
+                 x, spline->knots->data.F32[0],
+                 spline->knots->data.F32[n-1]);
+
+        if (x < spline->knots->data.F32[0]) {
+            return(psPolynomial1DEval(spline->spline[0],
+                                      x));
+        } else if (x > spline->knots->data.F32[n-1]) {
+            return(psPolynomial1DEval(spline->spline[n-1],
+                                      x));
+        }
+    }
+
+    return(psPolynomial1DEval(spline->spline[binNum],
+                              x));
+}
+
+// XXX: The spline eval functions require input and output to be F32.
+// however the spline fit functions require F32 and F64.
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x
+)
+{
+    PS_PTR_CHECK_NULL(spline, NULL);
+    PS_VECTOR_CHECK_NULL(x, NULL);
+    PS_VECTOR_CHECK_TYPE_F32_OR_F64(x, NULL);
+    PS_VECTOR_CHECK_TYPE(spline->knots, PS_TYPE_F32, NULL);
+
+    psS32 i;
+    psVector *tmpVector;
+
+    tmpVector = psVectorAlloc(x->n, PS_TYPE_F32);
+    if (x->type.type == PS_TYPE_F32) {
+        for (i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(
+                                         spline,
+                                         x->data.F32[i]
+                                     );
+        }
+    } else if (x->type.type == PS_TYPE_F64) {
+        for (i=0;i<x->n;i++) {
+            tmpVector->data.F32[i] = psSpline1DEval(
+                                         spline,
+                                         (psF32) x->data.F64[i]
+                                     );
+        }
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType,x->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE,
+                PS_ERRORTEXT_psFunctions_TYPE_NOT_SUPPORTED,
+                strType);
+        return(NULL);
+    }
+
+    return(tmpVector);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psSpline.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psSpline.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psSpline.h	(revision 22271)
@@ -0,0 +1,440 @@
+/** @file psFunctions.h
+ *  @brief Standard Mathematical Functions.
+ *  @ingroup Stats
+ *
+ *  This file will hold the prototypes for procedures which allocate, free,
+ *  and evaluate various polynomials.  Those polynomial structures are also
+ *  defined here.
+ *
+ *  @ingroup Stats
+ *
+ *  @author Someone at IfA
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.44 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-31 23:01:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_FUNCTIONS_H)
+#define PS_FUNCTIONS_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <float.h>
+#include <math.h>
+
+#include "psVector.h"
+#include "psScalar.h"
+
+/** \addtogroup Stats
+ *  \{
+ */
+
+/** Evaluate a non-normalized Gaussian with the given mean and sigma at the
+ *  given coordianate.  
+ *
+ *  Note that this is not a Gaussian deviate.  The evaluated Gaussian is: 
+ *        \f[ exp(-\frac{(x-mean)^2}{2\sigma^2}) \f] 
+ *
+ *  @return psF32      value on the gaussian curve given the input parameters
+ */
+psF32 psGaussian(
+    psF32 x,                           ///< Value at which to evaluate
+    psF32 mean,                        ///< Mean for the Gaussian
+    psF32 stddev,                      ///< Standard deviation for the Gaussian
+    psBool normal                        ///< Indicates whether result should be normalized
+);
+
+/** Produce a vector of random numbers from a Gaussian distribution with
+ *  the specified mean and sigma 
+ *  
+ *  @return psVector*    vector of random numbers
+ *  
+ */
+psVector* p_psGaussianDev(
+    psF32 mean,                        ///< The mean of the Gaussian
+    psF32 sigma,                       ///< The sigma of the Gaussian
+    psS32 Npts                           ///< The size of the vector
+);
+
+typedef enum {
+    PS_POLYNOMIAL_ORD,                 ///< Ordinary Polynomial
+    PS_POLYNOMIAL_CHEB                 ///< Chebyshev Polynomial
+} psPolynomialType;
+
+/** One-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 n;                             ///< Number of terms
+    psF32 *coeff;                      ///< Coefficients
+    psF32 *coeffErr;                   ///< Error in coefficients
+    psU8 *mask;                        ///< Coefficient mask
+}
+psPolynomial1D;
+
+/** Two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psF32 **coeff;                     ///< Coefficients
+    psF32 **coeffErr;                  ///< Error in coefficients
+    psU8 **mask;                       ///< Coefficients mask
+}
+psPolynomial2D;
+
+/** Three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF32 ***coeff;                    ///< Coefficients
+    psF32 ***coeffErr;                 ///< Error in coefficients
+    psU8 ***mask;                      ///< Coefficients mask
+}
+psPolynomial3D;
+
+/** Four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nW;                            ///< Number of terms in w
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF32 ****coeff;                   ///< Coefficients
+    psF32 ****coeffErr;                ///< Error in coefficients
+    psU8 ****mask;                     ///< Coefficients mask
+}
+psPolynomial4D;
+
+
+/** Allocates a psPolynomial1D structure with n terms
+ *
+ *  @return  psPolynomial1D*    new 1-D polynomial struct
+ */
+psPolynomial1D* psPolynomial1DAlloc(
+    psS32 n,                              ///< Number of terms
+    psPolynomialType type               ///< Polynomial Type
+);
+
+/** Allocates a 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new 2-D polynomial struct
+ */
+psPolynomial2D* psPolynomial2DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new 3-D polynomial struct
+ */
+psPolynomial3D* psPolynomial3DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new 4-D polynomial struct
+ */
+psPolynomial4D* psPolynomial4DAlloc(
+    psS32 nW,                            ///< Number of terms in w
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Evaluates a 1-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial1DEval(
+    const psPolynomial1D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x                           ///< location at which to evaluate
+);
+
+/** Evaluates a 2-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial2DEval(
+    const psPolynomial2D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y                           ///< y location at which to evaluate
+);
+
+/** Evaluates a 3-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial3DEval(
+    const psPolynomial3D* myPoly,       ///< Coefficients for the polynomial
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y,                           ///< y location at which to evaluate
+    psF32 z                           ///< z location at which to evaluate
+);
+
+/** Evaluates a 4-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF32 psPolynomial4DEval(
+    const psPolynomial4D* myPoly,       ///< Coefficients for the polynomial
+    psF32 w,                           ///< w location at which to evaluate
+    psF32 x,                           ///< x location at which to evaluate
+    psF32 y,                           ///< y location at which to evaluate
+    psF32 z                           ///< z location at which to evaluate
+);
+
+psVector *psPolynomial1DEvalVector(
+    const psPolynomial1D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x             ///< x locations at which to evaluate
+);
+
+psVector *psPolynomial2DEvalVector(
+    const psPolynomial2D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y             ///< y locations at which to evaluate
+);
+
+psVector *psPolynomial3DEvalVector(
+    const psPolynomial3D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+psVector *psPolynomial4DEvalVector(
+    const psPolynomial4D *myPoly,   ///< Coefficients for the polynomial
+    const psVector *w,             ///< w locations at which to evaluate
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+/*****************************************************************************/
+
+/* Double-precision polynomials, mainly for use in astrometry */
+
+/** Double-precision one-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 n;                             ///< Number of terms
+    psF64 *coeff;                     ///< Coefficients
+    psF64 *coeffErr;                  ///< Error in coefficients
+    psU8 *mask;                        ///< Coefficient mask
+}
+psDPolynomial1D;
+
+/** Double-precision two-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psF64 **coeff;                    ///< Coefficients
+    psF64 **coeffErr;                 ///< Error in coefficients
+    psU8 **mask;                       ///< Coefficients mask
+}
+psDPolynomial2D;
+
+/** Double-precision three-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF64 ***coeff;                   ///< Coefficients
+    psF64 ***coeffErr;                ///< Error in coefficients
+    psU8 ***mask;                      ///< Coefficient mask
+}
+psDPolynomial3D;
+
+/** Double-precision four-dimensional polynomial */
+typedef struct
+{
+    psPolynomialType type;             ///< Polynomial type
+    psS32 nW;                            ///< Number of terms in w
+    psS32 nX;                            ///< Number of terms in x
+    psS32 nY;                            ///< Number of terms in y
+    psS32 nZ;                            ///< Number of terms in z
+    psF64 ****coeff;                  ///< Coefficients
+    psF64 ****coeffErr;               ///< Error in coefficients
+    psU8 ****mask;                     ///< Coefficients mask
+}
+psDPolynomial4D;
+
+/** Allocates a double-precision 1-D polynomial structure with n terms
+ *
+ *  @return  psPolynomial1D*    new double-precision 1-D polynomial struct
+ */
+psDPolynomial1D* psDPolynomial1DAlloc(
+    psS32 n,                             ///< Number of terms
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 2-D polynomial structure
+ *
+ *  @return  psPolynomial2D*    new double-precision 2-D polynomial struct
+ */
+psDPolynomial2D* psDPolynomial2DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 3-D polynomial structure
+ *
+ *  @return  psPolynomial3D*    new double-precision 3-D polynomial struct
+ */
+psDPolynomial3D* psDPolynomial3DAlloc(
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Allocates a double-precision 4-D polynomial structure
+ *
+ *  @return  psPolynomial4D*    new double-precision 4-D polynomial struct
+ */
+psDPolynomial4D* psDPolynomial4DAlloc(
+    psS32 nW,                            ///< Number of terms in w
+    psS32 nX,                            ///< Number of terms in x
+    psS32 nY,                            ///< Number of terms in y
+    psS32 nZ,                            ///< Number of terms in z
+    psPolynomialType type              ///< Polynomial Type
+);
+
+/** Evaluates a double-precision 1-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial1DEval(
+    const psDPolynomial1D* myPoly,      ///< Coefficients for the polynomial
+    psF64 x                          ///< Value at which to evaluate
+);
+
+/** Evaluates a double-precision 2-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial2DEval(
+    const psDPolynomial2D* myPoly,       ///< Coefficients for the polynomial
+    psF64 x,                           ///< Value x at which to evaluate
+    psF64 y            ///< Value y at which to evaluate
+);
+
+/** Evaluates a double-precision 3-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial3DEval(
+    const psDPolynomial3D* myPoly,      ///< Coefficients for the polynomial
+    psF64 x,                          ///< Value x at which to evaluate
+    psF64 y,                          ///< Value y at which to evaluate
+    psF64 z     ///< Value z at which to evaluate
+);
+
+/** Evaluates a double-precision 4-D polynomial at specific coordinates.
+ *
+ *  @return psF32    result of polynomial at given location
+ */
+psF64 psDPolynomial4DEval(
+    const psDPolynomial4D* myPoly,      ///< Coefficients for the polynomial
+    psF64 w,                          ///< Value w at which to evaluate
+    psF64 x,                          ///< Value x at which to evaluate
+    psF64 y,                          ///< Value y at which to evaluate
+    psF64 z     ///< Value z at which to evaluate
+);
+
+psVector *psDPolynomial1DEvalVector(
+    const psDPolynomial1D *myPoly, ///< Coefficients for the polynomial
+    const psVector *x             ///< x locations at which to evaluate
+);
+
+psVector *psDPolynomial2DEvalVector(
+    const psDPolynomial2D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y             ///< y locations at which to evaluate
+);
+
+psVector *psDPolynomial3DEvalVector(
+    const psDPolynomial3D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+psVector *psDPolynomial4DEvalVector(
+    const psDPolynomial4D *myPoly,  ///< Coefficients for the polynomial
+    const psVector *w,             ///< w locations at which to evaluate
+    const psVector *x,             ///< x locations at which to evaluate
+    const psVector *y,             ///< y locations at which to evaluate
+    const psVector *z             ///< z locations at which to evaluate
+);
+
+
+
+typedef struct
+{
+    psS32 n;                       ///< The number of spline polynomials
+    psPolynomial1D **spline;       ///< An array of n pointers to the spline polynomials
+    psF32 *p_psDeriv2;             ///< For cubic splines, the second derivative at each domain point.  Size is n+1.
+    psF32 *domains;                ///< The boundaries between each spline piece.  Size is n+1.
+    psVector *knots;               ///< The boundaries between each spline piece.  Size is n+1.
+}
+psSpline1D;
+
+psSpline1D *psSpline1DAlloc(psS32 n,
+                            psS32 order,
+                            psF32 min,
+                            psF32 max);
+
+psSpline1D *psSpline1DAllocGeneric(const psVector *bounds,
+                                   psS32 order);
+
+psF32 psSpline1DEval(
+    const psSpline1D *spline,
+    psF32 x
+);
+
+psVector *psSpline1DEvalVector(
+    const psSpline1D *spline,
+    const psVector *x
+);
+
+psS32 p_psVectorBinDisect(psVector *bins,
+                          psScalar *x);
+
+psScalar *p_psVectorInterpolate(psVector *domain,
+                                psVector *range,
+                                psS32 order,
+                                psScalar *x);
+
+#if 0
+psF32 p_psNRSpline1DEval(psSpline1D *spline,
+                         const psVector* x,
+                         const psVector* y,
+                         psF32 X);
+#endif
+
+/* \} */// End of MathGroup Functions
+
+#endif
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psStats.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psStats.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psStats.c	(revision 22271)
@@ -0,0 +1,2328 @@
+/** @file  psStats.c
+ *  \brief basic statistical operations
+ *  @ingroup Stats
+ *
+ *  This file will hold the definition of the histogram and stats data
+ *  structures.  It also contains prototypes for procedures which operate
+ *  on those data structures.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.126 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-26 19:53:30 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <float.h>
+#include <math.h>
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include "psMemory.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psStats.h"
+#include "psMinimize.h"
+#include "psFunctions.h"
+#include "psConstants.h"
+
+#include "psDataManipErrors.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+#define PS_GAUSS_WIDTH 5       // The width of the Gaussian or boxcar smoothing.
+#define PS_CLIPPED_NUM_ITER_LB 1
+#define PS_CLIPPED_NUM_ITER_UB 10
+#define PS_CLIPPED_SIGMA_LB 1.0
+#define PS_CLIPPED_SIGMA_UB 10.0
+#define PS_POLY_MEDIAN_MAX_ITERATIONS 10
+
+#define PS_BIN_MIDPOINT(HISTOGRAM, BIN_NUM) \
+(0.5 * (HISTOGRAM->bounds->data.F32[(BIN_NUM)] + HISTOGRAM->bounds->data.F32[(BIN_NUM)+1]))
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+psVector* p_psConvertToF32(psVector* in);
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+psBool p_psGetStatValue(const psStats* stats, psF64 *value)
+{
+
+    switch (stats->options & ~(PS_STAT_USE_RANGE | PS_STAT_USE_BINSIZE | PS_STAT_ROBUST_FOR_SAMPLE)) {
+    case PS_STAT_SAMPLE_MEAN:
+        *value = stats->sampleMean;
+        return true;
+
+    case PS_STAT_SAMPLE_MEDIAN:
+        *value = stats->sampleMedian;
+        return true;
+
+    case PS_STAT_SAMPLE_STDEV:
+        *value = stats->sampleStdev;
+        return true;
+
+    case PS_STAT_ROBUST_MEAN:
+        *value = stats->robustMean;
+        return true;
+
+    case PS_STAT_ROBUST_MEDIAN:
+        *value = stats->robustMedian;
+        return true;
+
+    case PS_STAT_ROBUST_MODE:
+        *value = stats->robustMode;
+        return true;
+
+    case PS_STAT_ROBUST_STDEV:
+        *value = stats->robustStdev;
+        return true;
+
+    case PS_STAT_CLIPPED_MEAN:
+        *value = stats->clippedMean;
+        return true;
+
+    case PS_STAT_CLIPPED_STDEV:
+        *value = stats->clippedStdev;
+        return true;
+
+    case PS_STAT_MAX:
+        *value = stats->max;
+        return true;
+
+    case PS_STAT_MIN:
+        *value = stats->min;
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+/******************************************************************************
+MISC PRIVATE STATISTICAL FUNCTIONS
+ 
+NOTE: it is assumed that any call to these statistical functions will have
+been preceded by a call to the psVectorStats() function.  Various sanity tests
+will only be performed in psVectorStats().  Should we perform the sanity
+checks in each routine anyway?
+ 
+XXX: For many of these private stats routines, what should be done if there
+are no acceptable elements in the input vector (if no elements lie within
+range, or there are no unmasked elements, or the input vector is NULL)?
+Currently we set the value to NAN.
+ 
+XXX: Optimization: many routines have an "empty" boolean variable which keeps
+track of whether or not the vector has any valid elements.  This code can
+possibly be optimized away.
+ *****************************************************************************/
+/******************************************************************************
+p_psVectorSampleMean(myVector, maskVector, maskVal, stats): calculates the
+mean of the input vector.  If there was a problem with the mean calculation,
+this routine sets stats->sampleMean to NAN.
+ *****************************************************************************/
+psS32 p_psVectorSampleMean(const psVector* myVector,
+                           const psVector* errors,
+                           const psVector* maskVector,
+                           psU32 maskVal,
+                           psStats* stats)
+{
+
+    psS32 i = 0;                // Loop index variable
+    psF32 mean = 0.0;           // The mean
+    psS32 count = 0;            // # of points in this mean
+
+    // If PS_STAT_USE_RANGE is requested, then we enter a slightly different
+    // loop.
+    if (errors == NULL) {
+        if (stats->options & PS_STAT_USE_RANGE) {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    // Check if the data is with the specified range
+                    if (!(maskVal & maskVector->data.U8[i]) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        mean += myVector->data.F32[i];
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= (psF32)count;
+                } else {
+                    mean = NAN;
+                }
+
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    if ((stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        mean += myVector->data.F32[i];
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= (psF32)count;
+                } else {
+                    mean = NAN;
+                }
+            }
+        } else {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    if (!(maskVal & maskVector->data.U8[i])) {
+                        mean += myVector->data.F32[i];
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= (psF32)count;
+                } else {
+                    mean = NAN;
+                }
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    mean += myVector->data.F32[i];
+                }
+                mean /= (psF32)myVector->n;
+            }
+        }
+    } else {
+        psF32 errorDivisor = 0.0;
+        psF32 errorSqr = 0.0;
+        if (stats->options & PS_STAT_USE_RANGE) {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    // Check if the data is with the specified range
+                    if (!(maskVal & maskVector->data.U8[i]) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                        mean += myVector->data.F32[i]/errorSqr;
+                        errorDivisor+= (1.0 / errorSqr);
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= errorDivisor;
+                } else {
+                    mean = NAN;
+                }
+
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    if ((stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                        mean += myVector->data.F32[i]/errorSqr;
+                        errorDivisor+= (1.0 / errorSqr);
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= errorDivisor;
+                } else {
+                    mean = NAN;
+                }
+            }
+        } else {
+            if (maskVector != NULL) {
+                for (i = 0; i < myVector->n; i++) {
+                    if (!(maskVal & maskVector->data.U8[i])) {
+                        errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                        mean += myVector->data.F32[i]/errorSqr;
+                        errorDivisor+= (1.0 / errorSqr);
+                        count++;
+                    }
+                }
+                if (count != 0) {
+                    mean /= errorDivisor;
+                } else {
+                    mean = NAN;
+                }
+            } else {
+                for (i = 0; i < myVector->n; i++) {
+                    errorSqr = errors->data.F32[i]*errors->data.F32[i];
+                    mean += myVector->data.F32[i]/errorSqr;
+                    errorDivisor+= (1.0 / errorSqr);
+                }
+                mean /= errorDivisor;
+            }
+        }
+    }
+
+    stats->sampleMean = mean;
+    if (isnan(mean)) {
+        return(-1);
+    } else {
+        return(0);
+    }
+
+}
+
+/******************************************************************************
+p_psVectorSampleMax(myVector, maskVector, maskVal, stats): calculates the
+max of the input vector.  If there was a problem with the max calculation,
+this routine sets stats->max to NAN.
+ *****************************************************************************/
+psS32 p_psVectorMax(const psVector* myVector,
+                    const psVector* maskVector,
+                    psU32 maskVal,
+                    psStats* stats)
+{
+    psS32 i = 0;                // Loop index variable
+    psF32 max = -PS_MAX_F32;    // The calculated maximum
+    psS32 empty = true;         // Does this vector have valid elements?
+
+    // If PS_STAT_USE_RANGE is requested, then we enter a different loop.
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if ((myVector->data.F32[i] > max) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        max = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((myVector->data.F32[i] > max) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    max = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if (myVector->data.F32[i] > max) {
+                        max = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if (myVector->data.F32[i] > max) {
+                    max = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    }
+    if (empty == false) {
+        stats->max = max;
+    } else {
+        stats->max = NAN;
+        return(1);
+    }
+    return(0);
+}
+
+/******************************************************************************
+p_psVectorSampleMin(myVector, maskVector, maskVal, stats): calculates the
+minimum of the input vector.  If there was a problem with the min calculation,
+this routine sets stats->min to NAN.
+ *****************************************************************************/
+psS32 p_psVectorMin(const psVector* myVector,
+                    const psVector* maskVector,
+                    psU32 maskVal,
+                    psStats* stats)
+{
+    psS32 i = 0;                // Loop index variable
+    psF32 min = PS_MAX_F32;   // The calculated maximum
+    psS32 empty = true;         // Does this vector have valid elements?
+
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if ((myVector->data.F32[i] < min) &&
+                            (stats->min <= myVector->data.F32[i]) &&
+                            (myVector->data.F32[i] <= stats->max)) {
+                        min = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((myVector->data.F32[i] < min) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    min = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    if (myVector->data.F32[i] < min) {
+                        min = myVector->data.F32[i];
+                        empty = false;
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if (myVector->data.F32[i] < min) {
+                    min = myVector->data.F32[i];
+                    empty = false;
+                }
+            }
+        }
+    }
+
+    if (empty == false) {
+        stats->min = min;
+    } else {
+        stats->min = NAN;
+        return(1);
+    }
+    return(0);
+}
+
+/******************************************************************************
+p_psVectorNValues(myVector, maskVector, maskVal, stats): This routine returns
+"true" if the inputPsVector has 1 or more valid elements (a valid element is an
+unmasked element within the specified min/max range).  Otherwise, return
+"false".
+ *****************************************************************************/
+bool p_psVectorCheckNonEmpty(const psVector* myVector,
+                             const psVector* maskVector,
+                             psU32 maskVal,
+                             psStats* stats)
+{
+    PS_VECTOR_CHECK_NULL(myVector, -1);
+    PS_PTR_CHECK_NULL(stats, -1);
+    psS32 i = 0;                // Loop index variable
+
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    return(true);
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    return(true);
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    return(true);
+                }
+            }
+        } else {
+            if (myVector->n > 0) {
+                return(true);
+            }
+        }
+    }
+    return(false);
+}
+
+
+/******************************************************************************
+p_psVectorNValues(myVector, maskVector, maskVal, stats): calculates the
+number of non-masked pixels in the vector that fall within the min/max
+range, if specified.
+ *****************************************************************************/
+psS32 p_psVectorNValues(const psVector* myVector,
+                        const psVector* maskVector,
+                        psU32 maskVal,
+                        psStats* stats)
+{
+    PS_VECTOR_CHECK_NULL(myVector, -1);
+    PS_PTR_CHECK_NULL(stats, -1);
+    psS32 i = 0;                // Loop index variable
+    psS32 numData = 0;          // The number of data points
+
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    numData++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    numData++;
+                }
+            }
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    numData++;
+                }
+            }
+        } else {
+            numData = myVector->n;
+        }
+    }
+
+    return (numData);
+}
+
+/******************************************************************************
+p_psVectorSampleMedian(myVector, maskVector, maskVal, stats): calculates the
+median of the input vector.  Returns true on success (including if there were
+no valid input vector elements).
+ 
+XXX: Use static vectors for sort arrays.
+ *****************************************************************************/
+bool p_psVectorSampleMedian(const psVector* myVector,
+                            const psVector* maskVector,
+                            psU32 maskVal,
+                            psStats* stats)
+{
+    psVector* unsortedVector = NULL;    // Temporary vector
+    psVector* sortedVector = NULL;      // Temporary vector
+    psS32 i = 0;                        // Loop index variable
+    psS32 count = 0;
+    psS32 nValues = 0;
+
+    // Determine how many data points fit inside this min/max range
+    // and are not masked, if the maskVector is not NULL.
+    nValues = p_psVectorNValues(myVector, maskVector, maskVal, stats);
+
+    // XXX: Is a warning message appropriate?  What value should we set the
+    // sampleMedian to?  Should we generate an error?
+    if (nValues <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleMedian(): no valid elements in input vector.\n");
+        return(true);
+        stats->sampleMedian = NAN;
+    }
+
+    // Allocate temporary vectors for the data.
+    unsortedVector = psVectorAlloc(nValues, PS_TYPE_F32);
+
+    // Determine if we must only use data points within a min/max range.
+    if (stats->options & PS_STAT_USE_RANGE) {
+        // Store all non-masked data points within the min/max range
+        // into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count] = myVector->data.F32[i];
+                    count++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count] = myVector->data.F32[i];
+                    count++;
+                }
+            }
+        }
+    } else {
+        // Store all non-masked data points into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    unsortedVector->data.F32[count] = myVector->data.F32[i];
+                    count++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                unsortedVector->data.F32[i] = myVector->data.F32[i];
+            }
+        }
+    }
+    // Sort the temporary vectors.
+    sortedVector = psVectorSort(sortedVector, unsortedVector);
+    if (sortedVector == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                false,
+                PS_ERRORTEXT_psStats_STATS_SAMPLE_MEDIAN_SORT_PROBLEM);
+        return(false);
+    }
+
+    // Calculate the median exactly.  Use the average if the number of samples
+    // is even.
+    if (0 == (nValues % 2)) {
+        stats->sampleMedian = 0.5 * (sortedVector->data.F32[(nValues / 2) - 1] +
+                                     sortedVector->data.F32[nValues / 2]);
+    } else {
+        stats->sampleMedian = sortedVector->data.F32[nValues / 2];
+    }
+
+    // Free the temporary data structures.
+    psFree(unsortedVector);
+    psFree(sortedVector);
+
+    // Return "true" on success.
+    return(true);
+}
+
+/******************************************************************************
+p_psVectorSmoothHistGaussian(): This routine smoothes the data in the input
+robustHistogram with a Gaussian of width sigma.
+ 
+XXX: Only PS_TYPE_F32 is supported.
+ 
+XXX: Write a general routine which smoothes a psVector.  This routine should
+call that.  Is that possible?
+ *****************************************************************************/
+psVector* p_psVectorSmoothHistGaussian(psHistogram* robustHistogram,
+                                       psF32 sigma)
+{
+    PS_PTR_CHECK_NULL(robustHistogram, NULL);
+    PS_PTR_CHECK_NULL(robustHistogram->bounds, NULL);
+
+    psS32 i = 0;                  // Loop index variable
+    psS32 j = 0;                  // Loop index variable
+    psF32 iMid;
+    psF32 jMid;
+    psS32 numBins = robustHistogram->nums->n;
+    psS32 numBounds = robustHistogram->bounds->n;
+    psVector* smooth = psVectorAlloc(numBins, PS_TYPE_F32);
+    psS32 jMin = 0;
+    psS32 jMax = 0;
+    psF32 firstBound = robustHistogram->bounds->data.F32[0];
+    psF32 lastBound = robustHistogram->bounds->data.F32[numBounds-1];
+    psScalar x;
+
+    x.type.type = PS_TYPE_F32;
+    for (i = 0; i < numBins; i++) {
+        // Determine the midpoint of bin i.
+        iMid = (robustHistogram->bounds->data.F32[i] +
+                robustHistogram->bounds->data.F32[i+1]) / 2.0;
+
+
+        // We determine the bin numbers corresponding to a range of data
+        // values surrounding iMid.  The ranges is of size
+        // s*PS_GAUSS_WIDTH*sigma
+
+        // YYY: The p_psVectorBinDisect() routine does much of the work of
+        // the following conditionals, however, it also reports a warning
+        // message.  I don't want the warning message so I reproduce the
+        // conditionals here.  Maybe p_psVectorBinDisect() should not produce
+        // warnings?
+
+        x.data.F32 = iMid - (PS_GAUSS_WIDTH * sigma);
+        if ((x.data.F32 >= firstBound) && (x.data.F32 <= lastBound)) {
+            jMin = p_psVectorBinDisect(robustHistogram->bounds, &x);
+            if (jMin < 0) {
+                psError(PS_ERR_UNEXPECTED_NULL,
+                        false,
+                        PS_ERRORTEXT_psStats_STATS_VECTOR_BIN_DISECT_PROBLEM);
+                return(NULL);
+            }
+        } else if (x.data.F32 <= firstBound) {
+            jMin = 0;
+        } else if (x.data.F32 >= lastBound) {
+            jMin = robustHistogram->bounds->n - 1;
+        }
+
+        x.data.F32 = iMid + (PS_GAUSS_WIDTH * sigma);
+        if ((x.data.F32 >= firstBound) && (x.data.F32 <= lastBound)) {
+            jMax = p_psVectorBinDisect(robustHistogram->bounds, &x);
+            if (jMax < 0) {
+                psError(PS_ERR_UNEXPECTED_NULL,
+                        false,
+                        PS_ERRORTEXT_psStats_STATS_VECTOR_BIN_DISECT_PROBLEM);
+                return(NULL);
+            }
+        } else if (x.data.F32 <= firstBound) {
+            jMax = 0;
+        } else if (x.data.F32 >= lastBound) {
+            jMax = robustHistogram->bounds->n - 1;
+        }
+
+        smooth->data.F32[i] = 0.0;
+        for (j = jMin ; j <= jMax ; j++) {
+            jMid = (robustHistogram->bounds->data.F32[j] +
+                    robustHistogram->bounds->data.F32[j+1]) / 2.0;
+            smooth->data.F32[i] +=
+                robustHistogram->nums->data.F32[j] *
+                psGaussian(jMid, iMid, sigma, true);
+        }
+    }
+
+    return(smooth);
+}
+
+/******************************************************************************
+p_psVectorSampleQuartiles(myVector, maskVector, maskVal, stats): calculates
+the upper and/or lower quartiles of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+ *****************************************************************************/
+bool p_psVectorSampleQuartiles(const psVector* myVector,
+                               const psVector* maskVector,
+                               psU32 maskVal,
+                               psStats* stats)
+{
+    psVector* unsortedVector = NULL;    // Temporary vector
+    psVector* sortedVector = NULL;      // Temporary vector
+    psS32 i = 0;                  // Loop index variable
+    psS32 count = 0;              // # of points in this mean.
+    psS32 nValues = 0;            // # data points
+
+    // Determine how many data points fit inside this min/max range
+    // and are not maxed, IF the maskVector is not NULL.
+    nValues = p_psVectorNValues(myVector, maskVector, maskVal, stats);
+
+    // Allocate temporary vectors for the data.
+    unsortedVector = psVectorAlloc(nValues, PS_TYPE_F32);
+
+    // Determine if we must only use data points within a min/max range.
+    if (stats->options & PS_STAT_USE_RANGE) {
+        // Store all non-masked data points within the min/max range
+        // into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) && (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count++] = myVector->data.F32[i];
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) && (myVector->data.F32[i] <= stats->max)) {
+                    unsortedVector->data.F32[count++] = myVector->data.F32[i];
+                }
+            }
+        }
+    } else {
+        // Store all non-masked data points into the temporary vectors.
+        count = 0;
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    unsortedVector->data.F32[count++] = myVector->data.F32[i];
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                unsortedVector->data.F32[i] = myVector->data.F32[i];
+            }
+        }
+    }
+
+    // Sort the temporary vectors.
+    sortedVector = psVectorSort(sortedVector, unsortedVector);
+    if (sortedVector == NULL) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                false,
+                PS_ERRORTEXT_psStats_STATS_SAMPLE_MEDIAN_SORT_PROBLEM);
+        return(false);
+    }
+
+    // Calculate the quartile points exactly.
+    // XXX: We should probably do a bin-midpoint if the quartile is not an
+    // integer.
+    stats->sampleUQ = sortedVector->data.F32[3 * (nValues / 4)];
+    stats->sampleLQ = sortedVector->data.F32[nValues / 4];
+
+    // Free the temporary data structures.
+    psFree(unsortedVector);
+    psFree(sortedVector);
+    return(true);
+}
+
+/******************************************************************************
+p_psVectorSampleStdev(myVector, maskVector, maskVal, stats): calculates the
+stdev of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+ 
+ *****************************************************************************/
+void p_psVectorSampleStdevOLD(const psVector* myVector,
+                              const psVector* errors,
+                              const psVector* maskVector,
+                              psU32 maskVal,
+                              psStats* stats)
+{
+    psS32 i = 0;                  // Loop index variable
+    psS32 countInt = 0;           // # of data points being used
+    psF32 countFloat = 0.0;     // # of data points being used
+    psF32 mean = 0.0;           // The mean
+    psF32 diff = 0.0;           // Used in calculating stdev
+    psF32 sumSquares = 0.0;     // temporary variable
+    psF32 sumDiffs = 0.0;       // temporary variable
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call p_psVectorSampleMean()
+    if (0 != isnan(stats->sampleMean)) {
+        p_psVectorSampleMean(myVector, errors, maskVector, maskVal, stats);
+    }
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (0 != isnan(stats->sampleMean)) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): p_psVectorSampleMean() reported a NAN mean.\n");
+        return;
+    }
+
+    mean = stats->sampleMean;
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                }
+            }
+            countInt = myVector->n;
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                diff = myVector->data.F32[i] - mean;
+                sumSquares += (diff * diff);
+                sumDiffs += diff;
+                countInt++;
+            }
+            countInt = myVector->n;
+        }
+    }
+    if (countInt == 0) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): no valid psVector elements (%d).  Setting stats->sampleStdev = NAN.\n", countInt);
+    } else if (countInt == 1) {
+        stats->sampleStdev = 0.0;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): only one valid psVector elements (%d).  Setting stats->sampleStdev = 0.0.\n", countInt);
+    } else {
+        countFloat = (psF32)countInt;
+        stats->sampleStdev = PS_SQRT_F32((sumSquares - (sumDiffs * sumDiffs / countFloat)) / (countFloat - 1));
+    }
+}
+/******************************************************************************
+p_psVectorSampleStdev(myVector, maskVector, maskVal, stats): calculates the
+stdev of the input vector.
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    NULL
+ 
+ *****************************************************************************/
+void p_psVectorSampleStdev(const psVector* myVector,
+                           const psVector* errors,
+                           const psVector* maskVector,
+                           psU32 maskVal,
+                           psStats* stats)
+{
+    psS32 i = 0;                  // Loop index variable
+    psS32 countInt = 0;           // # of data points being used
+    psF32 countFloat = 0.0;     // # of data points being used
+    psF32 mean = 0.0;           // The mean
+    psF32 diff = 0.0;           // Used in calculating stdev
+    psF32 sumSquares = 0.0;     // temporary variable
+    psF32 sumDiffs = 0.0;       // temporary variable
+    //    psF32 sum1;
+    //    psF32 sum2;
+    psF32 errorDivisor = 0.0f;
+
+    // This procedure requires the mean.  If it has not been already
+    // calculated, then call p_psVectorSampleMean()
+    if (0 != isnan(stats->sampleMean)) {
+        p_psVectorSampleMean(myVector, errors, maskVector, maskVal, stats);
+    }
+    // If the mean is NAN, then generate a warning and set the stdev to NAN.
+    if (0 != isnan(stats->sampleMean)) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): p_psVectorSampleMean() reported a NAN mean.\n");
+        return;
+    }
+
+    mean = stats->sampleMean;
+    if (stats->options & PS_STAT_USE_RANGE) {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i]) &&
+                        (stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                    if (errors != NULL) {
+                        errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                if ((stats->min <= myVector->data.F32[i]) &&
+                        (myVector->data.F32[i] <= stats->max)) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                    if (errors != NULL) {
+                        errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+                    }
+                }
+            }
+            countInt = myVector->n;
+        }
+    } else {
+        if (maskVector != NULL) {
+            for (i = 0; i < myVector->n; i++) {
+                if (!(maskVal & maskVector->data.U8[i])) {
+                    diff = myVector->data.F32[i] - mean;
+                    sumSquares += (diff * diff);
+                    sumDiffs += diff;
+                    countInt++;
+                    if (errors != NULL) {
+                        errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+                    }
+                }
+            }
+        } else {
+            for (i = 0; i < myVector->n; i++) {
+                diff = myVector->data.F32[i] - mean;
+                sumSquares += (diff * diff);
+                sumDiffs += diff;
+                countInt++;
+            }
+            countInt = myVector->n;
+            if (errors != NULL) {
+                errorDivisor+= (1.0 / PS_SQR(errors->data.F32[i]));
+            }
+        }
+    }
+
+    if (countInt == 0) {
+        stats->sampleStdev = NAN;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): no valid psVector elements (%d).  Setting stats->sampleStdev = NAN.\n", countInt);
+    } else if (countInt == 1) {
+        stats->sampleStdev = 0.0;
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: p_psVectorSampleStdev(): only one valid psVector elements (%d).  Setting stats->sampleStdev = 0.0.\n", countInt);
+    } else {
+        // XXX: The ADD specifies this as the definition of the standard
+        // deviation if the errors are known.  Verify this with IfA: none of
+        // the data points in the vector are used.  Verify that the masks and
+        // data ranges are used correctly.
+        if (errors != NULL) {
+            stats->sampleStdev = (1.0 / PS_SQRT_F32(errorDivisor));
+        } else {
+            countFloat = (psF32)countInt;
+            stats->sampleStdev = PS_SQRT_F32((sumSquares - (sumDiffs * sumDiffs / countFloat)) / (countFloat - 1));
+
+        }
+    }
+}
+
+/******************************************************************************
+p_psVectorClippedStats(myVector, errors, maskVector, maskVal, stats): calculates the
+clipped stats (mean or stdev) of the input vector.
+ 
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    0 for success.
+    -1: error
+    -2: warning
+ 
+XXX: Do we really need to calculate median and mean?
+ 
+XXX: Use static vectors for tmpMask.
+ 
+XXX: This has not been tested.
+ *****************************************************************************/
+psS32 p_psVectorClippedStats(const psVector* myVector,
+                             const psVector* errors,
+                             const psVector* maskVector,
+                             psU32 maskVal,
+                             psStats* stats)
+{
+    psF32 clippedMean = 0.0;    // self-explanatory
+    psF32 clippedStdev = 0.0;   // self-explanatory
+    psVector* tmpMask = NULL;   // Temporary vector for masks during iterations.
+    static psStats *statsTmp = NULL;   // Temporary psStats struct.
+    psS32 rc = 0;               // Return code.
+
+    // Ensure that stats->clipIter is within the proper range.
+    PS_INT_CHECK_RANGE(stats->clipIter,
+                       PS_CLIPPED_NUM_ITER_LB,
+                       PS_CLIPPED_NUM_ITER_UB, -1);
+
+    // Ensure that stats->clipSigma is within the proper range.
+    PS_INT_CHECK_RANGE(stats->clipSigma,
+                       PS_CLIPPED_SIGMA_LB,
+                       PS_CLIPPED_SIGMA_UB, -1);
+
+    // Allocate a psStats structure for calculating the mean, median, and
+    // stdev.
+    if (statsTmp == NULL) {
+        statsTmp = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        p_psMemSetPersistent(statsTmp, true);
+    }
+
+    // We allocate a temporary mask vector since during the iterative
+    // steps that follow, we will be masking off additional data points.
+    // However, we do no want to modify the original mask vector.
+    tmpMask = psVectorAlloc(myVector->n, PS_TYPE_U8);
+
+    // If we were called with a mask vector, then initialize the temporary
+    // mask vector with those values.  Otherwise, initialize to zero.
+    if (maskVector != NULL) {
+        for (psS32 i = 0; i < tmpMask->n; i++) {
+            tmpMask->data.U8[i] = maskVector->data.U8[i];
+        }
+    } else {
+        for (psS32 i = 0; i < tmpMask->n; i++) {
+            tmpMask->data.U8[i] = 0;
+        }
+    }
+
+    // 1. Compute the sample median.
+    p_psVectorSampleMedian(myVector, maskVector, maskVal, statsTmp);
+    if (isnan(statsTmp->sampleMedian)) {
+        psLogMsg(__func__, PS_LOG_WARN, "Call to p_psVectorSampleMedian returned NAN\n");
+        stats->clippedMean = NAN;
+        stats->clippedStdev = NAN;
+        return(-2);
+    }
+
+    // 2. Compute the sample standard deviation.
+    p_psVectorSampleStdev(myVector, errors, maskVector, maskVal, statsTmp);
+    if (isnan(statsTmp->sampleStdev)) {
+        psLogMsg(__func__, PS_LOG_WARN, "Call to p_psVectorSampleStdev returned NAN\n");
+        stats->clippedMean = NAN;
+        stats->clippedStdev = NAN;
+        return(-2);
+    }
+
+    // 3. Use the sample median as the first estimator of the mean X.
+    clippedMean = statsTmp->sampleMedian;
+
+    // 4. Use the sample stdev as the first estimator of the mean stdev.
+    clippedStdev = statsTmp->sampleStdev;
+
+    // 5. Repeat N (stats->clipIter) times:
+    for (psS32 iter = 0; iter < stats->clipIter; iter++) {
+        // a) Exclude all values x_i for which |x_i - x| > K * stdev
+        if (errors != NULL) {
+            for (psS32 j = 0; j < myVector->n; j++) {
+                if (fabs(myVector->data.F32[j] - clippedMean) >
+                        (stats->clipSigma * errors->data.F32[j])) {
+                    tmpMask->data.U8[j] = 0xff;
+                }
+            }
+        } else {
+            for (psS32 j = 0; j < myVector->n; j++) {
+                if (fabs(myVector->data.F32[j] - clippedMean) >
+                        (stats->clipSigma * clippedStdev)) {
+                    tmpMask->data.U8[j] = 0xff;
+                }
+            }
+        }
+
+        // b) compute new mean and stdev
+        p_psVectorSampleMean(myVector, errors, tmpMask, 0xff, statsTmp);
+        p_psVectorSampleStdev(myVector, errors, tmpMask, 0xff, statsTmp);
+
+        // If the new mean and stdev are NAN, we must exit the loop.
+        // Otherwise, use the new results and continue.
+        if (isnan(statsTmp->sampleMean) || isnan(statsTmp->sampleStdev)) {
+            // Exit loop.  XXX: Should we throw an error/warning here?
+            iter = stats->clipIter;
+            rc = -1;
+        } else {
+            clippedMean = statsTmp->sampleMean;
+            clippedStdev = statsTmp->sampleStdev;
+        }
+    }
+
+    // 7. The last calcuated value of x is the cliped mean.
+    if (stats->options & PS_STAT_CLIPPED_MEAN) {
+        stats->clippedMean = clippedMean;
+    }
+    // 8. The last calcuated value of stdev is the cliped stdev.
+    if (stats->options & PS_STAT_CLIPPED_STDEV) {
+        stats->clippedStdev = clippedStdev;
+    }
+
+    psFree(tmpMask);
+    return(rc);
+}
+
+/*****************************************************************************
+These macros and functions define the following functions:
+ 
+<    p_psNormalizeVectorRange(myData, low, high)
+ 
+That assumes that the low/high arguments are PS_TYPE_F64; the vector myData
+can be of any type.  Arguments low/high will be converted to the appropriate
+type and one of the type-specific functions below will be called:
+ 
+    p_psNormalizeVectorRangeU8(myData, low, high)
+    p_psNormalizeVectorRangeU16(myData, low, high)
+    p_psNormalizeVectorRangeU32(myData, low, high)
+    p_psNormalizeVectorRangeU64(myData, low, high)
+    p_psNormalizeVectorRangeS8(myData, low, high)
+    p_psNormalizeVectorRangeS16(myData, low, high)
+    p_psNormalizeVectorRangeS32(myData, low, high)
+    p_psNormalizeVectorRangeS64(myData, low, high)
+    p_psNormalizeVectorRangeF32(myData, low, high)
+    p_psNormalizeVectorRangeF64(myData, low, high)
+ *****************************************************************************/
+#define PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(TYPE) \
+void p_psNormalizeVectorRange##TYPE(psVector* myData, \
+                                    ps##TYPE outLow, \
+                                    ps##TYPE outHigh) \
+{ \
+    ps##TYPE min = (ps##TYPE) PS_MAX_##TYPE; \
+    ps##TYPE max = (ps##TYPE) -PS_MAX_##TYPE; \
+    psS32 i = 0; \
+    \
+    for (i = 0; i < myData->n; i++) { \
+        if (myData->data.TYPE[i] < min) { \
+            min = myData->data.TYPE[i]; \
+        } \
+        if (myData->data.TYPE[i] > max) { \
+            max = myData->data.TYPE[i]; \
+        } \
+    } \
+    \
+    /* Ensure that max!=min before we divide by (max-min) */ \
+    if (max != min) { \
+        for (i = 0; i < myData->n; i++) { \
+            myData->data.TYPE[i] = (outLow + (myData->data.TYPE[i] - min) * \
+                                    (outHigh - outLow) / (max - min)); \
+        } \
+    } else { \
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: (max==min).  Setting all elements to min.\n"); \
+        for (i = 0; i < myData->n; i++) \
+        { \
+            \
+            myData->data.TYPE[i] = outLow; \
+            \
+        } \
+    } \
+} \
+
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U8)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U16)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U32)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(U64)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S8)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S16)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S32)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(S64)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(F32)
+PS_FUNC_MACRO_NORMALIZE_VECTOR_RANGE(F64)
+
+void p_psNormalizeVectorRange(psVector* myData,
+                              psF64 outLow,
+                              psF64 outHigh)
+{
+    switch (myData->type.type) {
+    case PS_TYPE_U8:
+        p_psNormalizeVectorRangeU8(myData, (psU8) outLow, (psU8) outHigh);
+        break;
+    case PS_TYPE_U16:
+        p_psNormalizeVectorRangeU16(myData, (psU16) outLow, (psU16) outHigh);
+        break;
+    case PS_TYPE_U32:
+        p_psNormalizeVectorRangeU32(myData, (psU32) outLow, (psU32) outHigh);
+        break;
+    case PS_TYPE_U64:
+        p_psNormalizeVectorRangeU64(myData, (psU64) outLow, (psU64) outHigh);
+        break;
+    case PS_TYPE_S8:
+        p_psNormalizeVectorRangeS8(myData, (psS8) outLow, (psS8) outHigh);
+        break;
+    case PS_TYPE_S16:
+        p_psNormalizeVectorRangeS16(myData, (psS16) outLow, (psS16) outHigh);
+        break;
+    case PS_TYPE_S32:
+        p_psNormalizeVectorRangeS32(myData, (psS32) outLow, (psS32) outHigh);
+        break;
+    case PS_TYPE_S64:
+        p_psNormalizeVectorRangeS64(myData, (psS64) outLow, (psS64) outHigh);
+        break;
+    case PS_TYPE_F32:
+        p_psNormalizeVectorRangeF32(myData, (psF32) outLow, (psF32) outHigh);
+        break;
+    case PS_TYPE_F64:
+        p_psNormalizeVectorRangeF64(myData, (psF64) outLow, (psF64) outHigh);
+        break;
+    case PS_TYPE_C32:
+    case PS_TYPE_C64:
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                "Unallowable operation: %s has incorrect type.",
+                myData);
+        break;
+    }
+}
+
+/******************************************************************************
+p_ps1DPolyMedian(myPoly, rangeLow, rangeHigh, midpoint): This routine takes
+as input a 1-D polynomial of arbitrary order (though we are using 2nd-order
+polynomials here) and a range of x-values for which it is defined:
+[rangeLow, rangeHigh].  It determines the x-value of that polynomial such
+that f(x) == midpoint.  This functions uses a binary-search algorithm on the
+range and assumes that the polynomial is monotonically increasing or
+decreasing within that range.
+ 
+XXX: Terminate when f(x)-getThisValue is within some error tolerance.
+ 
+XXX: Create a 2nd-order polynomial version and solve for X analytically.
+ *****************************************************************************/
+psF32 p_ps1DPolyMedian(psPolynomial1D* myPoly,
+                       psF32 rangeLow,
+                       psF32 rangeHigh,
+                       psF32 getThisValue)
+{
+    PS_POLY_CHECK_NULL(myPoly, NAN);
+    PS_FLOAT_COMPARE(rangeLow, rangeHigh, NAN);
+    // We ensure that the requested f(y) value, which is getThisValue, is
+    // falls within the range of y-values of the polynomial "myPoly" in the
+    // specified x-range (rangeLow:rangeHigh).
+    psF32 fLo = psPolynomial1DEval(
+                    myPoly,
+                    rangeLow
+                );
+    psF32 fHi = psPolynomial1DEval(
+                    myPoly,
+                    rangeHigh
+                );
+    if (!((fLo <= getThisValue) && (fHi >= getThisValue))) {
+        psError(PS_ERR_UNKNOWN,
+                true,
+                PS_ERRORTEXT_psStats_STATS_POLY_MEDIAN_OUT_OF_RANGE);
+        return(NAN);
+    }
+
+    psS32 numIterations = 0;
+    psF32 midpoint = 0.0;
+    psF32 oldMidpoint = 1.0;
+    psF32 f = 0.0;
+
+    while (numIterations < PS_POLY_MEDIAN_MAX_ITERATIONS) {
+        midpoint = (rangeHigh + rangeLow) / 2.0;
+        if (fabs(midpoint - oldMidpoint) <= FLT_EPSILON) {
+            return (midpoint);
+        }
+        oldMidpoint = midpoint;
+
+        f = psPolynomial1DEval(
+                myPoly,
+                midpoint
+            );
+        if (fabs(f - getThisValue) <= FLT_EPSILON) {
+            return (midpoint);
+        }
+
+        if (f > getThisValue) {
+            rangeHigh = midpoint;
+        } else {
+            rangeLow = midpoint;
+        }
+        numIterations++;
+    }
+    return (midpoint);
+}
+
+/******************************************************************************
+fitQuadraticSearchForYThenReturnX(*xVec, *yVec, binNum, yVal): A general
+routine which fits a quadratic to three points and returns the x-value
+corresponding to the input y-value.  This routine takes psVectors of x/y pairs
+as input, and fits a quadratic to the 3 points surrounding element binNum in
+the vectors (the midpoint between element i and i+1 is used for x[i]).  It
+then determines for what value x does that quadratic f(x) = yVal (the input
+parameter).
+ 
+XXX: After you fit the polynomial, solve for X analytically.
+ 
+XXX: the vectors do not have to be the same length.  Must insert the proper
+tests to ensure that binNum is within acceptable ranges for both vectors.
+*****************************************************************************/
+psF32 fitQuadraticSearchForYThenReturnX(psVector *xVec,
+                                        psVector *yVec,
+                                        psS32 binNum,
+                                        psF32 yVal)
+{
+    PS_VECTOR_CHECK_NULL(xVec, NAN);
+    PS_VECTOR_CHECK_NULL(yVec, NAN);
+    PS_VECTOR_CHECK_TYPE(xVec, PS_TYPE_F32, NAN);
+    PS_VECTOR_CHECK_TYPE(yVec, PS_TYPE_F32, NAN);
+    //    PS_VECTOR_CHECK_SIZE_EQUAL(xVec, yVec, NAN);
+    PS_INT_CHECK_RANGE(binNum, 0, (xVec->n - 1), NAN);
+    PS_INT_CHECK_RANGE(binNum, 0, (yVec->n - 1), NAN);
+
+    //    PS_VECTOR_DECLARE_ALLOC_STATIC(x, 3, PS_TYPE_F64);
+    //    PS_VECTOR_DECLARE_ALLOC_STATIC(y, 3, PS_TYPE_F64);
+    //    PS_VECTOR_DECLARE_ALLOC_STATIC(yErr, 3, PS_TYPE_F64);
+    //    PS_POLY_1D_DECLARE_ALLOC_STATIC(myPoly, 2, PS_POLYNOMIAL_ORD);
+    psVector *x = psVectorAlloc(3, PS_TYPE_F64);
+    psVector *y = psVectorAlloc(3, PS_TYPE_F64);
+    psVector *yErr = psVectorAlloc(3, PS_TYPE_F64);
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(2, PS_POLYNOMIAL_ORD);
+
+    psF32 tmpFloat = 0.0f;
+
+    if ((binNum > 0) && (binNum < (yVec->n - 2))) {
+        // The general case.  We have all three points.
+        x->data.F64[0] = (psF64) (0.5 * (xVec->data.F32[binNum - 1] + xVec->data.F32[binNum]));
+        x->data.F64[1] = (psF64) (0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum+1]));
+        x->data.F64[2] = (psF64) (0.5 * (xVec->data.F32[binNum+1] + xVec->data.F32[binNum+2]));
+        y->data.F64[0] = yVec->data.F32[binNum - 1];
+        y->data.F64[1] = yVec->data.F32[binNum];
+        y->data.F64[2] = yVec->data.F32[binNum + 1];
+
+        // Ensure that yVal is within the range of the bins we are using.
+        if (!((y->data.F64[0] <= yVal) && (yVal <= y->data.F64[2]))) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psStats_YVAL_OUT_OF_RANGE,
+                    (psF64)yVal,y->data.F64[2],y->data.F64[0]);
+        }
+        yErr->data.F64[0] = 1.0;
+        yErr->data.F64[1] = 1.0;
+        yErr->data.F64[2] = 1.0;
+
+        // Determine the coefficients of the polynomial.
+        myPoly = psVectorFitPolynomial1D(myPoly, x, y, yErr);
+        if (myPoly == NULL) {
+            psError(PS_ERR_UNEXPECTED_NULL,
+                    false,
+                    PS_ERRORTEXT_psStats_STATS_FIT_QUADRATIC_POLYNOMIAL_1D_FIT);
+            psFree(myPoly);
+            psFree(x);
+            psFree(y);
+            psFree(yErr);
+            return(NAN);
+        }
+        // Call p_ps1DPolyMedian(), which does a binary search on the
+        // polynomial, looking for the value x such that f(x) = yVal
+        tmpFloat = p_ps1DPolyMedian(myPoly, x->data.F64[0], x->data.F64[2], yVal);
+        if (isnan(tmpFloat)) {
+            psError(PS_ERR_UNEXPECTED_NULL,
+                    false,
+                    PS_ERRORTEXT_psStats_STATS_FIT_QUADRATIC_POLY_MEDIAN);
+            psFree(myPoly);
+            psFree(x);
+            psFree(y);
+            psFree(yErr);
+            return(NAN);
+        }
+
+    } else {
+        // The special case where we have two points only at the beginning of
+        // the vectors x and y.
+        if (binNum == 0) {
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
+                              xVec->data.F32[binNum + 1]);
+        } else if (binNum == (xVec->n - 1)) {
+            // The special case where we have two points only at the end of
+            // the vectors x and y.
+            // XXX: Is this right?
+            tmpFloat = xVec->data.F32[binNum];
+        } else if (binNum == (xVec->n - 2)) {
+            // XXX: Is this right?
+            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
+                              xVec->data.F32[binNum + 1]);
+        }
+    }
+
+    psFree(myPoly);
+    psFree(x);
+    psFree(y);
+    psFree(yErr);
+    return(tmpFloat);
+}
+
+/******************************************************************************
+p_psVectorRobustStats(myVector, maskVector, maskVal, stats): this procedure
+calculates a variety of robust stat measures:
+    PS_STAT_ROBUST_MEAN
+    PS_STAT_ROBUST_MEDIAN
+    PS_STAT_ROBUST_MODE
+    PS_STAT_ROBUST_STDEV
+    PS_STAT_ROBUST_QUARTILE
+I have included all that computation in a single function, as opposed to
+breaking it across several functions for one primary reason:  they all require
+the same basic initial processing steps (calculate the histogram, etc.).
+ 
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    0 on success.
+ 
+XXX: Check for errors in psLib routines that we call.
+*****************************************************************************/
+psS32 p_psVectorRobustStats(const psVector* myVector,
+                            const psVector* errors,
+                            const psVector* maskVector,
+                            psU32 maskVal,
+                            psStats* stats)
+{
+    psHistogram* robustHistogram = NULL;
+    psVector* robustHistogramVector = NULL;
+    psF32 binSize = 0.0;        // Size of the histogram bins
+    psS32 LQBinNum = -1;          // Bin num for lower quartile
+    psS32 UQBinNum = -1;          // Bin num for upper quartile
+    psS32 medianBinNum = -1;
+    psS32 i = 0;                  // Loop index variable
+    psS32 modeBinNum = 0;
+    psF32 modeBinCount = 0.0;
+    psF32 dL = 0.0;
+    psS32 numBins = 0;
+    psF32 myMean = 0.0;
+    psF32 myStdev = 0.0;
+    psF32 countFloat = 0.0;
+    psF32 diff = 0.0;
+    psF32 sumSquares = 0.0;
+    psF32 sumDiffs = 0.0;
+    psVector* cumulativeRobustSums = NULL;
+    psF32 sumRobust = 0.0;
+    psF32 sumN50 = 0.0;
+    psF32 sumNfit = 0.0;
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F32;
+    psStats* tmpStats = psStatsAlloc(PS_STAT_CLIPPED_STDEV | PS_STAT_CLIPPED_MEAN);
+
+    // Compute the initial bin size of the robust histogram.  This is done
+    // by computing the clipped standard deviation of the vector, and dividing
+    // that by 10.0;
+    //XXX: add errors
+    psS32 rc = p_psVectorClippedStats(myVector, NULL, maskVector, maskVal, tmpStats);
+    if (rc != 0) {
+        psError(PS_ERR_UNEXPECTED_NULL,
+                false,
+                PS_ERRORTEXT_psStats_ROBUST_STATS_CLIPPED_STATS);
+        return(1);
+    }
+    binSize = tmpStats->clippedStdev / 10.0f;
+
+    // If stats->clippedStdev == 0.0, then all data elements have the same
+    // value.  Therefore, we can set the appropiate results and return.
+    if (fabs(binSize) <= FLT_EPSILON) {
+        if (stats->options & PS_STAT_ROBUST_MEAN) {
+            stats->robustMean = tmpStats->clippedMean;
+        }
+        if (stats->options & PS_STAT_ROBUST_MEDIAN) {
+            stats->robustMedian = tmpStats->clippedMean;
+        }
+        if (stats->options & PS_STAT_ROBUST_MODE) {
+            stats->robustMode = tmpStats->clippedMean;
+        }
+        if (stats->options & PS_STAT_ROBUST_STDEV) {
+            stats->robustStdev = 0.0;
+        }
+        if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+            stats->robustUQ = tmpStats->clippedMean;
+            stats->robustLQ = tmpStats->clippedMean;
+        }
+        // XXX: Set these to the number of unmasked data points?
+        stats->robustNfit = 0.0;
+        stats->robustN50 = 0.0;
+        psFree(tmpStats);
+        return(0);
+    }
+
+    // Determine minimum and maximum values in the data vector.
+    if (isnan(tmpStats->min)) {
+        if (0 != p_psVectorMin(myVector, maskVector, maskVal, tmpStats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: p_psVectorMin(): p_psVectorMin() reported a NAN mean.\n");
+            return(1);
+        }
+    }
+    if (isnan(tmpStats->max)) {
+        if (0 != p_psVectorMax(myVector, maskVector, maskVal, tmpStats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: p_psVectorMin(): p_psVectorMax() reported a NAN mean.\n");
+            return(1);
+        }
+    }
+
+    // Create the histogram structure.  NOTE: we can not specify the bin size
+    // precisely since the argument to psHistogramAlloc() is the number of
+    // bins, not the binSize.  Also, if we get here, we know that
+    // binSize != 0.0.
+    numBins = (psS32)((tmpStats->max - tmpStats->min) / binSize);
+    robustHistogram = psHistogramAlloc(tmpStats->min, tmpStats->max, numBins);
+
+    // Populate the histogram array.
+    psVectorHistogram(robustHistogram, myVector, errors, maskVector, maskVal);
+
+    // Smooth the histogram, Gaussian-style.
+    robustHistogramVector = p_psVectorSmoothHistGaussian(robustHistogram,
+                            tmpStats->clippedStdev / 4.0f);
+
+    /**************************************************************************
+    Determine the median/lower/upper quartile bin numbers.
+
+    We define a vector called "cumulativeRobustSums" where the value at
+    index position i is equal to the sum of bins 0:i.  This will be used in
+    determining the median and lower/upper quartiles.
+    **************************************************************************/
+    cumulativeRobustSums = psVectorAlloc(robustHistogramVector->n, PS_TYPE_F32);
+    cumulativeRobustSums->data.F32[0] = robustHistogramVector->data.F32[0];
+    for (i = 1; i < robustHistogramVector->n; i++) {
+        cumulativeRobustSums->data.F32[i] = cumulativeRobustSums->data.F32[i - 1] +
+                                            robustHistogramVector->data.F32[i];
+    }
+    sumRobust = cumulativeRobustSums->data.F32[robustHistogramVector->n - 1];
+
+    tmpScalar.data.F32 = sumRobust / 4.0;
+    LQBinNum = p_psVectorBinDisect(cumulativeRobustSums, &tmpScalar);
+    tmpScalar.data.F32 = 3.0 * sumRobust / 4.0;
+    UQBinNum = p_psVectorBinDisect(cumulativeRobustSums, &tmpScalar);
+    tmpScalar.data.F32 = sumRobust / 2.0;
+    medianBinNum = p_psVectorBinDisect(cumulativeRobustSums, &tmpScalar);
+
+    if ((LQBinNum < 0) || (UQBinNum < 0)) {
+        psError(PS_ERR_UNKNOWN, true,
+                PS_ERRORTEXT_psStats_ROBUST_QUARTILE_BINS_FAILED);
+        return(1);
+    }
+    if (medianBinNum < 0) {
+        psError(PS_ERR_UNKNOWN, true,
+                PS_ERRORTEXT_psStats_ROBUST_QUARTILE_BINS_FAILED);
+        return(1);
+    }
+    /**************************************************************************
+    Determine the mode in the range LQ:UQ.
+    **************************************************************************/
+    // Determine the bin with the peak value in the range LQ to UQ.
+    modeBinNum = LQBinNum;
+    modeBinCount = robustHistogramVector->data.F32[LQBinNum];
+    sumN50 = robustHistogram->nums->data.F32[LQBinNum];
+    for (i = LQBinNum + 1; i <= UQBinNum; i++) {
+        if (robustHistogramVector->data.F32[i] > modeBinCount) {
+            modeBinNum = i;
+            modeBinCount = robustHistogramVector->data.F32[i];
+        }
+        sumN50 += robustHistogram->nums->data.F32[i];
+    }
+
+    dL = (UQBinNum - LQBinNum) / 4;
+    /**************************************************************************
+    Determine the mean/stdev for the bins in the range mode-dL to mode+dL
+    **************************************************************************/
+    // Calculate the mean of the smoothed robust histogram in the range
+    // mode-dL to mode+dL.  We use the midpoint of each bin as the mean for
+    // that bin (this is a non-exact approximation).
+    sumNfit = 0.0;
+    myMean = 0.0;
+    for (i = modeBinNum - dL; i <= modeBinNum + dL; i++) {
+        if ((0 <= i) && (i < robustHistogramVector->n)) {
+            myMean += (robustHistogramVector->data.F32[i]) * PS_BIN_MIDPOINT(robustHistogram, i);
+            countFloat += robustHistogramVector->data.F32[i];
+        }
+
+        sumNfit += robustHistogram->nums->data.F32[i];
+    }
+    // XXX: divide by zero?
+    myMean /= countFloat;
+
+    // Calculate the stdev of the smoothed robust histogram in the range
+    // mode-dL to mode+dL.  We use the midpoint of each bin as the mean for
+    // that bin.
+    for (i = modeBinNum - dL; i <= modeBinNum + dL; i++) {
+        if ((0 <= i) && (i < robustHistogramVector->n)) {
+            diff = PS_BIN_MIDPOINT(robustHistogram, i) - myMean;
+            sumSquares += diff * diff * robustHistogramVector->data.F32[i];
+            sumDiffs += diff * robustHistogramVector->data.F32[i];
+        }
+    }
+    myStdev = PS_SQRT_F32((sumSquares - (sumDiffs * sumDiffs / countFloat)) / (countFloat - 1));
+
+    p_psNormalizeVectorRangeF32(robustHistogramVector, 0.0, 1.0);
+
+    if ((stats->options & PS_STAT_ROBUST_MEAN) ||
+            (stats->options & PS_STAT_ROBUST_STDEV)) {
+
+        // Using the above (myMean, myStdev) as initial estimates, we fit a
+        // Gaussian to the robustHistogramVector.
+        psImage *covar = NULL;
+        /* XXX: Old, remove.
+                psMinimization *min = psMinimizationAlloc(100, 0.1);
+                psVector *myParams = psVectorAlloc(2, PS_TYPE_F32);
+                psArray *myCoords = psArrayAlloc(robustHistogramVector->n);
+                psVector *y = psVectorAlloc(robustHistogramVector->n, PS_TYPE_F32);
+                myParams->data.F32[0] = myMean;
+                myParams->data.F32[1] = myStdev;
+                for (i=0;i<robustHistogramVector->n;i++) {
+                    myCoords->data[i] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+                    ((psVector *) (myCoords->data[i]))->data.F32[0] = (psF32) i;
+                    y->data.F32[i] = robustHistogramVector->data.F32[i];
+                }
+        */
+
+        psMinimization *min = psMinimizationAlloc(100, 0.01);
+        psVector *myParams = psVectorAlloc(2, PS_TYPE_F32);
+        psArray *myCoords = psArrayAlloc(2 * dL + 1);
+        psVector *y = psVectorAlloc(2 * dL + 1, PS_TYPE_F32);
+        myParams->data.F32[0] = myMean;
+        myParams->data.F32[1] = myStdev;
+
+        for (i = modeBinNum - dL; i <= modeBinNum + dL; i++) {
+            int index = i - modeBinNum + dL;
+            myCoords->data[index] = (psPtr *) psVectorAlloc(2, PS_TYPE_F32);
+            ((psVector *) (myCoords->data[index]))->data.F32[0] = PS_BIN_MIDPOINT(robustHistogram, i);
+            y->data.F32[index] = robustHistogramVector->data.F32[i];
+        }
+
+
+        psBool rc = psMinimizeLMChi2(min,
+                                     NULL,
+                                     myParams,
+                                     NULL,
+                                     myCoords,
+                                     y,
+                                     NULL,
+                                     (psMinimizeLMChi2Func) psMinimizeLMChi2Gauss1D);
+        if (rc == false) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: failed to minimize with psMinimizeLMChi2().\n");
+        }
+
+        // XXX: Verify this with IfA
+        // XXX: The check on the minimization is better than the difference from myMean.
+        //      Do they still want this code?
+
+        if (stats->options & PS_STAT_ROBUST_MEAN) {
+            if (fabs((myParams->data.F32[0] - myMean)/myMean) > 0.1) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: the fitted Gaussian has more than 10%% error for the mean.\n");
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: Using the calculated mean instead of Gaussian-fitted mean.");
+                //                     "WARNING: Using the calculated mean instead of Gaussian-fitted mean.(calc, fit) is (%f, %f)\n",
+                //                     myMean, myParams->data.F32[0]);
+                stats->robustMean = myMean;
+            } else {
+                stats->robustMean = myParams->data.F32[0];
+            }
+        }
+
+        if (stats->options & PS_STAT_ROBUST_STDEV) {
+            if (fabs((myParams->data.F32[1] - myStdev)/myStdev) > 0.1) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: the fitted Gaussian has more than 10%% error for the stdev.\n");
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: Using the calculated stdev instead of Gaussian-fitted stdev.");
+                //                     "WARNING: Using the calculated stdev instead of Gaussian-fitted stdev.(calc, fit) is (%f, %f)\n",
+                //                     myStdev, myParams->data.F32[1]);
+                stats->robustStdev = myStdev;
+            } else {
+                stats->robustStdev = myParams->data.F32[1];
+            }
+        }
+        psFree(covar);
+        psFree(min);
+        psFree(myCoords);
+        psFree(y);
+        psFree(myParams);
+    }
+
+
+    /**************************************************************************
+    Set the appropriate members in the output stats struct.
+    **************************************************************************/
+
+    if (stats->options & PS_STAT_ROBUST_MODE) {
+        stats->robustMode = PS_BIN_MIDPOINT(robustHistogram, modeBinNum);
+    }
+
+
+    // To determine the median, we fit a quadratic y=f(x) to the three bins
+    // surrounding the bin containing the median (x is the midpoint of each
+    // bin and y is the value of each bin).  Then we figure out what value
+    // of x corresponds to f(x) being the median (half of all points).
+    if (stats->options & PS_STAT_ROBUST_MEDIAN) {
+        // Take a psVector.  Fit a polynomial y = f(x) to the 3 data elements
+        // surrounding medianBinNum, then find the x-value corresponding y = sumRobust/2.0.
+
+        //XXX: robustHistogram->bounds and cumulativeRobustSums are of different lengths.
+        //Determine id that is okay.
+        stats->robustMedian = fitQuadraticSearchForYThenReturnX(
+                                  robustHistogram->bounds,
+                                  cumulativeRobustSums,
+                                  medianBinNum,
+                                  sumRobust/2.0);
+    }
+
+    // To determine the quartiles, we fit a quadratic y=f(x) to the three bins
+    // surrounding the bin containing LQ/UQ (x is the midpoint of each
+    // bin and y is the value of each bin).  Then we figure out what value
+    // of x corresponds to f(x) being the LQ/UQ.
+    if (stats->options & PS_STAT_ROBUST_QUARTILE) {
+        countFloat = cumulativeRobustSums->data.F32[robustHistogramVector->n - 1];
+
+        //XXX: robustHistogram->bounds and cumulativeRobustSums are of different lengths.
+        //Determine id that is okay.
+        stats->robustLQ = fitQuadraticSearchForYThenReturnX(
+                              robustHistogram->bounds,
+                              cumulativeRobustSums,
+                              LQBinNum,
+                              countFloat/4.0);
+        //XXX: robustHistogram->bounds and cumulativeRobustSums are of different lengths.
+        //Determine id that is okay.
+        stats->robustUQ = fitQuadraticSearchForYThenReturnX(
+                              robustHistogram->bounds,
+                              cumulativeRobustSums,
+                              UQBinNum,
+                              3.0 * countFloat/4.0);
+    }
+    // XXX: I think sumNfit == sumN50 here.
+    stats->robustNfit = sumNfit;
+    stats->robustN50 = sumN50;
+
+    psFree(tmpStats);
+    psFree(robustHistogram);
+    psFree(robustHistogramVector);
+    psFree(cumulativeRobustSums);
+    return(0);
+}
+
+/*****************************************************************************/
+
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+
+/*****************************************************************************/
+
+static void histogramFree(psHistogram* myHist);
+
+/******************************************************************************
+    psStatsAlloc(): This routine must create a new psStats data structure.
+ *****************************************************************************/
+psStats* psStatsAlloc(psStatsOptions options)
+{
+    psStats* newStruct = NULL;
+
+    newStruct = (psStats* ) psAlloc(sizeof(psStats));
+    newStruct->sampleMean = NAN;
+    newStruct->sampleMedian = NAN;
+    newStruct->sampleStdev = NAN;
+    newStruct->sampleUQ = NAN;
+    newStruct->sampleLQ = NAN;
+    newStruct->robustMean = NAN;
+    newStruct->robustMedian = NAN;
+    newStruct->robustMode = NAN;
+    newStruct->robustStdev = NAN;
+    newStruct->robustUQ = NAN;
+    newStruct->robustLQ = NAN;
+    newStruct->robustN50 = 0;
+    newStruct->robustNfit = 0;
+    newStruct->clippedMean = NAN;
+    newStruct->clippedStdev = NAN;
+    newStruct->clipSigma = 3.0;
+    newStruct->clipIter = 3.0;
+    newStruct->min = NAN;
+    newStruct->max = NAN;
+    newStruct->binsize = NAN;
+    newStruct->options = options;
+
+    return (newStruct);
+}
+
+/******************************************************************************
+psHistogramAlloc(lower, upper, n): allocate a uniform histogram structure
+with the specifed upper and lower limits, and the specifed number of bins.
+This routine will also set the bounds for each of the bins.
+ 
+Input:
+    lower
+    upper
+    n
+Returns:
+    The histogram structure
+ *****************************************************************************/
+psHistogram* psHistogramAlloc(psF32 lower, psF32 upper, psS32 n)
+{
+    PS_INT_CHECK_POSITIVE(n, NULL);
+    PS_FLOAT_COMPARE(lower, upper, NULL);
+
+    psS32 i = 0;                  // Loop index variable
+    psHistogram* newHist = NULL;        // The new histogram structure
+    psF32 binSize = 0.0;        // The histogram bin size
+
+    // Allocate memory for the new histogram structure.  If there are N
+    // bins, then there are N+1 bounds to those bins.
+    newHist = (psHistogram* ) psAlloc(sizeof(psHistogram));
+    psMemSetDeallocator(newHist, (psFreeFcn) histogramFree);
+    newHist->bounds = psVectorAlloc(n + 1, PS_TYPE_F32);
+    newHist->bounds->n = newHist->bounds->nalloc;
+
+    // Calculate the bounds for each bin.
+    binSize = (upper - lower) / (psF32)n;
+    // XXX: Is the following necessary? It prevents the max data point
+    // from being in a non-existant bin.
+    binSize += FLT_EPSILON;
+    for (i = 0; i < n + 1; i++) {
+        newHist->bounds->data.F32[i] = lower + (binSize * (psF32)i);
+    }
+
+    // Allocate the bins, and initialize them to zero.
+    newHist->nums = psVectorAlloc(n, PS_TYPE_F32);
+    for (i = 0; i < newHist->nums->n; i++) {
+        newHist->nums->data.F32[i] = 0.0;
+    }
+
+    // Initialize the other members.
+    newHist->minNum = 0;
+    newHist->maxNum = 0;
+    newHist->uniform = true;
+
+    return (newHist);
+}
+
+/******************************************************************************
+psHistogramAllocGeneric(bounds): allocate a non-uniform histogram structure
+with the specifed bounds.
+ 
+Input:
+    bounds
+Returns:
+    The histogram structure
+ *****************************************************************************/
+psHistogram* psHistogramAllocGeneric(const psVector* bounds)
+{
+    PS_VECTOR_CHECK_NULL(bounds, NULL);
+    PS_VECTOR_CHECK_TYPE(bounds, PS_TYPE_F32, NULL);
+    PS_INT_COMPARE(2, bounds->n, NULL);
+
+    psHistogram* newHist = NULL;        // The new histogram structure
+    psS32 i;                      // Loop index variable
+
+    // Allocate memory for the new histogram structure.
+    newHist = (psHistogram* ) psAlloc(sizeof(psHistogram));
+    psMemSetDeallocator(newHist, (psFreeFcn) histogramFree);
+    newHist->bounds = psVectorAlloc(bounds->n, PS_TYPE_F32);
+    newHist->bounds->n = newHist->bounds->nalloc;
+    for (i = 0; i < bounds->n; i++) {
+        newHist->bounds->data.F32[i] = bounds->data.F32[i];
+    }
+
+    // Allocate the bins, and initialize them to zero.  If there are N bounds,
+    // then there are N-1 bins.
+    newHist->nums = psVectorAlloc((bounds->n) - 1, PS_TYPE_F32);
+    for (i = 0; i < newHist->nums->n; i++) {
+        newHist->nums->data.F32[i] = 0.0;
+    }
+
+    // Initialize the other members.
+    newHist->minNum = 0;
+    newHist->maxNum = 0;
+    newHist->uniform = false;
+
+    return (newHist);
+}
+
+static void histogramFree(psHistogram* myHist)
+{
+    psFree(myHist->bounds);
+    psFree(myHist->nums);
+}
+
+/*****************************************************************************
+UpdateHistogramBins(binNum, out, data, error): This routine is to be used when
+updating the histogram in the presence of errors in the input data.  We treat
+the data point as a boxcar PDF and update a range of points surrounding the
+histogram bin which contains the point.  The width of that boxcar is defined
+as 2.35 * error.  Inputs:
+    binNum: the bin number of the data point in the histogram
+    out: the histogram structure
+    data: the data point value
+    error: the error in that data point
+ 
+XXX: Must test this.
+ *****************************************************************************/
+psS32 UpdateHistogramBins(psS32 binNum,
+                          psHistogram* out,
+                          psF32 data,
+                          psF32 error)
+{
+    PS_PTR_CHECK_NULL(out, -1);
+    PS_PTR_CHECK_NULL(out->bounds, -1);
+    PS_PTR_CHECK_NULL(out->nums, -1);
+    PS_INT_CHECK_RANGE(binNum, 0, ((out->nums->n)-1), -2);
+    PS_FLOAT_COMPARE(0.0, error, -3);
+    PS_FLOAT_CHECK_RANGE(data, out->bounds->data.F32[0], out->bounds->data.F32[(out->bounds->n)-1], -4);
+
+    psF32 boxcarWidth = 2.35 * error;
+    psF32 boxcarCenter = (out->bounds->data.F32[binNum] +
+                          out->bounds->data.F32[binNum+1]) / 2.0;
+    psF32 boxcarLeft = boxcarCenter - (boxcarWidth / 2.0);
+    psF32 boxcarRight = boxcarCenter + (boxcarWidth / 2.0);
+    psS32 bin;
+    psS32 boxcarLeftBinNum = 0;
+    psS32 boxcarRightBinNum = 0;
+
+    // Determine the left endpoint of the boxcar for the PDF.
+    for (bin=binNum ; bin >= 0 ; bin--) {
+        if (out->nums->data.F32[bin] <= boxcarLeft) {
+            boxcarLeftBinNum = bin;
+            break;
+        }
+    }
+
+    // Determine the right endpoint of the boxcar for the PDF.
+    for (bin=binNum ; bin < out->nums->n ; bin++) {
+        if (out->nums->data.F32[bin] >= boxcarRight) {
+            boxcarRightBinNum = bin;
+            break;
+        }
+    }
+
+    //
+    // If the boxcar fits entirely inside this bin, then simply add 1.0 to the
+    // bin and return.
+    //
+    if (boxcarLeftBinNum == boxcarRightBinNum) {
+        out->nums->data.F32[binNum]+= 1.0;
+        return(0);
+    }
+
+    //
+    // If we get here, multiple bins must be updated.  We handle the left
+    // endpoint, and right endpoint differently.
+    //
+    out->nums->data.F32[boxcarLeftBinNum]+=
+        (out->bounds->data.F32[boxcarLeftBinNum+1] - boxcarLeft) / boxcarWidth;
+
+    //
+    // Loop through the center bins, if any.
+    //
+    for (bin = boxcarLeftBinNum + 1 ; bin < (boxcarRightBinNum - 1) ; bin++) {
+        out->nums->data.F32[bin]+=
+            (out->bounds->data.F32[bin+1] - out->bounds->data.F32[bin]) / boxcarWidth;
+    }
+
+    //
+    // Handle the right endpoint differently.
+    //
+    out->nums->data.F32[boxcarRightBinNum]+=
+        (boxcarRight - out->bounds->data.F32[boxcarRightBinNum]) / boxcarWidth;
+
+    //
+    // Return 0 on success.
+    //
+    return(0);
+}
+
+
+/*****************************************************************************
+psVectorHistogram(out, in, errors, mask, maskVal): this procedure takes as
+input a preallocated and initialized histogram structure.  It fills the bins
+in that histogram structure in accordance with the input data "in" and the,
+possibly NULL, mask vector.
+ 
+Inputs:
+    out
+    in
+    mask
+    maskVal
+Returns:
+    The histogram structure "out".
+ *****************************************************************************/
+psHistogram* psVectorHistogram(psHistogram* out,
+                               const psVector* in,
+                               const psVector* errors,
+                               const psVector* mask,
+                               psU32 maskVal)
+{
+    PS_PTR_CHECK_NULL(out, NULL);
+    PS_VECTOR_CHECK_NULL(out->bounds, NULL);
+    PS_VECTOR_CHECK_TYPE(out->bounds, PS_TYPE_F32, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(out->bounds->n, NULL);
+    PS_VECTOR_CHECK_NULL(out->nums, NULL);
+    PS_VECTOR_CHECK_TYPE(out->nums, PS_TYPE_F32, NULL);
+    PS_INT_CHECK_NON_NEGATIVE(out->nums->n, NULL);
+    PS_VECTOR_CHECK_NULL(in, out);
+    if (mask != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(in, mask, NULL);
+        PS_VECTOR_CHECK_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    if (errors != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(in, errors, NULL);
+        PS_VECTOR_CHECK_TYPE(errors, in->type.type, NULL);
+    }
+
+    psS32 i = 0;                  // Loop index variable
+    psF32 binSize = 0.0;          // Histogram bin size
+    psS32 binNum = 0;             // A temporary bin number
+    psS32 numBins = 0;            // The total number of bins
+    psScalar tmpScalar;
+    tmpScalar.type.type = PS_TYPE_F32;
+    psVector* inF32 = NULL;
+    psVector* errorsF32 = NULL;
+    psS32 mustFreeVectorIn = 1;
+    psS32 mustFreeVectorErrors = 1;
+
+    // Convert input and errors vectors to F32 if necessary.
+    inF32 = p_psConvertToF32((psVector *) in);
+    if (inF32 == NULL) {
+        inF32 = (psVector *) in;
+        mustFreeVectorIn = 0;
+    }
+    errorsF32 = p_psConvertToF32((psVector *) errors);
+    if (errorsF32 == NULL) {
+        errorsF32 = (psVector *) errors;
+        mustFreeVectorErrors = 0;
+    }
+
+    numBins = out->nums->n;
+    for (i = 0; i < inF32->n; i++) {
+        // Check if this pixel is masked, and if so, skip it.
+        if ((mask == NULL) || ((mask != NULL) && (!(mask->data.U8[i] & maskVal)))) {
+            if (inF32->data.F32[i] < out->bounds->data.F32[0]) {
+                // If this pixel is below minimum value, count it, then skip.
+                out->minNum++;
+            } else if (inF32->data.F32[i] > out->bounds->data.F32[numBins]) {
+                // If this pixel is above maximum value, count it, then skip.
+                out->maxNum++;
+            } else {
+                // If this is a uniform histogram, determining the correct
+                // number is trivial.
+                if (out->uniform == true) {
+                    binSize = out->bounds->data.F32[1] - out->bounds->data.F32[0];
+                    binNum = (psS32)((inF32->data.F32[i] - out->bounds->data.F32[0]) / binSize);
+                    if (errorsF32 != NULL) {
+                        // XXX: Check return codes.
+                        UpdateHistogramBins(binNum, out,
+                                            inF32->data.F32[i],
+                                            errorsF32->data.F32[i]);
+                    } else {
+                        // XXX: This if-statement really shouldn't be necessary.
+                        // However, due to numerical lack of precision, we
+                        // occasionally produce a binNum outside the range.
+                        if (binNum >= out->nums->n) {
+                            binNum = out->nums->n - 1;
+                        }
+                        (out->nums->data.F32[binNum])+= 1.0;
+                    }
+
+                } else {
+                    // If this is a non-uniform histogram, determining the
+                    // correct bin number requires a bit more work.
+                    tmpScalar.data.F32 = inF32->data.F32[i];
+                    binNum = p_psVectorBinDisect(out->bounds, &tmpScalar);
+                    if (binNum < 0) {
+                        psLogMsg(__func__, PS_LOG_WARN,
+                                 "WARNING: psVectorHistogram(): element outside histogram bounds.\n");
+                    } else {
+                        if (errorsF32 != NULL) {
+                            // XXX: Check return codes.
+                            UpdateHistogramBins(binNum, out,
+                                                inF32->data.F32[i],
+                                                errors->data.F32[i]);
+                        } else {
+                            (out->nums->data.F32[binNum])+= 1.0;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if (mustFreeVectorIn == 1) {
+        psFree(inF32);
+    }
+    if (mustFreeVectorErrors == 1) {
+        psFree(errorsF32);
+    }
+    return (out);
+}
+
+/******************************************************************************
+p_psConvertToF32(in): this is the cheap way to support a variety of vector
+data types: we simply convert the input vector to F32 at the beginning, and
+write all of our functions in F32.  If the vast majority of all vector stat
+operations are F32 (or any other single type), then this is probably the
+best way to go.  Otherwise, when the algorithms stablize, we will then macro
+everything and put type support in the various stat functions.
+ 
+XXX: Should the default data type be F64?  Since we are buying Opterons...
+ *****************************************************************************/
+psVector* p_psConvertToF32(psVector* in)
+{
+    if (in == NULL) {
+        return(NULL);
+    }
+    psS32 i = 0;
+    psVector* tmp = NULL;
+
+    if (in->type.type == PS_TYPE_S8) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.S8[i];
+        }
+    } else if (in->type.type == PS_TYPE_S16) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32) in->data.S16[i];
+        }
+    } else if (in->type.type == PS_TYPE_S32) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.S32[i];
+        }
+    } else if (in->type.type == PS_TYPE_S64) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.S64[i];
+        }
+    } else if (in->type.type == PS_TYPE_U8) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U8[i];
+        }
+    } else if (in->type.type == PS_TYPE_U16) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U16[i];
+        }
+    } else if (in->type.type == PS_TYPE_U32) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U32[i];
+        }
+    } else if (in->type.type == PS_TYPE_U64) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.U64[i];
+        }
+    } else if (in->type.type == PS_TYPE_F64) {
+        tmp = psVectorAlloc(in->n, PS_TYPE_F32);
+        for (i = 0; i < in->n; i++) {
+            tmp->data.F32[i] = (psF32)in->data.F64[i];
+        }
+    } else if (in->type.type == PS_TYPE_F32) {
+        // do nothing
+    } else {
+        char* strType;
+        PS_TYPE_NAME(strType, in->type.type);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psStats_VECTOR_TYPE_UNSUPPORTED,
+                strType);
+    }
+    return (tmp);
+}
+
+/******************************************************************************
+psVectorStats(myVector, maskVector, maskVal, stats): this is the public API
+function which calls the above private stats functions based on what bits
+were set in stats->options.
+ 
+Inputs
+    myVector
+    maskVector
+    maskVal
+    stats
+Returns
+    The stats structure.
+ *****************************************************************************/
+psStats* psVectorStats(psStats* stats,
+                       const psVector* in,
+                       const psVector* errors,
+                       const psVector* mask,
+                       psU32 maskVal)
+{
+    PS_PTR_CHECK_NULL(stats, NULL);
+    PS_VECTOR_CHECK_NULL(in, stats);
+    if (mask != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(mask, in, stats);
+        PS_VECTOR_CHECK_TYPE(mask, PS_TYPE_U8, stats);
+    }
+    if (errors != NULL) {
+        PS_VECTOR_CHECK_SIZE_EQUAL(errors, in, stats);
+        PS_VECTOR_CHECK_TYPE(errors, in->type.type, stats);
+    }
+
+    psVector* inF32 = NULL;
+    psVector* errorsF32 = NULL;
+    psS32 mustFreeVectorIn = 1;
+    psS32 mustFreeVectorErrors = 1;
+
+    inF32 = p_psConvertToF32((psVector *) in);
+    if (inF32 == NULL) {
+        inF32 = (psVector *) in;
+        mustFreeVectorIn = 0;
+    }
+    errorsF32 = p_psConvertToF32((psVector *) errors);
+    if (errorsF32 == NULL) {
+        errorsF32 = (psVector *) errors;
+        mustFreeVectorErrors = 0;
+    }
+
+    if ((stats->options & PS_STAT_USE_RANGE) && (stats->min >= stats->max)) {
+        PS_FLOAT_COMPARE(stats->min, stats->max, stats);
+    }
+
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_MEAN) {
+        if (0 != p_psVectorSampleMean(inF32, errorsF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleMean() returned an error.\n");
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_MEDIAN) {
+        if (false == p_psVectorSampleMedian(inF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleMedian() returned an error.\n");
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_STDEV) {
+        if (0 != p_psVectorSampleMean(inF32, errorsF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleMean() returned an error.\n");
+        }
+        p_psVectorSampleStdev(inF32, errorsF32, mask, maskVal, stats);
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_SAMPLE_QUARTILE) {
+        if (false == p_psVectorSampleQuartiles(inF32, mask, maskVal, stats)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psVectorStats(): p_psVectorSampleQuartiles() returned an error.\n");
+        }
+    }
+    // Since the various robust stats quantities share much computation, they
+    // are grouped together in a single private function:
+    // p_psVectorRobustStats()
+    if ((stats->options & PS_STAT_ROBUST_MEAN) ||
+            (stats->options & PS_STAT_ROBUST_MEDIAN) ||
+            (stats->options & PS_STAT_ROBUST_MODE) ||
+            (stats->options & PS_STAT_ROBUST_STDEV) ||
+            (stats->options & PS_STAT_ROBUST_QUARTILE)) {
+        if (0 != p_psVectorRobustStats(inF32, errorsF32, mask, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psStats_STATS_FAILED);
+            // XXX: Set to NAN
+        }
+    }
+
+    // XXX: Different conditions for return -1 and -2?
+    if ((stats->options & PS_STAT_CLIPPED_MEAN) || (stats->options & PS_STAT_CLIPPED_STDEV)) {
+        psS32 rc = p_psVectorClippedStats(inF32, errorsF32, mask, maskVal, stats);
+        if (-1 == rc) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to calculate clipped statistics for input psVector.\n");
+            stats->clippedMean = NAN;
+            stats->clippedStdev = NAN;
+        } else if (-2 == rc) {
+            psLogMsg(__func__, PS_LOG_WARN, "Failed to calculate clipped statistics for input psVector.");
+            stats->clippedMean = NAN;
+            stats->clippedStdev = NAN;
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_MAX) {
+        if (0 != p_psVectorMax(inF32, mask, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to calculate vector maximum");
+            stats->max = NAN;
+        }
+    }
+    // ************************************************************************
+    if (stats->options & PS_STAT_MIN) {
+        if (0 != p_psVectorMin(inF32, mask, maskVal, stats)) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to calculate vector minimum");
+            stats->min = NAN;
+        }
+    }
+
+    if (mustFreeVectorIn == 1) {
+        psFree(inF32);
+    }
+    if (mustFreeVectorErrors == 1) {
+        psFree(errorsF32);
+    }
+    return (stats);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psStats.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psStats.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psStats.h	(revision 22271)
@@ -0,0 +1,191 @@
+
+/** @file  psStats.h
+ *  \brief basic statistical operations
+ *  @ingroup Stats
+ *
+xd *  This file will hold the definition of the histogram and stats data
+ *  structures.  It also contains prototypes for procedures which operate
+ *  on those data structures.
+ *
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.40 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-29 22:34:59 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_STATS_H)
+#define PS_STATS_H
+
+#include "psVector.h"
+
+/// @addtogroup Stats
+/// @{
+
+/******************************************************************************
+    Statistical functions and data structures.
+ *****************************************************************************/
+
+/** enumeration of statistical calculation options
+ *
+ *  @see psStats, psVectorStats, psImageStats
+ */
+// XXX: Is PS_STAT_ROBUST_FOR_SAMPLE obsolete?
+typedef enum {
+    PS_STAT_SAMPLE_MEAN = 0x000001,
+    PS_STAT_SAMPLE_MEDIAN = 0x000002,
+    PS_STAT_SAMPLE_STDEV = 0x000004,
+    PS_STAT_SAMPLE_QUARTILE = 0x000008,
+    PS_STAT_ROBUST_MEAN = 0x000010,
+    PS_STAT_ROBUST_MEDIAN = 0x000020,
+    PS_STAT_ROBUST_MODE = 0x000040,
+    PS_STAT_ROBUST_STDEV = 0x000080,
+    PS_STAT_ROBUST_QUARTILE = 0x000100,
+    PS_STAT_CLIPPED_MEAN = 0x000200,
+    PS_STAT_CLIPPED_STDEV = 0x000400,
+    PS_STAT_MAX =  0x000800,
+    PS_STAT_MIN =  0x001000,
+    PS_STAT_USE_RANGE =  0x002000,
+    PS_STAT_USE_BINSIZE = 0x004000,
+    PS_STAT_ROBUST_FOR_SAMPLE = 0x008000
+} psStatsOptions;
+
+/** This is the generic statistics structure.  It contails the data members
+    for the various statistic values.  It also contains the options member to
+    specifiy which statistics should be calculated. */
+typedef struct
+{
+    psF64 sampleMean;          ///< formal mean of sample
+    psF64 sampleMedian;        ///< formal median of sample
+    psF64 sampleStdev;         ///< standard deviation of sample
+    psF64 sampleUQ;            ///< upper quartile of sample
+    psF64 sampleLQ;            ///< lower quartile of sample
+    psF64 robustMean;          ///< robust mean of array
+    psF64 robustMedian;        ///< robust median of array
+    psF64 robustMode;          ///< Robust mode of array
+    psF64 robustStdev;         ///< robust standard deviation of array
+    psF64 robustUQ;            ///< robust upper quartile
+    psF64 robustLQ;            ///< robust lower quartile
+    psS32 robustN50;              ///<
+    psS32 robustNfit;             ///<
+    psF64 clippedMean;         ///< Nsigma clipped mean
+    psF64 clippedStdev;        ///< standard deviation after clipping
+    psS32 clippedNvalues;         ///< ???
+    psF64 clipSigma;           ///< Nsigma used for clipping; user input
+    psS32 clipIter;               ///< Number of clipping iterations; user input
+    psF64 min;                 ///< minimum data value in array
+    psF64 max;                 ///< maximum data value in array
+    psF64 binsize;             ///<
+    psStatsOptions options;     ///< bitmask of calculated values
+}
+psStats;
+
+/** Performs statistical calculations on a vector.
+ *
+ *  @return psStats*    the statistical results as specified by stats->options
+ */
+psStats* psVectorStats(
+    psStats* stats,    ///< stats structure defines stats to be calculated and how
+    const psVector* in,      ///< Vector to be analysed.
+    const psVector* errors,  ///< Errors.
+    const psVector* mask,    ///< Ignore elements where (maskVector & maskVal) != 0: must be INT or NULL
+    psU32 maskVal      ///< Only mask elements with one of these bits set in maskVector
+);
+
+/** Allocator of the psStats structure.
+ *
+ *  @return psStats*    A new psStats struct with the options member set to the
+ *                      value given.
+ */
+psStats* psStatsAlloc(
+    psStatsOptions options             ///< Statistics to calculate
+);
+
+/******************************************************************************
+    Histogram functions and data structures.
+ *****************************************************************************/
+
+/** The basic histogram structure which contains bounds and bins.
+ *
+ *  In this structure, the vector bounds specifies the boundaries of the 
+ *  histogram bins, and must of type psF32, while nums specifies the number 
+ *  of entries in the bin, and must of type psU32. The value of bounds.n must 
+ *  therefore be 1 greater than than nums.n. The two values minNum and maxNum 
+ *  are the number of data values which fell below the lower limit bound or 
+ *  above the upper limit bound, respectively.
+ */
+typedef struct
+{
+    psVector* bounds;                  ///< Bounds for the bins (type F32)
+    psVector* nums;                    ///< Number in each of the bins (INT)
+    psS32 minNum;                        ///< Number below the minimum
+    psS32 maxNum;                        ///< Number above the maximum
+    psBool uniform;                      ///< Is it a uniform distribution?
+}
+psHistogram;
+
+/** Allocator for psHistogram where the bounds of the bins are implicitly
+ *  specified through simply specifying an upper and lower limit along with 
+ *  the size of the bins. 
+ *
+ *  @return psHistogram*    Newly allocated psHistogram
+ */
+psHistogram* psHistogramAlloc(
+    psF32 lower,                       ///< Lower limit for the bins
+    psF32 upper,                       ///< Upper limit for the bins
+    psS32 n                              ///< Number of bins
+);
+
+/** Allocator for psHistogram where the bounds of the bins are explicitly
+ *  specified. 
+ *
+ *  @return psHistogram*    Newly allocated psHistogram
+ */
+psHistogram* psHistogramAllocGeneric(
+    const psVector* bounds             ///< Bounds for the bins
+);
+
+/** Calculate a histogram
+ *
+ *  The following function populates the histogram bins from the specified 
+ *  vector (in). It alters and returns the histogram out structure. The input
+ *  vector may be of types psU8, psU16, psF32, psF64.
+ *
+ *  @return psHistogram*   histogram result
+ */
+psHistogram* psVectorHistogram(
+    psHistogram* out,                  ///< Histogram data
+    const psVector* in,                ///< Vector to analyse
+    const psVector* errors,            ///< Errors
+    const psVector* mask,              ///< Mask dat for input vector
+    psU32 maskVal                      ///< Mask value
+);
+
+/** Extracts the statistic value specified by stats->options.
+ *
+ *  @return psBool    If more than one statistic result is set in stats->options,
+ *                  false is returned and the value parameter is not set, 
+ *                  otherwise true is returned.
+ */
+psBool p_psGetStatValue(
+    const psStats* stats,
+    ///< the statistic struct to operate on
+
+    psF64 *value
+    ///< if return is true, this is set to the specified statistic value by stats->options
+);
+
+void p_psNormalizeVectorRangeU8(psVector* myData, psU8 low, psU8 high);
+void p_psNormalizeVectorRangeU16(psVector* myData, psU16 low, psU16 high);
+void p_psNormalizeVectorRangeU32(psVector* myData, psU32 low, psU32 high);
+void p_psNormalizeVectorRangeU64(psVector* myData, psU64 low, psU64 high);
+void p_psNormalizeVectorRangeS8(psVector* myData, psS8 low, psS8 high);
+void p_psNormalizeVectorRangeS16(psVector* myData, psS16 low, psS16 high);
+void p_psNormalizeVectorRangeS32(psVector* myData, psS32 low, psS32 high);
+void p_psNormalizeVectorRangeS64(psVector* myData, psS64 low, psS64 high);
+void p_psNormalizeVectorRangeF32(psVector* myData, psF32 low, psF32 high);
+void p_psNormalizeVectorRangeF64(psVector* myData, psF64 low, psF64 high);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psUnaryOp.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psUnaryOp.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psUnaryOp.c	(revision 22271)
@@ -0,0 +1,388 @@
+/** @file  psUnary.c
+ *
+ *  @brief Provides unary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.2.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************
+ *  INCLUDE FILES                                                             *
+ ******************************************************************************/
+#include <string.h>
+#include <complex.h>
+#include <math.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psVector.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psConstants.h"
+#include "psDataManipErrors.h"
+
+/*****************************************************************************
+ *  FUNCTION IMPLEMENTATION - LOCAL                                          *
+ *****************************************************************************/
+
+// Conversion for degrees to radians
+#define D2R 0.01745329252111111  /* PI/180 */
+
+// Conversion for radians to degrees
+#define R2D 57.29577950924861   /* 180.0/PI */
+
+
+// Unary SCALAR operations
+#define SCALAR(OUT,IN,OP,TYPE)                                                                               \
+{                                                                                                            \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    o  = &((psScalar* )OUT)->data.TYPE;                                                                      \
+    i1 = &((psScalar* )IN)->data.TYPE;                                                                       \
+    *o = OP;                                                                                                 \
+}
+
+// Unary IMAGE operations
+#define VECTOR(OUT,IN,OP,TYPE)                                                                               \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 nIn = 0;                                                                                           \
+    psS32 nOut = 0;                                                                                          \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    nIn = ((psVector* )IN)->n;                                                                               \
+    nOut = ((psVector* )OUT)->n;                                                                             \
+    if(nIn != nOut) {                                                                                        \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_COUNT_DIFFERS,                                                         \
+                nIn, nOut);                                                                                  \
+        if (OUT != IN) {                                                                                     \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    o  = ((psVector* )OUT)->data.TYPE;                                                                       \
+    i1 = ((psVector* )IN)->data.TYPE;                                                                        \
+    for(i = 0; i < nIn; i++, o++, i1++) {                                                                    \
+        *o = OP;                                                                                             \
+    }                                                                                                        \
+}
+
+// Unary IMAGE operations
+#define IMAGE(OUT,IN,OP,TYPE)                                                                                \
+{                                                                                                            \
+    psS32 i = 0;                                                                                             \
+    psS32 j = 0;                                                                                             \
+    psS32 numRowsIn = 0;                                                                                     \
+    psS32 numColsIn = 0;                                                                                     \
+    psS32 numRowsOut = 0;                                                                                    \
+    psS32 numColsOut = 0;                                                                                    \
+    ps##TYPE *o = NULL;                                                                                      \
+    ps##TYPE *i1 = NULL;                                                                                     \
+    numRowsIn = ((psImage* )IN)->numRows;                                                                    \
+    numColsIn = ((psImage* )IN)->numCols;                                                                    \
+    numRowsOut = ((psImage* )OUT)->numRows;                                                                  \
+    numColsOut = ((psImage* )OUT)->numCols;                                                                  \
+    if(numRowsIn!=numRowsOut || numColsIn!=numColsOut) {                                                     \
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,                                                             \
+                PS_ERRORTEXT_psMatrix_IMAGE_SIZE_DIFFERS,                                                    \
+                numColsIn, numRowsIn, numColsOut, numRowsOut);                                               \
+        if (OUT != IN) {                                                                                     \
+            psFree(OUT);                                                                                     \
+        }                                                                                                    \
+        return NULL;                                                                                         \
+    }                                                                                                        \
+    for(j = 0; j < numRowsIn; j++) {                                                                         \
+        o  = ((psImage* )OUT)->data.TYPE[j];                                                                 \
+        i1 = ((psImage* )IN)->data.TYPE[j];                                                                  \
+        for(i = 0; i < numColsIn; i++, o++, i1++) {                                                          \
+            *o = OP;                                                                                         \
+        }                                                                                                    \
+    }                                                                                                        \
+}
+
+// Preprocessor macro function to create arithmetic function based on input type
+#define UNARY_TYPE(DIM,OUT,IN,OP)                                                                            \
+switch (IN->type) {                                                                                          \
+case PS_TYPE_S32:                                                                                            \
+    DIM(OUT,IN,OP,S32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_F32:                                                                                            \
+    DIM(OUT,IN,OP,F32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_F64:                                                                                            \
+    DIM(OUT,IN,OP,F64);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_C32:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_S8:                                                                                             \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U8:                                                                                             \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_S16:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U16:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U32:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_S64:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_U64:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+case PS_TYPE_C64:                                                                                            \
+    DIM(OUT,IN,OP,C32);                                                                                      \
+    break;                                                                                                   \
+default: {                                                                                                     \
+        char* strType;                                                                                           \
+        PS_TYPE_NAME(strType, IN->type);                                                                         \
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,                                                                 \
+                PS_ERRORTEXT_psMatrix_TYPE_MISMATCH,                                                             \
+                strType);                                                                                        \
+        if (OUT != IN) {                                                                                         \
+            psFree(OUT);                                                                                         \
+        }                                                                                                        \
+        return NULL;                                                                                             \
+    } \
+}
+
+// Preprocessor macro function to create arithmetic function operation name. Functions below that add
+// FLT_EPSILON are done so to align results with a 64 bit computing architecture
+#define UNARY_OP(DIM,OUT,IN,OP)                                                                              \
+if(!strncmp(OP, "abs", 3)) {                                                                                 \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cabs(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,fabs(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "exp", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cexp(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,exp(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "ln", 2)) {                                                                           \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,clog(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,log(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "ten", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cpow(10.0,*i1));                                                               \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,pow(10.0,*i1));                                                                \
+    }                                                                                                        \
+} else if(!strncmp(OP, "log", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,clog(*i1)/log(10.0));                                                          \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,log10(*i1));                                                                   \
+    }                                                                                                        \
+} else if(!strncmp(OP, "sin", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,csin(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,sin(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dsin", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,csin(*i1*D2R));                                                                \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,sin(*i1*D2R));                                                                 \
+    }                                                                                                        \
+} else if(!strncmp(OP, "cos", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ccos(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cos(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dcos", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ccos(*i1*D2R));                                                                \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cos(*i1*D2R));                                                                 \
+    }                                                                                                        \
+} else if(!strncmp(OP, "tan", 3)) {                                                                          \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ctan(*i1));                                                                    \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,tan(*i1));                                                                     \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dtan", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,ctan(*i1*D2R));                                                                \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,tan(*i1*D2R));                                                                 \
+    }                                                                                                        \
+} else if(!strncmp(OP, "asin", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,casin(*i1));                                                                   \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,asin(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dasin", 5)) {                                                                        \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*casin(*i1));                                                               \
+    } else if(PS_IS_PSELEMTYPE_INT(IN->type)) {                                                              \
+        UNARY_TYPE(DIM,OUT,IN,(R2D*asin(*i1)));                                                              \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,(R2D*asin(*i1)));                                                              \
+    }                                                                                                        \
+} else if(!strncmp(OP, "acos", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,cacos(*i1));                                                                   \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,acos(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "dacos", 5)) {                                                                        \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*cacos(*i1));                                                               \
+    } else if(PS_IS_PSELEMTYPE_INT(IN->type)) {                                                              \
+        UNARY_TYPE(DIM,OUT,IN,(R2D*acos(*i1)));                                                              \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*acos(*i1));                                                                \
+    }                                                                                                        \
+} else if(!strncmp(OP, "atan", 4)) {                                                                         \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,catan(*i1));                                                                   \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,atan(*i1));                                                                    \
+    }                                                                                                        \
+} else if(!strncmp(OP, "datan", 5)) {                                                                        \
+    if(PS_IS_PSELEMTYPE_COMPLEX(IN->type)) {                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*catan(*i1));                                                               \
+    } else {                                                                                                 \
+        UNARY_TYPE(DIM,OUT,IN,R2D*atan(*i1));                                                                \
+    }                                                                                                        \
+} else {                                                                                                     \
+    psError(PS_ERR_BAD_PARAMETER_VALUE, true,                                                                \
+            PS_ERRORTEXT_psMatrix_OPERATION_UNSUPPORTED,                                                     \
+            OP);                                                                                             \
+    if (OUT != IN) {                                                                                         \
+        psFree(OUT);                                                                                         \
+    }                                                                                                        \
+    return NULL;                                                                                             \
+}
+
+psPtr psUnaryOp(psPtr out, const psPtr in, const char *op)
+{
+    #define psUnaryOp_EXIT { \
+                             if (out != in) { \
+                             psFree(out); \
+                             } \
+                             return NULL; \
+                           }
+
+    psType* psTypeIn = (psType* ) in;
+
+    PS_PTR_CHECK_NULL_GENERAL(in, psUnaryOp_EXIT);
+    PS_PTR_CHECK_NULL_GENERAL(op, psUnaryOp_EXIT);
+
+    psDimen dimIn = psTypeIn->dimen;
+    psElemType elTypeIn = psTypeIn->type;
+
+    switch (dimIn) {
+    case PS_DIMEN_SCALAR:
+        if (out == NULL ||
+                ((psType*)out)->dimen != PS_DIMEN_SCALAR ||
+                ((psScalar*)out)->type.type != elTypeIn) {
+            psFree(out);
+            out = psScalarAlloc(0.0,elTypeIn);
+        }
+        UNARY_OP(SCALAR, out, psTypeIn, op);    // scalar
+        break;
+    case PS_DIMEN_VECTOR:
+    case PS_DIMEN_TRANSV:
+        if (((psVector*)in)->n == 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psMatrix_VECTOR_EMPTY);
+            psUnaryOp_EXIT;
+        }
+
+        out = psVectorRecycle(out,
+                              ((psVector*)in)->n,
+                              elTypeIn);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psMatrix_OUTPUT_VECTOR_NOT_CREATED);
+            psUnaryOp_EXIT;
+        }
+
+        UNARY_OP(VECTOR, out, psTypeIn, op);    // vector
+        break;
+    case PS_DIMEN_IMAGE:
+        if (((psImage* ) in)->numCols == 0 || ((psImage* ) in)->numRows == 0) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psMatrix_IMAGE_EMPTY);
+            psUnaryOp_EXIT;
+        }
+
+        out = psImageRecycle(out,
+                             ((psImage*)in)->numCols,
+                             ((psImage*)in)->numRows,
+                             elTypeIn);
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psMatrix_OUTPUT_IMAGE_NOT_CREATED);
+            psUnaryOp_EXIT;
+        }
+
+        UNARY_OP(IMAGE, out, psTypeIn, op);     // image
+        break;
+    default:
+        if (out != in) {
+            psFree(out);
+        }
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                PS_ERRORTEXT_psMatrix_DIMEN_INVALID,
+                "in", dimIn);
+        psUnaryOp_EXIT;
+    }
+
+    // Automtically free psScalar types, since they are usually allocated in the argument list when this
+    // function is called, provided that the input is not the output.
+    if(psTypeIn->dimen==PS_DIMEN_SCALAR && in!=out) {
+        psFree(in);
+    }
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/math/psUnaryOp.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/math/psUnaryOp.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/math/psUnaryOp.h	(revision 22271)
@@ -0,0 +1,69 @@
+/** @file  psUnaryOp.h
+ *
+ *  @brief Provides unary functions for simple matrix and vector element operations. Functions
+ *  include:
+ *
+ *      Addition (+)
+ *      Subtraction (-)
+ *      Multiplication (*)
+ *      Division (/)
+ *      Power (^)
+ *      Minimum (min)
+ *      Maximum (max)
+ *      Absolute value (abs)
+ *      Exponent (exp)
+ *      Natural Log (ln)
+ *      Power of 10 (ten)
+ *      Log (log)
+ *      Sine (sin or dsin)
+ *      Cosine (cos or dcos)
+ *      Tangent (tan or dtan)
+ *      Arcsine (asin or dasin)
+ *      Arccosine (acos or dacos)
+ *      Arctan (atan or datan)
+ *
+ *  Currently only vector-vector and image-image binary operations are supported.
+ *
+ *  @ingroup MatrixArithmetic
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSUNARY_OP_H
+#define PSUNARY_OP_H
+
+/// @addtogroup MatrixArithmetic
+/// @{
+
+
+/** Perform simple unary arithmetic with images or vectors
+ *
+ *  Performs absolute value, exponent, natural log, power of 10, log, sine, cosine, tangent, arcsine,
+ *  arccosine, or arctan. operations with images and vectors. Uses the form:
+ *
+ *     out = op(in),
+ *
+ *     Where op is: "abs", "exp", "ln", "ten", "log", "sin", "cos", "tan" "asin", "acos", "atan", "dsin",
+ *                  "dcos", dtan", "dasin", "dacos", or "datan".
+ *
+ *  Trigometric Operations with "d" prefix use units of degrees. Those without are in radians.
+ *
+ *  This function only supports vector-vector or image-image opertions.
+ *
+ *  @return  psType* : Pointer to either psImage or psVector.
+ */
+psType* psUnaryOp(
+    psPtr out,                         ///< Output type, either psImage or psVector.
+    const psPtr in,   ///< Input, either psImage or psVector.
+    const char *op   ///< operator.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psImage.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psImage.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psImage.c	(revision 22271)
@@ -0,0 +1,805 @@
+/** @file  psImage.c
+ *
+ *  @brief Contains basic image definitions and operations.
+ *
+ *  This file defines the basic type for an image struct and functions useful
+ *  in manupulating images.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.65.2.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  That is the routine used to generate matrices.
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psImage.h"
+#include "psString.h"
+
+#include "psImageErrors.h"
+
+#define SQUARE(x) ((x)*(x))
+#define MIN(x,y) (((x) > (y)) ? (y) : (x))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+static void imageFree(psImage* image)
+{
+    if (image == NULL) {
+        return;
+    }
+
+    if (image->parent != NULL) {
+        psArrayRemove(image->parent->children,image);
+        image->parent = NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psFree(image->rawDataBuffer);
+    psFree(image->data.V);
+}
+
+psImage* psImageAlloc(psU32 numCols,
+                      psU32 numRows,
+                      const psElemType type)
+{
+    psS32 area = 0;
+    psS32 elementSize = PSELEMTYPE_SIZEOF(type);  // element size in bytes
+    psS32 rowSize = numCols * elementSize;        // row size in bytes.
+
+    area = numCols * numRows;
+
+    if (area < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_AREA_NEGATIVE,
+                numRows, numCols);
+        return NULL;
+    }
+
+    psImage* image = (psImage* ) psAlloc(sizeof(psImage));
+
+    psMemSetDeallocator(image, (psFreeFcn) imageFree);
+
+    image->data.V = psAlloc(sizeof(psPtr ) * numRows);
+
+    image->rawDataBuffer = psAlloc(area * elementSize);
+
+    // set the row pointers.
+    image->data.V[0] = image->rawDataBuffer;
+    for (psS32 i = 1; i < numRows; i++) {
+        image->data.V[i] = (psPtr )((int8_t *) image->data.V[i - 1] + rowSize);
+    }
+
+    *(psS32 *)&image->col0 = 0;
+    *(psS32 *)&image->row0 = 0;
+    *(psU32 *)&image->numCols = numCols;
+    *(psU32 *)&image->numRows = numRows;
+    *(psDimen* ) & image->type.dimen = PS_DIMEN_IMAGE;
+    *(psElemType* ) & image->type.type = type;
+    image->parent = NULL;
+    image->children = NULL;
+
+    return image;
+}
+
+psRegion* psRegionAlloc(psF32 x0,
+                        psF32 x1,
+                        psF32 y0,
+                        psF32 y1)
+{
+    psRegion* out = psAlloc(sizeof(psRegion));
+
+    out->x0 = x0;
+    out->y0 = y0;
+    out->x1 = x1;
+    out->y1 = y1;
+
+    return out;
+}
+
+psRegion* psRegionFromString(const char* region)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (region == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(region,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                region);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return psRegionAlloc(col0,col1,row0,row1);
+}
+
+char* psRegionToString(const psRegion* region)
+{
+    char tmpText[256]; // big enough to store any region as text
+
+    if (region == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_REGION_NULL);
+        return NULL;
+    }
+
+    snprintf(tmpText,256,"[%g:%g,%g:%g]",
+             region->x0, region->x1,
+             region->y0, region->y1);
+
+    return psStringCopy(tmpText);
+}
+
+psImage* psImageRecycle(psImage* old,
+                        psU32 numCols,
+                        psU32 numRows,
+                        const psElemType type)
+{
+    psS32 elementSize = PSELEMTYPE_SIZEOF(type);  // element size in bytes
+    psS32 rowSize = numCols * elementSize;        // row size in bytes.
+
+    if (old == NULL) {
+        old = psImageAlloc(numCols, numRows, type);
+        return old;
+    }
+
+    if (old->type.dimen != PS_DIMEN_IMAGE) {
+        psFree(old);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return NULL;
+    }
+
+    /* image already the right size/type? */
+    if (numCols == old->numCols && numRows == old->numRows &&
+            type == old->type.type) {
+        return old;
+    }
+    // Resize the image buffer
+    old->rawDataBuffer = psRealloc(old->data.V[0],
+                                   numCols * numRows * elementSize);
+    old->data.V = (psPtr *)psRealloc(old->data.V, numRows * sizeof(psPtr ));
+
+    // recreate the row pointers
+    old->data.V[0] = old->rawDataBuffer;
+    for (psS32 i = 1; i < numRows; i++) {
+        old->data.V[i] = (psPtr )((int8_t *) old->data.V[i - 1] + rowSize);
+    }
+
+    *(psU32 *)&old->numCols = numCols;
+    *(psU32 *)&old->numRows = numRows;
+    *(psElemType* ) & old->type.type = type;
+
+    return old;
+}
+
+psImage* psImageCopy(psImage* output,
+                     const psImage* input,
+                     psElemType type)
+{
+    psElemType inDatatype;
+    psS32 elementSize;
+    psS32 elements;
+    psS32 numRows;
+    psS32 numCols;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        psFree(output);
+        return NULL;
+    }
+
+    if (input == output) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_INPLACE_NOTSUPPORTED);
+        psFree(output);
+        return NULL;
+    }
+
+    if (input->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        psFree(output);
+        return NULL;
+    }
+
+    inDatatype = input->type.type;
+    numRows = input->numRows;
+    numCols = input->numCols;
+    elements = numRows * numCols;
+    elementSize = PSELEMTYPE_SIZEOF(inDatatype);
+
+    output = psImageRecycle(output, numCols, numRows, type);
+
+    // Preserve col0,row0
+    output->col0 = input->col0;
+    output->row0 = input->row0;
+
+    // cover the trival case of copy of the same
+    // datatype.
+    if (type == inDatatype) {
+        for (psS32 row=0;row<numRows;row++) {
+            memcpy(output->data.V[row], input->data.V[row], elementSize * numCols);
+        }
+        return output;
+    }
+
+    #define PSIMAGE_ELEMENT_COPY(IN,INTYPE,OUT,OUTTYPE,ELEMENTS) { \
+        ps##INTYPE *in; \
+        ps##OUTTYPE *out; \
+        for(psS32 row=0;row<numRows;row++) { \
+            in = IN->data.INTYPE[row]; \
+            out = OUT->data.OUTTYPE[row]; \
+            for (psS32 col=0;col<numCols;col++) { \
+                *(out++) = *(in++); \
+            } \
+        } \
+    }
+
+    #define PSIMAGE_COPY_CASE(OUT,OUTTYPE) { \
+        switch (inDatatype) { \
+        case PS_TYPE_S8: \
+            PSIMAGE_ELEMENT_COPY(input,S8,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_ELEMENT_COPY(input,S16,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S32: \
+            PSIMAGE_ELEMENT_COPY(input,S32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_S64: \
+            PSIMAGE_ELEMENT_COPY(input,S64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U8: \
+            PSIMAGE_ELEMENT_COPY(input,U8,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_ELEMENT_COPY(input,U16,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U32: \
+            PSIMAGE_ELEMENT_COPY(input,U32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_U64: \
+            PSIMAGE_ELEMENT_COPY(input,U64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_ELEMENT_COPY(input,F32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_ELEMENT_COPY(input,F64,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_ELEMENT_COPY(input,C32,OUT,OUTTYPE,elements); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_ELEMENT_COPY(input,C64,OUT,OUTTYPE,elements); \
+            break; \
+        default: \
+            break; \
+        } \
+    }
+
+    switch (type) {
+    case PS_TYPE_S8:
+        PSIMAGE_COPY_CASE(output, S8);
+        break;
+    case PS_TYPE_S16:
+        PSIMAGE_COPY_CASE(output, S16);
+        break;
+    case PS_TYPE_S32:
+        PSIMAGE_COPY_CASE(output, S32);
+        break;
+    case PS_TYPE_S64:
+        PSIMAGE_COPY_CASE(output, S64);
+        break;
+    case PS_TYPE_U8:
+        PSIMAGE_COPY_CASE(output, U8);
+        break;
+    case PS_TYPE_U16:
+        PSIMAGE_COPY_CASE(output, U16);
+        break;
+    case PS_TYPE_U32:
+        PSIMAGE_COPY_CASE(output, U32);
+        break;
+    case PS_TYPE_U64:
+        PSIMAGE_COPY_CASE(output, U64);
+        break;
+    case PS_TYPE_F32:
+        PSIMAGE_COPY_CASE(output, F32);
+        break;
+    case PS_TYPE_F64:
+        PSIMAGE_COPY_CASE(output, F64);
+        break;
+    case PS_TYPE_C32:
+        PSIMAGE_COPY_CASE(output, C32);
+        break;
+    case PS_TYPE_C64:
+        PSIMAGE_COPY_CASE(output, C64);
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            psFree(output);
+
+            break;
+        }
+    }
+    return output;
+}
+
+bool p_psImageCopyToRawBuffer(void* buffer,
+                              const psImage* input,
+                              psElemType type)
+{
+    psElemType inDatatype;
+    psS32 numRows;
+    psS32 numCols;
+
+    if (input == NULL || input->data.V == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return false;
+    }
+
+    if (input->type.dimen != PS_DIMEN_IMAGE) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psImage_NOT_AN_IMAGE);
+        return false;
+    }
+
+    inDatatype = input->type.type;
+    numRows = input->numRows;
+    numCols = input->numCols;
+
+    // cover the trival case of copy of the same
+    // datatype.
+    if (type == inDatatype) {
+        int rowSize = PSELEMTYPE_SIZEOF(inDatatype)*numCols;
+        for (psS32 row=0;row<numRows;row++) {
+            memcpy(&((psS8*)buffer)[row*rowSize], input->data.V[row], rowSize);
+        }
+        return true;
+    }
+
+    #define PSIMAGE_BUFFER_COPY(INTYPE,OUTTYPE) { \
+        ps##INTYPE *in; \
+        ps##OUTTYPE *out = buffer; \
+        for(psS32 row=0;row<numRows;row++) { \
+            in = input->data.INTYPE[row]; \
+            for (psS32 col=0;col<numCols;col++) { \
+                *(out++) = *(in++); \
+            } \
+        } \
+    }
+
+    #define PSIMAGE_BUFFER_COPY_CASE(OUT,OUTTYPE) { \
+        switch (inDatatype) { \
+        case PS_TYPE_S8: \
+            PSIMAGE_BUFFER_COPY(S8,OUTTYPE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSIMAGE_BUFFER_COPY(S16,OUTTYPE); \
+            break; \
+        case PS_TYPE_S32: \
+            PSIMAGE_BUFFER_COPY(S32,OUTTYPE); \
+            break; \
+        case PS_TYPE_S64: \
+            PSIMAGE_BUFFER_COPY(S64,OUTTYPE); \
+            break; \
+        case PS_TYPE_U8: \
+            PSIMAGE_BUFFER_COPY(U8,OUTTYPE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSIMAGE_BUFFER_COPY(U16,OUTTYPE); \
+            break; \
+        case PS_TYPE_U32: \
+            PSIMAGE_BUFFER_COPY(U32,OUTTYPE); \
+            break; \
+        case PS_TYPE_U64: \
+            PSIMAGE_BUFFER_COPY(U64,OUTTYPE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSIMAGE_BUFFER_COPY(F32,OUTTYPE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSIMAGE_BUFFER_COPY(F64,OUTTYPE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSIMAGE_BUFFER_COPY(C32,OUTTYPE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSIMAGE_BUFFER_COPY(C64,OUTTYPE); \
+            break; \
+        default: \
+            break; \
+        } \
+    }
+
+    switch (type) {
+    case PS_TYPE_S8:
+        PSIMAGE_BUFFER_COPY_CASE(output, S8);
+        break;
+    case PS_TYPE_S16:
+        PSIMAGE_BUFFER_COPY_CASE(output, S16);
+        break;
+    case PS_TYPE_S32:
+        PSIMAGE_BUFFER_COPY_CASE(output, S32);
+        break;
+    case PS_TYPE_S64:
+        PSIMAGE_BUFFER_COPY_CASE(output, S64);
+        break;
+    case PS_TYPE_U8:
+        PSIMAGE_BUFFER_COPY_CASE(output, U8);
+        break;
+    case PS_TYPE_U16:
+        PSIMAGE_BUFFER_COPY_CASE(output, U16);
+        break;
+    case PS_TYPE_U32:
+        PSIMAGE_BUFFER_COPY_CASE(output, U32);
+        break;
+    case PS_TYPE_U64:
+        PSIMAGE_BUFFER_COPY_CASE(output, U64);
+        break;
+    case PS_TYPE_F32:
+        PSIMAGE_BUFFER_COPY_CASE(output, F32);
+        break;
+    case PS_TYPE_F64:
+        PSIMAGE_BUFFER_COPY_CASE(output, F64);
+        break;
+    case PS_TYPE_C32:
+        PSIMAGE_BUFFER_COPY_CASE(output, C32);
+        break;
+    case PS_TYPE_C64:
+        PSIMAGE_BUFFER_COPY_CASE(output, C64);
+        break;
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+            break;
+        }
+    }
+    return true;
+}
+
+
+psS32 psImageFreeChildren(psImage* image)
+{
+    psS32 numFreed = 0;
+
+    if (image == NULL) {
+        return numFreed;
+    }
+
+    if (image->children != NULL) {
+        psImage** children = (psImage**)image->children->data;
+        numFreed = image->children->n;
+
+        // orphan the children first
+        // (so psFree doesn't try to modify the parent's children array while I'm using it)
+        for (psS32 i=0;i<numFreed;i++) {
+            children[i]->parent = NULL;
+        }
+
+        psFree(image->children);
+        image->children = NULL;
+    }
+
+    return numFreed;
+}
+
+psC64 psImagePixelInterpolate(const psImage* input,
+                              float x,
+                              float y,
+                              const psImage* mask,
+                              psU32 maskVal,
+                              psC64 unexposedValue,
+                              psImageInterpolateMode mode)
+{
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,
+                PS_ERRORTEXT_psImage_IMAGE_NULL);
+        return unexposedValue;
+    }
+
+    #define PSIMAGE_PIXEL_INTERPOLATE_CASE(TYPE)                             \
+case PS_TYPE_##TYPE:                                                 \
+    switch (mode) {                                                  \
+    case PS_INTERPOLATE_FLAT:                                        \
+        return p_psImagePixelInterpolateFLAT_##TYPE(                 \
+                input,                                               \
+                x,                                                   \
+                y,                                                   \
+                mask,                                                \
+                maskVal,                                             \
+                unexposedValue);                                     \
+        break;                                                       \
+    case PS_INTERPOLATE_BILINEAR:                                    \
+        return p_psImagePixelInterpolateBILINEAR_##TYPE(             \
+                input,                                               \
+                x,                                                   \
+                y,                                                   \
+                mask,                                                \
+                maskVal,                                             \
+                unexposedValue);                                     \
+        break;                                                       \
+    case PS_INTERPOLATE_BILINEAR_VARIANCE:                           \
+        return p_psImagePixelInterpolateBILINEAR_VARIANCE_##TYPE(    \
+                input,                                               \
+                x,                                                   \
+                y,                                                   \
+                mask,                                                \
+                maskVal,                                             \
+                unexposedValue);                                     \
+        break;                                                       \
+    default:                                                         \
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,                     \
+                PS_ERRORTEXT_psImage_INTERPOLATE_METHOD_INVALID,     \
+                mode);                                               \
+    }                                                                \
+    break;
+
+    switch (input->type.type) {
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U8);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U16);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(U64);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S8);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S16);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(S64);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(F32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(F64);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(C32);
+        PSIMAGE_PIXEL_INTERPOLATE_CASE(C64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,input->type.type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true,
+                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                    typeStr);
+        }
+    }
+
+    return unexposedValue;
+}
+
+psF64 p_psImageGetElementF64(psImage* image,
+                             int col,
+                             int row)
+{
+    if (image == NULL) {
+        return NAN;
+    }
+    if (col < 0 || col >= image->numCols) {
+        return NAN;
+    }
+    if (row < 0 || row >= image->numRows) {
+        return NAN;
+    }
+
+    switch (image->type.type) {
+    case PS_TYPE_U8:
+        return image->data.U8[row][col];
+        break;
+    case PS_TYPE_U16:
+        return image->data.U16[row][col];
+        break;
+    case PS_TYPE_U32:
+        return image->data.U32[row][col];
+        break;
+    case PS_TYPE_U64:
+        return image->data.U64[row][col];
+        break;
+    case PS_TYPE_S8:
+        return image->data.S8[row][col];
+        break;
+    case PS_TYPE_S16:
+        return image->data.S16[row][col];
+        break;
+    case PS_TYPE_S32:
+        return image->data.S32[row][col];
+        break;
+    case PS_TYPE_S64:
+        return image->data.S64[row][col];
+        break;
+    case PS_TYPE_F32:
+        return image->data.F32[row][col];
+        break;
+    case PS_TYPE_F64:
+        return image->data.F64[row][col];
+    default:
+        return NAN;
+    }
+}
+
+#define PSIMAGE_PIXEL_INTERPOLATE_FLAT(TYPE,RETURNTYPE) \
+inline RETURNTYPE p_psImagePixelInterpolateFLAT_##TYPE( \
+        const psImage* input, \
+        float x, \
+        float y, \
+        const psImage* mask, \
+        psU32 maskVal, \
+        RETURNTYPE unexposedValue) \
+{ \
+    psS32 intX = (psS32) round((psF64)(x) - 0.5 + FLT_EPSILON); \
+    psS32 intY = (psS32) round((psF64)(y) - 0.5 + FLT_EPSILON); \
+    psS32 lastX = input->numCols - 1; \
+    psS32 lastY = input->numRows - 1; \
+    \
+    if ((intX < 0) || \
+            (intX > lastX) || \
+            (intY < 0) || \
+            (intY > lastY) || \
+            ( (mask!=NULL) && \
+              ((mask->data.PS_TYPE_MASK_DATA[intY][intX] & maskVal) != 0) ) ) { \
+        return unexposedValue; \
+    } \
+    \
+    return input->data.TYPE[intY][intX]; \
+}
+
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U8,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U16,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U32,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(U64,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S8,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S16,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S32,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(S64,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(F32,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(F64,psF64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(C32,psC64)
+PSIMAGE_PIXEL_INTERPOLATE_FLAT(C64,psC64)
+
+#define PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(TYPE, RETURNTYPE, SUFFIX, FRACFUNC) \
+inline RETURNTYPE p_psImagePixelInterpolateBILINEAR_##SUFFIX( \
+        const psImage* input, \
+        float x, \
+        float y, \
+        const psImage* mask, \
+        psU32 maskVal, \
+        RETURNTYPE unexposedValue) \
+{ \
+    double floorX = floor((psF64)(x) - 0.5); \
+    double floorY = floor((psF64)(y) - 0.5); \
+    psF64 fracX = x - 0.5 - floorX; \
+    psF64 fracY = y - 0.5 - floorY; \
+    psS32 intFloorX = (psS32) floorX; \
+    psS32 intFloorY = (psS32) floorY; \
+    psS32 lastX = input->numCols - 1; \
+    psS32 lastY = input->numRows - 1; \
+    ps##TYPE V00 = 0; \
+    ps##TYPE V01 = 0; \
+    ps##TYPE V10 = 0; \
+    ps##TYPE V11 = 0; \
+    psBool valid00 = false; \
+    psBool valid01 = false; \
+    psBool valid10 = false; \
+    psBool valid11 = false; \
+    \
+    if (intFloorY >= 0 && intFloorY <= lastY) { \
+        if (intFloorX >= 0 && intFloorX <= lastX) { \
+            V00 = input->data.TYPE[intFloorY][intFloorX]; \
+            valid00 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY][intFloorX] & maskVal) == 0); \
+        } \
+        if (intFloorX >= -1 && intFloorX < lastX) { \
+            V10 = input->data.TYPE[intFloorY][intFloorX+1]; \
+            valid10 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY][intFloorX+1] & maskVal) == 0); \
+        } \
+    } \
+    if (intFloorY >= -1 && intFloorY < lastY) { \
+        if (intFloorX >= 0 && intFloorX <= lastX) { \
+            V01 = input->data.TYPE[intFloorY+1][intFloorX]; \
+            valid01 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY+1][intFloorX] & maskVal) == 0); \
+        } \
+        if (intFloorX >= -1 && intFloorX < lastX) { \
+            V11 = input->data.TYPE[intFloorY+1][intFloorX+1]; \
+            valid11 = (mask == NULL) || \
+                      ((mask->data.PS_TYPE_MASK_DATA[intFloorY+1][intFloorX+1] & maskVal) == 0); \
+        } \
+    } \
+    \
+    /* cover likely case of all pixels being valid more efficiently */  \
+    if (valid00 && valid10 && valid01 && valid11) { \
+        /* formula from the ADD */ \
+        return V00*FRACFUNC((1.0-fracX)*(1.0-fracY)) + V10*FRACFUNC(fracX*(1.0-fracY)) + \
+               V01*FRACFUNC(fracY*(1.0-fracX)) + V11*FRACFUNC(fracX*fracY); \
+    } \
+    \
+    /* OK, at least one pixel is not valid - need to do it piecemeal */ \
+    \
+    RETURNTYPE V0 = 0.0; \
+    psBool valid0 = true; \
+    if (valid00 && valid10) { \
+        V0 = V00*FRACFUNC(1-fracX)+V10*FRACFUNC(fracX); \
+    } else if (valid00) { \
+        V0 = V00; \
+    } else if (valid10) { \
+        V0 = V10; \
+    } else { \
+        valid0 = false; \
+    } \
+    \
+    RETURNTYPE V1 = 0.0; \
+    psBool valid1 = true; \
+    if (valid01 && valid11) { \
+        V1 = V01*FRACFUNC(1-fracX)+V11*FRACFUNC(fracX); \
+    } else if (valid01) { \
+        V1 = V01; \
+    } else if (valid11) { \
+        V1 = V11; \
+    } else { \
+        valid1 = false; \
+    } \
+    \
+    if (valid0 && valid1) { \
+        return V0*FRACFUNC(1-fracY) + V1*FRACFUNC(fracY); \
+    } else if (valid0) { \
+        return V0; \
+    } else if (valid1) { \
+        return V1; \
+    } \
+    \
+    return unexposedValue; \
+}
+
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U8,psF64,U8,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U16,psF64,U16,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U32,psF64,U32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U64,psF64,U64,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S8,psF64,S8,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S16,psF64,S16,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S32,psF64,S32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S64,psF64,S64,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F32,psF64,F32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F64,psF64,F64,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C32,psC64,C32,)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C64,psC64,C64,)
+
+// Variance Version
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U8,psF64,VARIANCE_U8,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U16,psF64,VARIANCE_U16,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U32,psF64,VARIANCE_U32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(U64,psF64,VARIANCE_U64,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S8,psF64,VARIANCE_S8,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S16,psF64,VARIANCE_S16,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S32,psF64,VARIANCE_S32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(S64,psF64,VARIANCE_S64,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F32,psF64,VARIANCE_F32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(F64,psF64,VARIANCE_F64,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C32,psC64,VARIANCE_C32,SQUARE)
+PSIMAGE_PIXEL_INTERPOLATE_BILINEAR(C64,psC64,VARIANCE_C64,SQUARE)
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psImage.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psImage.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psImage.h	(revision 22271)
@@ -0,0 +1,244 @@
+/** @file  psImage.h
+ *
+ *  @brief Contains basic image definitions and operations
+ *
+ *  This file defines the basic type for an image struct and functions useful
+ *  in manupulating images.
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.51.2.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_IMAGE_H
+#define PS_IMAGE_H
+
+#include <complex.h>
+
+#include "psType.h"
+#include "psArray.h"
+
+/// @addtogroup Image
+/// @{
+
+/** enumeration of options in interpolation
+ *
+ */
+typedef enum {
+    PS_INTERPOLATE_FLAT,               ///< 'flat' interpolation (nearest pixel)
+    PS_INTERPOLATE_BILINEAR,           ///< bi-linear interpolation
+    PS_INTERPOLATE_LANCZOS2,           ///< Sinc interpolation with 4x4 pixel kernel
+    PS_INTERPOLATE_LANCZOS3,           ///< Sinc interpolation with 6x6 pixel kernel
+    PS_INTERPOLATE_LANCZOS4,           ///< Sinc interpolation with 8x8 pixel kernel
+    PS_INTERPOLATE_BILINEAR_VARIANCE,  ///< Variance version of PS_INTERPOLATE_BILINEAR
+    PS_INTERPOLATE_LANCZOS2_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS2
+    PS_INTERPOLATE_LANCZOS3_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS3
+    PS_INTERPOLATE_LANCZOS4_VARIANCE,  ///< Variance version of PS_INTERPOLATE_LANCZOS4
+    PS_INTERPOLATE_NUM_MODES           ///< enum end-marker; does not coorespond to a interpolation mode
+} psImageInterpolateMode;
+
+/** Basic image data structure.
+ *
+ * Struct for maintaining image data of varying types. It also contains
+ * information about image size, parent images and children images.
+ *
+ */
+typedef struct psImage
+{
+    const psType type;                 ///< Image data type and dimension.
+    const psU32 numCols;               ///< Number of columns in image
+    const psU32 numRows;               ///< Number of rows in image.
+    psS32 col0;          ///< Column position relative to parent.
+    psS32 row0;          ///< Row position relative to parent.
+
+    union {
+        psU8**  U8;                    ///< Unsigned 8-bit integer data.
+        psU16** U16;                   ///< Unsigned 16-bit integer data.
+        psU32** U32;                   ///< Unsigned 32-bit integer data.
+        psU64** U64;                   ///< Unsigned 64-bit integer data.
+        psS8**  S8;                    ///< Signed 8-bit integer data.
+        psS16** S16;                   ///< Signed 16-bit integer data.
+        psS32** S32;                   ///< Signed 32-bit integer data.
+        psS64** S64;                   ///< Signed 64-bit integer data.
+        psF32** F32;                   ///< Single-precision float data.
+        psF64** F64;                   ///< Double-precision float data.
+        psC32** C32;                   ///< Single-precision complex data.
+        psC64** C64;                   ///< Double-precision complex data.
+        psPtr** PTR;                   ///< Void pointers.
+        psPtr*  V;                     ///< Pointer to data.
+    } data;                            ///< Union for data types.
+    const struct psImage* parent;      ///< Parent, if a subimage.
+    psArray* children;                 ///< Children of this region.
+
+    psPtr rawDataBuffer;
+}
+psImage;
+
+/** Basic image region structure.
+ *
+ * Struct for specifying a rectangular area in an image.
+ *
+ */
+typedef struct
+{
+    psF32 x0;                         ///< the first column of the region.
+    psF32 x1;                         ///< the last column of the region.
+    psF32 y0;                         ///< the first row of the region.
+    psF32 y1;                         ///< the last row of the region.
+}
+psRegion;
+
+/** Create an image of the specified size and type.
+ *
+ * Uses psLib memory allocation functions to create an image struct of the
+ * specified size and type.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psImage* psImageAlloc(
+    psU32 numCols,                     ///< Number of rows in image.
+    psU32 numRows,                     ///< Number of columns in image.
+    const psElemType type              ///< Type of data for image.
+);
+
+/** Create a psRegion with the specified attributes.
+ *
+ * Uses psLib memory allocation functions to create a psRegion the
+ * specified x0, x1, y0, and y1.
+ *
+ * @return psRegion* : Pointer to psRegion.
+ *
+ */
+psRegion* psRegionAlloc(
+    psF32 x0,                         ///< the first column of the region.
+    psF32 x1,                         ///< the last column of the region.
+    psF32 y0,                         ///< the first row of the region.
+    psF32 y1                          ///< the last row of the region.
+);
+
+/** Create a psRegion with the attribute values given as a string.
+ *
+ *  Create a psRegion with the attribute values given as a string.  The format
+ *  shall be of the standard IRAF form '[x0:x1,y0:y1]'
+ *
+ *  @return psRegion*:  A new psRegion struct, or NULL is not successful.
+ */
+psRegion* psRegionFromString(
+    const char* region   ///< image rectangular region in the form '[x0:x1,y0:y1]'
+);
+
+/** Create a string of the standard IRAF form '[x0:x1,y0:y1]' from a psRegion.
+ *
+ *  @return char*:  A new string representing the psRegion as text, or NULL
+ *                  is not successful.
+ */
+char* psRegionToString(
+    const psRegion* region  ///< the psRegion to convert to a string
+);
+
+/** Resize a given image to the given size/type.
+ *
+ *  @return psImage* Resized psImage.
+ *
+ */
+psImage* psImageRecycle(
+    psImage* old,                      ///< the psImage to recycle by resizing image buffer
+    psU32 numCols,                     ///< the desired number of columns in image
+    psU32 numRows,                     ///< the desired number of rows in image
+    const psElemType type              ///< the desired datatype of the image
+);
+
+/** Makes a copy of a psImage
+ *
+ * @return psImage* Copy of the input psImage.  This may not be equal to the
+ * output parameter
+ *
+ */
+psImage* psImageCopy(
+    psImage* output,                   ///< if not NULL, a psImage that could be recycled.
+    const psImage* input,              ///< the psImage to copy
+    psElemType type                    ///< the desired datatype of the returned copy
+);
+
+bool p_psImageCopyToRawBuffer(
+    void* buffer,
+    const psImage* input,
+    psElemType type
+);
+
+/** Frees all children of a psImage.
+ *
+ *  @return psS32      Number of children freed.
+ *
+ */
+psS32 psImageFreeChildren(
+    psImage* image                     ///< psImage in which all children shall be deallocated
+);
+
+/** get an element of an image as a psF64.
+ *
+ *  @return psF64   pixel value at specified location
+ */
+psF64 p_psImageGetElementF64(
+    psImage* image,                    ///< input image
+    int col,                           ///< pixel column
+    int row                            ///< pixel row
+);
+
+/** Interpolate image pixel value given floating point coordinates.
+ *
+ *  @return psF32    Pixel value interpolated from image or unexposedValue if
+ *                   given x,y doesn't coorespond to a valid image location
+ */
+psC64 psImagePixelInterpolate(
+    const psImage* input,              ///< input image for interpolation
+    float x,                           ///< column location to derive value of
+    float y,                           ///< row location ot derive value of
+    const psImage* mask,               ///< if not NULL, the mask of the input image
+    psU32 maskVal,              ///< the mask value
+    psC64 unexposedValue,              ///< return value if x,y location is not in image.
+    psImageInterpolateMode mode        ///< interpolation mode
+);
+
+#define PIXEL_INTERPOLATE_FCN_PROTOTYPE(SUFFIX, RETURNTYPE) \
+inline RETURNTYPE p_psImagePixelInterpolate##SUFFIX( \
+        const psImage* input,          /**< input image for interpolation */ \
+        float x,                       /**< column location to derive value of */ \
+        float y,                       /**< row location ot derive value of */ \
+        const psImage* mask,           /**< if not NULL, the mask of the input image */ \
+        psU32 maskVal,                 /**< the mask value */ \
+        RETURNTYPE unexposedValue      /**< return value if x,y location is not in image. */ \
+                                                   );
+
+#define PIXEL_INTERPOLATE_FCNS(MODE) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U8,psF64)  \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U16,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U32,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_U64,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S8,psF64)  \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S16,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S32,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_S64,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_F32,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_F64,psF64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_C32,psC64) \
+PIXEL_INTERPOLATE_FCN_PROTOTYPE(MODE##_C64,psC64)
+
+#ifndef SWIG
+PIXEL_INTERPOLATE_FCNS(FLAT)
+PIXEL_INTERPOLATE_FCNS(BILINEAR)
+PIXEL_INTERPOLATE_FCNS(BILINEAR_VARIANCE)
+#endif // ! SWIG
+
+#undef PIXEL_INTERPOLATE_FCN_PROTOTYPE
+#undef PIXEL_INTERPOLATE_FCNS
+
+/// @}
+
+#endif // PS_IMAGE_H
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psScalar.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psScalar.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psScalar.c	(revision 22271)
@@ -0,0 +1,139 @@
+/** @file  psScalar.c
+ *
+ *  @brief Contains basic scalar definitions and operations
+ *
+ *  This file defines the basic type for a scalar struct and functions useful
+ *  in manupulating scalars.
+ * *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.14.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psScalar.h"
+#include "psLogMsg.h"
+#include "psAbort.h"
+
+#include "psCollectionsErrors.h"
+
+psScalar* psScalarAlloc(psC64 value, psElemType dataType)
+{
+    psScalar* scalar = NULL;
+
+    // Create scalar
+    scalar = (psScalar* ) psAlloc(sizeof(psScalar));
+
+    scalar->type.dimen = PS_DIMEN_SCALAR;
+    scalar->type.type = dataType;
+
+    switch (dataType) {
+    case PS_TYPE_S8:
+        scalar->data.S8 = (psS8) value;
+        break;
+    case PS_TYPE_U8:
+        scalar->data.U8 = (psU8) value;
+        break;
+    case PS_TYPE_S16:
+        scalar->data.S16 = (psS16) value;
+        break;
+    case PS_TYPE_U16:
+        scalar->data.U16 = (psU16) value;
+        break;
+    case PS_TYPE_S32:
+        scalar->data.S32 = (psS32) value;
+        break;
+    case PS_TYPE_U32:
+        scalar->data.U32 = (psU32) value;
+        break;
+    case PS_TYPE_S64:
+        scalar->data.S64 = (psS64) value;
+        break;
+    case PS_TYPE_U64:
+        scalar->data.U64 = (psU64) value;
+        break;
+    case PS_TYPE_F32:
+        scalar->data.F32 = (psF32) value;
+        break;
+    case PS_TYPE_F64:
+        scalar->data.F64 = (psF64) value;
+        break;
+    case PS_TYPE_C32:
+        scalar->data.C32 = (psC32) value;
+        break;
+    case PS_TYPE_C64:
+        scalar->data.C64 = (psC64) value;
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psScalar_UNSUPPORTED_TYPE,
+                dataType);
+        psFree(scalar);
+        return NULL;
+    }
+
+    return scalar;
+}
+
+psScalar* psScalarCopy(const psScalar *scalar)
+{
+    psElemType dataType;
+    psScalar *newScalar = NULL;
+
+    if (scalar == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psScalar_COPY_NULL);
+        return NULL;
+    }
+
+    dataType = scalar->type.type;
+    switch (dataType) {
+    case PS_TYPE_S8:
+        newScalar =  psScalarAlloc(scalar->data.S8, dataType);
+        break;
+    case PS_TYPE_U8:
+        newScalar =  psScalarAlloc(scalar->data.U8, dataType);
+        break;
+    case PS_TYPE_S16:
+        newScalar =  psScalarAlloc(scalar->data.S16, dataType);
+        break;
+    case PS_TYPE_U16:
+        newScalar =  psScalarAlloc(scalar->data.U16, dataType);
+        break;
+    case PS_TYPE_S32:
+        newScalar =  psScalarAlloc(scalar->data.S32, dataType);
+        break;
+    case PS_TYPE_U32:
+        newScalar =  psScalarAlloc(scalar->data.U32, dataType);
+        break;
+    case PS_TYPE_S64:
+        newScalar =  psScalarAlloc(scalar->data.S64, dataType);
+        break;
+    case PS_TYPE_U64:
+        newScalar =  psScalarAlloc(scalar->data.U64, dataType);
+        break;
+    case PS_TYPE_F32:
+        newScalar =  psScalarAlloc(scalar->data.F32, dataType);
+        break;
+    case PS_TYPE_F64:
+        newScalar =  psScalarAlloc(scalar->data.F64, dataType);
+        break;
+    case PS_TYPE_C32:
+        newScalar =  psScalarAlloc(scalar->data.C32, dataType);
+        break;
+    case PS_TYPE_C64:
+        newScalar =  psScalarAlloc(scalar->data.C64, dataType);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psScalar_UNSUPPORTED_TYPE,
+                dataType);
+        return NULL;
+    }
+
+    return newScalar;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psScalar.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psScalar.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psScalar.h	(revision 22271)
@@ -0,0 +1,84 @@
+
+/** @file  psScalar.h
+ *
+ *  @brief Contains basic scalar definitions and operations
+ *
+ *  This file defines the basic type for a scalar struct and functions useful
+ *  in manupulating scalars.
+ *
+ *  @ingroup Scalar
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.11.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_SCALAR_H
+#define PS_SCALAR_H
+
+#include "psType.h"
+
+/// @addtogroup Scalar
+/// @{
+
+/** Basic scalar data structure.
+ *
+ * Struct for maintaining a scalar of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psType type;                ///< Type of data.
+
+    union {
+        psU8 U8;                ///< Unsigned 8-bit integer data.
+        psU16 U16;              ///< Unsigned 16-bit integer data.
+        psU32 U32;              ///< Unsigned 32-bit integer data.
+        psU64 U64;              ///< Unsigned 64-bit integer data.
+        psS8 S8;                ///< Signed 8-bit integer data.
+        psS16 S16;              ///< Signed 16-bit integer data.
+        psS32 S32;              ///< Signed 32-bit integer data.
+        psS64 S64;              ///< Signed 64-bit integer data.
+        psF32 F32;              ///< Single-precision float data.
+        psF64 F64;              ///< Double-precision float data.
+        psC32 C32;              ///< Single-precision complex data.
+        psC64 C64;              ///< Double-precision complex data.
+    } data;                     ///< Union for data types.
+}
+psScalar;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Allocate a scalar.
+ *
+ * Uses psLib memory allocation functions to create scalar data as defined by the psType type.
+ * Accepts a complex 64 bit float for input value, as max size, but resizes according to
+ * correct type.
+ *
+ * @return psScalar*   Pointer to a new psScalar.
+ */
+psScalar* psScalarAlloc(
+    psC64 value,                       ///< Data to be put into psScalar.
+    psElemType dataType                ///< Type of data to be held by psScalar.
+);
+
+/** Copy a scalar.
+ *
+ * Uses psLib memory allocation functions to copy a scalar.
+ *
+ * @return psScalar*    A copy of the input scalar
+ */
+psScalar* psScalarCopy(
+    const psScalar *scalar  ///< Scalar to copy.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psVector.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psVector.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psVector.c	(revision 22271)
@@ -0,0 +1,539 @@
+/** @file  psVector.c
+*
+*  @brief Contains support for basic vector types
+*
+*  This file defines the basic type for a vector struct and functions useful
+*  in manupulating vectors.
+*
+*  @author Ross Harman, MHPCC
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.41 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-29 02:25:09 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <string.h>                        // for memcpy
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psVector.h"
+#include "psLogMsg.h"
+#include "psCompare.h"
+
+#include "psCollectionsErrors.h"
+
+typedef struct
+{
+    p_psVectorData data; // need this first for psVectorSortIndex to work.
+    psU32 index;
+}
+indexedVector;
+
+static void vectorFree(psVector* psVec);
+
+static void vectorFree(psVector* psVec)
+{
+    if (psVec == NULL) {
+        return;
+    }
+
+    psFree(psVec->data.U8);
+}
+
+// FUNCTION IMPLEMENTATION - PUBLIC
+
+psVector* psVectorAlloc(psU32 nalloc, psElemType elemType)
+{
+    psVector* psVec = NULL;
+    psS32 elementSize = 0;
+
+    elementSize = PSELEMTYPE_SIZEOF(elemType);
+
+    // Create vector struct
+    psVec = (psVector* ) psAlloc(sizeof(psVector));
+    psMemSetDeallocator(psVec, (psFreeFcn) vectorFree);
+
+    psVec->type.dimen = PS_DIMEN_VECTOR;
+    psVec->type.type = elemType;
+    *(int*)&psVec->nalloc = nalloc;
+    psVec->n = nalloc;
+
+    // Create vector data array
+    psVec->data.U8 = psAlloc(nalloc * elementSize);
+
+    return psVec;
+}
+
+psVector* psVectorRealloc(psVector* in, psU32 nalloc)
+{
+    psS32 elementSize = 0;
+    psElemType elemType;
+
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_REALLOC_NULL);
+        return NULL;
+    } else if (in->nalloc != nalloc) {     // No need to realloc to same size
+        elemType = in->type.type;
+        elementSize = PSELEMTYPE_SIZEOF(elemType);
+        if (nalloc < in->n) {
+            in->n = nalloc;
+        }
+        // Realloc after decrementation to avoid accessing freed array elements
+        in->data.U8 = psRealloc(in->data.U8, nalloc * elementSize);
+        *(int*)&in->nalloc = nalloc;
+    }
+
+    return in;
+}
+
+psVector* psVectorRecycle(psVector* in, psU32 n, psElemType type)
+{
+    psS32 byteSize;
+
+    if (in == NULL) {
+        return psVectorAlloc(n, type);
+    }
+
+    if (in->type.dimen !=  PS_DIMEN_VECTOR &&
+            in->type.dimen !=  PS_DIMEN_TRANSV) {
+        psFree(in);
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVector_NOT_A_VECTOR);
+        return NULL;
+    }
+
+    byteSize = n * PSELEMTYPE_SIZEOF(type);
+
+    // need to increase data buffer?
+    if (byteSize > in->nalloc*PSELEMTYPE_SIZEOF(in->type.type)) {
+        in->data.U8 = psRealloc(in->data.U8, byteSize);
+        *(int*)&in->nalloc = n;
+    }
+
+    in->type.dimen = PS_DIMEN_VECTOR;
+    in->type.type = type;
+    in->n = n;
+    return in;
+}
+
+psVector* psVectorCopy(psVector* out, const psVector* in, psElemType type)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_SORT_NULL);
+        psFree(out);
+        return NULL;
+    }
+
+    psS32 nElements = in->n;
+
+    out = psVectorRecycle(out, nElements, type);
+
+    #define PSVECTOR_COPY(INTYPE,OUTTYPE) { \
+        ps##INTYPE *inVec = in->data.INTYPE; \
+        ps##OUTTYPE *outVec = out->data.OUTTYPE; \
+        for (psS32 col=0;col<nElements;col++) { \
+            *(outVec++) = *(inVec++); \
+        } \
+    }
+
+    #define PSVECTOR_COPY_CASE(OUTTYPE) \
+case PS_TYPE_##OUTTYPE: { \
+        switch (in->type.type) { \
+        case PS_TYPE_S8: \
+            PSVECTOR_COPY(S8,OUTTYPE); \
+            break; \
+        case PS_TYPE_S16: \
+            PSVECTOR_COPY(S16,OUTTYPE); \
+            break; \
+        case PS_TYPE_S32: \
+            PSVECTOR_COPY(S32,OUTTYPE); \
+            break; \
+        case PS_TYPE_S64: \
+            PSVECTOR_COPY(S64,OUTTYPE); \
+            break; \
+        case PS_TYPE_U8: \
+            PSVECTOR_COPY(U8,OUTTYPE); \
+            break; \
+        case PS_TYPE_U16: \
+            PSVECTOR_COPY(U16,OUTTYPE); \
+            break; \
+        case PS_TYPE_U32: \
+            PSVECTOR_COPY(U32,OUTTYPE); \
+            break; \
+        case PS_TYPE_U64: \
+            PSVECTOR_COPY(U64,OUTTYPE); \
+            break; \
+        case PS_TYPE_F32: \
+            PSVECTOR_COPY(F32,OUTTYPE); \
+            break; \
+        case PS_TYPE_F64: \
+            PSVECTOR_COPY(F64,OUTTYPE); \
+            break; \
+        case PS_TYPE_C32: \
+            PSVECTOR_COPY(C32,OUTTYPE); \
+            break; \
+        case PS_TYPE_C64: \
+            PSVECTOR_COPY(C64,OUTTYPE); \
+            break; \
+        default: { \
+                char* typeStr; \
+                PS_TYPE_NAME(typeStr,type); \
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
+                        PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE, \
+                        typeStr); \
+                psFree(out); \
+            } \
+        } \
+        break; \
+    }
+
+    switch (type) {
+        PSVECTOR_COPY_CASE(S8);
+        PSVECTOR_COPY_CASE(S16);
+        PSVECTOR_COPY_CASE(S32);
+        PSVECTOR_COPY_CASE(S64);
+        PSVECTOR_COPY_CASE(U8);
+        PSVECTOR_COPY_CASE(U16);
+        PSVECTOR_COPY_CASE(U32);
+        PSVECTOR_COPY_CASE(U64);
+        PSVECTOR_COPY_CASE(F32);
+        PSVECTOR_COPY_CASE(F64);
+        PSVECTOR_COPY_CASE(C32);
+        PSVECTOR_COPY_CASE(C64);
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE,
+                    typeStr);
+            psFree(out);
+
+            break;
+        }
+    }
+    return out;
+
+
+}
+
+psVector* psVectorSort(psVector* outVector, const psVector* inVector)
+{
+    psS32 N = 0;
+    psS32 elSize = 0;
+    psPtr inVec = NULL;
+    psPtr outVec = NULL;
+    psElemType inType = 0;
+
+    if (inVector == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_SORT_NULL);
+        psFree(outVector);
+        return NULL;
+    }
+
+    inType = inVector->type.type;
+    N = inVector->n;
+    inVec = (psPtr)inVector->data.U8;
+    elSize = PSELEMTYPE_SIZEOF(inType);
+
+    if (outVector == NULL) {
+        outVector = psVectorAlloc(N, inType);
+    }
+
+    // check to see if output vector needs to be resized/retyped
+    if ( (N > outVector->nalloc) ||
+            (inType != outVector->type.type) ) {
+        // reshape the output vector to match the input vector's size/type.
+        outVector = psVectorRecycle(outVector,N,inType);
+    }
+    outVector->n = N;
+    outVec = outVector->data.U8;
+
+    if (N == 0) {
+        // no need to sort anything, as there are no elements in input vector.
+        return outVector;
+    }
+
+    // Copy input vector values into output vector if not in-place sorting
+    if (inVector != outVector) {
+        memcpy(outVec, inVec, elSize * N);
+    }
+
+    // Sort output vector
+    switch (inType) {
+    case PS_TYPE_U8:
+        qsort(outVec, N, elSize, psCompareU8);
+        break;
+    case PS_TYPE_U16:
+        qsort(outVec, N, elSize, psCompareU16);
+        break;
+    case PS_TYPE_U32:
+        qsort(outVec, N, elSize, psCompareU32);
+        break;
+    case PS_TYPE_U64:
+        qsort(outVec, N, elSize, psCompareU64);
+        break;
+    case PS_TYPE_S8:
+        qsort(outVec, N, elSize, psCompareS8);
+        break;
+    case PS_TYPE_S16:
+        qsort(outVec, N, elSize, psCompareS16);
+        break;
+    case PS_TYPE_S32:
+        qsort(outVec, N, elSize, psCompareS32);
+        break;
+    case PS_TYPE_S64:
+        qsort(outVec, N, elSize, psCompareS64);
+        break;
+    case PS_TYPE_F32:
+        qsort(outVec, N, elSize, psCompareF32);
+        break;
+    case PS_TYPE_F64:
+        qsort(outVec, N, elSize, psCompareF64);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE,
+                inType);
+        psFree(outVector);
+        return NULL;
+    }
+
+    return outVector;
+}
+
+psVector* psVectorSortIndex(psVector* outVector, const psVector* inVector)
+{
+    psS32 N = 0;
+    psElemType inType = 0;
+
+    if (inVector == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psVector_SORT_NULL);
+        psFree(outVector);
+        return NULL;
+    }
+
+    inType = inVector->type.type;
+    N = inVector->n;
+
+    if (N == 0) {
+        // no need to sort anything, as there are no elements in input vector.
+        return outVector;
+    }
+
+    // ok, let's create a temporary indexed vector
+    indexedVector* idxVector = psAlloc(sizeof(indexedVector)*N);
+    int elSize = PSELEMTYPE_SIZEOF(inType);
+    for (int i = 0; i < N; i++) {
+        idxVector[i].data.U8 = inVector->data.U8+i*elSize;
+        idxVector[i].index = i;
+    }
+
+    // Sort indexed vector
+    // n.b., since first element in indexedVector is a pointer to the data,
+    // we can use the 'Ptr' version of the standard compare functions
+    switch (inType) {
+    case PS_TYPE_U8:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU8Ptr);
+        break;
+    case PS_TYPE_U16:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU16Ptr);
+        break;
+    case PS_TYPE_U32:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU32Ptr);
+        break;
+    case PS_TYPE_U64:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareU64Ptr);
+        break;
+    case PS_TYPE_S8:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS8Ptr);
+        break;
+    case PS_TYPE_S16:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS16Ptr);
+        break;
+    case PS_TYPE_S32:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS32Ptr);
+        break;
+    case PS_TYPE_S64:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareS64Ptr);
+        break;
+    case PS_TYPE_F32:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareF32Ptr);
+        break;
+    case PS_TYPE_F64:
+        qsort(idxVector, N, sizeof(indexedVector), (psCompareFcn)psCompareF64Ptr);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psVector_UNSUPPORTED_TYPE,
+                inType);
+        psFree(idxVector);
+        psFree(outVector);
+        return NULL;
+    }
+
+    // extract the indices to the output vector
+    outVector = psVectorRecycle(outVector, N, PS_TYPE_U32);
+    psU32* outData = outVector->data.U32;
+    for (int i = 0; i < N; i++) {
+        outData[i] = idxVector[i].index;
+    }
+
+    // Free temp memory
+    psFree(idxVector);
+
+    return outVector;
+}
+
+char* psVectorToString(psVector* vector, int maxLength)
+{
+
+    if (maxLength < 5) {
+        return NULL;
+    }
+
+    char* str = psAlloc(sizeof(char)*maxLength+1);
+
+    if (vector == NULL) {
+        snprintf(str,maxLength, "NULL");
+        return str;
+    }
+
+    int size = vector->n;
+
+    if (size == 0) {
+        snprintf(str,maxLength, "[]");
+        return str;
+    }
+
+    char* tempStr = psAlloc(sizeof(char)*maxLength+1);
+    *str = '\0';
+    bool full = false;
+
+    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
+case PS_TYPE_##TYPE: \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
+        strncat(str,tempStr,maxLength); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    #define APPEND_ELEMENTS_CASE_COMPLEX(TYPE,CREAL,CIMAG) \
+case PS_TYPE_##TYPE: \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s%g%+gi", prefix, \
+                 CREAL(vector->data.TYPE[lcv]), \
+                 CIMAG(vector->data.TYPE[lcv])); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    int lcv;
+    char* prefix = "[";
+    switch(vector->type.type) {
+        APPEND_ELEMENTS_CASE(S8,char,"%hd")
+        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
+        APPEND_ELEMENTS_CASE(S32,int,"%d")
+        APPEND_ELEMENTS_CASE(S64,long,"%ld")
+        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
+        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
+        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
+        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
+        APPEND_ELEMENTS_CASE(F32,double,"%g")
+        APPEND_ELEMENTS_CASE(F64,double,"%g")
+        APPEND_ELEMENTS_CASE_COMPLEX(C32,crealf,cimagf)
+        APPEND_ELEMENTS_CASE_COMPLEX(C64,creal,cimag)
+    default:
+        snprintf(str,maxLength,"[...]");
+        break;
+    }
+
+    if (full) {
+        // couldn't all fit in given string length
+
+        // remove elements until there is room for ",...]"
+        while (strlen(str) > maxLength - 5) {
+            char* lastComma = strrchr(str,',');
+            if (lastComma == NULL) { // no comma, must be first number
+                str[1] = '\0';
+            } else {
+                *lastComma = '\0';
+            }
+        }
+        strncat(str,",...]",maxLength);
+    } else {
+        strncat(str,"]",maxLength);
+    }
+
+    psFree(tempStr);
+
+    return str;
+}
+
+psF64 p_psVectorGetElementF64(psVector* vector,
+                              int position)
+{
+    if (vector == NULL) {
+        return NAN;
+    }
+    if (position < 0 || position >= vector->n) {
+        return NAN;
+    }
+
+    switch (vector->type.type) {
+    case PS_TYPE_U8:
+        return vector->data.U8[position];
+        break;
+    case PS_TYPE_U16:
+        return vector->data.U16[position];
+        break;
+    case PS_TYPE_U32:
+        return vector->data.U32[position];
+        break;
+    case PS_TYPE_U64:
+        return vector->data.U64[position];
+        break;
+    case PS_TYPE_S8:
+        return vector->data.S8[position];
+        break;
+    case PS_TYPE_S16:
+        return vector->data.S16[position];
+        break;
+    case PS_TYPE_S32:
+        return vector->data.S32[position];
+        break;
+    case PS_TYPE_S64:
+        return vector->data.S64[position];
+        break;
+    case PS_TYPE_F32:
+        return vector->data.F32[position];
+        break;
+    case PS_TYPE_F64:
+        return vector->data.F64[position];
+    default:
+        return NAN;
+    }
+}
+
+bool p_psVectorPrint (FILE *f, psVector *a, char *name)
+{
+
+    fprintf (f, "vector: %s\n", name);
+
+    for (int i = 0; i < a[0].n; i++) {
+        fprintf (f, "%f\n", p_psVectorGetElementF64(a, i));
+    }
+    fprintf (f, "\n");
+    return (true);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psVector.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psVector.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/mathtypes/psVector.h	(revision 22271)
@@ -0,0 +1,178 @@
+/** @file  psVector.h
+ *
+ *  @brief Contains basic vector definitions and operations
+ *
+ *  This file defines the basic type for a vector struct and functions useful
+ *  in manupulating vectors.
+ *
+ *  @ingroup Vector
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-29 02:25:09 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_VECTOR_H
+#define PS_VECTOR_H
+
+#include<stdio.h>
+
+#include "psType.h"
+
+/// @addtogroup Vector
+/// @{
+
+///< Union of psVector data types.
+typedef union {
+    psU8* U8;               ///< Unsigned 8-bit integer data.
+    psU16* U16;             ///< Unsigned 16-bit integer data.
+    psU32* U32;             ///< Unsigned 32-bit integer data.
+    psU64* U64;             ///< Unsigned 64-bit integer data.
+    psS8* S8;               ///< Signed 8-bit integer data.
+    psS16* S16;             ///< Signed 16-bit integer data.
+    psS32* S32;             ///< Signed 32-bit integer data.
+    psS64* S64;             ///< Signed 64-bit integer data.
+    psF32* F32;             ///< Single-precision float data.
+    psF64* F64;             ///< Double-precision float data.
+    psC32* C32;             ///< Single-precision complex data.
+    psC64* C64;             ///< Double-precision complex data.
+} p_psVectorData;
+
+/** An vector to support primitive types.
+ *
+ * Struct for maintaining an vector of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psType type;                ///< Type of data.
+    int n;                      ///< Number of elements in use.
+    const int nalloc;           ///< Total number of elements available.
+    p_psVectorData data;        ///< Union for data types.
+}
+psVector;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+
+/** Allocate a vector.
+ *
+ *  Uses psLib memory allocation functions to create a vector collection of
+ *  data as defined by the psType type.
+ *
+ * @return psVector*    Pointer to psVector.
+ */
+psVector* psVectorAlloc(
+    psU32 nalloc,               ///< Total number of elements to make available.
+    psElemType dataType                ///< Type of data to be held by vector.
+);
+
+/** Reallocate a vector.
+ *
+ *  Uses psLib memory allocation functions to reallocate a vector collection
+ *  of data. The vector is reallocated according to the psType type member
+ *  contained within the vector.
+ *
+ *  @return psVector*      Pointer to psVector.
+ *
+ */
+psVector* psVectorRealloc(
+    psVector* psVec,                   ///< Vector to reallocate.
+    psU32 nalloc                       ///< Total number of elements to make available.
+);
+
+/** Recycle a vector.
+ *
+ *  Uses psLib memory allocation functions to reallocate a vector collection
+ *  of data. The vector is reallocated according to the psElemType type
+ *  parameter.
+ *
+ * @return psVector*       Pointer to psVector.
+ *
+ */
+psVector* psVectorRecycle(
+    psVector* psVec,
+    ///< Vector to recycle.  If NULL, a new vector is created.  No effort
+    ///< taken to preserve the values.
+
+    psU32 nalloc,                      ///< Total number of elements to make available.
+    psElemType type                    ///< the datatype of the returned vector
+);
+
+/** Copy a vector, converting types.
+ *
+ *  Performs a deep copy of the elements of one psVector to a new psVector,
+ *  converting numeric types to a specified type.
+ *
+ * @return psVector*       Pointer to resulting psVector.
+ *
+ */
+psVector* psVectorCopy(
+    psVector* out,                     ///< if non-NULL, a psVector to recycle
+    const psVector* in,                ///< the vector to copy.
+    psElemType type                    ///< the data type of the resulting psVector
+);
+
+/** Sort an array of floats.
+ *
+ *  Sorts an array of floats in ascending order.  This function is valid for
+ *  all non-complex data types.
+ *
+ *  @return  psVector*     Pointer to sorted psVector.
+ */
+psVector* psVectorSort(
+    psVector* outVector,               ///< the output vector to recycle, or NULL if new vector desired.
+    const psVector* inVector           ///< the vector to sort.
+);
+
+/** Creates an array of indices based on sort ordered of array.
+ *
+ *  Sorts a vector and creates an integer array holding indices of
+ *  sorted float values based on pre-sort index positions.
+ *
+ *  @return  psVector*     vector of the indices of sort.
+ */
+psVector* psVectorSortIndex(
+    psVector* outVector,               ///< vector to recycle
+    const psVector* inVector           ///< vector to sort
+);
+
+/** Creates a string from a psVector's values in the form "[x0,x1,x2]".
+ *
+ *  @return psPtr          a newly allocated string
+ */
+char* psVectorToString(
+    psVector* vector,                  ///< vector to create a string from
+    int maxLength                      ///< the maximum length of the resulting string
+);
+
+/** Returns an element in the vector as a psF64 value
+ *
+ *  @return psF64          the value at specified position, or NAN if position is invalid.
+ */
+psF64 p_psVectorGetElementF64(
+    psVector* vector,                  ///< vector to retrieve element
+    int position                       ///< the vector position to get
+);
+
+/** Print a vector to a stream
+ *
+ *  @return psBool          TRUE is successful, otherwise FALSE.
+ */
+bool p_psVectorPrint (
+    FILE *f,                           ///< output stream
+    psVector *a,                       ///< vector to print
+    char *name                         ///< name of vector (for title)
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/parseErrorCodes.pl
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/parseErrorCodes.pl	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/parseErrorCodes.pl	(revision 22271)
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+my @ErrorCodes        = ();
+my @ErrorDescriptions = ();
+
+my $data = "psErrorCodes.dat";
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "data=s"  => \$data,
+    "verbose" => \$verbose,
+    "help"    => \$help
+);
+
+if ($help) {
+    print "Usage: parseErrorCodes ", "[--data=$data] ", "[--help] ",
+      "[--verbose] filename [filename ...]\n\n";
+    exit(0);
+}
+
+print "Using data file '$data'\n" if $verbose;
+unless ( open( DATAFILE, "<", $data ) ) {
+    die "Can not open data file $data.";
+}
+
+print "Datafile:\n" if $verbose;
+while (<DATAFILE>) {
+    chop;
+    if (/^\s*\#/) {
+        print "C $_\n" if $verbose;
+    }
+    else {
+        if (/^\s*(\w+)\s+([\%\w].*)/) {
+            my $ErrorCode        = $1;
+            my $ErrorDescription = $2;
+            print "  $ErrorCode: '$ErrorDescription'\n" if $verbose;
+            push( @ErrorCodes,        $ErrorCode );
+            push( @ErrorDescriptions, $ErrorDescription );
+        } else {
+            print "I $_\n" if $verbose;
+        }
+    }
+}
+close(DATAFILE);
+
+my $found = $#ErrorCodes + 1;
+print "\nFound $found error codes.\n" if $verbose;
+
+foreach (@ARGV) {
+    my $filename = $_;
+    my @result   = ();
+
+    die "Failed to open input file '$filename'"
+      if !open( INFILE, "<", $filename );
+
+    print "\nOutput File:\n" if $verbose;
+    while (<INFILE>) {
+        chop;
+        push( @result, $_ );
+        if (/^\s*\/\/~Start(.*)$/) {
+            $line = $1;
+            for ( $n = 0 ; $n < $found ; $n++ ) {
+                $_ = $line;
+                s/\$1/$ErrorCodes[$n]/g;
+                s/\$2/$ErrorDescriptions[$n]/g;
+                s/\$n/$n/g;
+                push( @result, $_ );
+                print "$_\n" if $verbose;
+            }
+
+            $break = 0;
+            while ( ( $break == 0 ) && ( $_ = <INFILE> ) ) {
+                if (/^\s*\/\/~End/) {
+                    $break = 1;
+                }
+            }
+            chop;
+            push( @result, $_ );
+        }
+    }
+
+    close(INFILE);
+
+    die "Failed to overwrite input file"
+      if !open( OUTFILE, ">", $filename );
+
+    foreach (@result) {
+        print OUTFILE "$_\n";
+        print "$_\n" if $verbose;
+    }
+
+    close(OUTFILE);
+
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/psErrorCodes.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/psErrorCodes.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/psErrorCodes.dat	(revision 22271)
@@ -0,0 +1,19 @@
+#
+#  This file is used to generate psErrorCode.h content
+#
+# Format:
+#    ERROR_CLASS_NAME    ERROR_CLASS_DESCRIPTION
+#
+# Note: the ERROR_CLASS_NAME will appear in the code with a "PS_ERR_" prefix
+#
+UNKNOWN                        unknown error
+IO                             I/O error
+LOCATION_INVALID               specified location is unknown
+MEMORY_CORRUPTION              memory corruption detected
+MEMORY_DEREF_USAGE             dereferenced memory still used
+BAD_PARAMETER_VALUE            parameter is out-of-range
+BAD_PARAMETER_TYPE             parameter is of unsupported type
+BAD_PARAMETER_NULL             parameter is null
+BAD_PARAMETER_SIZE             size of parameter's data is outside of acceptable range.
+UNEXPECTED_NULL                unexpected NULL found
+OS_CALL_FAILED                 unexpected result from an OS standard library call.
Index: /tags/pap_tags/pap_branch_050518/psLib/src/psTest.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/psTest.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/psTest.c	(revision 22271)
@@ -0,0 +1,281 @@
+//
+// C Implementation: psTest
+//
+// Description:
+//
+//
+// Author: Robert DeSonia <robert.desonia@mhpcc.hpc.mil>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "config.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "psTest.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psTrace.h"
+
+#define HEADER_TOP    "/***************************** TESTPOINT ******************************************\\\n"
+#define HEADER_LINE_STRING   "* %20s: %-58s *\n"
+#define HEADER_LINE_INT      "* %20s: %-58d *\n"
+#define HEADER_BOTTOM "\\**********************************************************************************/\n\n"
+
+psBool p_runTestSuite( FILE *fp, const char* testPointFile, const char* packageName,
+                       testDescription tests[], psS32 argc, char * const argv[] )
+{
+    psBool success = true;
+    psBool runAll = true;
+    psBool useFork = true;
+    psBool found;
+    psS32 c;
+    psS32 n;
+    extern char *optarg;
+
+    char* fileName = (char*)testPointFile;
+    char* lastSlashInFile = strrchr(testPointFile,'/');
+    if (lastSlashInFile != NULL) {
+        fileName = lastSlashInFile+1;
+    }
+
+    if ( argc > 0 ) {
+        while ( ( c = getopt( argc, argv, "lhn:dt:" ) ) != -1 ) {
+            switch ( c ) {
+            case 'h':
+                printf( "Usage: %s [-l] [-d] [-h] [-n=Testpoint#] [-t=TestpointName]\n"
+                        "    where:\n"
+                        "           -l  : lists the testpoints contained in this test driver executable\n"
+                        "           -d  : turns on debugger-friendly mode (no forking, aborts/signals are not handled)\n"
+                        "           -h  : prints this help\n"
+                        "           -n  : specifies a particular testpoint by number to run\n"
+                        "           -t  : specifies a particular testpoint by name to run\n"
+                        "    (if no -l, -n or -t options are given, all testpoints are run)\n",
+                        argv[ 0 ] );
+                runAll = false;
+                break;
+            case 'd':
+                useFork = false;
+                break;
+            case 'l':
+                printf( "Test Driver:  %s\n", fileName );
+                printf( "Package Name: %s\n", packageName );
+                printf( "Testpoints:\n" );
+                runAll = false;
+                for ( psS32 index = 0; tests[ index ].fcn != NULL; index++ ) {
+                    printf( "    %6d - %s \n", tests[ index ].testPointNumber,
+                            tests[ index ].testPointName );
+                }
+                printf( "\n" );
+                break;
+            case 't':
+                runAll = false;
+                for ( psS32 index = 0; tests[ index ].fcn != NULL; index++ ) {
+                    if ( strcmp( optarg, tests[ index ].testPointName ) == 0 ) {
+                        success = p_runTest( fp,
+                                             fileName,
+                                             packageName,
+                                             tests[ index ].testPointName,
+                                             tests[ index ].fcn,
+                                             tests[ index ].expectedReturn,
+                                             useFork ) && success;
+                    }
+                }
+                break;
+            case 'n':
+                runAll = false;
+                if ( sscanf( optarg, "%i", &n ) != 1 ) {
+                    psError(PS_ERR_UNKNOWN, true, "Failed to parse the testpoint number (%s).",
+                            optarg );
+                    break;
+                }
+                found = false;
+                for ( psS32 index = 0; tests[ index ].fcn != NULL; index++ ) {
+                    if ( n == tests[ index ].testPointNumber ) {
+                        found = true;
+                        success = p_runTest( fp,
+                                             fileName,
+                                             packageName,
+                                             tests[ index ].testPointName,
+                                             tests[ index ].fcn,
+                                             tests[ index ].expectedReturn,
+                                             useFork ) && success;
+                    }
+                }
+                if ( ! found ) {
+                    psError(PS_ERR_UNKNOWN, true, "The specified testpoint number (%d) doesn't exist in this test driver.",
+                            n );
+                    break;
+                }
+                break;
+            case '?':
+                psError(PS_ERR_UNKNOWN, true, "Option %s is not recognized and is ignored.", optarg );
+                break;
+            }
+        }
+    }
+
+    if ( runAll ) {
+        for ( psS32 index = 0; tests[ index ].fcn != NULL; index++ ) {
+            if ( ! tests[ index ].isDuplicateEntry ) {
+                success = p_runTest( fp,
+                                     fileName,
+                                     packageName,
+                                     tests[ index ].testPointName,
+                                     tests[ index ].fcn,
+                                     tests[ index ].expectedReturn,
+                                     useFork ) && success;
+            }
+        }
+    }
+
+    if ( ! success ) {
+        psError( PS_ERR_UNKNOWN, true,
+                 "One or more tests failed" );
+    }
+
+    return success;
+}
+
+psBool p_runTest( FILE *fp, const char* testPointFile, const char* packageName, const char* testPointName,
+                  testFcn fcn, psS32 expectedReturn, psBool useFork )
+{
+    psS32 childReturn = 0;
+    pid_t child;
+
+    char* fileName = (char*)testPointFile;
+    char* lastSlashInFile = strrchr(testPointFile,'/');
+    if (lastSlashInFile != NULL) {
+        fileName = lastSlashInFile+1;
+    }
+
+    p_printPositiveTestHeader( fp, fileName, packageName, testPointName );
+
+    if ( useFork ) {
+        child = fork();
+        if ( child == 0 ) {                   // I am the child process, run the test
+            psS32 currentId = psMemGetId();
+            psS32 retVal = fcn();
+            fflush(stdout);
+            fflush(stderr);
+            if ( retVal == 0 ) { // only bother checking memory if test executed to end.
+                if ( psMemCheckLeaks( currentId, NULL, stderr, false ) != 0 ) {
+                    psError(PS_ERR_UNKNOWN, true, "Memory Leaks Detected" );
+                    retVal = 64;
+                }
+                psMemCheckCorruption( 1 );
+            }
+            exit( retVal );
+        } else
+            if ( child < 0 ) {
+                fprintf( fp, "Couldn't fork a process to run a negative test (%s|%s)",
+                         packageName, testPointName );
+                abort();
+            }
+
+        waitpid( child, &childReturn, 0 );
+        if ( WIFSIGNALED( childReturn ) ) {
+            childReturn = -WTERMSIG( childReturn );
+        } else {
+            childReturn = WEXITSTATUS( childReturn );
+        }
+    } else {
+        psS32 currentId = psMemGetId();
+        childReturn = fcn();
+        fflush(stdout);
+        fflush(stderr);
+        if ( childReturn == 0 ) { // only bother checking memory if test executed to end.
+            if ( psMemCheckLeaks( currentId, NULL, stderr, false ) != 0 ) {
+                psError(PS_ERR_UNKNOWN, true, "Memory Leaks Detected" );
+                childReturn = 64;
+            }
+            psMemCheckCorruption( 1 );
+        }
+    }
+
+
+    if ( childReturn != expectedReturn ) {
+        fprintf( fp, "Return value mismatch: expected %d, got %d",
+                 expectedReturn, childReturn );
+    }
+
+    p_printFooter( fp, fileName, packageName, testPointName,
+                   ( childReturn == expectedReturn ) );
+
+    return ( childReturn == expectedReturn );
+}
+
+void p_printPositiveTestHeader( FILE *fp,
+                                const char* testPointFile,
+                                const char* packageName,
+                                const char* testPointName )
+{
+    char TP[ 80 ];
+
+    char* fileName = (char*)testPointFile;
+    char* lastSlashInFile = strrchr(testPointFile,'/');
+    if (lastSlashInFile != NULL) {
+        fileName = lastSlashInFile+1;
+    }
+
+    snprintf( TP, 80, "%s{%s}", packageName, testPointName );
+
+    fprintf( fp, HEADER_TOP );
+    fprintf( fp, HEADER_LINE_STRING, "TestFile", fileName);
+    fprintf( fp, HEADER_LINE_STRING, "TestPoint", TP );
+    fprintf( fp, HEADER_LINE_STRING, "TestType", "Positive" );
+    fprintf( fp, HEADER_BOTTOM );
+}
+
+void p_printNegativeTestHeader( FILE *fp,
+                                const char* testPointFile,
+                                const char* packageName,
+                                const char* testPointName,
+                                const char* expectedError,
+                                psS32 exitValue )
+{
+    char TP[ 80 ];
+
+    char* fileName = (char*)testPointFile;
+    char* lastSlashInFile = strrchr(testPointFile,'/');
+    if (lastSlashInFile != NULL) {
+        fileName = lastSlashInFile+1;
+    }
+
+    snprintf( TP, 80, "%s{%s}", packageName, testPointName );
+
+    fprintf( fp, HEADER_TOP );
+    fprintf( fp, HEADER_LINE_STRING, "TestFile", fileName);
+    fprintf( fp, HEADER_LINE_STRING, "TestPoint", TP );
+    fprintf( fp, HEADER_LINE_STRING, "TestType", "Negative" );
+    fprintf( fp, HEADER_LINE_STRING, "ExpectedErrorText", expectedError );
+    fprintf( fp, HEADER_LINE_INT, "ExpectedStatusValue", exitValue );
+    fprintf( fp, HEADER_BOTTOM );
+}
+
+
+void p_printFooter( FILE *fp,
+                    const char* testPointFile,
+                    const char* packageName,
+                    const char* testPointName,
+                    psBool success )
+{
+    char* fileName = (char*)testPointFile;
+    char* lastSlashInFile = strrchr(testPointFile,'/');
+    if (lastSlashInFile != NULL) {
+        fileName = lastSlashInFile+1;
+    }
+
+    if ( success ) {
+        fprintf( fp, "\n---> TESTPOINT PASSED (%s{%s} | %s)\n\n", packageName, testPointName, fileName);
+    } else {
+        fprintf( fp, "\n---> TESTPOINT FAILED (%s{%s} | %s)\n\n", packageName, testPointName, fileName);
+    }
+    fflush(fp);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/psTest.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/psTest.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/psTest.h	(revision 22271)
@@ -0,0 +1,95 @@
+/** @file  psTest.h
+ *
+ *  Testing infrastructure functions.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-31 23:01:46 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+
+#ifndef PSTEST_H
+#define PSTEST_H
+
+#include <stdio.h>
+
+#include "psType.h"
+
+#define printPositiveTestHeader(filePtr, packageName, testPointName) \
+p_printPositiveTestHeader(filePtr, __FILE__, packageName, testPointName)
+
+#define printNegativeTestHeader(filePtr, packageName, testPointName, expectedError, exitValue) \
+p_printNegativeTestHeader(filePtr, __FILE__, packageName, testPointName, expectedError, exitValue)
+
+#define printFooter(filePtr, packageName, testPointName, success) \
+p_printFooter(filePtr, __FILE__, packageName, testPointName, success)
+
+typedef psS32 (*testFcn)(void);
+
+typedef struct
+{
+    testFcn     fcn;
+    psS32       testPointNumber;
+    const char* testPointName;
+    psS32       expectedReturn;
+    psBool      isDuplicateEntry;
+}
+testDescription;
+
+#define runTest(filePtr, packageName, testPointName, fcn, expectedReturn, useFork) \
+p_runTest(filePtr, __FILE__, packageName, testPointName, fcn, expectedReturn, useFork)
+
+#define runTestSuite(filePtr, packageName, tests, argc, argv) \
+p_runTestSuite(filePtr, __FILE__, packageName, tests, argc, argv)
+
+
+/////////////////////////// PRIVATE FUNCTIONS //////////////////////////////
+
+psBool p_runTest(
+    FILE *fp,
+    const char* testPointFile,
+    const char* packageName,
+    const char* testPointName,
+    testFcn fcn,
+    psS32 expectedReturn,
+    psBool useFork
+);
+
+psBool p_runTestSuite(
+    FILE *fp,
+    const char* testPointFile,
+    const char* packageName,
+    testDescription tests[],
+    psS32 argc,
+    char * const argv[]
+);
+
+void p_printPositiveTestHeader(
+    FILE *fp,
+    const char* testPointFile,
+    const char* packageName,
+    const char* testPointName
+);
+
+void p_printNegativeTestHeader(
+    FILE *fp,
+    const char* testPointFile,
+    const char* packageName,
+    const char* testPointName,
+    const char* expectedError,
+    psS32 exitValue
+);
+
+void p_printFooter(
+    FILE *fp,
+    const char* testPointFile,
+    const char* packageName,
+    const char* testPointName,
+    psBool success
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/pslib.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/pslib.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/pslib.h	(revision 22271)
@@ -0,0 +1,21 @@
+/** @file  pslib.h
+*
+*  @brief Contains the complete list of header files for pslib.
+*
+*  This header file includes all the necessary header files for a user to
+*  user all public functions within the pslib library.
+*
+*  @author Eric Van Alst, MHPCC
+*
+*  @version $Revision: 1.35 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-07 20:27:41 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LIB_H
+#define PS_LIB_H
+
+#include "pslib_strict.h"
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/pslib_strict.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/pslib_strict.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/pslib_strict.h	(revision 22271)
@@ -0,0 +1,187 @@
+/** @file  pslib_strict.h
+*
+*  @brief Contains the complete list of header files for pslib while poisoning
+*         the use of standard memory allocation routines.
+*
+*  This header file includes all the necessary header files for a user to
+*  user all public functions within the pslib library.
+*
+*  @author Eric Van Alst, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-07 20:27:41 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LIB_STRICT_H
+#define PS_LIB_STRICT_H
+
+#ifdef PS_LIB_H  /* this is included from pslib.h, so don't poison anything */
+#ifndef PS_ALLOW_MALLOC
+#define PS_ALLOW_MALLOC
+#endif
+#else
+#undef PS_ALLOW_MALLOC /* don't allow code to not poison malloc, i.e., strict poisioning */
+#endif
+
+// // System Utilities
+
+/// @defgroup SysUtils System Utilities
+/// @{
+
+/** @defgroup MemoryManagement Memory Management Utilities
+ *
+ *  This is the generic memory management system put inbetween the user's high level code and the OS-level
+ *  memory allocation routines.  This system adds such features as callback routines for memory error events,
+ *  tracing capabilities, and reference counting.
+ *
+ *  @ingroup SysUtils
+ */
+
+#include "fitsio.h"
+
+#include "psMemory.h"
+
+/// @defgroup LogTrace Tracing and Logging
+/// @ingroup SysUtils
+#include "psLogMsg.h"
+#include "psTrace.h"
+
+/// @defgroup ErrorHandling Error Handling
+/// @ingroup SysUtils
+#include "psAbort.h"
+#include "psError.h"
+
+#include "psString.h"
+
+/// @defgroup Configure Configure psLib
+/// @ingroup SysUtils
+#include "psConfigure.h"
+
+/// @}
+
+// Collections
+/// @defgroup DataContainer Data Containers
+/// @{
+
+#include "psType.h"
+
+/// @defgroup LinkedList Linked List
+/// @ingroup DataContainer
+#include "psList.h"
+
+/// @defgroup HashTable Hash Table
+/// @ingroup DataContainer
+#include "psHash.h"
+
+/// @defgroup Scalar Scalar
+/// @ingroup DataContainer
+#include "psScalar.h"
+
+/// @defgroup Vector Vector Container
+/// @ingroup DataContainer
+#include "psVector.h"
+
+/// @defgroup Array Array Container
+/// @ingroup DataContainer
+#include "psArray.h"
+
+/// @defgroup Image Image Container
+/// @ingroup DataContainer
+/// @{
+#include "psImage.h"
+#include "psImageExtraction.h"
+#include "psImageManip.h"
+#include "psImageConvolve.h"
+
+/// @defgroup ImageIO Image File I/O Functions
+/// @ingroup Image
+#include "psImageIO.h"
+
+/// @defgroup ImageStats Image Statistical Functions
+/// @ingroup Image
+#include "psImageStats.h"
+
+/// @}
+
+/// @defgroup BitSet Bit Set Container
+/// @ingroup DataContainer
+#include "psBitSet.h"
+
+/// @}
+
+// Data Manipulation
+/// @defgroup DataManip Data Manipulation
+/// @{
+
+/// @defgroup Compare Comparison Functions
+/// @ingroup DataManip
+#include "psCompare.h"
+
+/// @defgroup Stats Statistic Functions
+/// @ingroup DataManip
+#include "psStats.h"
+
+/// @defgroup Matrix Matrix Operations
+/// @ingroup DataManip
+#include "psMatrix.h"
+
+/// @defgroup MatrixArithmetic Matrix Arithmetic Operations
+/// @ingroup DataManip
+#include "psBinaryOp.h"
+#include "psUnaryOp.h"
+
+#include "psRandom.h"
+
+/// @defgroup Transform Fourier Transform Operations
+/// @ingroup DataManip
+#include "psVectorFFT.h"
+#include "psImageFFT.h"
+
+#include "psFunctions.h"
+#include "psMinimize.h"
+
+/// @}
+
+// Astronomy
+/// @defgroup Astronomy Astronomy Functions
+/// @{
+
+/// @defgroup CoordinateTransform Coordinate Functions
+/// @ingroup Astronomy
+#include "psCoord.h"
+
+/// @defgroup Photometry Photometry
+/// @ingroup Astronomy
+#include "psPhotometry.h"
+
+/// @defgroup Time Time Functions
+/// @ingroup Astronomy
+#include "psTime.h"
+
+/// @defgroup Metadata Metadata Functions
+/// @ingroup Astronomy
+#include "psMetadata.h"
+#include "psMetadataIO.h"
+
+/// @defgroup AstroImage Astrometry Functions
+/// @ingroup Astronomy
+#include "psAstrometry.h"
+#include "psConstants.h"
+#include "psDB.h"
+
+/// @}
+
+// FileUtils
+/// @defgroup FileUtils File Utilities
+/// @{
+#include "psFits.h"
+
+/// @defgroup LookupTable Lookup Table
+/// @ingroup FileUtils
+#include "psLookupTable.h"
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psAbort.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psAbort.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psAbort.c	(revision 22271)
@@ -0,0 +1,38 @@
+
+/** @file  psAbort.c
+ *
+ *  @brief Contains the definition for abort function
+ *
+ *  The abort logging and handling shall be performed by psAbort function.
+ *  This will allow for consistent handling of other software units
+ *  needing to abort from program execution.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *   
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "psAbort.h"
+#include "psLogMsg.h"
+
+void psAbort(const char *name, const char *fmt, ...)
+{
+    va_list argPtr;             // variable list arguement pointer
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    // Call logging function with PS_LOG_ABORT level
+    psLogMsgV(name, PS_LOG_ABORT, fmt, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    // Call system abort function to terminate program execution
+    abort();
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psAbort.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psAbort.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psAbort.h	(revision 22271)
@@ -0,0 +1,47 @@
+
+/** @file  psAbort.h
+ *
+ *  @brief Contains the declarations for the abort function
+ *
+ *  The abort logging and handling shall be performed by psAbort function.
+ *  This will allow for consistent handling of other software units
+ *  needing to abort from program execution.
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ABORT_H
+#define PS_ABORT_H
+
+// Doxygen grouping tags
+
+/** @addtogroup ErrorHandling
+ *  @{
+ */
+
+/** Reports an abort message to logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of 
+ *  PS_LOG_ABORT and pass the parameters name and fmt to generate a proper
+ *  log message.  After logging, this function will call system abort 
+ *  function to abnormally terminate the program.
+ *
+ *  @return  void No return value
+ *
+ */
+void psAbort(
+    const char *name,                  ///< Source of abort such as file or function detected
+    const char *fmt,                   ///< A printf style formatting statement defining msg
+    ...
+);
+
+/* @} */// Doxygen - End of SystemGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psConfigure.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psConfigure.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psConfigure.c	(revision 22271)
@@ -0,0 +1,55 @@
+/** @file  psConfigure.c
+ *
+ *  @brief Contains the declarations for initialization, memory finalization, and configuration.
+ *
+ *  These functions initalize psLib data before the beginning of a run and remove (finalize) the
+ *  same data after the run is complete. A function is also provided to return the current
+ *  psLib version.
+ *
+ *  @ingroup Configure
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.7.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-13 21:27:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psString.h"
+#include "psTime.h"
+#include "psError.h"
+#include "psConfigure.h"
+#include "psSysUtilsErrors.h"
+#include "config.h"
+
+char* psLibVersion(void)
+{
+    char version[80];
+    snprintf(version,80,"%s-v%s",PACKAGE,VERSION);
+
+    return(psStringCopy(version));
+}
+
+void psLibInit(const char* timeConfig)
+{
+    // Still needs error codes to be set
+    // Still needs random number generator initialization
+    // Please code me, Robert and George.
+    // PAP: RNG seeding not required --- this is done in psRandom.
+
+    if(!p_psTimeInit(timeConfig)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psConfigure_INITIALIZATION_FAILED, "psTime");
+        return;
+    }
+}
+
+void psLibFinalize(void)
+{
+    // Users of persistent memory should free them in this function
+
+    if(!p_psTimeFinalize()) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psConfigure_FINALIZATION_FAILED, "psTime");
+        return;
+    }
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psConfigure.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psConfigure.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psConfigure.h	(revision 22271)
@@ -0,0 +1,65 @@
+/** @file  psConfigure.h
+ *
+ *  @brief Contains the declarations for initialization, memory finalization, and configuration.
+ *
+ *  These functions initalize psLib data before the beginning of a run and remove (finalize) the
+ *  same data after the run is complete. A function is also provided to return the current
+ *  psLib version.
+ *
+ *  @ingroup Configure
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author George Gusciora, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.3.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-13 21:27:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_CONFIGURE_H
+#define PS_CONFIGURE_H
+
+
+/** @addtogroup Configure
+ *  @{
+ */
+
+/** Get current psLib version
+ *
+ *  Returns the current psLib version name as a string.
+ *
+ *  @return char*: String with version name.
+ */
+char* psLibVersion(
+    void
+);
+
+/** Initializes persistent memory.
+ *
+ *  Creates persistant memory items used throughout psLib. Items created within this method should be freed
+ *  with the psLibFinalize function.
+ *  current, a non-NULL psErr is returned with code PS_ERR_NONE.
+ *
+ *  @return void: void.
+ */
+void psLibInit(
+    const char* timeConfig
+);
+
+/** Removes persistant memory created with the psLibInit function.
+ *
+ *  The memory created but not freed by psLib modules should be freed within this
+ *  function at the end of a psLib execution cycle.
+ *
+ *  @return void: void.
+ */
+void psLibFinalize(
+    void
+);
+
+
+/* @} */
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psError.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psError.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psError.c	(revision 22271)
@@ -0,0 +1,216 @@
+/** @file  psError.c
+ *
+ *  @brief Contains the definitions for the error reporting functions
+ *
+ *  Error reporting functions shall be used to create log entries in the
+ *  event errors are detected.  The messages shall give enough information
+ *  to allow the user to know where the error has occurred and the type
+ *  of error detected.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-07 20:27:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdarg.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psMemory.h"
+
+#define MAX_ERROR_STACK_SIZE 64
+static psErr* errorStack[MAX_ERROR_STACK_SIZE];
+static psU32 errorStackSize = 0;
+pthread_mutex_t lockErrorStack = PTHREAD_MUTEX_INITIALIZER;
+
+static void pushErrorStack(psErr* err);
+
+static void pushErrorStack(psErr* err)
+{
+
+    pthread_mutex_lock(&lockErrorStack);
+
+    if (errorStackSize < MAX_ERROR_STACK_SIZE) {
+        errorStack[errorStackSize] = psMemIncrRefCounter(err);
+        errorStackSize++;
+        p_psMemSetPersistent(err,true);
+        p_psMemSetPersistent(err->msg,true);
+        p_psMemSetPersistent(err->name,true);
+    }
+
+    pthread_mutex_unlock(&lockErrorStack);
+}
+
+static void errFree(psErr* err)
+{
+    if (err != NULL) {
+        psFree(err->msg);
+        psFree(err->name);
+    }
+}
+
+psErr* psErrAlloc(const char* name, psErrorCode code, const char* msg)
+{
+    psErr* err = psAlloc(sizeof(psErr));
+    err->msg = strcpy(psAlloc(strlen(msg) + 1), msg);
+    err->name = strcpy(psAlloc(strlen(name) + 1), name);
+    err->code = code;
+
+    psMemSetDeallocator(err,(psFreeFcn)errFree);
+
+    return err;
+}
+
+psErrorCode p_psError(const char* file,
+                      int lineno,
+                      const char* func,
+                      psErrorCode code,
+                      psBool new,
+                      const char* fmt,
+                      ...)
+{
+    char errMsg[2048];
+    psErr* err;
+    char msgName[1024];
+
+    snprintf(msgName,1024,"%s (%s:%d)",func,file,lineno);
+
+    va_list argPtr;             // variable list arguement pointer
+
+    if (new) {
+        psErrorClear();
+    }
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    vsnprintf(errMsg,2048,fmt,argPtr);
+    err = psErrAlloc(msgName,code,errMsg);
+    pushErrorStack(err);
+
+    // Call logging function with PS_LOG_ERROR level
+    psLogMsg(msgName, PS_LOG_ERROR, errMsg);
+
+    // Clean up stack after variable argument has been used
+    va_end(argPtr);
+
+    psFree(err);
+
+    return code;
+}
+
+void p_psWarning(const char* file,
+                 int lineno,
+                 const char* func,
+                 const char* fmt,
+                 ...)
+{
+    char msgName[1024];
+
+    snprintf(msgName,1024,"%s (%s:%d)",func,file,lineno);
+
+    va_list argPtr;             // variable list argument pointer
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    psLogMsgV(msgName, PS_LOG_WARN, fmt, argPtr);
+
+    // Clean up stack after variable argument has been used
+    va_end(argPtr);
+
+    return;
+}
+
+psErr* psErrorGet(psS32 which)
+{
+    psErr* result;
+
+    pthread_mutex_lock(&lockErrorStack);
+
+    // Check for negative reference and if found return PS_ERR_NONE
+    if (which < 0 ) {
+        result = psErrAlloc("", PS_ERR_NONE, "");
+    } else {
+
+        which = errorStackSize-1-which;     // the which input is from the end of errorStack
+        if (which < 0 || which >= errorStackSize) {
+            result = psErrAlloc("",PS_ERR_NONE,"");    // no error at the given location
+        } else {
+            result = psMemIncrRefCounter(errorStack[which]); // a new reference passed back
+        }
+    }
+
+    pthread_mutex_unlock(&lockErrorStack);
+
+    return result;
+}
+
+psS32 psErrorGetStackSize()
+{
+    return errorStackSize;
+}
+
+psErr* psErrorLast(void)
+{
+    return psErrorGet(0);
+}
+
+void psErrorClear(void)
+{
+    pthread_mutex_lock(&lockErrorStack);
+
+    for (int lcv=0;lcv < errorStackSize; lcv++) {
+        p_psMemSetPersistent(errorStack[lcv],false);
+        p_psMemSetPersistent(errorStack[lcv]->msg,false);
+        p_psMemSetPersistent(errorStack[lcv]->name,false);
+        psFree(errorStack[lcv]);
+    }
+    errorStackSize = 0;
+
+    pthread_mutex_unlock(&lockErrorStack);
+
+}
+void psErrorStackPrint(FILE *fd, const char *fmt, ...)
+{
+    va_list argPtr;             // variable list arguement pointer
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    psErrorStackPrintV(fd,fmt,argPtr);
+
+    va_end(argPtr);
+}
+
+void psErrorStackPrintV(FILE *fd, const char *fmt, va_list va)
+{
+
+    pthread_mutex_lock(&lockErrorStack);
+
+    if (errorStackSize > 0) {
+        vfprintf(fd,fmt,va);
+
+        for (psS32 lcv=0;lcv<errorStackSize;lcv++) {
+            if(errorStack[lcv]->code >= PS_ERR_BASE) {
+                fprintf(fd," -> %s: %s\n     %s\n",
+                        errorStack[lcv]->name,
+                        psErrorCodeString(errorStack[lcv]->code),
+                        errorStack[lcv]->msg);
+            } else {
+                fprintf(fd," -> %s: %s\n     %s\n",
+                        errorStack[lcv]->name,
+                        strerror(errorStack[lcv]->code),
+                        errorStack[lcv]->msg);
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&lockErrorStack);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psError.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psError.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psError.h	(revision 22271)
@@ -0,0 +1,177 @@
+/** @file  psError.h
+ *
+ *  @brief Contains the declarations for the error reporting functions
+ *
+ *  Error reporting functions shall be used to create log entries in the
+ *  event errors are detected.  The messages shall give enough information
+ *  to allow the user to know where the error has occurred and the type
+ *  of error detected.
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ERROR_H
+#define PS_ERROR_H
+
+#include<stdio.h>
+#include<stdbool.h>
+#include<stdarg.h>
+
+#include "psErrorCodes.h"
+
+/** @addtogroup ErrorHandling
+ *  @{
+ */
+
+/** Error message object */
+typedef struct
+{
+    char* name;                        ///< category of code that caused the error
+    psErrorCode code;                  ///< class of error
+    char* msg;                         ///< the message associated with the error
+}
+psErr;
+
+/** Get a error from the error stack
+ *
+ *  Previous errors on the stack are returned by psErrorGet (a value of 0
+ *  passed to psErrorGet is equivalent to a call to psErrorLast).
+ *
+ *  if no error is at the which position, a non-NULL psErr is returned with
+ *  code PS_ERR_NONE.
+ *
+ *  @return    Error message object at 'which'
+ */
+psErr* psErrorGet(
+    psS32 which                          ///< position in the error stack. 0 is last error on stack.
+);
+
+/** Get last error put on the error stack
+ *
+ *  The last error reported is available from psErrorLast; if no errors are
+ *  current, a non-NULL psErr is returned with code PS_ERR_NONE.
+ *
+ *  @return psErr*     Reference to last error message on error stack
+ */
+psErr* psErrorLast(void);
+
+/** Clears the error stack.
+ *
+ *  The error stack may be completely cleared with psErrorClear.
+ *
+ */
+void psErrorClear(void);
+
+/** Get the error stack depth
+ *
+ *  @return psS32   The number of items on the error stack
+ */
+psS32 psErrorGetStackSize();
+
+/** Prints error stack to specified open file descriptor
+ *
+ *  The entire error stack may be printed to an open file descriptor by
+ *  calling psErrorStackPrint; if and only if there are current errors, the
+ *  printf-style string fmt is first printed to the file descriptor fd. In
+ *  this printout, error codes are replaced by their string equivalents.
+ *
+ */
+void psErrorStackPrint(
+    FILE* fd,                          ///< destination file descriptor
+    const char* fmt,                   ///< printf-style format of header line
+    ...                                ///< any parameters required in fmt
+);
+
+#ifndef SWIG
+/** Prints error stack to specified open file descriptor
+ *
+ *  The entire error stack may be printed to an open file descriptor by
+ *  calling psErrorStackPrintV; if and only if there are current errors, the
+ *  vprintf-style string fmt is first printed to the file descriptor fd. In
+ *  this printout, error codes are replaced by their string equivalents.
+ *
+ */
+void psErrorStackPrintV(
+    FILE* fd,                          ///< destination file descriptor
+    const char* fmt,                   ///< printf-style format of header line
+    va_list va                         ///< any parameters required in fmt
+);
+#endif
+
+#ifdef DOXYGEN
+/** Reports an error message to the logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of
+ *  PS_LOG_ERROR and pass the parameters name and fmt to generate a proper
+ *  log message.
+ *
+ *  This function modifies the error stack.
+ *
+ *  @return psErrorCode    the given error code
+ */
+psErrorCode psError(
+    psErrorCode code,                  ///< Error class code
+    psBool new,                        ///< true if error originates at this location
+    const char* fmt,
+    ...
+);
+
+/** Logs a warning message.
+ *
+ *  This procedure logs a message to the destination set by a prior
+ *  call to psLogSetDestination(), This is equivalent to calling
+ *  psLogMsg with a level of PS_LOG_WARN.
+ *
+ */
+void psWarning(
+    const char* fmt,
+    ...
+);
+#else
+psErrorCode p_psError(
+    const char* file,
+    int lineno,
+    const char* func,
+    psErrorCode code,                  ///< Error class code
+    psBool new,                        ///< true if error originates at this location
+    const char* fmt,
+    ...
+);
+void p_psWarning(
+    const char* file,
+    int lineno,
+    const char* func,
+    const char* fmt,
+    ...
+);
+
+
+#ifndef SWIG
+#define psError(code,new,...) p_psError(__FILE__,__LINE__,__func__,code,new,__VA_ARGS__)
+#define psWarning(...) p_psWarning(__FILE__,__LINE__,__func__,__VA_ARGS__)
+#endif
+
+#endif
+
+/** Create a new psErr struct
+ *
+ *  Creates a new psErr struct, making a copy of the parameters.
+ *
+ *  @return psErr*     new psErr object
+ */
+psErr* psErrAlloc(
+    const char* name,                  ///< Name of error in the form aaa.bbb.ccc
+    psErrorCode code,                  ///< Error class code
+    const char* msg                    ///< Error message
+);
+
+/* @} */// End of SysUtils Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psErrorCodes.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psErrorCodes.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psErrorCodes.c	(revision 22271)
@@ -0,0 +1,188 @@
+/** @file  psErrorCodes.c
+ *
+ *  @brief Contains the error codes for the error classes
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-07 20:27:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+
+#include "psError.h"
+#include "psErrorCodes.h"
+#include "psList.h"
+#include "psMemory.h"
+
+#include "psSysUtilsErrors.h"
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error class in psErrorCodes.dat, the following
+ * substitutions are made:
+ *     $1  The error code name (first word in the psErrorCodes.dat lines)
+ *     $2  The error description (rest of the line in psErrorCodes.dat)
+ *     $n  The order of the source line in psErrorCodes.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+static psErrorDescription staticErrorCodes[] = {
+            {PS_ERR_NONE,"not an error"},
+            {PS_ERR_BASE,"base error"},
+            //~Start    {PS_ERR_$1,"$2"},
+            {PS_ERR_UNKNOWN,"unknown error"},
+            {PS_ERR_IO,"I/O error"},
+            {PS_ERR_LOCATION_INVALID,"specified location is unknown"},
+            {PS_ERR_MEMORY_CORRUPTION,"memory corruption detected"},
+            {PS_ERR_MEMORY_DEREF_USAGE,"dereferenced memory still used"},
+            {PS_ERR_BAD_PARAMETER_VALUE,"parameter is out-of-range"},
+            {PS_ERR_BAD_PARAMETER_TYPE,"parameter is of unsupported type"},
+            {PS_ERR_BAD_PARAMETER_NULL,"parameter is null"},
+            {PS_ERR_BAD_PARAMETER_SIZE,"size of parameter's data is outside of acceptable range."},
+            {PS_ERR_UNEXPECTED_NULL,"unexpected NULL found"},
+            {PS_ERR_OS_CALL_FAILED,"unexpected result from an OS standard library call"},
+            //~End
+            {PS_ERR_N_ERR_CLASSES,"error classes end marker"}
+        };
+
+static psList* dynamicErrorCodes = NULL;
+static const psErrorDescription* getErrorDescription(psErrorCode code);
+
+
+static const psErrorDescription* getErrorDescription(psErrorCode code)
+{
+    // first, search the static error codes
+
+    psS32 n = 0;
+    while(staticErrorCodes[n].code != PS_ERR_N_ERR_CLASSES &&
+            staticErrorCodes[n].code != code) {
+        n++;
+    }
+
+    if (staticErrorCodes[n].code == code) {
+        return &staticErrorCodes[n];
+    } else {
+        psErrorDescription* desc;
+        // make sure there is a list to search
+        if (dynamicErrorCodes == NULL) {
+            return NULL;
+        }
+
+        // search dynamic list of error descriptions before giving up.
+        psListIterator* iter = psListIteratorAlloc(dynamicErrorCodes,PS_LIST_HEAD,true);
+        while ((desc = (psErrorDescription*)psListGetAndIncrement(iter)) != NULL) {
+            if (desc->code == code) {
+                psFree(iter);
+                return desc;
+            }
+        }
+        psFree(iter);
+    }
+    return NULL;
+}
+
+static void freeErrorDescription(psErrorDescription* err)
+{
+    psFree((psPtr)err->description);
+}
+
+psErrorDescription* psErrorDescriptionAlloc(psErrorCode code,
+        const char *description)
+{
+    psErrorDescription* err = psAlloc(sizeof(psErrorDescription));
+    err->code = code;
+    if (description == NULL) {
+        err->description = NULL;
+    } else {
+        err->description = psAlloc(sizeof(char)*strlen(description)+1);
+        strcpy((char*)err->description,description);
+    }
+
+    psMemSetDeallocator(err,(psFreeFcn)freeErrorDescription);
+    return err;
+}
+
+
+const char *psErrorCodeString(psErrorCode code)
+{
+    // Check input argument is non-negative
+    if ( code < 0 ) {
+        return NULL;
+    }
+
+    const psErrorDescription* desc = getErrorDescription(code);
+
+    if (desc == NULL) {
+        return NULL;
+    }
+
+    return desc->description;
+}
+
+void psErrorRegister(const psErrorDescription* errors,
+                     psS32 nerror)
+{
+    if (nerror < 1) {
+        return;
+    }
+
+    if (errors == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psErrorCode_NULL_ERRORDESCRIPTION);
+        return;
+    }
+
+    if (dynamicErrorCodes == NULL) {
+        dynamicErrorCodes = psListAlloc(NULL);
+        p_psMemSetPersistent(dynamicErrorCodes,true);
+
+        p_psMemSetPersistent(dynamicErrorCodes->iterators,true);
+        p_psMemSetPersistent(dynamicErrorCodes->iterators->data,true);
+        for (int i = 0; i < dynamicErrorCodes->iterators->n;i++) {
+            p_psMemSetPersistent(dynamicErrorCodes->iterators->data[i],true);
+        }
+    }
+
+    for (psS32 i=0;i<nerror;i++) {
+        psErrorDescription* err = psErrorDescriptionAlloc(
+                                      errors[i].code, errors[i].description);
+        p_psMemSetPersistent(err,true);
+        p_psMemSetPersistent((psPtr)err->description,true);
+        if (! psListAdd(dynamicErrorCodes,
+                        PS_LIST_HEAD,
+                        err) ) {
+
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psErrorCode_ERRORCODE_REGISTER_FAILED,
+                    i);
+        }
+        p_psMemSetPersistent(dynamicErrorCodes->head,true);
+        psFree(err);
+    }
+}
+
+psBool p_psErrorUnregister(psErrorCode code)
+{
+    // Check input argument is non-negative
+    if ( code < 0 ) {
+        return false;
+    }
+
+    const psErrorDescription* desc = getErrorDescription(code);
+
+    if (desc == NULL) {
+        return false;
+    }
+
+    if (dynamicErrorCodes == NULL) {
+        return false;
+    }
+
+    return psListRemoveData(dynamicErrorCodes,(psPtr)desc);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psErrorCodes.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psErrorCodes.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psErrorCodes.h	(revision 22271)
@@ -0,0 +1,111 @@
+/** @file  psErrorCodes.h
+ *
+ *  @brief Contains the error codes for the error classes
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ERROR_CODES_H
+#define PS_ERROR_CODES_H
+
+#include "psType.h"
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error class in psErrorCodes.dat, the following
+ * substitutions are made:
+ *     $1  The error code name (first word in the psErrorCodes.dat lines)
+ *     $2  The error description (rest of the line in psErrorCodes.dat)
+ *     $n  The order of the source line in psErrorCodes.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+/** @addtogroup ErrorHandling
+ *  @{
+ */
+
+/** enumeration of the static error code classes
+ */
+typedef enum {
+    PS_ERR_NONE = 0,                   ///< not an error
+    PS_ERR_BASE = 256,
+    /**< base error.  Any psErrorCode less than this should be taken to be
+     *   valid values of errno
+     */
+
+    //~Start     PS_ERR_$1,   ///< $2
+    PS_ERR_UNKNOWN,   ///< unknown error
+    PS_ERR_IO,   ///< I/O error
+    PS_ERR_LOCATION_INVALID,   ///< specified location is unknown
+    PS_ERR_MEMORY_CORRUPTION,   ///< memory corruption detected
+    PS_ERR_MEMORY_DEREF_USAGE,   ///< dereferenced memory still used
+    PS_ERR_BAD_PARAMETER_VALUE,   ///< parameter is out-of-range
+    PS_ERR_BAD_PARAMETER_TYPE,   ///< parameter is of unsupported type
+    PS_ERR_BAD_PARAMETER_NULL,   ///< parameter is null
+    PS_ERR_BAD_PARAMETER_SIZE,   ///< size of parameter's data is outside of acceptable range.
+    PS_ERR_UNEXPECTED_NULL,   ///< unexpected NULL found
+    PS_ERR_OS_CALL_FAILED,   ///< unexpected result from an OS standard library call
+    //~End
+    PS_ERR_N_ERR_CLASSES               ///< end marker - should not be used as a true error
+} psErrorCode;
+
+/** An error code with description
+ */
+typedef struct
+{
+    psErrorCode code;                  ///< An error code
+    const char *description;           ///< the associated description
+}
+psErrorDescription;
+
+/** Allocates a new psErrorDescription
+ *
+ *  @return psErrorDescription*        new psErrorDescription struct.
+ */
+psErrorDescription* psErrorDescriptionAlloc(
+    psErrorCode code,                  ///< An error code
+    const char *description            ///< the associated description
+);
+
+/** Retrieves the description of an error code.
+ *
+ *  The routine psErrorCodeString returns the string associated with an error 
+ *  code.
+ *
+ *  @return const char*     the description associated with the given code.
+ */
+const char *psErrorCodeString(
+    psErrorCode code                   ///< the associated error code
+);
+
+/** Register an error code
+ *
+ *  Any project needed to use psLib must define the necessary error codes and
+ *  associated message strings.  This function registers an array of error 
+ *  codes with the error handling subsystem.
+ *
+ */
+void psErrorRegister(
+    const psErrorDescription* errors,  ///< Array of error codes to register
+    psS32 nerror                         ///< number of errors in input array
+);
+
+/** Clears error codes registered via psErrorRegister.
+ *
+ *  @return psBool    TRUE if given errorcode was removed, otherwise FALSE.
+ */
+psBool p_psErrorUnregister(
+    psErrorCode code                   ///< the error code to find and remove
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psLogMsg.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psLogMsg.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psLogMsg.c	(revision 22271)
@@ -0,0 +1,387 @@
+/** @file  psLogMsg.c
+ *  @brief Procedures for logging messages.
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the prototypes for defining procedure which set
+ *  message log levels, messahe log formats, message log destinations, and
+ *  for generating the messages themselves.
+ *  @ingroup LogTrace
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.39.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-13 21:40:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/*****************************************************************************
+NOTES: currently, the prototype code has the following global variables:
+    static psS32 p_psGlobalLogDest;
+    static psS32 p_psGlobalLogLevel;
+    static psS32 p_psLogTime;
+    static psS32 p_psLogHost;
+    static psS32 p_psLogLevel;
+    static psS32 p_psLogName;
+    static psS32 p_psLogMsg;
+ *****************************************************************************/
+#include "config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psTrace.h"
+
+#include "psSysUtilsErrors.h"
+
+#define MIN_LOG_LEVEL 0
+#define MAX_LOG_LEVEL 9
+
+#define MAX_LOG_LINE_LENGTH 256
+
+static FILE *logDest = (FILE *) 1;      // flag to initialize to stderr before using.
+static psS32 globalLogLevel = PS_LOG_INFO;        // log all messages at this or above
+static psBool logTime = true;     // Flag to include time info
+static psBool logHost = true;     // Flag to include host info
+static psBool logLevel = true;    // Flag to include level info
+static psBool logName = true;     // Flag to include name info
+static psBool logMsg = true;      // Flag to include message info
+
+/*****************************************************************************
+psLogSetLevel(): Set the current log level and return old level.
+Input:
+ level (psS32): the new log level.
+Output:
+ none
+Return:
+ The old log level.
+ *****************************************************************************/
+psS32 psLogSetLevel(psS32 level)
+{
+    // Save old global log level for changing it.
+    psS32 oldLevel = globalLogLevel;
+
+    if ((level < MIN_LOG_LEVEL) || (level > MAX_LOG_LEVEL)) {
+        psLogMsg("logmsg", PS_LOG_WARN, "Attempt to set invalid logMsg level: %d", level);
+        level = (level < MIN_LOG_LEVEL) ? MIN_LOG_LEVEL : MAX_LOG_LEVEL;
+    }
+    // Set new global log level
+    globalLogLevel = level;
+
+    // Return old global log level
+    return oldLevel;
+}
+
+/*****************************************************************************
+psLogSetDestination(): sets the destination where log messages will be
+sent to.
+ 
+Input:
+ dest (psS32): the new log destination
+Output:
+ None.
+Return:
+ An integer specifying the old log destination.
+ *****************************************************************************/
+psBool psLogSetDestination(const char *dest)
+{
+    char protocol[5];
+    char location[257];
+
+    // if logDest has not been initialized, do so before using it
+    if (logDest == (FILE *) 1) {
+        logDest = stderr;
+    }
+
+    if (dest == NULL || strcmp(dest, "none") == 0) {
+        if (logDest != NULL && logDest != stderr && logDest != stdout) {
+            fclose(logDest);
+        }
+        logDest = NULL;
+        return true;
+    }
+
+    if (sscanf(dest, "%4s:%256s", protocol, location) < 2) {
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psLogMsg_DESTINATION_MALFORMED,
+                dest);
+        return false;
+    }
+
+    if (strcmp(protocol, "dest") == 0) {
+        if (strcmp(location, "stderr") == 0) {
+            if (logDest != NULL && logDest != stderr && logDest != stdout) {
+                fclose(logDest);
+            }
+            logDest = stderr;
+            return true;
+        }
+        if (strcmp(location, "stdout") == 0) {
+            if (logDest != NULL && logDest != stderr && logDest != stdout) {
+                fclose(logDest);
+            }
+            logDest = stdout;
+            return true;
+        }
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psLogMsg_DEST_LOCATION_INVALID,
+                location);
+        return 1;
+    } else if (strcmp(protocol, "file") == 0) {
+        FILE *file = fopen(location, "w");
+
+        if (file == NULL) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psLogMsg_OPEN_FILE_FAILED,
+                    location);
+            return false;
+        }
+        if (logDest != NULL && logDest != stderr && logDest != stdout) {
+            fclose(logDest);
+        }
+        logDest = file;
+        return true;
+    }
+
+    psError(PS_ERR_LOCATION_INVALID, true,
+            PS_ERRORTEXT_psLogMsg_UNSUPPORTED_PROTOCOL,
+            protocol);
+    return false;
+}
+
+/*****************************************************************************
+psLogSetFormat(): Set the format of psLogMsg output.  More precisely,
+    provide a string consisting of the letters {H (host), L (level), M
+    (message), N (name), T (time)}.  The default is "HLMNT".  This string
+    determines whether or not they associated type of information will be
+    included in message logs.  It does not determine the order in which that
+    information will appear (that order is fixed).
+ 
+Input:
+    fmt: a string specifying the format.
+Output:
+    none.
+Return:
+    NULL.
+ *****************************************************************************/
+bool psLogSetFormat(const char *fmt)
+{
+    // assume nothing desired unless specified
+    logHost = false;
+    logLevel = false;
+    logMsg = false;
+    logName = false;
+    logTime = false;
+
+    // if fmt is NULL, no logging is desired.
+    if (fmt == NULL) {
+        return true;   // No problems encountered in parsing
+    }
+
+    if (strlen(fmt) == 0) {
+        fmt = "THLNM";
+    }
+    // Step through each character in the format string.  For each letter
+    // in that string, set/unset the appropriate logging.
+
+    for (const char *ptr = fmt; *ptr != '\0'; ptr++) {
+        switch (*ptr) {
+        case 'H':
+        case 'h':
+            logHost = true;
+            break;
+        case 'L':
+        case 'l':
+            logLevel = true;
+            break;
+        case 'M':
+        case 'm':
+            logMsg = true;
+            break;
+        case 'N':
+        case 'n':
+            logName = true;
+            break;
+        case 'T':
+        case 't':
+            logTime = true;
+            break;
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psLogMsg_UNKNOWN_KEY, *ptr);
+            break;
+        }
+    }
+
+    if (!logMsg) {
+        psTrace("utils.logMsg", 1, "You must at least log error messages (You chose \"%s\")", fmt);
+        return false;
+    }
+
+    return true;   // No problems
+}
+
+#if !defined(HOST_NAME_MAX)                // should be in limits.h
+#define HOST_NAME_MAX 256
+#endif
+
+/*****************************************************************************
+    psVLogMsg(): This routine sends the message, which is a printf style
+ string specified in the "..." argument, to the current message log
+ destination with the severity specified by the "level" argument.
+    Input:
+ name
+ level
+ fmt
+ ap
+    Output:
+ none
+    Return:
+ NULL.
+ *****************************************************************************/
+void psLogMsgV(const char *name, psS32 level, const char *fmt, va_list ap)
+{
+    static psS32 first = 1;       // Flag for calling gethostname()
+    static char hostname[HOST_NAME_MAX + 1];
+
+    // Buffer for hostname.
+    char clevel = 0;            // letter-name for level
+    char head[MAX_LOG_LINE_LENGTH + 2]; // the added two are for the ending | and \0
+    char *head_ptr = head;      // where we've got to in head
+    psS32 maxLength = MAX_LOG_LINE_LENGTH;
+    time_t clock = time(NULL);  // The current time.
+    struct tm *utc = gmtime(&clock);    // The current gm time.
+
+    // if logDest has not been initialized, do so before using it
+    if (logDest == (FILE *) 1) {
+        logDest = stderr;
+    }
+    // If logging is off, or if the level is too high, return immediately.
+    if ((level > globalLogLevel) || (logDest == NULL)) {
+        return;
+    }
+    // If I have not been here yet, determine my hostname and save it.
+    if (first) {
+        first = 0;
+        gethostname(hostname, HOST_NAME_MAX);
+    }
+
+    switch (level) {
+    case PS_LOG_ABORT:
+        clevel = 'A';
+        break;
+
+    case PS_LOG_ERROR:
+        clevel = 'E';
+        break;
+
+    case PS_LOG_WARN:
+        clevel = 'W';
+        break;
+
+    case PS_LOG_INFO:
+        clevel = 'I';
+        break;
+
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+        clevel = level + '0';
+        break;
+
+    default:
+        psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n", level, fmt);
+        level = (level < 0) ? 0 : 9;
+        clevel = level + '0';
+        break;
+    }
+
+    // Create the various log fields...
+    if (logTime) {
+        maxLength -= snprintf(head_ptr, maxLength, "%4d:%02d:%02d %02d:%02d:%02dZ",
+                              utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
+                              utc->tm_hour, utc->tm_min, utc->tm_sec) - 1;
+        head_ptr += strlen(head_ptr);
+    }
+    // Hostname should be 20 characters.
+    if (logHost) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%-20s", hostname);
+        head_ptr += strlen(head_ptr);
+    }
+    if (logLevel) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%c", clevel);
+        head_ptr += strlen(head_ptr);
+    }
+    if (logName) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%s", name);
+
+        head_ptr += strlen(head_ptr);
+    }
+
+    if (head_ptr > head) {
+        *head_ptr++ = '\n';
+    } else if (!logMsg) {                  // no output desired
+        return;
+    }
+    *head_ptr = '\0';
+
+    fputs(head, logDest);
+    if (logMsg) {
+        char msg[1024];
+        char* msgPtr;
+        vsnprintf(msg,1024, fmt, ap);  // create message
+
+        // detect multiple lines in message and indent each line by 4 spaces.
+        char* line = strtok_r(msg,"\n",&msgPtr);
+        while (line != NULL) {
+            fprintf(logDest,"    %s\n",line);
+            line = strtok_r(NULL,"\n",&msgPtr);
+        }
+    } else {
+        fputc('\n', logDest);
+    }
+}
+
+/*****************************************************************************
+    psLogMsg(): This routine sends the message, which is a printf style
+ string specified in the "..." argument, to the current message log
+ destination with the severity specified by the "level" argument.
+    Input:
+ name: Indicates the source of this log message.
+ level: The severity of this log message.
+ fmt: The printf-stype formatted string, followed by the arguments
+  to that string.
+ ... The arguments to the above printf-style string.
+    Output:
+ none
+    Return:
+ NULL
+ *****************************************************************************/
+void psLogMsg(const char *name, psS32 level, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    psLogMsgV(name, level, fmt, ap);
+    va_end(ap);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psLogMsg.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psLogMsg.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psLogMsg.h	(revision 22271)
@@ -0,0 +1,106 @@
+/** @file  psLogMsg.h
+ *  @brief Procedures for logging messages.
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the prototypes for defining procedure which set
+ *  message log levels, messahe log formats, message log destinations, and
+ *  for generating the messages themselves.
+ *  @ingroup LogTrace
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_LOG_MSG_H)
+#define PS_LOG_MSG_H
+#include <stdarg.h>
+
+#include "psType.h"
+
+/** @addtogroup LogTrace
+ *  @{
+ */
+
+/** This procedure sets the destination for future log messages.  Currently
+ *  the destination is specified by an integer which can have the following
+ *  pre-defined values: PS_LOG_NONE, PS_LOG_TO_STDOUT, and PS_LOG_TO_STDERR.
+ *  In future versions, this procedure will take a character string as an
+ *  argument which can specify more general log destinations.
+ *
+ *  @return psS32     true if set successfully, otherwise false.
+ */
+psBool psLogSetDestination(
+    const char *dest                   ///< Specifies where to send messages.
+);
+
+/** This procedure sets the message level for future log messages.  Subsequent
+ *  log messages, with a log level of "mylevel", will only be logged if
+ *  "mylevel" is less than the current log level set by this procedure.
+ *  Ie. higher values set by this procedure will cause more log messages to
+ *  be displayed.
+ *
+ *  @return psS32    old logging level
+ */
+psS32 psLogSetLevel(
+    psS32 level                          ///< Specifies the system log level
+);
+
+/** This procedure sets the log format for future log messages.  The argument
+ *  must be a character string consistsing of the letters H (host), L
+ *  (level), M (message), N (name), and T (time).  The default is "THLNM".
+ *  Deleting a letter from the string will cause the associated information
+ *  to not be logged.
+ *
+ */
+void psLogSetFormat(
+    const char *fmt                    ///< Specifies the system log format
+);
+
+/** This procedure logs a message to the destination set by a prior
+ *  call to psLogSetDestination(), if myLevel is less than the level
+ *  specified by a prior call to psLogSetLevel().  The message is specified
+ *  with a printf-stype string an arguments.
+ *
+ */
+void psLogMsg(
+    const char *name,                  ///< name of the log source
+    psS32 myLevel,                       ///< severity level of this log message
+    const char *fmt,                   ///< printf-style format command
+    ...
+);
+
+#ifndef SWIG
+/** This procedure is functionally equivalent to psLogMsg(), except that
+ *  it takes a va_list as the message parameter, not a printf-style string.
+ *
+ */
+void psLogMsgV(
+    const char *name,                  ///< name of the log source
+    psS32 myLevel,                       ///< severity level of this log message
+    const char *fmt,                   ///< printf-style format command
+    va_list ap                         ///< varargs argument list
+);
+#endif
+
+///< Status codes for log messages
+enum {
+    PS_LOG_ABORT = 0,                  ///< log message is a critical error, perform an abort after printing
+    PS_LOG_ERROR,                      ///< log message is an error, but don't abort
+    PS_LOG_WARN,                       ///< log message is a warning
+    PS_LOG_INFO                        ///< log message is informational only
+};
+
+///< Destinations for log messages
+enum {
+    PS_LOG_NONE,                       ///< turn off logging
+    PS_LOG_TO_STDERR,                  ///< log to system's stderr
+    PS_LOG_TO_STDOUT                   ///< log to system's stdout
+};
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psMemory.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psMemory.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psMemory.c	(revision 22271)
@@ -0,0 +1,700 @@
+/** @file  psMemory.c
+*
+*  @brief Contains the definitions for the memory management system
+*
+*  psMemory.h has additional information and documentation of the routines found in this file.
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Robert Lupton, Princeton University
+*
+*  @version $Revision: 1.51.2.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:57 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#define PS_ALLOW_MALLOC                    // we're allowed to call malloc()
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psLogMsg.h"
+
+#include "psSysUtilsErrors.h"
+
+#define P_PS_MEMMAGIC (psPtr )0xdeadbeef   // Magic number in psMemBlock header
+
+#define P_PS_LARGE_BLOCK_SIZE 65536        // size where under, we try to recycle
+
+static psS32 checkMemBlock(const psMemBlock* m, const char *funcName);
+static psMemBlock* lastMemBlockAllocated = NULL;
+static pthread_mutex_t memBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t memIdMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_mutex_t recycleMemBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static psS32 recycleBins = 13;
+static psS32 recycleBinSize[14] = {
+                                      8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, P_PS_LARGE_BLOCK_SIZE
+                                  };
+
+// N.B. recycleBinSize should be terminated by P_PS_LARGE_BLOCK_SIZE (simplifies search loops)
+static psMemBlock* recycleMemBlockList[13] = {
+            NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+        };
+
+#ifdef PS_MEM_DEBUG
+static psMemBlock* deadBlockList;       // a place to put dead memBlocks in debug mode.
+#endif
+
+/**
+ * Unique ID for allocated blocks
+ */
+static psMemoryId memid = 0;
+
+/**
+ *  Default memExhausted callback.
+ */
+static psPtr memExhaustedCallbackDefault(size_t size)
+{
+    psPtr ptr = NULL;
+
+    pthread_mutex_lock(&recycleMemBlockListMutex);
+    psS32 level = recycleBins - 1;
+
+    while (level >= 0 && ptr == NULL) {
+        while (recycleMemBlockList[level] != NULL && ptr == NULL) {
+            psMemBlock* old = recycleMemBlockList[level];
+
+            recycleMemBlockList[level] = recycleMemBlockList[level]->nextBlock;
+            free(old);
+            ptr = malloc(size);
+        }
+        level--;
+    }
+    pthread_mutex_unlock(&recycleMemBlockListMutex);
+
+    return ptr;
+}
+
+/*
+ * Default callback for both allocate and free. Note that the
+ * value of p_psMemAllocateID/p_psMemFreeID is incremented
+ * by the return value (so returning 0 means that the callback
+ * isn't resignalled)
+ */
+static psMemoryId memAllocateCallbackDefault(const psMemBlock* ptr)
+{
+    static psMemoryId incr = 0; // "p_psMemAllocateID += incr"
+
+    return incr;
+}
+
+static psMemoryId memFreeCallbackDefault(const psMemBlock* ptr)
+{
+    static psMemoryId incr = 0; // "p_psMemFreeID += incr"
+
+    return incr;
+}
+
+static void memProblemCallbackDefault(const psMemBlock* ptr, const char *file, psS32 lineno)
+{
+    if (ptr->refCounter < 1) {
+        psError(PS_ERR_MEMORY_CORRUPTION, false,
+                PS_ERRORTEXT_psMemory_MULTIPLE_FREE,
+                ptr->id, ptr->file, ptr->lineno, file, lineno);
+    }
+
+    if (lineno > 0) {
+        psAbort(__func__, "Detected a problem in the memory system at %s:%d", file, lineno);
+    }
+}
+
+/*
+ * Routines to check the consistency of the allocated and/or free memory arena
+ *
+ * N.b. If the block wasn't allocated by psAlloc, it will appear corrupted
+ */
+static psS32 checkMemBlock(const psMemBlock* m, const char *funcName)
+{
+    // n.b. since this is called by psMemCheckCorruption while the memblock list is mutex locked,
+    // we shouldn't call such things as p_psAlloc/p_psFree here.
+
+    if (m == NULL) {
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_NULL_BLOCK);
+        return 1;
+    }
+
+    if (m->refCounter == 0) {
+        // using an unreferenced block of memory, are you?
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_DEREF_BLOCK_USE,
+                m->id);
+        return 1;
+    }
+
+    if (m->startblock != P_PS_MEMMAGIC || m->endblock != P_PS_MEMMAGIC) {
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_UNDERFLOW,
+                m->id);
+        return 1;
+    }
+    if (*(psPtr *)((int8_t *) (m + 1) + m->userMemorySize) != P_PS_MEMMAGIC) {
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_OVERFLOW,
+                m->id);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * The default callbacks
+ */
+static psMemAllocateCallback memAllocateCallback = memAllocateCallbackDefault;
+static psMemFreeCallback memFreeCallback = memFreeCallbackDefault;
+static psMemProblemCallback memProblemCallback = memProblemCallbackDefault;
+static psMemExhaustedCallback memExhaustedCallback = memExhaustedCallbackDefault;
+
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func)
+{
+    psMemExhaustedCallback old = memExhaustedCallback;
+
+    if (func != NULL) {
+        memExhaustedCallback = func;
+    } else {
+        memExhaustedCallback = memExhaustedCallbackDefault;
+    }
+
+    return old;
+}
+
+psMemProblemCallback psMemProblemCallbackSet(psMemProblemCallback func)
+{
+    psMemProblemCallback old = memProblemCallback;
+
+    if (func != NULL) {
+        memProblemCallback = func;
+    } else {
+        memProblemCallback = memProblemCallbackDefault;
+    }
+
+    return old;
+}
+
+/*
+ * And now the I-want-to-be-informed callbacks
+ *
+ * Call the callbacks when these IDs are allocated/freed
+ */
+psMemoryId p_psMemAllocateID = 0;       // notify user this block is allocated
+psMemoryId p_psMemFreeID = 0;   // notify user this block is freed
+
+psMemoryId psMemAllocateCallbackSetID(psMemoryId id)
+{
+    psMemoryId old = p_psMemAllocateID;
+
+    p_psMemAllocateID = id;
+
+    return old;
+}
+
+psMemoryId psMemFreeCallbackSetID(psMemoryId id)
+{
+    psMemoryId old = p_psMemFreeID;
+
+    p_psMemFreeID = id;
+
+    return old;
+}
+
+psMemAllocateCallback psMemAllocateCallbackSet(psMemAllocateCallback func)
+{
+    psMemFreeCallback old = memAllocateCallback;
+
+    if (func != NULL) {
+        memAllocateCallback = func;
+    } else {
+        memAllocateCallback = memAllocateCallbackDefault;
+    }
+
+    return old;
+}
+
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func)
+{
+    psMemFreeCallback old = memFreeCallback;
+
+    if (func != NULL) {
+        memFreeCallback = func;
+    } else {
+        memFreeCallback = memFreeCallbackDefault;
+    }
+
+    return old;
+}
+
+/*
+ * Return memory ID counter for next block to be allocated
+ */
+psMemoryId psMemGetId(void)
+{
+    psMemoryId id;
+
+    pthread_mutex_lock(&memIdMutex);
+    id = memid + 1;
+    pthread_mutex_unlock(&memIdMutex);
+
+    return id;
+}
+
+psS32 psMemCheckCorruption(psBool abort_on_error)
+{
+    psS32 nbad = 0;               // number of bad blocks
+    psBool failure = false;
+
+    // get exclusive access to the memBlock list to avoid it changing on us while we use it.
+    //    pthread_mutex_lock(&memBlockListMutex);
+
+    for (psMemBlock* iter = lastMemBlockAllocated; iter != NULL; iter = iter->nextBlock) {
+        pthread_mutex_unlock(&memBlockListMutex);
+        failure = checkMemBlock(iter, __func__);
+        pthread_mutex_lock(&memBlockListMutex);
+        if ( failure ) {
+            nbad++;
+
+            memProblemCallback(iter, __func__, __LINE__);
+
+            if (abort_on_error) {
+                // release the lock on the memblock list
+                pthread_mutex_unlock(&memBlockListMutex);
+                psAbort(__func__, "Detected memory corruption");
+                return nbad;
+            }
+        }
+    }
+
+    // release the lock on the memblock list
+    pthread_mutex_unlock(&memBlockListMutex);
+    return nbad;
+}
+
+psPtr p_psAlloc(size_t size, const char *file, psS32 lineno)
+{
+
+    psMemBlock* ptr = NULL;
+
+    size = (size < recycleBinSize[0]) ? recycleBinSize[0] : size; // set the minimum size to allocate
+
+    // memory is of the size I want to bother recycling?
+    if (size < P_PS_LARGE_BLOCK_SIZE) {
+        // find the bin we need.
+        psS32 level = 0;
+
+        while (size > recycleBinSize[level]) {
+            level++;
+        }
+        // Are we in one of the bins
+        if (level < recycleBins) {
+
+            size = recycleBinSize[level];  // round-up size to next sized bin.
+
+            pthread_mutex_lock(&recycleMemBlockListMutex);
+
+            if (recycleMemBlockList[level] != NULL) {
+                ptr = recycleMemBlockList[level];
+                recycleMemBlockList[level] = ptr->nextBlock;
+                if (recycleMemBlockList[level] != NULL) {
+                    recycleMemBlockList[level]->previousBlock = NULL;
+                }
+                size = ptr->userMemorySize;
+            }
+
+            pthread_mutex_unlock(&recycleMemBlockListMutex);
+        }
+    }
+
+    if (ptr == NULL) {
+        ptr = malloc(sizeof(psMemBlock) + size + sizeof(psPtr ));
+
+        if (ptr == NULL) {
+            ptr = memExhaustedCallback(size);
+            if (ptr == NULL) {
+                psAbort(__func__, "Failed to allocate %u bytes at %s:%d", size, file, lineno);
+            }
+        }
+
+        *(psPtr*)&ptr->startblock = P_PS_MEMMAGIC;
+        *(psPtr*)&ptr->endblock = P_PS_MEMMAGIC;
+        ptr->userMemorySize = size;
+        pthread_mutex_init(&ptr->refCounterMutex, NULL);
+    }
+    // increment the memory id safely.
+    pthread_mutex_lock(&memBlockListMutex);
+    *(psMemoryId* ) & ptr->id = ++memid;
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    ptr->file = file;
+    ptr->freeFcn = NULL;
+    ptr->persistent = false;
+    *(psU32 *)&ptr->lineno = lineno;
+    *(psPtr *)((int8_t *) (ptr + 1) + size) = P_PS_MEMMAGIC;
+    ptr->previousBlock = NULL;
+
+    ptr->refCounter = 1;                   // one user so far
+
+    // need exclusive access of the memory block list now...
+    pthread_mutex_lock(&memBlockListMutex);
+
+    // insert the new block to the front of the memBlock linked-list
+    ptr->nextBlock = lastMemBlockAllocated;
+    if (ptr->nextBlock != NULL) {
+        ptr->nextBlock->previousBlock = ptr;
+    }
+    lastMemBlockAllocated = ptr;
+
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    // Did the user ask to be informed about this allocation?
+    if (ptr->id == p_psMemAllocateID) {
+        p_psMemAllocateID += memAllocateCallback(ptr);
+    }
+    // And return the user the memory that they allocated
+    return ptr + 1;                        // user memory
+}
+
+psPtr p_psRealloc(psPtr vptr, size_t size, const char *file, psS32 lineno)
+{
+    size = (size < recycleBinSize[0]) ? recycleBinSize[0] : size; // set the minimum size to allocate
+
+    if (vptr == NULL) {
+        return p_psAlloc(size, file, lineno);
+    } else {
+        psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+        psBool isBlockLast = false;
+
+        if (checkMemBlock(ptr, __func__) != 0) {
+            memProblemCallback(ptr, file, lineno);
+            psAbort(file, "Realloc detected a memory corruption (id %lld @ %s:%d).",
+                    ptr->id, ptr->file, ptr->lineno);
+        }
+
+        pthread_mutex_lock(&memBlockListMutex);
+
+        isBlockLast = (ptr == lastMemBlockAllocated);
+
+        ptr = (psMemBlock* ) realloc(ptr, sizeof(psMemBlock) + size + sizeof(psPtr ));
+
+        if (ptr == NULL) {
+            ptr = memExhaustedCallback(size);
+            if(ptr == NULL) {
+                psAbort(__func__, "Failed to reallocate %ld bytes at %s:%d", size, file, lineno);
+            }
+        }
+
+        ptr->userMemorySize = size;
+        *(psPtr *)((int8_t *) (ptr + 1) + size) = P_PS_MEMMAGIC;
+
+        if (isBlockLast) {
+            lastMemBlockAllocated = ptr;
+        }
+        // the block location may have changed, so fix the linked list addresses.
+        if (ptr->nextBlock != NULL) {
+            ptr->nextBlock->previousBlock = ptr;
+        }
+        if (ptr->previousBlock != NULL) {
+            ptr->previousBlock->nextBlock = ptr;
+        }
+
+        pthread_mutex_unlock(&memBlockListMutex);
+
+        // Did the user ask to be informed about this allocation?
+        if (ptr->id == p_psMemAllocateID) {
+            p_psMemAllocateID += memAllocateCallback(ptr);
+        }
+
+        return ptr + 1;                    // usr memory
+    }
+}
+
+void p_psFree(psPtr vptr, const char *file, psS32 lineno)
+{
+    if (vptr == NULL) {
+        return;
+    }
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+    if (ptr->refCounter < 1) {
+        psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+        psAbort(__func__,PS_ERRORTEXT_psMemory_MULTIPLE_FREE,
+                ptr->id, ptr->file, ptr->lineno, file, lineno);
+    }
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, file, lineno);
+        psAbort(__func__,"Memory Corruption Detected.");
+    }
+
+    (void)p_psMemDecrRefCounter(vptr, file, lineno);    // this handles the free, if required.
+}
+
+/*
+ * Check for memory leaks.
+ */
+psS32 psMemCheckLeaks(psMemoryId id0, psMemBlock* ** arr, FILE * fd, psBool persistence)
+{
+    psS32 nleak = 0;
+    psS32 j = 0;
+    psMemBlock* topBlock = lastMemBlockAllocated;
+
+    pthread_mutex_lock(&memBlockListMutex);
+
+    for (psMemBlock* iter = topBlock; iter != NULL; iter = iter->nextBlock) {
+        if ( (iter->refCounter > 0) &&
+                ( (persistence) || (!persistence && !iter->persistent) ) &&
+                (iter->id >= id0)) {
+
+            nleak++;
+
+            if (fd != NULL) {
+                if (nleak == 1) {
+                    fprintf(fd, "   %20s:line ID\n", "file");
+                }
+
+                fprintf(fd, "   %20s:%-4d %ld\n", iter->file, (int)iter->lineno, (long)iter->id);
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    if (nleak == 0 || arr == NULL) {
+        return nleak;
+    }
+
+    *arr = p_psAlloc(nleak * sizeof(psMemBlock), __FILE__, __LINE__);
+    pthread_mutex_lock(&memBlockListMutex);
+
+    for (psMemBlock* iter = topBlock; iter != NULL; iter = iter->nextBlock) {
+        if ( (iter->refCounter > 0) &&
+                ( (persistence) || (!persistence && !iter->persistent) ) &&
+                (iter->id >= id0)) {
+
+            (*arr)[j++] = iter;
+            if (j == nleak) {              // found them all
+                break;
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    return nleak;
+}
+
+/*
+ * Reference counting APIs
+ */
+
+// return refCounter
+psReferenceCount psMemGetRefCounter(const psPtr vptr)
+{
+    psMemBlock* ptr;
+    psU32 refCount;
+
+    if (vptr == NULL) {
+        return 0;
+    }
+
+    ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    pthread_mutex_lock(&ptr->refCounterMutex);
+    refCount = ptr->refCounter;
+    pthread_mutex_unlock(&ptr->refCounterMutex);
+
+    return refCount;
+}
+
+// increment and return refCounter
+psPtr p_psMemIncrRefCounter(const psPtr vptr, const char *file, psS32 lineno)
+{
+    psMemBlock* ptr;
+
+    if (vptr == NULL) {
+        return vptr;
+    }
+
+    ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__)) {
+        memProblemCallback(ptr, file, lineno);
+    }
+
+    pthread_mutex_lock(&ptr->refCounterMutex);
+    ptr->refCounter++;
+    pthread_mutex_unlock(&ptr->refCounterMutex);
+
+    return vptr;
+}
+
+// decrement and return refCounter
+psPtr p_psMemDecrRefCounter(psPtr vptr, const char *file, psS32 lineno)
+{
+    if (vptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, file, lineno);
+        return NULL;
+    }
+
+    // Did the user ask to be informed about this deallocation?
+    if (ptr->id == p_psMemFreeID) {
+        p_psMemFreeID += memFreeCallback(ptr);
+    }
+
+    pthread_mutex_lock(&ptr->refCounterMutex);
+
+    if (ptr->refCounter > 1) {
+        ptr->refCounter--;                 // multiple references, just decrement the count.
+        pthread_mutex_unlock(&ptr->refCounterMutex);
+
+    } else {
+        pthread_mutex_unlock(&ptr->refCounterMutex);
+
+        if (ptr->freeFcn != NULL) {
+            ptr->freeFcn(vptr);
+        }
+
+        pthread_mutex_lock(&memBlockListMutex);
+
+        // cut the memBlock out of the memBlock list
+        if (ptr->nextBlock != NULL) {
+            ptr->nextBlock->previousBlock = ptr->previousBlock;
+        }
+        if (ptr->previousBlock != NULL) {
+            ptr->previousBlock->nextBlock = ptr->nextBlock;
+        }
+        if (lastMemBlockAllocated == ptr) {
+            lastMemBlockAllocated = ptr->nextBlock;
+        }
+
+        pthread_mutex_unlock(&memBlockListMutex);
+
+        // do we need to recycle?
+        if (ptr->userMemorySize < P_PS_LARGE_BLOCK_SIZE) {
+
+            psS32 level = 1;
+
+            while (ptr->userMemorySize >= recycleBinSize[level]) {
+                level++;
+            }
+            level--;
+
+            ptr->refCounter = 0;
+            ptr->previousBlock = NULL;
+
+            pthread_mutex_lock(&recycleMemBlockListMutex);
+            ptr->nextBlock = recycleMemBlockList[level];
+            if (recycleMemBlockList[level] != NULL) {
+                recycleMemBlockList[level]->previousBlock = ptr;
+            }
+            recycleMemBlockList[level] = ptr;
+            pthread_mutex_unlock(&recycleMemBlockListMutex);
+
+        } else {
+            // memory is larger than I want to recycle.
+            #ifdef PS_MEM_DEBUG
+            (void)p_psRealloc(vptr, 0, file, lineno);
+            ptr->previousBlock = NULL;
+            ptr->nextBlock = deadBlockList;
+            if (deadBlockList != NULL) {
+                deadBlockList->previous = ptr;
+            }
+            deadBlockList = ptr;
+            #else
+
+            pthread_mutex_destroy(&ptr->refCounterMutex);
+            free(ptr);
+            #endif
+
+        }
+
+        vptr = NULL;                       // since we freed it, make sure we return NULL.
+    }
+
+    return vptr;
+}
+
+void psMemSetDeallocator(psPtr vptr, psFreeFcn freeFcn)
+{
+    if (vptr == NULL) {
+        return;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    ptr->freeFcn = freeFcn;
+
+}
+psFreeFcn psMemGetDeallocator(psPtr vptr)
+{
+    if (vptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    return ptr->freeFcn;
+}
+
+psBool p_psMemGetPersistent(psPtr vptr)
+{
+    if (vptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    return ptr->persistent;
+}
+
+void p_psMemSetPersistent(psPtr vptr,psBool value)
+{
+    if (vptr == NULL) {
+        return;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    ptr->persistent = value;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psMemory.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psMemory.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psMemory.h	(revision 22271)
@@ -0,0 +1,455 @@
+/** @file  psMemory.h
+ *
+ *  @brief Contains the definitions for the memory management system
+ *
+ *  This is the generic memory management system put inbetween the user's high level code and the OS-level
+ *  memory allocation routines.  This system adds such features as callback routines for memory error events,
+ *  tracing capabilities, and reference counting.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Robert Lupton, Princeton University
+ *
+ *  @ingroup MemoryManagement
+ *
+ *  @version $Revision: 1.36.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_MEMORY_H)
+#define PS_MEMORY_H
+
+#include <stdio.h>                     // needed for FILE
+#include <pthread.h>                   // we need a mutex to make this stuff thread safe.
+
+#include "psType.h"
+
+/** @addtogroup MemoryManagement
+ *  @{
+ */
+
+/**
+ *  @addtogroup memCallback Memory Callbacks
+ *
+ *  Routines dealing with the creating and setting of memory management callback functions.
+ */
+
+/**
+ *  @addtogroup memTracing Memory Tracing
+ *
+ *  Routines dealing with memory tracing and corruption checking.
+ */
+
+/**
+ *  @addtogroup memRefCount Reference Count
+ *
+ *  Routines dealing with the reference counting of allocated buffers.
+ */
+
+/// typedef for memory identification numbers.  Guaranteed to be some variety of integer.
+typedef psU64 psMemoryId;
+
+/// typedef for a memory block's reference count. Guaranteed to be some variety of integer.
+typedef psU64 psReferenceCount;
+
+/// typedef for deallocator.
+typedef void (*psFreeFcn) (psPtr ptr);
+
+/** Book-keeping data for storage allocator.
+ *  N.b. sizeof(psMemBlock) must be chosen such that if ptr is a pointer
+ *  returned by malloc, then ((char *)ptr + sizeof(psMemBlock)) is properly
+ *  aligned for all storage types.
+ */
+typedef struct psMemBlock
+{
+    const psPtr startblock;            ///< initialised to p_psMEMMAGIC
+    struct psMemBlock* previousBlock;  ///< previous block in allocation list
+    struct psMemBlock* nextBlock;      ///< next block allocation list
+    psFreeFcn freeFcn;                 ///< deallocator.  If NULL, use generic deallocation.
+    size_t userMemorySize;             ///< the size of the user-portion of the memory block
+    const psMemoryId id;               ///< a unique ID for this allocation
+    const char *file;                  ///< set from __FILE__ in e.g. p_psAlloc
+    const psS32 lineno;                  ///< set from __LINE__ in e.g. p_psAlloc
+    pthread_mutex_t refCounterMutex;   ///< mutex to ensure exclusive access to reference counter
+    psReferenceCount refCounter;       ///< how many times pointer is referenced
+    psBool persistent;                   ///< marks if this non-user persistent data like error stack, etc.
+    const psPtr endblock;              ///< initialised to p_psMEMMAGIC
+}
+psMemBlock;
+
+/** prototype of a basic callback used by memory functions
+ *
+ *  @see psMemAllocateCallbackSet
+ *  @ingroup memCallback
+ */
+typedef psMemoryId(*psMemAllocateCallback) (
+    const psMemBlock* ptr              ///< the psMemBlock just allocated
+);
+
+/** prototype of memory free callback used by memory functions
+ *
+ *  @see psMemFreeCallbackSet
+ *  @ingroup memCallback
+ */
+typedef psMemoryId(*psMemFreeCallback) (
+    const psMemBlock* ptr              ///< the psMemBlock being freed
+);
+
+/** prototype of a callback used in error conditions
+ *
+ *  This callback should not try to call psAlloc or psFree.
+ *
+ *  @see psMemProblemCallbackSet
+ *  @ingroup memCallback
+ */
+typedef void (*psMemProblemCallback) (
+    const psMemBlock* ptr,             ///< the pointer to the problematic memory block.
+    const char *file,                  ///< the file in which the problem originated
+    psS32 lineno                         ///< the line number in which the problem originated
+);
+
+/** prototype of a callback function used when memory runs out
+ *
+ *  @return psPtr pointer to requested buffer of the size size_t, or NULL if memory could not
+ *          be found.
+ *
+ *  @see psMemExhaustedCallbackSet
+ *  @ingroup memCallback
+ */
+typedef psPtr (*psMemExhaustedCallback) (
+    size_t size                        ///< the size of buffer required
+);
+
+/** Memory allocation.  This operates much like malloc(), but is guaranteed to return a non-NULL value.
+ *
+ *  @return psPtr pointer to the allocated buffer. This will not be NULL.
+ *  @see psFree 
+ */
+#ifdef DOXYGEN
+psPtr psAlloc(size_t size       ///< Size required
+             );
+#else
+psPtr p_psAlloc(size_t size,    ///< Size required
+                const char *file,       ///< File of call
+                psS32 lineno      ///< Line number of call
+               );
+
+/// Memory allocation. psAlloc sends file and line number to p_psAlloc.
+#ifndef SWIG
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Set the deallocator routine
+ *
+ *  A deallocator routine can optionally be assigned to a memory block to 
+ *  ensure that associated memory blocks also get freed, e.g., memory buffers
+ *  referenced within a struct.
+ *
+ */
+void psMemSetDeallocator(
+    psPtr ptr,                         ///< the memory block to operate on
+    psFreeFcn freeFcn                  ///< the function to be executed at deallocation
+);
+
+/** Get the deallocator routine
+ *
+ *  This function returns the deallocator for a memory block.  A deallocator 
+ *  routine can optionally be assigned to a memory block to ensure that 
+ *  associated memory blocks also get freed, e.g., memory buffers referenced 
+ *  within a struct.  
+ *
+ *  @return psFreeFcn    the routine to be called at deallocation.
+ */
+psFreeFcn psMemGetDeallocator(
+    psPtr ptr                          ///< the memory block
+);
+
+/** Set the memory as persistent so that it is ignored when detecting memory leaks.
+ *
+ *  Used to mark a memory block as persistent data within the library, 
+ *  i.e., non user-level data used to hold psLib's state or cache data.  Such
+ *  examples of this class of memory is psTrace's trace-levels and dynamic
+ *  error codes.
+ *
+ *  Memory marked as persistent is excluded from memory leak checks.
+ *
+ */
+void p_psMemSetPersistent(
+    psPtr ptr,                         ///< the memory block to operate on
+    psBool value                         ///< true if memory is persistent, otherwise false
+);
+
+/** Get the memory's persistent flag.
+ *
+ *  Checks if a memory block has been marked as persistent by 
+ *  p_psMemSetPresistent.
+ *
+ *  Memory marked as persistent is excluded from memory leak checks.
+ *
+ *  @return psBool    true if memory is marked persistent, otherwise false.
+ */
+psBool p_psMemGetPersistent(
+    psPtr ptr                          ///< the memory block to check.
+);
+
+
+/** Memory re-allocation.  This operates much like realloc(), but is guaranteed to return a non-NULL value.
+ *
+ *  @return psPtr pointer to resized buffer. This will not be NULL.
+ *  @see psAlloc, psFree
+ */
+#ifdef DOXYGEN
+psPtr psRealloc(
+    psPtr ptr,                          ///< Pointer to re-allocate
+    size_t size                         ///< Size required
+);
+#else
+psPtr p_psRealloc(
+    psPtr ptr,                         ///< Pointer to re-allocate
+    size_t size,                       ///< Size required
+    const char *file,                  ///< File of call
+    psS32 lineno                       ///< Line number of call
+);
+
+/// Memory re-allocation.  psRealloc sends file and line number to p_psRealloc.
+#ifndef SWIG
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Free memory.  This operates much like free().
+ *
+ *  @see psAlloc, psRealloc
+ */
+#ifdef DOXYGEN
+void psFree(
+    psPtr ptr                          ///< Pointer to free, if NULL, function returns immediately.
+);
+#else
+void p_psFree(
+    psPtr ptr,                         ///< Pointer to free
+    const char *file,                  ///< File of call
+    psS32 lineno                       ///< Line number of call
+);
+
+/// Free memory.  psFree sends file and line number to p_psFree.
+#ifndef SWIG
+#define psFree(ptr) p_psFree(ptr, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Check for memory leaks.  This scans for allocated memory buffers not freed with an ID not less than id0.
+ *  This is used to check for memory leaks by:
+ *      -# before a block of code to be checked, store the current ID count via psGetMemId
+ *      -# after the block of code to be checked, call this function using the ID stored above.  If all
+ *         memory in the block that was allocated has been freed, this call should output nothing and
+ *         return 0.
+ *
+ *  If memory leaks are found, the Memory Problem callback will be called as well.
+ *
+ *  return psS32  number of memory blocks found as 'leaks', i.e., the number of currently allocated memory
+ *              blocks above id0 that have not been freed.
+ *  @see psAlloc, psFree, psgetMemId, psMemProblemCallbackSet
+ *  @ingroup memTracing
+ */
+psS32 psMemCheckLeaks(
+    psMemoryId id0,                    ///< don't list blocks with id < id0
+    psMemBlock* ** arr,                ///< pointer to array of pointers to leaked blocks, or NULL
+    FILE * fd,                         ///< print list of leaks to fd (or NULL)
+    psBool persistence                 ///< make check across all object even persistent ones
+);
+
+/** Check for memory corruption.  Scans all currently allocated memory buffers and checks for corruptions,
+ *  i.e., invalid markers that signify a buffer under/overflow.
+ *
+ *  @ingroup memTracing
+ */
+psS32 psMemCheckCorruption(
+    psBool abort_on_error                ///< Abort on detecting corruption?
+);
+
+/** Return reference counter
+ *
+ *  @ingroup memRefCount
+ */
+psReferenceCount psMemGetRefCounter(
+    const psPtr vptr   ///< Pointer to get refCounter for
+);
+
+/** Increment reference counter and return the pointer
+ *
+ *  @ingroup memRefCount
+ */
+#ifdef DOXYGEN
+psPtr psMemIncrRefCounter(
+    const psPtr vptr                         ///< Pointer to increment refCounter, and return
+);
+#else
+psPtr p_psMemIncrRefCounter(
+    const psPtr vptr,                        ///< Pointer to increment refCounter, and return
+    const char *file,                  ///< File of call
+    psS32 lineno                         ///< Line number of call
+);
+
+#ifndef SWIG
+#define psMemIncrRefCounter(vptr) p_psMemIncrRefCounter(vptr, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Decrement reference counter and return the pointer
+ *
+ *  @ingroup memRefCount
+ *
+ *  @return psPtr    the pointer deremented in refCount, or NULL if pointer is 
+ *                   fully dereferenced.
+ */
+#ifdef DOXYGEN
+psPtr psMemDecrRefCounter(
+    psPtr vptr                         ///< Pointer to decrement refCounter, and return
+);
+#else
+psPtr p_psMemDecrRefCounter(
+    psPtr vptr,                        ///< Pointer to decrement refCounter, and return
+    const char *file,                  ///< File of call
+    psS32 lineno                         ///< Line number of call
+);
+
+#ifndef SWIG
+#define psMemDecrRefCounter(vptr) p_psMemDecrRefCounter(vptr, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Set callback for problems.
+ *
+ *  At various occasions, the memory manager can check the state of the memory 
+ *  stack. If any of these checks discover that the memory stack is corrupted,
+ *  the psMemProblemCallback is called.
+ 
+ *  @ingroup memCallback
+ *
+ *  @return psMemProblemCallback       old psMemProblemCallback function
+ */
+psMemProblemCallback psMemProblemCallbackSet(
+    psMemProblemCallback func          ///< Function to run at memory problem detection
+);
+
+/** Set callback for out-of-memory.
+ *
+ *  If not enough memory is available to satisfy a request by psAlloc or 
+ *  psRealloc, these functions attempt to find an alternative solution by 
+ *  calling the psMemExhaustedCallback, a function which may be set by the 
+ *  programmer in appropriate circumstances, rather than immediately fail. 
+ *  The typical use of such a feature may be when a program needs a large 
+ *  chunk of memory to do an operation, but the exact size is not critical. 
+ *  This feature gives the programmer the opportunity to make a smaller 
+ *  request and try again, limiting the size of the operating buffer.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemExhaustedCallback     old psMemExhaustedCallback function
+ */
+psMemExhaustedCallback psMemExhaustedCallbackSet(
+    psMemExhaustedCallback func        ///< Function to run at memory exhaustion
+);
+
+/** Set call back for when a particular memory block is allocated
+ *
+ *  A private variable, p_psMemAllocateID, can be used to trace the allocation 
+ *  and freeing of specific memory blocks. If p_psMemAllocateID is set and a 
+ *  memory block with that ID is allocated, psMemAllocateCallback is called 
+ *  just before memory is returned to the calling function.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemAllocateCallback      old psMemAllocateCallback function
+ */
+psMemAllocateCallback psMemAllocateCallbackSet(
+    psMemAllocateCallback func       ///< Function to run at memory allocation of specific mem block
+);
+
+/** Set call back for when a particular memory block is freed
+ *
+ *  A private variable, p_psMemFreeID, can be used to trace the freeing of 
+ *  specific memory blocks. If p_psMemFreeID is set and the memory block with 
+ *  the ID is about to be freed, the psMemFreeCallback callback is called just
+ *  before the memory block is freed.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemFreeCallback          old psMemFreeCallback function
+ */
+psMemFreeCallback psMemFreeCallbackSet(
+    psMemFreeCallback func             ///< Function to run at memory free of specific mem block
+);
+
+/** get next memory ID
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemoryId                 the next memory ID to be used
+ */
+psMemoryId psMemGetId(void);
+
+/** set p_psMemAllocateID to specific id
+ *
+ *  A private variable, p_psMemAllocateID, can be used to trace the allocation 
+ *  and freeing of specific memory blocks. If p_psMemAllocateID is set and a 
+ *  memory block with that ID is allocated, psMemAllocateCallback is called 
+ *  just before memory is returned to the calling function.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemoryId       
+ *
+ *  @see psMemAllocateCallbackSet    
+ */
+psMemoryId psMemAllocateCallbackSetID(
+    psMemoryId id                      ///< ID to set
+);
+
+/** set p_psMemFreeID to id
+ *
+ *  A private variable, p_psMemFreeID, can be used to trace the freeing of 
+ *  specific memory blocks. If p_psMemFreeID is set and the memory block with 
+ *  the ID is about to be freed, the psMemFreeCallback callback is called just
+ *  before the memory block is freed.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemoryId                 the old p_psMemFreeID
+ *
+ *  @see psMemFreeCallbackSet
+ */
+psMemoryId psMemFreeCallbackSetID(
+    psMemoryId id                      ///< ID to set
+);
+
+//@} End of Memory Management Functions
+
+#ifndef DOXYGEN
+
+/*
+ * Ensure that any program using malloc/realloc/free will fail to compile
+ */
+#ifndef PS_ALLOW_MALLOC
+#ifdef __GNUC__
+#pragma GCC poison malloc realloc calloc free
+#else
+#define malloc(S)       _Pragma("error Use of malloc is not allowed.  Use psAlloc instead.")
+#define realloc(P,S)    _Pragma("error Use of realloc is not allowed.  Use psRealloc instead.")
+#define calloc(S)       _Pragma("error Use of calloc is not allowed.  Use psAlloc instead.")
+#define free(P)         _Pragma("error Use of free is not allowed.  Use psFree instead.")
+#endif
+#endif
+
+#endif
+// doxygen skip
+
+#endif // end of header file
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psString.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psString.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psString.c	(revision 22271)
@@ -0,0 +1,147 @@
+
+/** @file  psString.c
+ *
+ *  @brief Contains the definition of string utility functions
+ *
+ *  String utility functions defined shall provide basic string copying
+ *  capabilities while using the preferred memory management utilities.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-06 18:15:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "psString.h"
+#include "psMemory.h"
+#include "psError.h"
+
+#include "psSysUtilsErrors.h"
+
+char *psStringCopy(const char *str)
+{
+    // Allocate memory using psAlloc function
+    // Copy input string to memory just allocated
+    // Return the copy
+    return strcpy(psAlloc(strlen(str) + 1), str);
+}
+
+char *psStringNCopy(const char *str, psS32 nChar)
+{
+    char *returnValue = NULL;
+
+    // Check the number of characters to copy is non-negative
+    if (nChar < 0) {
+        // Log error message and return NULL
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psString_NCHAR_NEGATIVE,
+                nChar);
+        return NULL;
+    }
+    // Allocate memory using psAlloc function - nChar bytes
+    // Copy input string to memory allocated up to nChar characters
+    // Return the copy
+    returnValue = strncpy(psAlloc((size_t) nChar + 1), str, (size_t) nChar);
+
+    // Ensure the last byte is NULL character
+    if (nChar > 0) {
+        returnValue[nChar] = '\0';
+    }
+    // Return the string pointer
+    return returnValue;
+}
+
+ssize_t psStringAppend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    size_t          oldLength;          // original string length (sans \0)
+    ssize_t         tailLength;         // length of string to append
+
+    if (!*dest) {
+        *dest = psStringCopy("");
+        oldLength = 0;
+    } else {
+        // size of existing string
+        oldLength = strlen(*dest);
+    }
+
+    // find the size of the string to append
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    tailLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new tail is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (tailLength < 1) {
+        return tailLength == 0 ? oldLength : tailLength;
+    }
+
+    // new string length (sans \0)
+    length = oldLength + tailLength;
+
+    // realloc string to string + tail + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // append tail + \0
+    va_start(args, format);
+    vsnprintf(*dest + oldLength, tailLength + 1, format, args);
+    va_end(args);
+
+    return length;
+}
+
+ssize_t psStringPrepend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    ssize_t         headLength;         // length of string to prepend
+    char            *oldDest;           // copy of original string
+
+    if (!*dest) {
+        // makes the string backup and concatination pointless
+        *dest = psStringCopy("");
+        length = 0;
+    } else {
+        // size of existing string
+        length = strlen(*dest);
+    }
+
+    // find the size of the string to prepend
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    headLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new head is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (headLength < 1) {
+        return headLength == 0 ? length : headLength;
+    }
+
+    // backup original string
+    oldDest = psStringCopy(*dest);
+
+    // new string length (sans \0)
+    length += headLength;
+
+    // realloc string to head + string + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // copy the new head to the beginning of string
+    va_start(args, format);
+    vsnprintf(*dest, length + 1, format, args);
+    va_end(args);
+
+    // append the original string
+    strncat(*dest, oldDest, length + 1);
+
+    psFree(oldDest);
+
+    return length;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psString.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psString.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psString.h	(revision 22271)
@@ -0,0 +1,100 @@
+/** @file  psString.h
+ *
+ *  @brief Contains the declarations of string utility functions
+ *
+ *  @ingroup SysUtils
+ *
+ *  String utility functions defined shall provide basic string copying
+ *  capabilities.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-06 18:15:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_STRING_H
+#define PS_STRING_H
+
+#include <sys/types.h>
+#include "psType.h"
+
+/** This macro will convert the argument to a quoted string */
+#define PS_STRING(S)  #S
+
+// Doxygen group tags
+
+/** @addtogroup SysUtils
+ *  @{
+ */
+
+/** Copies the input string
+ *
+ *  This function shall allocate memory to the length of the input string
+ *  plus one and copy the input string to the newly allocated memory.
+ *
+ *  @return char*      Copy of input string
+ *
+ */
+char *psStringCopy(
+    const char *str
+    /**< Input string of characters to copy */
+);
+
+/** Copies the input string up to the specified number of characters
+ *
+ *  This function shall allocate memory to the length specified by nChar
+ *  plus one and copy the input string to the newly allocated memory.
+ *  This function will only copy nChar bytes from the input to new string,
+ *  so if the input string is larger than nChar characters the copied
+ *  string will be a substring of the input string.  If the input string
+ *  is smaller than nChar bytes then the remaining bytes allocated will
+ *  be set to NULL.
+ *
+ *  @return  char* Copy of input string
+ *
+ */
+
+/*@null@*/
+
+char *psStringNCopy(
+    const char *str,
+    /**< Input string of characters to copy */
+
+    psS32 nChar
+    /**< Number of bytes to allocate for string copy */
+);
+
+/** Appends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return The length of the new string (excluding '\0')
+ */
+
+ssize_t psStringAppend(
+    char **dest,                        ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+);
+
+/** Prepends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return The length of the new string (excluding '\0')
+ */
+
+ssize_t psStringPrepend(
+    char **dest,                        ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+);
+
+/* @} */// Doxygen - End of SystemGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psTrace.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psTrace.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psTrace.c	(revision 22271)
@@ -0,0 +1,557 @@
+/** @file psTrace.c
+ *  \brief basic run-time trace facilities
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the code for procedures to insert
+ *  trace messages into the code.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.48 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-05 21:24:50 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/*****************************************************************************
+    NOTES:
+ In the SRD, higher trace levels correspond to a numerically lower trace
+ value in the code.  This is a bit confusing.  For example, a high-level
+ message might be something like "Begin Processing".  The module programmer
+ might give that a numerically low trace level, such as 1, so then any
+ non-zero trace level in that code component will display thatmessage.
+ 
+ We build a tree of trace components.  Every node in the tree has a
+ depth, which is it's distance from the root.  However, this is not
+ not the same thing as a node's "level", which corresponds to the
+ trace level of that node.
+ 
+I think the following is the correct behavior, but not sure:
+    PS_UNKNOWN_TRACE_LEVEL: We never set the level of a component to this
+    value.  This value is only used when psTraceGetLevel is called with
+    a bad component name, or if the component root is undefined, I think.
+ 
+    PS_DEFAULT_TRACE_LEVEL: This should only be used when adding the
+    intermediate components of a psS64 name.  Ie. the "B" in .A.B.C
+ 
+ *****************************************************************************/
+
+#ifndef PS_NO_TRACE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psMemory.h"
+#include "psTrace.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLogMsg.h"
+
+#include "psSysUtilsErrors.h"
+
+static p_psComponent* cRoot = NULL; // The root of the trace component
+static FILE *traceFP = NULL;        // File destination for messages.
+
+static void componentFree(p_psComponent* comp);
+static p_psComponent* componentAlloc(const char *name, psS32 level);
+
+/*****************************************************************************
+componentAlloc(): allocate memory for a new node, and initialize members.
+ *****************************************************************************/
+static p_psComponent* componentAlloc(const char *name, psS32 level)
+{
+    p_psComponent* comp = psAlloc(sizeof(p_psComponent));
+
+    p_psMemSetPersistent(comp,true);
+    psMemSetDeallocator(comp, (psFreeFcn) componentFree);
+    comp->name = psStringCopy(name);
+    p_psMemSetPersistent((psPtr)comp->name,true);
+    comp->level = level;
+    comp->n = 0;
+    comp->p_psSpecified = false;
+    comp->subcomp = NULL;
+    return comp;
+}
+
+/*****************************************************************************
+componentFree(): free the current node in the root tree, and all children
+nodes as well.
+ *****************************************************************************/
+static void componentFree(p_psComponent* comp)
+{
+    if (comp == NULL) {
+        return;
+    }
+
+    if (comp->subcomp != NULL) {
+        for (psS32 i = 0; i < comp->n; i++) {
+            p_psMemSetPersistent(comp->subcomp[i],false);
+            psFree(comp->subcomp[i]);
+        }
+        p_psMemSetPersistent(comp->subcomp,false);
+        psFree(comp->subcomp);
+    }
+
+    p_psMemSetPersistent((psPtr)comp->name,false);
+    psFree((psPtr)comp->name);
+}
+
+/*****************************************************************************
+initTrace(): simply initialize the component root tree.
+*****************************************************************************/
+static void initTrace(void)
+{
+    if (cRoot == NULL) {
+        cRoot = componentAlloc(".", PS_DEFAULT_TRACE_LEVEL);
+    }
+}
+
+/*****************************************************************************
+Set all trace levels to zero.
+ 
+XXX: Currently, no function calls this routine.
+ *****************************************************************************/
+void p_psTraceReset(p_psComponent* currentNode)
+{
+    psS32 i = 0;
+
+    if (NULL == currentNode) {
+        return;
+    }
+
+    currentNode->level = 0;
+    for (i = 0; i < currentNode->n; i++) {
+        if (NULL == currentNode->subcomp[i]) {
+            psLogMsg("p_psTraceReset", PS_LOG_WARN,
+                     PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT,
+                     i, currentNode->name);
+        } else {
+            p_psTraceReset(currentNode->subcomp[i]);
+        }
+    }
+    return;
+}
+
+/*****************************************************************************
+Set all trace levels to zero.
+ *****************************************************************************/
+void psTraceReset()
+{
+    psFree(cRoot);
+    cRoot = NULL;
+}
+
+/*****************************************************************************
+componentAdd(): Adds the component named "addNodeName" to the root tree.
+ *****************************************************************************/
+static psBool componentAdd(const char *addNodeName, psS32 level)
+{
+    psS32 i = 0;                        // Loop index variable.
+    char name[strlen(addNodeName) + 1]; // buffer for writeable copy.
+    char *pname = name;
+    char *firstComponent = NULL;        // first component of name
+    p_psComponent* currentNode = cRoot;
+    psS32 nodeExists = 0;
+
+    // XXX: Verify that this is the correct behavior.
+    if (strcmp("", addNodeName) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,
+                PS_ERRORTEXT_psTrace_ADD_NULL_COMPONENT);
+        return false;
+    }
+
+    // Is this the root node? If so, simply set level and return.
+    if (strcmp(".", addNodeName) == 0) {
+        cRoot->level = level;
+        return true;
+    }
+
+    if (addNodeName[0] != '.') {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                PS_ERRORTEXT_psTrace_MALFORMED_COMPONENT_NAME,
+                addNodeName);
+        return false;
+    }
+
+    strcpy(name, addNodeName);
+    pname = name+1;
+    // Iterate through the components of addNodeName.  Strip off the first
+    // component of the name, find that in the root tree, or add it if it
+    // does not exist, then move to the next component in the name.
+
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        nodeExists = 0;
+        for (i = 0; i < currentNode->n; i++) {
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                nodeExists = 1;
+                if (pname == NULL) {
+                    currentNode->level = level;
+                }
+            }
+        }
+
+        if (nodeExists == 0) {
+            currentNode->subcomp = psRealloc(currentNode->subcomp,
+                                             (currentNode->n + 1) * sizeof(p_psComponent* ));
+            p_psMemSetPersistent(currentNode->subcomp,true);
+
+            currentNode->n = (currentNode->n) + 1;
+
+            if (pname == NULL) {
+                // This is the final component to add.
+                currentNode->subcomp[(currentNode->n) - 1] = componentAlloc(firstComponent, level);
+            } else {
+                // We are adding an intermediate component.  The trace level
+                // is not defined.  An undefined trace level inherits the
+                // trace level of it's parent.  However, we do not set that
+                // specifically here since that would inheritance to be a
+                // static, one-time, type of behavior.
+
+                currentNode->subcomp[(currentNode->n) - 1] = componentAlloc(firstComponent, PS_DEFAULT_TRACE_LEVEL);
+            }
+            currentNode = currentNode->subcomp[(currentNode->n) - 1];
+        }
+    }
+
+    return true;
+}
+
+/*****************************************************************************
+    psSetTraceLevel(): add the component named "comp" to the component tree,
+ if it is not already there, and set it's trace level to "level".
+ 
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+ 
+    Input:
+ comp
+ level
+    Output:
+ none
+    Returns:
+ zero
+*****************************************************************************/
+psBool psTraceSetLevel(const char *comp,   // component of interest
+                       psS32 level)  // desired trace level
+{
+    char *compName = NULL;
+
+    // If the root component tree does not exist, then initialize it.
+    if (cRoot == NULL) {
+        initTrace();
+    }
+
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+
+    // If the component name has no leading dot, then supply it.
+    if (comp[0] != '.') {
+        compName = (char *) psAlloc(10 + strlen(comp));
+        strcpy(compName, ".");
+        compName = strcat(compName, comp);
+    } else {
+        compName = (char *) comp;
+    }
+
+    // Add the new component to the component tree.
+    if ( !componentAdd(compName, level) ) {
+        psError(PS_ERR_UNKNOWN, false,
+                PS_ERRORTEXT_psTrace_FAILED_TO_ADD_COMPONENT,
+                level,
+                compName);
+
+        if (comp[0] != '.') {
+            psFree(compName);
+        }
+        return false;
+    }
+
+    if (comp[0] != '.') {
+        psFree(compName);
+    }
+
+    return true;
+}
+
+/*****************************************************************************
+    doGetTraceLevel()
+ This function recursively searches the root component tree for the
+ component named "name", which is supplied by a parameter.  If it
+ finds that component, it returns the level of that component.
+ Otherwise, it returns ???.
+ 
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+ 
+    Inputs:
+ name:
+    Outputs:
+ none
+    Returns:
+ The trace level of the "name" component.
+ *****************************************************************************/
+static psS32 doGetTraceLevel(const char *aname)
+{
+    char name[strlen(aname) + 1];       // need a writeable copy: for strsep()
+    char *pname = name;
+    char *firstComponent = NULL;        // first component of name
+    p_psComponent* currentNode = cRoot;
+    psS32 i = 0;
+    psS32 defaultLevel = 0;
+
+    if (NULL == currentNode) {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    if (strcmp(".", aname) == 0) {
+        return (cRoot->level);
+    }
+
+    if (aname[0] != '.') {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    defaultLevel = cRoot->level;
+    strcpy(name, aname);
+    pname = name+1;
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        for (i = 0; i < currentNode->n; i++) {
+            if (NULL == currentNode->subcomp[i]) {
+                psLogMsg("p_psTraceReset", PS_LOG_WARN,
+                         PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT,
+                         i, currentNode->name);
+            }
+
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                // For level inheritance purpose, we save the level of this
+                // component if it is not DEFAULT.
+                if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
+                    defaultLevel = currentNode->level;
+                }
+                // Determine if this is the last component:
+                if (pname == NULL) {
+                    if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
+                        return (currentNode->level);
+                    } else {
+                        return(defaultLevel);
+                    }
+                }
+            }
+        }
+    }
+    return(defaultLevel);
+}
+
+/*****************************************************************************
+    psTraceLevelGet()
+ Return a trace level of "name" in the root component tree.  If the
+ exact string of components in "name" does not exist in the root
+ tree, we return the deepest level of the match.
+    Input:
+ name
+    Output:
+ none
+    Return:
+ The level of "name" in the root component tree.
+ *****************************************************************************/
+psS32 psTraceGetLevel(const char *name)
+{
+    char *compName = NULL;
+    psS32 traceLevel;
+
+    if (cRoot == NULL) {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    // If the component name has no leading dot, then supply it.
+    if (name[0] != '.') {
+        compName = (char *) psAlloc(10 + strlen(name));
+        strcpy(compName, ".");
+        compName = strcat(compName, name);
+        traceLevel = doGetTraceLevel(compName);
+        psFree(compName);
+    } else {
+        // Search the component root tree, determine the trace level.
+        traceLevel = doGetTraceLevel(name);
+    }
+
+    // XXX: The default trace level is currently set at -1, which is not a
+    // valid trace level.  This is convenient in determining whether or not
+    // a component should inherit the trace level from parent nodes.  However,
+    // it's not clear that -1 should ever be returned by this function.
+    // The SDR is unclear on this point and we should probably request IfA
+    // comment.
+    if (traceLevel == PS_DEFAULT_TRACE_LEVEL) {
+        traceLevel = PS_THE_OTHER_DEFAULT_TRACE_LEVEL;
+    }
+
+    return(traceLevel);
+}
+
+/*****************************************************************************
+    doPrintTraceLevels()
+ This function recursively searches the component tree supplied by the
+ parameter "comp" and prints the name and level of each component.
+    Inputs:
+ comp: a node in the component tree.
+ level: the level of that node
+    Outputs:
+ none
+    Returns:
+ null
+ *****************************************************************************/
+static void doPrintTraceLevels(const p_psComponent* comp,
+                               psS32 depth,
+                               psS32 defLevel)
+{
+    psS32 i = 0;
+
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+
+    if (comp->name[0] == '\0') {
+        return;
+    } else {
+        if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
+            fprintf(traceFP,"%*s%-*s %d\n", depth, "", 20 - depth, comp->name, defLevel);
+        } else {
+            fprintf(traceFP, "%*s%-*s %d\n", depth, "", 20 - depth, comp->name, comp->level);
+        }
+    }
+
+    for (i = 0; i < comp->n; i++) {
+        if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
+            doPrintTraceLevels(comp->subcomp[i], depth + 1, defLevel);
+        } else {
+            doPrintTraceLevels(comp->subcomp[i], depth + 1, comp->level);
+        }
+    }
+}
+
+
+/*****************************************************************************
+psPrintTraceLevels(): Simply print all the trace levels in the trace level
+component tree.
+Inputs:
+ none
+Outputs:
+ none
+Returns:
+ null
+*****************************************************************************/
+void psTracePrintLevels(void)
+{
+    if (cRoot == NULL) {
+        return;
+    }
+
+    doPrintTraceLevels(cRoot, 0, PS_THE_OTHER_DEFAULT_TRACE_LEVEL);
+}
+
+/*****************************************************************************
+p_psTrace(): we display the trace message to standard output if the trace
+level of that message, supplied by the parameter "level" is higher than the
+trace level that is currently associated with the component named by the
+parameter "comp".
+Input:
+ comp
+ level
+ ...  a printf-style output string.
+Output:
+ none
+Return:
+ null
+ *****************************************************************************/
+void p_psTrace(const char *comp,        // component being traced
+               psS32 level,       // desired trace level
+               ...)             // arguments
+{
+    char *fmt = NULL;
+    va_list ap;
+    psS32 i = 0;
+
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+
+    if (NULL == comp) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psTrace_NULL_TRACETREE,
+                __func__);
+        return;
+    }
+    // Only display this message if it's trace level is less than the level
+    // of it's associatedcomponent.
+    if (level <= psTraceGetLevel(comp)) {
+        va_start(ap, level);
+
+        // The following functions get the variable list of parameters with
+        // which this function was called, and print them to the standard
+        // output.
+        fmt = va_arg(ap, char *);
+
+        // We indent each message one space for each level of the message.
+        for (i = 0; i < level; i++) {
+            fprintf(traceFP, " ");
+        }
+        vfprintf(traceFP, fmt, ap);
+        va_end(ap);
+    }
+    fflush(traceFP);
+}
+
+// XXX EAM : I've added code to close the old traceFP (safely)
+void psTraceSetDestination(FILE * fp)
+{
+
+    bool special;
+
+    // XXX EAM perhaps return an error?
+    if (fp == NULL) {
+        return;
+    }
+
+    // cannot close traceFP if one of the special FILE ptrs
+    special  = (traceFP == NULL);
+    special |= (traceFP == stdin);
+    special |= (traceFP == stdout);
+    special |= (traceFP == stderr);
+
+    if (!special) {
+        fclose (traceFP);
+    }
+    traceFP = fp;
+}
+
+FILE *psTraceGetDestination()
+{
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+    return traceFP;
+}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psTrace.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psTrace.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psTrace.h	(revision 22271)
@@ -0,0 +1,105 @@
+/** @file psTrace.h
+ *  \brief basic run-time trace facilities
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the prototypes for defining procedures to insert
+ *  trace messages into the code.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-28 23:46:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_TRACE_H)
+#define PS_TRACE_H 1
+
+#define PS_UNKNOWN_TRACE_LEVEL -9999   // we don't know this name's level
+#define PS_DEFAULT_TRACE_LEVEL -1
+#define PS_THE_OTHER_DEFAULT_TRACE_LEVEL 0
+
+/** \addtogroup LogTrace
+ *  \{
+ */
+
+/** Functions **************************************************************/
+
+//#define PS_NO_TRACE 1   ///< to turn off all tracing
+
+// XXX EAM : the old 'empty' values of (void) 0 are dangerous
+# if defined(PS_NO_TRACE)
+    #   define psTrace(facil, level, ...)   /* do nothing */
+    #   define p_psTrace(facil, level, ...) /* do nothing */
+    #   define psTraceSetLevel(facil,level) /* do nothing */
+    #   define psTraceGetLevel(facil)       /* do nothing */
+    #   define psTraceReset()               /* do nothing */
+    #   define psTraceFree()                /* do nothing */
+    #   define psTracePrintLevels()         /* do nothing */
+    #   define psTraceSetDestination(fp)    /* do nothing */
+    #   define psTraceSetDestination()      /* do nothing */
+    #   define PS_TRACE_ON 0
+
+    # else /* PS_NO_TRACE */
+        #   define PS_TRACE_ON 1
+
+        /** Basic structure for the component tree.  A component is a string of the
+            form aaa.bbb.ccc, and may itself contain further subcomponents.  The
+            Component structure doesn't in fact contain it's full name, but only the
+            last part. */
+        typedef struct p_psComponent
+        {
+            const char *name;   // last part of name of component
+            psS32 level;   // trace level for this component
+            bool p_psSpecified;
+            psS32 n;    // number of subcomponents
+            struct p_psComponent* *subcomp;     // next level of subcomponents
+        }
+p_psComponent;
+
+#ifdef DOXYGEN
+void psTrace(const char *facil,        ///< facilty of interest
+             psS32 myLevel,            ///< desired trace level
+             ...)                      ///< trace message arguments
+;
+#else
+/// Send a trace message
+void p_psTrace(const char *facil,      ///< facilty of interest
+               psS32 myLevel,          ///< desired trace level
+               ...)                    ///< trace message arguments
+;
+
+#ifndef SWIG
+#define psTrace(facil, level, ...) p_psTrace(facil, level, __VA_ARGS__)
+#endif /* SWIG */
+
+#endif /* DOXYGEN */
+
+/// Set trace level
+psBool psTraceSetLevel(const char *facil,     ///< facilty of interest
+                       psS32 level)     ///< desired trace level
+;
+
+/// Get the trace level
+psS32 psTraceGetLevel(const char *facil) ///< facilty of interest
+;
+
+/// Set all trace levels to zero (do not free nodes in the component tree).
+void psTraceReset();
+
+/// print trace levels
+void psTracePrintLevels(void);
+
+/// Set the destination of future trace messages.
+void psTraceSetDestination(FILE * fp);
+
+/// Get the current destination for trace messages.
+FILE *psTraceGetDestination(void);
+
+/* \} */// End of SystemGroup Functions
+
+#endif /* PS_NO_TRACE */
+
+#endif /* PS_TRACE_H */
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sys/psType.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sys/psType.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sys/psType.h	(revision 22271)
@@ -0,0 +1,245 @@
+/** @file  psType.h
+*
+*  @brief Contains support for basic types
+*
+*  This file defines common datatypes used throughout psLib.
+*
+*  @ingroup DataContainer
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-06 01:12:58 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_TYPE_H
+#define PS_TYPE_H
+
+#include <complex.h>
+#include <stdint.h>
+#include <float.h>
+#include <stdbool.h>
+
+/// @addtogroup DataContainer
+/// @{
+
+/******************************************************************************/
+
+/*  TYPE DEFINITIONS                                                          */
+
+/******************************************************************************/
+
+/** Basic data types used by the containers.
+ *
+ * The basic types of the primitives used by psLib are defined within this enum. This enum is in turn used by
+ * the psType struct.
+ *
+ */
+
+typedef uint8_t psU8;                  ///< 8-bit unsigned int
+typedef uint16_t psU16;                ///< 16-bit unsigned int
+typedef uint32_t psU32;                ///< 32-bit unsigned int
+typedef uint64_t psU64;                ///< 64-bit unsigned int
+typedef int8_t psS8;                   ///< 8-bit signed int
+typedef int16_t psS16;                 ///< 16-bit signed int
+typedef int32_t psS32;                 ///< 32-bit signed int
+typedef int64_t psS64;                 ///< 64-bit signed int
+typedef float psF32;                   ///< 32-bit floating point
+typedef double psF64;                  ///< 64-bit floating point
+
+#ifdef SWIG
+typedef struct
+{
+    float re, im;
+}
+psC32;
+typedef struct
+{
+    double re,im;
+}
+psC64;
+#else
+typedef float _Complex psC32;          ///< complex with 32-bit floating point Real and Imagary numbers
+typedef double _Complex psC64;         ///< complex with 64-bit floating point Real and Imagary numbers
+#endif
+
+typedef void* psPtr;                   ///< void pointer
+typedef bool psBool;                   ///< boolean value
+
+typedef enum {
+    PS_TYPE_S8   = 0x0101,             ///< Character.
+    PS_TYPE_S16  = 0x0102,             ///< Short integer.
+    PS_TYPE_S32  = 0x0104,             ///< Integer.
+    PS_TYPE_S64  = 0x0108,             ///< Long integer.
+    PS_TYPE_U8   = 0x0301,             ///< Unsigned character.
+    PS_TYPE_U16  = 0x0302,             ///< Unsigned psS16 integer.
+    PS_TYPE_U32  = 0x0304,             ///< Unsigned integer.
+    PS_TYPE_U64  = 0x0308,             ///< Unsigned psS64 integer.
+    PS_TYPE_F32  = 0x0404,             ///< Single-precision Floating point.
+    PS_TYPE_F64  = 0x0408,             ///< Double-precision floating point.
+    PS_TYPE_C32  = 0x0808,             ///< Complex numbers consisting of single-precision floating point.
+    PS_TYPE_C64  = 0x0810,             ///< Complex numbers consisting of double-precision floating point.
+    PS_TYPE_BOOL = 0x1301              ///< Boolean.
+} psElemType;
+
+#define PS_TYPE_MASK PS_TYPE_U8        /**< the psElemType to use for mask image */
+#define PS_TYPE_MASK_DATA U8           /**< the data member to use for mask image */
+#define PS_TYPE_MASK_NAME "psU8"       /**< the data type for mask as a string */
+
+typedef psU8 psMaskType;               ///< the C datatype for a mask image
+typedef psBool psBOOL;                 ///< allow psBOOL to be used instead of psBool (for macros)
+
+#define PS_MIN_S8        INT8_MIN      /**< minimum valid psS8 value */
+#define PS_MIN_S16       INT16_MIN     /**< minimum valid psS16 value */
+#define PS_MIN_S32       INT32_MIN     /**< minimum valid psS32 value */
+#define PS_MIN_S64       INT64_MIN     /**< minimum valid psS64 value */
+#define PS_MIN_U8        0             /**< minimum valid psU8 value */
+#define PS_MIN_U16       0             /**< minimum valid psU16 value */
+#define PS_MIN_U32       0             /**< minimum valid psU32 value */
+#define PS_MIN_U64       0             /**< minimum valid psU64 value */
+#define PS_MIN_F32       -FLT_MAX      /**< minimum valid psF32 value */
+#define PS_MIN_F64       -DBL_MAX      /**< minimum valid psF64 value */
+#define PS_MIN_C32       -FLT_MAX      /**< minimum valid real or imaginary psC32 value */
+#define PS_MIN_C64       -DBL_MAX      /**< minimum valid real or imaginary psC32 value */
+
+#define PS_MAX_S8        INT8_MAX      /**< maximum valid psS8 value */
+#define PS_MAX_S16       INT16_MAX     /**< maximum valid psS16 value */
+#define PS_MAX_S32       INT32_MAX     /**< maximum valid psS32 value */
+#define PS_MAX_S64       INT64_MAX     /**< maximum valid psS64 value */
+#define PS_MAX_U8        UINT8_MAX     /**< maximum valid psU8 value */
+#define PS_MAX_U16       UINT16_MAX    /**< maximum valid psU16 value */
+#define PS_MAX_U32       UINT32_MAX    /**< maximum valid psU32 value */
+#define PS_MAX_U64       UINT64_MAX    /**< maximum valid psU64 value */
+#define PS_MAX_F32       FLT_MAX       /**< maximum valid psF32 value */
+#define PS_MAX_F64       DBL_MAX       /**< maximum valid psF64 value */
+#define PS_MAX_C32       FLT_MAX       /**< maximum valid real or imaginary psC32 value */
+#define PS_MAX_C64       DBL_MAX       /**< maximum valid real or imaginary psC32 value */
+
+#define PS_TYPE_BOOL_NAME "psBool"
+#define PS_TYPE_S8_NAME   "psS8"
+#define PS_TYPE_S16_NAME  "psS16"
+#define PS_TYPE_S32_NAME  "psS32"
+#define PS_TYPE_S64_NAME  "psS64"
+#define PS_TYPE_U8_NAME   "psU8"
+#define PS_TYPE_U16_NAME  "psU16"
+#define PS_TYPE_U32_NAME  "psU32"
+#define PS_TYPE_U64_NAME  "psU64"
+#define PS_TYPE_F32_NAME  "psF32"
+#define PS_TYPE_F64_NAME  "psF64"
+#define PS_TYPE_C32_NAME  "psC32"
+#define PS_TYPE_C64_NAME  "psC64"
+
+#define PS_TYPE_NAME(value,type) \
+switch(type) { \
+case PS_TYPE_BOOL: \
+    value = PS_TYPE_BOOL_NAME; \
+    break; \
+case PS_TYPE_S8: \
+    value = PS_TYPE_S8_NAME; \
+    break; \
+case PS_TYPE_S16: \
+    value = PS_TYPE_S16_NAME; \
+    break; \
+case PS_TYPE_S32: \
+    value = PS_TYPE_S32_NAME; \
+    break; \
+case PS_TYPE_S64: \
+    value = PS_TYPE_S64_NAME; \
+    break; \
+case PS_TYPE_U8: \
+    value = PS_TYPE_U8_NAME; \
+    break; \
+case PS_TYPE_U16: \
+    value = PS_TYPE_U16_NAME; \
+    break; \
+case PS_TYPE_U32: \
+    value = PS_TYPE_U32_NAME; \
+    break; \
+case PS_TYPE_U64: \
+    value = PS_TYPE_U64_NAME; \
+    break; \
+case PS_TYPE_F32: \
+    value = PS_TYPE_F32_NAME; \
+    break; \
+case PS_TYPE_F64: \
+    value = PS_TYPE_F64_NAME; \
+    break; \
+case PS_TYPE_C32: \
+    value = PS_TYPE_C32_NAME; \
+    break; \
+case PS_TYPE_C64: \
+    value = PS_TYPE_C64_NAME; \
+    break; \
+default: \
+    value = "unknown"; \
+};
+
+/// Macro to get the bad pixel reason code (stored as part of mask value)
+#define PS_BADPIXEL_BITMASK 0x0f
+#define PS_GET_BADPIXEL(maskValue) (maskValue & PS_BADPIXEL_BITMASK)
+
+#define PS_IS_BADPIXEL(maskValue) (PS_GET_BADPIXEL(maskValue) != 0)
+
+/// Macro to apply a bad pixel reason code to mask image
+#define PS_SET_BADPIXEL(maskValue, reasonCode) \
+{ \
+    maskValue = (psMaskType)((reasonCode & PS_BADPIXEL_BITMASK) | (maskValue & ~PS_BADPIXEL_BITMASK)); \
+}
+
+/// Macro to determine if the psElemType is an integer.
+#define PS_IS_PSELEMTYPE_INT(x) ((x & 0x100) == 0x100)
+/// Macro to determine if the psElemType is unsigned.
+#define PS_IS_PSELEMTYPE_UNSIGNED(x) ((x & 0x200) == 0x200)
+/// Macro to determine if the psElemType is a real (non-complex) floating-point type.
+#define PS_IS_PSELEMTYPE_REAL(x) ((x & 0x400) == 0x400)
+/// Macro to determine if the psElemType is complex number type.
+#define PS_IS_PSELEMTYPE_COMPLEX(x) ((x & 0x800) == 0x800)
+/// Macro to determine if the psElemType is boolean type.
+#define PS_IS_PSELEMTYPE_BOOL(x) ((x & 0x1000) == 0x1000)
+/// Macro to determine the storage size, in bytes, of the psElemType.
+#define PSELEMTYPE_SIZEOF(x) (x & 0xFF)
+
+/** Dimensions of a data type.
+ *
+ * The dimensions of containers used by psLib are defined within this enum. This enum is used by the psType
+struct. *
+ */
+typedef enum {
+    PS_DIMEN_SCALAR,            ///< Scalar.
+    PS_DIMEN_VECTOR,            ///< Vector.
+    PS_DIMEN_TRANSV,            ///< Transposed vector.
+    PS_DIMEN_IMAGE,             ///< Image.
+    PS_DIMEN_OTHER              ///< Something else that's not supported for arithmetic.
+} psDimen;
+
+/** The type of a data type.
+ *
+ * All psLib complex types consist of primitive components. This struct provides the description of those
+ * primitives.
+ *
+ */
+typedef struct
+{
+    psElemType type;            ///< Primitive type.
+    psDimen dimen;              ///< Dimensionality.
+}
+psType;
+
+/** The type of a basic data type
+ *
+ *  All psLib complex types consist of primitive components.  This structure provides the ability to cast
+ *  an unknown data structure to safely test the underlining data type.
+ *
+ */
+typedef struct
+{
+    psType  type;              ///< Data type information
+}
+psMath;
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/.cvsignore
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/.cvsignore	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/.cvsignore	(revision 22271)
@@ -0,0 +1,7 @@
+Makefile.in
+.deps
+.libs
+Makefile
+*.lo
+*.la
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/Makefile.am
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/Makefile.am	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/Makefile.am	(revision 22271)
@@ -0,0 +1,40 @@
+#Makefile for sysUtils functions of psLib
+#
+INCLUDES = \
+	-I$(top_srcdir)/src/astronomy \
+	-I$(top_srcdir)/src/collections \
+	-I$(top_srcdir)/src/dataManip \
+	-I$(top_srcdir)/src/dataIO \
+	-I$(top_srcdir)/src/image \
+	$(all_includes)
+
+noinst_LTLIBRARIES = libpslibsysUtils.la
+
+libpslibsysUtils_la_SOURCES = \
+	psMemory.c     \
+	psError.c      \
+	psTrace.c      \
+	psLogMsg.c     \
+	psAbort.c      \
+	psString.c     \
+	psConfigure.c  \
+	psErrorCodes.c 
+
+BUILT_SOURCES = psSysUtilsErrors.h
+EXTRA_DIST = psSysUtilsErrors.dat psSysUtilsErrors.h sysUtils.i
+
+psSysUtilsErrors.h: psSysUtilsErrors.dat
+	perl $(top_srcdir)/src/parseErrorCodes.pl --data=$? $@
+
+pslibincludedir = $(includedir)
+pslibinclude_HEADERS = \
+	psType.h       \
+	psMemory.h     \
+	psError.h      \
+	psTrace.h      \
+	psLogMsg.h     \
+	psAbort.h      \
+	psString.h     \
+	psConfigure.h  \
+	psErrorCodes.h
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psAbort.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psAbort.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psAbort.c	(revision 22271)
@@ -0,0 +1,38 @@
+
+/** @file  psAbort.c
+ *
+ *  @brief Contains the definition for abort function
+ *
+ *  The abort logging and handling shall be performed by psAbort function.
+ *  This will allow for consistent handling of other software units
+ *  needing to abort from program execution.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *   
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "psAbort.h"
+#include "psLogMsg.h"
+
+void psAbort(const char *name, const char *fmt, ...)
+{
+    va_list argPtr;             // variable list arguement pointer
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    // Call logging function with PS_LOG_ABORT level
+    psLogMsgV(name, PS_LOG_ABORT, fmt, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    // Call system abort function to terminate program execution
+    abort();
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psAbort.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psAbort.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psAbort.h	(revision 22271)
@@ -0,0 +1,47 @@
+
+/** @file  psAbort.h
+ *
+ *  @brief Contains the declarations for the abort function
+ *
+ *  The abort logging and handling shall be performed by psAbort function.
+ *  This will allow for consistent handling of other software units
+ *  needing to abort from program execution.
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ABORT_H
+#define PS_ABORT_H
+
+// Doxygen grouping tags
+
+/** @addtogroup ErrorHandling
+ *  @{
+ */
+
+/** Reports an abort message to logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of 
+ *  PS_LOG_ABORT and pass the parameters name and fmt to generate a proper
+ *  log message.  After logging, this function will call system abort 
+ *  function to abnormally terminate the program.
+ *
+ *  @return  void No return value
+ *
+ */
+void psAbort(
+    const char *name,                  ///< Source of abort such as file or function detected
+    const char *fmt,                   ///< A printf style formatting statement defining msg
+    ...
+);
+
+/* @} */// Doxygen - End of SystemGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psConfigure.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psConfigure.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psConfigure.c	(revision 22271)
@@ -0,0 +1,55 @@
+/** @file  psConfigure.c
+ *
+ *  @brief Contains the declarations for initialization, memory finalization, and configuration.
+ *
+ *  These functions initalize psLib data before the beginning of a run and remove (finalize) the
+ *  same data after the run is complete. A function is also provided to return the current
+ *  psLib version.
+ *
+ *  @ingroup Configure
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.7.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-13 21:27:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psString.h"
+#include "psTime.h"
+#include "psError.h"
+#include "psConfigure.h"
+#include "psSysUtilsErrors.h"
+#include "config.h"
+
+char* psLibVersion(void)
+{
+    char version[80];
+    snprintf(version,80,"%s-v%s",PACKAGE,VERSION);
+
+    return(psStringCopy(version));
+}
+
+void psLibInit(const char* timeConfig)
+{
+    // Still needs error codes to be set
+    // Still needs random number generator initialization
+    // Please code me, Robert and George.
+    // PAP: RNG seeding not required --- this is done in psRandom.
+
+    if(!p_psTimeInit(timeConfig)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psConfigure_INITIALIZATION_FAILED, "psTime");
+        return;
+    }
+}
+
+void psLibFinalize(void)
+{
+    // Users of persistent memory should free them in this function
+
+    if(!p_psTimeFinalize()) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psConfigure_FINALIZATION_FAILED, "psTime");
+        return;
+    }
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psConfigure.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psConfigure.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psConfigure.h	(revision 22271)
@@ -0,0 +1,65 @@
+/** @file  psConfigure.h
+ *
+ *  @brief Contains the declarations for initialization, memory finalization, and configuration.
+ *
+ *  These functions initalize psLib data before the beginning of a run and remove (finalize) the
+ *  same data after the run is complete. A function is also provided to return the current
+ *  psLib version.
+ *
+ *  @ingroup Configure
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author George Gusciora, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.3.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-13 21:27:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_CONFIGURE_H
+#define PS_CONFIGURE_H
+
+
+/** @addtogroup Configure
+ *  @{
+ */
+
+/** Get current psLib version
+ *
+ *  Returns the current psLib version name as a string.
+ *
+ *  @return char*: String with version name.
+ */
+char* psLibVersion(
+    void
+);
+
+/** Initializes persistent memory.
+ *
+ *  Creates persistant memory items used throughout psLib. Items created within this method should be freed
+ *  with the psLibFinalize function.
+ *  current, a non-NULL psErr is returned with code PS_ERR_NONE.
+ *
+ *  @return void: void.
+ */
+void psLibInit(
+    const char* timeConfig
+);
+
+/** Removes persistant memory created with the psLibInit function.
+ *
+ *  The memory created but not freed by psLib modules should be freed within this
+ *  function at the end of a psLib execution cycle.
+ *
+ *  @return void: void.
+ */
+void psLibFinalize(
+    void
+);
+
+
+/* @} */
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psError.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psError.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psError.c	(revision 22271)
@@ -0,0 +1,216 @@
+/** @file  psError.c
+ *
+ *  @brief Contains the definitions for the error reporting functions
+ *
+ *  Error reporting functions shall be used to create log entries in the
+ *  event errors are detected.  The messages shall give enough information
+ *  to allow the user to know where the error has occurred and the type
+ *  of error detected.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.24 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-07 20:27:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdarg.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psMemory.h"
+
+#define MAX_ERROR_STACK_SIZE 64
+static psErr* errorStack[MAX_ERROR_STACK_SIZE];
+static psU32 errorStackSize = 0;
+pthread_mutex_t lockErrorStack = PTHREAD_MUTEX_INITIALIZER;
+
+static void pushErrorStack(psErr* err);
+
+static void pushErrorStack(psErr* err)
+{
+
+    pthread_mutex_lock(&lockErrorStack);
+
+    if (errorStackSize < MAX_ERROR_STACK_SIZE) {
+        errorStack[errorStackSize] = psMemIncrRefCounter(err);
+        errorStackSize++;
+        p_psMemSetPersistent(err,true);
+        p_psMemSetPersistent(err->msg,true);
+        p_psMemSetPersistent(err->name,true);
+    }
+
+    pthread_mutex_unlock(&lockErrorStack);
+}
+
+static void errFree(psErr* err)
+{
+    if (err != NULL) {
+        psFree(err->msg);
+        psFree(err->name);
+    }
+}
+
+psErr* psErrAlloc(const char* name, psErrorCode code, const char* msg)
+{
+    psErr* err = psAlloc(sizeof(psErr));
+    err->msg = strcpy(psAlloc(strlen(msg) + 1), msg);
+    err->name = strcpy(psAlloc(strlen(name) + 1), name);
+    err->code = code;
+
+    psMemSetDeallocator(err,(psFreeFcn)errFree);
+
+    return err;
+}
+
+psErrorCode p_psError(const char* file,
+                      int lineno,
+                      const char* func,
+                      psErrorCode code,
+                      psBool new,
+                      const char* fmt,
+                      ...)
+{
+    char errMsg[2048];
+    psErr* err;
+    char msgName[1024];
+
+    snprintf(msgName,1024,"%s (%s:%d)",func,file,lineno);
+
+    va_list argPtr;             // variable list arguement pointer
+
+    if (new) {
+        psErrorClear();
+    }
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    vsnprintf(errMsg,2048,fmt,argPtr);
+    err = psErrAlloc(msgName,code,errMsg);
+    pushErrorStack(err);
+
+    // Call logging function with PS_LOG_ERROR level
+    psLogMsg(msgName, PS_LOG_ERROR, errMsg);
+
+    // Clean up stack after variable argument has been used
+    va_end(argPtr);
+
+    psFree(err);
+
+    return code;
+}
+
+void p_psWarning(const char* file,
+                 int lineno,
+                 const char* func,
+                 const char* fmt,
+                 ...)
+{
+    char msgName[1024];
+
+    snprintf(msgName,1024,"%s (%s:%d)",func,file,lineno);
+
+    va_list argPtr;             // variable list argument pointer
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    psLogMsgV(msgName, PS_LOG_WARN, fmt, argPtr);
+
+    // Clean up stack after variable argument has been used
+    va_end(argPtr);
+
+    return;
+}
+
+psErr* psErrorGet(psS32 which)
+{
+    psErr* result;
+
+    pthread_mutex_lock(&lockErrorStack);
+
+    // Check for negative reference and if found return PS_ERR_NONE
+    if (which < 0 ) {
+        result = psErrAlloc("", PS_ERR_NONE, "");
+    } else {
+
+        which = errorStackSize-1-which;     // the which input is from the end of errorStack
+        if (which < 0 || which >= errorStackSize) {
+            result = psErrAlloc("",PS_ERR_NONE,"");    // no error at the given location
+        } else {
+            result = psMemIncrRefCounter(errorStack[which]); // a new reference passed back
+        }
+    }
+
+    pthread_mutex_unlock(&lockErrorStack);
+
+    return result;
+}
+
+psS32 psErrorGetStackSize()
+{
+    return errorStackSize;
+}
+
+psErr* psErrorLast(void)
+{
+    return psErrorGet(0);
+}
+
+void psErrorClear(void)
+{
+    pthread_mutex_lock(&lockErrorStack);
+
+    for (int lcv=0;lcv < errorStackSize; lcv++) {
+        p_psMemSetPersistent(errorStack[lcv],false);
+        p_psMemSetPersistent(errorStack[lcv]->msg,false);
+        p_psMemSetPersistent(errorStack[lcv]->name,false);
+        psFree(errorStack[lcv]);
+    }
+    errorStackSize = 0;
+
+    pthread_mutex_unlock(&lockErrorStack);
+
+}
+void psErrorStackPrint(FILE *fd, const char *fmt, ...)
+{
+    va_list argPtr;             // variable list arguement pointer
+
+    // Get the variable list parameters to pass to logging function
+    va_start(argPtr, fmt);
+
+    psErrorStackPrintV(fd,fmt,argPtr);
+
+    va_end(argPtr);
+}
+
+void psErrorStackPrintV(FILE *fd, const char *fmt, va_list va)
+{
+
+    pthread_mutex_lock(&lockErrorStack);
+
+    if (errorStackSize > 0) {
+        vfprintf(fd,fmt,va);
+
+        for (psS32 lcv=0;lcv<errorStackSize;lcv++) {
+            if(errorStack[lcv]->code >= PS_ERR_BASE) {
+                fprintf(fd," -> %s: %s\n     %s\n",
+                        errorStack[lcv]->name,
+                        psErrorCodeString(errorStack[lcv]->code),
+                        errorStack[lcv]->msg);
+            } else {
+                fprintf(fd," -> %s: %s\n     %s\n",
+                        errorStack[lcv]->name,
+                        strerror(errorStack[lcv]->code),
+                        errorStack[lcv]->msg);
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&lockErrorStack);
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psError.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psError.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psError.h	(revision 22271)
@@ -0,0 +1,177 @@
+/** @file  psError.h
+ *
+ *  @brief Contains the declarations for the error reporting functions
+ *
+ *  Error reporting functions shall be used to create log entries in the
+ *  event errors are detected.  The messages shall give enough information
+ *  to allow the user to know where the error has occurred and the type
+ *  of error detected.
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ERROR_H
+#define PS_ERROR_H
+
+#include<stdio.h>
+#include<stdbool.h>
+#include<stdarg.h>
+
+#include "psErrorCodes.h"
+
+/** @addtogroup ErrorHandling
+ *  @{
+ */
+
+/** Error message object */
+typedef struct
+{
+    char* name;                        ///< category of code that caused the error
+    psErrorCode code;                  ///< class of error
+    char* msg;                         ///< the message associated with the error
+}
+psErr;
+
+/** Get a error from the error stack
+ *
+ *  Previous errors on the stack are returned by psErrorGet (a value of 0
+ *  passed to psErrorGet is equivalent to a call to psErrorLast).
+ *
+ *  if no error is at the which position, a non-NULL psErr is returned with
+ *  code PS_ERR_NONE.
+ *
+ *  @return    Error message object at 'which'
+ */
+psErr* psErrorGet(
+    psS32 which                          ///< position in the error stack. 0 is last error on stack.
+);
+
+/** Get last error put on the error stack
+ *
+ *  The last error reported is available from psErrorLast; if no errors are
+ *  current, a non-NULL psErr is returned with code PS_ERR_NONE.
+ *
+ *  @return psErr*     Reference to last error message on error stack
+ */
+psErr* psErrorLast(void);
+
+/** Clears the error stack.
+ *
+ *  The error stack may be completely cleared with psErrorClear.
+ *
+ */
+void psErrorClear(void);
+
+/** Get the error stack depth
+ *
+ *  @return psS32   The number of items on the error stack
+ */
+psS32 psErrorGetStackSize();
+
+/** Prints error stack to specified open file descriptor
+ *
+ *  The entire error stack may be printed to an open file descriptor by
+ *  calling psErrorStackPrint; if and only if there are current errors, the
+ *  printf-style string fmt is first printed to the file descriptor fd. In
+ *  this printout, error codes are replaced by their string equivalents.
+ *
+ */
+void psErrorStackPrint(
+    FILE* fd,                          ///< destination file descriptor
+    const char* fmt,                   ///< printf-style format of header line
+    ...                                ///< any parameters required in fmt
+);
+
+#ifndef SWIG
+/** Prints error stack to specified open file descriptor
+ *
+ *  The entire error stack may be printed to an open file descriptor by
+ *  calling psErrorStackPrintV; if and only if there are current errors, the
+ *  vprintf-style string fmt is first printed to the file descriptor fd. In
+ *  this printout, error codes are replaced by their string equivalents.
+ *
+ */
+void psErrorStackPrintV(
+    FILE* fd,                          ///< destination file descriptor
+    const char* fmt,                   ///< printf-style format of header line
+    va_list va                         ///< any parameters required in fmt
+);
+#endif
+
+#ifdef DOXYGEN
+/** Reports an error message to the logging facility
+ *
+ *  This function will invoke the psLogMsg function with a level of
+ *  PS_LOG_ERROR and pass the parameters name and fmt to generate a proper
+ *  log message.
+ *
+ *  This function modifies the error stack.
+ *
+ *  @return psErrorCode    the given error code
+ */
+psErrorCode psError(
+    psErrorCode code,                  ///< Error class code
+    psBool new,                        ///< true if error originates at this location
+    const char* fmt,
+    ...
+);
+
+/** Logs a warning message.
+ *
+ *  This procedure logs a message to the destination set by a prior
+ *  call to psLogSetDestination(), This is equivalent to calling
+ *  psLogMsg with a level of PS_LOG_WARN.
+ *
+ */
+void psWarning(
+    const char* fmt,
+    ...
+);
+#else
+psErrorCode p_psError(
+    const char* file,
+    int lineno,
+    const char* func,
+    psErrorCode code,                  ///< Error class code
+    psBool new,                        ///< true if error originates at this location
+    const char* fmt,
+    ...
+);
+void p_psWarning(
+    const char* file,
+    int lineno,
+    const char* func,
+    const char* fmt,
+    ...
+);
+
+
+#ifndef SWIG
+#define psError(code,new,...) p_psError(__FILE__,__LINE__,__func__,code,new,__VA_ARGS__)
+#define psWarning(...) p_psWarning(__FILE__,__LINE__,__func__,__VA_ARGS__)
+#endif
+
+#endif
+
+/** Create a new psErr struct
+ *
+ *  Creates a new psErr struct, making a copy of the parameters.
+ *
+ *  @return psErr*     new psErr object
+ */
+psErr* psErrAlloc(
+    const char* name,                  ///< Name of error in the form aaa.bbb.ccc
+    psErrorCode code,                  ///< Error class code
+    const char* msg                    ///< Error message
+);
+
+/* @} */// End of SysUtils Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psErrorCodes.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psErrorCodes.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psErrorCodes.c	(revision 22271)
@@ -0,0 +1,188 @@
+/** @file  psErrorCodes.c
+ *
+ *  @brief Contains the error codes for the error classes
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-07 20:27:41 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+
+#include "psError.h"
+#include "psErrorCodes.h"
+#include "psList.h"
+#include "psMemory.h"
+
+#include "psSysUtilsErrors.h"
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error class in psErrorCodes.dat, the following
+ * substitutions are made:
+ *     $1  The error code name (first word in the psErrorCodes.dat lines)
+ *     $2  The error description (rest of the line in psErrorCodes.dat)
+ *     $n  The order of the source line in psErrorCodes.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+static psErrorDescription staticErrorCodes[] = {
+            {PS_ERR_NONE,"not an error"},
+            {PS_ERR_BASE,"base error"},
+            //~Start    {PS_ERR_$1,"$2"},
+            {PS_ERR_UNKNOWN,"unknown error"},
+            {PS_ERR_IO,"I/O error"},
+            {PS_ERR_LOCATION_INVALID,"specified location is unknown"},
+            {PS_ERR_MEMORY_CORRUPTION,"memory corruption detected"},
+            {PS_ERR_MEMORY_DEREF_USAGE,"dereferenced memory still used"},
+            {PS_ERR_BAD_PARAMETER_VALUE,"parameter is out-of-range"},
+            {PS_ERR_BAD_PARAMETER_TYPE,"parameter is of unsupported type"},
+            {PS_ERR_BAD_PARAMETER_NULL,"parameter is null"},
+            {PS_ERR_BAD_PARAMETER_SIZE,"size of parameter's data is outside of acceptable range."},
+            {PS_ERR_UNEXPECTED_NULL,"unexpected NULL found"},
+            {PS_ERR_OS_CALL_FAILED,"unexpected result from an OS standard library call"},
+            //~End
+            {PS_ERR_N_ERR_CLASSES,"error classes end marker"}
+        };
+
+static psList* dynamicErrorCodes = NULL;
+static const psErrorDescription* getErrorDescription(psErrorCode code);
+
+
+static const psErrorDescription* getErrorDescription(psErrorCode code)
+{
+    // first, search the static error codes
+
+    psS32 n = 0;
+    while(staticErrorCodes[n].code != PS_ERR_N_ERR_CLASSES &&
+            staticErrorCodes[n].code != code) {
+        n++;
+    }
+
+    if (staticErrorCodes[n].code == code) {
+        return &staticErrorCodes[n];
+    } else {
+        psErrorDescription* desc;
+        // make sure there is a list to search
+        if (dynamicErrorCodes == NULL) {
+            return NULL;
+        }
+
+        // search dynamic list of error descriptions before giving up.
+        psListIterator* iter = psListIteratorAlloc(dynamicErrorCodes,PS_LIST_HEAD,true);
+        while ((desc = (psErrorDescription*)psListGetAndIncrement(iter)) != NULL) {
+            if (desc->code == code) {
+                psFree(iter);
+                return desc;
+            }
+        }
+        psFree(iter);
+    }
+    return NULL;
+}
+
+static void freeErrorDescription(psErrorDescription* err)
+{
+    psFree((psPtr)err->description);
+}
+
+psErrorDescription* psErrorDescriptionAlloc(psErrorCode code,
+        const char *description)
+{
+    psErrorDescription* err = psAlloc(sizeof(psErrorDescription));
+    err->code = code;
+    if (description == NULL) {
+        err->description = NULL;
+    } else {
+        err->description = psAlloc(sizeof(char)*strlen(description)+1);
+        strcpy((char*)err->description,description);
+    }
+
+    psMemSetDeallocator(err,(psFreeFcn)freeErrorDescription);
+    return err;
+}
+
+
+const char *psErrorCodeString(psErrorCode code)
+{
+    // Check input argument is non-negative
+    if ( code < 0 ) {
+        return NULL;
+    }
+
+    const psErrorDescription* desc = getErrorDescription(code);
+
+    if (desc == NULL) {
+        return NULL;
+    }
+
+    return desc->description;
+}
+
+void psErrorRegister(const psErrorDescription* errors,
+                     psS32 nerror)
+{
+    if (nerror < 1) {
+        return;
+    }
+
+    if (errors == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psErrorCode_NULL_ERRORDESCRIPTION);
+        return;
+    }
+
+    if (dynamicErrorCodes == NULL) {
+        dynamicErrorCodes = psListAlloc(NULL);
+        p_psMemSetPersistent(dynamicErrorCodes,true);
+
+        p_psMemSetPersistent(dynamicErrorCodes->iterators,true);
+        p_psMemSetPersistent(dynamicErrorCodes->iterators->data,true);
+        for (int i = 0; i < dynamicErrorCodes->iterators->n;i++) {
+            p_psMemSetPersistent(dynamicErrorCodes->iterators->data[i],true);
+        }
+    }
+
+    for (psS32 i=0;i<nerror;i++) {
+        psErrorDescription* err = psErrorDescriptionAlloc(
+                                      errors[i].code, errors[i].description);
+        p_psMemSetPersistent(err,true);
+        p_psMemSetPersistent((psPtr)err->description,true);
+        if (! psListAdd(dynamicErrorCodes,
+                        PS_LIST_HEAD,
+                        err) ) {
+
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psErrorCode_ERRORCODE_REGISTER_FAILED,
+                    i);
+        }
+        p_psMemSetPersistent(dynamicErrorCodes->head,true);
+        psFree(err);
+    }
+}
+
+psBool p_psErrorUnregister(psErrorCode code)
+{
+    // Check input argument is non-negative
+    if ( code < 0 ) {
+        return false;
+    }
+
+    const psErrorDescription* desc = getErrorDescription(code);
+
+    if (desc == NULL) {
+        return false;
+    }
+
+    if (dynamicErrorCodes == NULL) {
+        return false;
+    }
+
+    return psListRemoveData(dynamicErrorCodes,(psPtr)desc);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psErrorCodes.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psErrorCodes.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psErrorCodes.h	(revision 22271)
@@ -0,0 +1,111 @@
+/** @file  psErrorCodes.h
+ *
+ *  @brief Contains the error codes for the error classes
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ERROR_CODES_H
+#define PS_ERROR_CODES_H
+
+#include "psType.h"
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error class in psErrorCodes.dat, the following
+ * substitutions are made:
+ *     $1  The error code name (first word in the psErrorCodes.dat lines)
+ *     $2  The error description (rest of the line in psErrorCodes.dat)
+ *     $n  The order of the source line in psErrorCodes.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+/** @addtogroup ErrorHandling
+ *  @{
+ */
+
+/** enumeration of the static error code classes
+ */
+typedef enum {
+    PS_ERR_NONE = 0,                   ///< not an error
+    PS_ERR_BASE = 256,
+    /**< base error.  Any psErrorCode less than this should be taken to be
+     *   valid values of errno
+     */
+
+    //~Start     PS_ERR_$1,   ///< $2
+    PS_ERR_UNKNOWN,   ///< unknown error
+    PS_ERR_IO,   ///< I/O error
+    PS_ERR_LOCATION_INVALID,   ///< specified location is unknown
+    PS_ERR_MEMORY_CORRUPTION,   ///< memory corruption detected
+    PS_ERR_MEMORY_DEREF_USAGE,   ///< dereferenced memory still used
+    PS_ERR_BAD_PARAMETER_VALUE,   ///< parameter is out-of-range
+    PS_ERR_BAD_PARAMETER_TYPE,   ///< parameter is of unsupported type
+    PS_ERR_BAD_PARAMETER_NULL,   ///< parameter is null
+    PS_ERR_BAD_PARAMETER_SIZE,   ///< size of parameter's data is outside of acceptable range.
+    PS_ERR_UNEXPECTED_NULL,   ///< unexpected NULL found
+    PS_ERR_OS_CALL_FAILED,   ///< unexpected result from an OS standard library call
+    //~End
+    PS_ERR_N_ERR_CLASSES               ///< end marker - should not be used as a true error
+} psErrorCode;
+
+/** An error code with description
+ */
+typedef struct
+{
+    psErrorCode code;                  ///< An error code
+    const char *description;           ///< the associated description
+}
+psErrorDescription;
+
+/** Allocates a new psErrorDescription
+ *
+ *  @return psErrorDescription*        new psErrorDescription struct.
+ */
+psErrorDescription* psErrorDescriptionAlloc(
+    psErrorCode code,                  ///< An error code
+    const char *description            ///< the associated description
+);
+
+/** Retrieves the description of an error code.
+ *
+ *  The routine psErrorCodeString returns the string associated with an error 
+ *  code.
+ *
+ *  @return const char*     the description associated with the given code.
+ */
+const char *psErrorCodeString(
+    psErrorCode code                   ///< the associated error code
+);
+
+/** Register an error code
+ *
+ *  Any project needed to use psLib must define the necessary error codes and
+ *  associated message strings.  This function registers an array of error 
+ *  codes with the error handling subsystem.
+ *
+ */
+void psErrorRegister(
+    const psErrorDescription* errors,  ///< Array of error codes to register
+    psS32 nerror                         ///< number of errors in input array
+);
+
+/** Clears error codes registered via psErrorRegister.
+ *
+ *  @return psBool    TRUE if given errorcode was removed, otherwise FALSE.
+ */
+psBool p_psErrorUnregister(
+    psErrorCode code                   ///< the error code to find and remove
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psLogMsg.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psLogMsg.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psLogMsg.c	(revision 22271)
@@ -0,0 +1,387 @@
+/** @file  psLogMsg.c
+ *  @brief Procedures for logging messages.
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the prototypes for defining procedure which set
+ *  message log levels, messahe log formats, message log destinations, and
+ *  for generating the messages themselves.
+ *  @ingroup LogTrace
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.39.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-13 21:40:21 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/*****************************************************************************
+NOTES: currently, the prototype code has the following global variables:
+    static psS32 p_psGlobalLogDest;
+    static psS32 p_psGlobalLogLevel;
+    static psS32 p_psLogTime;
+    static psS32 p_psLogHost;
+    static psS32 p_psLogLevel;
+    static psS32 p_psLogName;
+    static psS32 p_psLogMsg;
+ *****************************************************************************/
+#include "config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "psLogMsg.h"
+#include "psError.h"
+#include "psTrace.h"
+
+#include "psSysUtilsErrors.h"
+
+#define MIN_LOG_LEVEL 0
+#define MAX_LOG_LEVEL 9
+
+#define MAX_LOG_LINE_LENGTH 256
+
+static FILE *logDest = (FILE *) 1;      // flag to initialize to stderr before using.
+static psS32 globalLogLevel = PS_LOG_INFO;        // log all messages at this or above
+static psBool logTime = true;     // Flag to include time info
+static psBool logHost = true;     // Flag to include host info
+static psBool logLevel = true;    // Flag to include level info
+static psBool logName = true;     // Flag to include name info
+static psBool logMsg = true;      // Flag to include message info
+
+/*****************************************************************************
+psLogSetLevel(): Set the current log level and return old level.
+Input:
+ level (psS32): the new log level.
+Output:
+ none
+Return:
+ The old log level.
+ *****************************************************************************/
+psS32 psLogSetLevel(psS32 level)
+{
+    // Save old global log level for changing it.
+    psS32 oldLevel = globalLogLevel;
+
+    if ((level < MIN_LOG_LEVEL) || (level > MAX_LOG_LEVEL)) {
+        psLogMsg("logmsg", PS_LOG_WARN, "Attempt to set invalid logMsg level: %d", level);
+        level = (level < MIN_LOG_LEVEL) ? MIN_LOG_LEVEL : MAX_LOG_LEVEL;
+    }
+    // Set new global log level
+    globalLogLevel = level;
+
+    // Return old global log level
+    return oldLevel;
+}
+
+/*****************************************************************************
+psLogSetDestination(): sets the destination where log messages will be
+sent to.
+ 
+Input:
+ dest (psS32): the new log destination
+Output:
+ None.
+Return:
+ An integer specifying the old log destination.
+ *****************************************************************************/
+psBool psLogSetDestination(const char *dest)
+{
+    char protocol[5];
+    char location[257];
+
+    // if logDest has not been initialized, do so before using it
+    if (logDest == (FILE *) 1) {
+        logDest = stderr;
+    }
+
+    if (dest == NULL || strcmp(dest, "none") == 0) {
+        if (logDest != NULL && logDest != stderr && logDest != stdout) {
+            fclose(logDest);
+        }
+        logDest = NULL;
+        return true;
+    }
+
+    if (sscanf(dest, "%4s:%256s", protocol, location) < 2) {
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psLogMsg_DESTINATION_MALFORMED,
+                dest);
+        return false;
+    }
+
+    if (strcmp(protocol, "dest") == 0) {
+        if (strcmp(location, "stderr") == 0) {
+            if (logDest != NULL && logDest != stderr && logDest != stdout) {
+                fclose(logDest);
+            }
+            logDest = stderr;
+            return true;
+        }
+        if (strcmp(location, "stdout") == 0) {
+            if (logDest != NULL && logDest != stderr && logDest != stdout) {
+                fclose(logDest);
+            }
+            logDest = stdout;
+            return true;
+        }
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psLogMsg_DEST_LOCATION_INVALID,
+                location);
+        return 1;
+    } else if (strcmp(protocol, "file") == 0) {
+        FILE *file = fopen(location, "w");
+
+        if (file == NULL) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psLogMsg_OPEN_FILE_FAILED,
+                    location);
+            return false;
+        }
+        if (logDest != NULL && logDest != stderr && logDest != stdout) {
+            fclose(logDest);
+        }
+        logDest = file;
+        return true;
+    }
+
+    psError(PS_ERR_LOCATION_INVALID, true,
+            PS_ERRORTEXT_psLogMsg_UNSUPPORTED_PROTOCOL,
+            protocol);
+    return false;
+}
+
+/*****************************************************************************
+psLogSetFormat(): Set the format of psLogMsg output.  More precisely,
+    provide a string consisting of the letters {H (host), L (level), M
+    (message), N (name), T (time)}.  The default is "HLMNT".  This string
+    determines whether or not they associated type of information will be
+    included in message logs.  It does not determine the order in which that
+    information will appear (that order is fixed).
+ 
+Input:
+    fmt: a string specifying the format.
+Output:
+    none.
+Return:
+    NULL.
+ *****************************************************************************/
+bool psLogSetFormat(const char *fmt)
+{
+    // assume nothing desired unless specified
+    logHost = false;
+    logLevel = false;
+    logMsg = false;
+    logName = false;
+    logTime = false;
+
+    // if fmt is NULL, no logging is desired.
+    if (fmt == NULL) {
+        return true;   // No problems encountered in parsing
+    }
+
+    if (strlen(fmt) == 0) {
+        fmt = "THLNM";
+    }
+    // Step through each character in the format string.  For each letter
+    // in that string, set/unset the appropriate logging.
+
+    for (const char *ptr = fmt; *ptr != '\0'; ptr++) {
+        switch (*ptr) {
+        case 'H':
+        case 'h':
+            logHost = true;
+            break;
+        case 'L':
+        case 'l':
+            logLevel = true;
+            break;
+        case 'M':
+        case 'm':
+            logMsg = true;
+            break;
+        case 'N':
+        case 'n':
+            logName = true;
+            break;
+        case 'T':
+        case 't':
+            logTime = true;
+            break;
+        default:
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psLogMsg_UNKNOWN_KEY, *ptr);
+            break;
+        }
+    }
+
+    if (!logMsg) {
+        psTrace("utils.logMsg", 1, "You must at least log error messages (You chose \"%s\")", fmt);
+        return false;
+    }
+
+    return true;   // No problems
+}
+
+#if !defined(HOST_NAME_MAX)                // should be in limits.h
+#define HOST_NAME_MAX 256
+#endif
+
+/*****************************************************************************
+    psVLogMsg(): This routine sends the message, which is a printf style
+ string specified in the "..." argument, to the current message log
+ destination with the severity specified by the "level" argument.
+    Input:
+ name
+ level
+ fmt
+ ap
+    Output:
+ none
+    Return:
+ NULL.
+ *****************************************************************************/
+void psLogMsgV(const char *name, psS32 level, const char *fmt, va_list ap)
+{
+    static psS32 first = 1;       // Flag for calling gethostname()
+    static char hostname[HOST_NAME_MAX + 1];
+
+    // Buffer for hostname.
+    char clevel = 0;            // letter-name for level
+    char head[MAX_LOG_LINE_LENGTH + 2]; // the added two are for the ending | and \0
+    char *head_ptr = head;      // where we've got to in head
+    psS32 maxLength = MAX_LOG_LINE_LENGTH;
+    time_t clock = time(NULL);  // The current time.
+    struct tm *utc = gmtime(&clock);    // The current gm time.
+
+    // if logDest has not been initialized, do so before using it
+    if (logDest == (FILE *) 1) {
+        logDest = stderr;
+    }
+    // If logging is off, or if the level is too high, return immediately.
+    if ((level > globalLogLevel) || (logDest == NULL)) {
+        return;
+    }
+    // If I have not been here yet, determine my hostname and save it.
+    if (first) {
+        first = 0;
+        gethostname(hostname, HOST_NAME_MAX);
+    }
+
+    switch (level) {
+    case PS_LOG_ABORT:
+        clevel = 'A';
+        break;
+
+    case PS_LOG_ERROR:
+        clevel = 'E';
+        break;
+
+    case PS_LOG_WARN:
+        clevel = 'W';
+        break;
+
+    case PS_LOG_INFO:
+        clevel = 'I';
+        break;
+
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+    case 9:
+        clevel = level + '0';
+        break;
+
+    default:
+        psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n", level, fmt);
+        level = (level < 0) ? 0 : 9;
+        clevel = level + '0';
+        break;
+    }
+
+    // Create the various log fields...
+    if (logTime) {
+        maxLength -= snprintf(head_ptr, maxLength, "%4d:%02d:%02d %02d:%02d:%02dZ",
+                              utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
+                              utc->tm_hour, utc->tm_min, utc->tm_sec) - 1;
+        head_ptr += strlen(head_ptr);
+    }
+    // Hostname should be 20 characters.
+    if (logHost) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%-20s", hostname);
+        head_ptr += strlen(head_ptr);
+    }
+    if (logLevel) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%c", clevel);
+        head_ptr += strlen(head_ptr);
+    }
+    if (logName) {
+        if (head_ptr > head) {
+            *head_ptr++ = '|';
+        }
+        maxLength -= snprintf(head_ptr, maxLength, "%s", name);
+
+        head_ptr += strlen(head_ptr);
+    }
+
+    if (head_ptr > head) {
+        *head_ptr++ = '\n';
+    } else if (!logMsg) {                  // no output desired
+        return;
+    }
+    *head_ptr = '\0';
+
+    fputs(head, logDest);
+    if (logMsg) {
+        char msg[1024];
+        char* msgPtr;
+        vsnprintf(msg,1024, fmt, ap);  // create message
+
+        // detect multiple lines in message and indent each line by 4 spaces.
+        char* line = strtok_r(msg,"\n",&msgPtr);
+        while (line != NULL) {
+            fprintf(logDest,"    %s\n",line);
+            line = strtok_r(NULL,"\n",&msgPtr);
+        }
+    } else {
+        fputc('\n', logDest);
+    }
+}
+
+/*****************************************************************************
+    psLogMsg(): This routine sends the message, which is a printf style
+ string specified in the "..." argument, to the current message log
+ destination with the severity specified by the "level" argument.
+    Input:
+ name: Indicates the source of this log message.
+ level: The severity of this log message.
+ fmt: The printf-stype formatted string, followed by the arguments
+  to that string.
+ ... The arguments to the above printf-style string.
+    Output:
+ none
+    Return:
+ NULL
+ *****************************************************************************/
+void psLogMsg(const char *name, psS32 level, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    psLogMsgV(name, level, fmt, ap);
+    va_end(ap);
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psLogMsg.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psLogMsg.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psLogMsg.h	(revision 22271)
@@ -0,0 +1,106 @@
+/** @file  psLogMsg.h
+ *  @brief Procedures for logging messages.
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the prototypes for defining procedure which set
+ *  message log levels, messahe log formats, message log destinations, and
+ *  for generating the messages themselves.
+ *  @ingroup LogTrace
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-22 21:52:49 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_LOG_MSG_H)
+#define PS_LOG_MSG_H
+#include <stdarg.h>
+
+#include "psType.h"
+
+/** @addtogroup LogTrace
+ *  @{
+ */
+
+/** This procedure sets the destination for future log messages.  Currently
+ *  the destination is specified by an integer which can have the following
+ *  pre-defined values: PS_LOG_NONE, PS_LOG_TO_STDOUT, and PS_LOG_TO_STDERR.
+ *  In future versions, this procedure will take a character string as an
+ *  argument which can specify more general log destinations.
+ *
+ *  @return psS32     true if set successfully, otherwise false.
+ */
+psBool psLogSetDestination(
+    const char *dest                   ///< Specifies where to send messages.
+);
+
+/** This procedure sets the message level for future log messages.  Subsequent
+ *  log messages, with a log level of "mylevel", will only be logged if
+ *  "mylevel" is less than the current log level set by this procedure.
+ *  Ie. higher values set by this procedure will cause more log messages to
+ *  be displayed.
+ *
+ *  @return psS32    old logging level
+ */
+psS32 psLogSetLevel(
+    psS32 level                          ///< Specifies the system log level
+);
+
+/** This procedure sets the log format for future log messages.  The argument
+ *  must be a character string consistsing of the letters H (host), L
+ *  (level), M (message), N (name), and T (time).  The default is "THLNM".
+ *  Deleting a letter from the string will cause the associated information
+ *  to not be logged.
+ *
+ */
+void psLogSetFormat(
+    const char *fmt                    ///< Specifies the system log format
+);
+
+/** This procedure logs a message to the destination set by a prior
+ *  call to psLogSetDestination(), if myLevel is less than the level
+ *  specified by a prior call to psLogSetLevel().  The message is specified
+ *  with a printf-stype string an arguments.
+ *
+ */
+void psLogMsg(
+    const char *name,                  ///< name of the log source
+    psS32 myLevel,                       ///< severity level of this log message
+    const char *fmt,                   ///< printf-style format command
+    ...
+);
+
+#ifndef SWIG
+/** This procedure is functionally equivalent to psLogMsg(), except that
+ *  it takes a va_list as the message parameter, not a printf-style string.
+ *
+ */
+void psLogMsgV(
+    const char *name,                  ///< name of the log source
+    psS32 myLevel,                       ///< severity level of this log message
+    const char *fmt,                   ///< printf-style format command
+    va_list ap                         ///< varargs argument list
+);
+#endif
+
+///< Status codes for log messages
+enum {
+    PS_LOG_ABORT = 0,                  ///< log message is a critical error, perform an abort after printing
+    PS_LOG_ERROR,                      ///< log message is an error, but don't abort
+    PS_LOG_WARN,                       ///< log message is a warning
+    PS_LOG_INFO                        ///< log message is informational only
+};
+
+///< Destinations for log messages
+enum {
+    PS_LOG_NONE,                       ///< turn off logging
+    PS_LOG_TO_STDERR,                  ///< log to system's stderr
+    PS_LOG_TO_STDOUT                   ///< log to system's stdout
+};
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psMemory.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psMemory.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psMemory.c	(revision 22271)
@@ -0,0 +1,700 @@
+/** @file  psMemory.c
+*
+*  @brief Contains the definitions for the memory management system
+*
+*  psMemory.h has additional information and documentation of the routines found in this file.
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Robert Lupton, Princeton University
+*
+*  @version $Revision: 1.51.2.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:57 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#define PS_ALLOW_MALLOC                    // we're allowed to call malloc()
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psLogMsg.h"
+
+#include "psSysUtilsErrors.h"
+
+#define P_PS_MEMMAGIC (psPtr )0xdeadbeef   // Magic number in psMemBlock header
+
+#define P_PS_LARGE_BLOCK_SIZE 65536        // size where under, we try to recycle
+
+static psS32 checkMemBlock(const psMemBlock* m, const char *funcName);
+static psMemBlock* lastMemBlockAllocated = NULL;
+static pthread_mutex_t memBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t memIdMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static pthread_mutex_t recycleMemBlockListMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static psS32 recycleBins = 13;
+static psS32 recycleBinSize[14] = {
+                                      8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, P_PS_LARGE_BLOCK_SIZE
+                                  };
+
+// N.B. recycleBinSize should be terminated by P_PS_LARGE_BLOCK_SIZE (simplifies search loops)
+static psMemBlock* recycleMemBlockList[13] = {
+            NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+        };
+
+#ifdef PS_MEM_DEBUG
+static psMemBlock* deadBlockList;       // a place to put dead memBlocks in debug mode.
+#endif
+
+/**
+ * Unique ID for allocated blocks
+ */
+static psMemoryId memid = 0;
+
+/**
+ *  Default memExhausted callback.
+ */
+static psPtr memExhaustedCallbackDefault(size_t size)
+{
+    psPtr ptr = NULL;
+
+    pthread_mutex_lock(&recycleMemBlockListMutex);
+    psS32 level = recycleBins - 1;
+
+    while (level >= 0 && ptr == NULL) {
+        while (recycleMemBlockList[level] != NULL && ptr == NULL) {
+            psMemBlock* old = recycleMemBlockList[level];
+
+            recycleMemBlockList[level] = recycleMemBlockList[level]->nextBlock;
+            free(old);
+            ptr = malloc(size);
+        }
+        level--;
+    }
+    pthread_mutex_unlock(&recycleMemBlockListMutex);
+
+    return ptr;
+}
+
+/*
+ * Default callback for both allocate and free. Note that the
+ * value of p_psMemAllocateID/p_psMemFreeID is incremented
+ * by the return value (so returning 0 means that the callback
+ * isn't resignalled)
+ */
+static psMemoryId memAllocateCallbackDefault(const psMemBlock* ptr)
+{
+    static psMemoryId incr = 0; // "p_psMemAllocateID += incr"
+
+    return incr;
+}
+
+static psMemoryId memFreeCallbackDefault(const psMemBlock* ptr)
+{
+    static psMemoryId incr = 0; // "p_psMemFreeID += incr"
+
+    return incr;
+}
+
+static void memProblemCallbackDefault(const psMemBlock* ptr, const char *file, psS32 lineno)
+{
+    if (ptr->refCounter < 1) {
+        psError(PS_ERR_MEMORY_CORRUPTION, false,
+                PS_ERRORTEXT_psMemory_MULTIPLE_FREE,
+                ptr->id, ptr->file, ptr->lineno, file, lineno);
+    }
+
+    if (lineno > 0) {
+        psAbort(__func__, "Detected a problem in the memory system at %s:%d", file, lineno);
+    }
+}
+
+/*
+ * Routines to check the consistency of the allocated and/or free memory arena
+ *
+ * N.b. If the block wasn't allocated by psAlloc, it will appear corrupted
+ */
+static psS32 checkMemBlock(const psMemBlock* m, const char *funcName)
+{
+    // n.b. since this is called by psMemCheckCorruption while the memblock list is mutex locked,
+    // we shouldn't call such things as p_psAlloc/p_psFree here.
+
+    if (m == NULL) {
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_NULL_BLOCK);
+        return 1;
+    }
+
+    if (m->refCounter == 0) {
+        // using an unreferenced block of memory, are you?
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_DEREF_BLOCK_USE,
+                m->id);
+        return 1;
+    }
+
+    if (m->startblock != P_PS_MEMMAGIC || m->endblock != P_PS_MEMMAGIC) {
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_UNDERFLOW,
+                m->id);
+        return 1;
+    }
+    if (*(psPtr *)((int8_t *) (m + 1) + m->userMemorySize) != P_PS_MEMMAGIC) {
+        psError(PS_ERR_MEMORY_CORRUPTION, true,
+                PS_ERRORTEXT_psMemory_OVERFLOW,
+                m->id);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * The default callbacks
+ */
+static psMemAllocateCallback memAllocateCallback = memAllocateCallbackDefault;
+static psMemFreeCallback memFreeCallback = memFreeCallbackDefault;
+static psMemProblemCallback memProblemCallback = memProblemCallbackDefault;
+static psMemExhaustedCallback memExhaustedCallback = memExhaustedCallbackDefault;
+
+psMemExhaustedCallback psMemExhaustedCallbackSet(psMemExhaustedCallback func)
+{
+    psMemExhaustedCallback old = memExhaustedCallback;
+
+    if (func != NULL) {
+        memExhaustedCallback = func;
+    } else {
+        memExhaustedCallback = memExhaustedCallbackDefault;
+    }
+
+    return old;
+}
+
+psMemProblemCallback psMemProblemCallbackSet(psMemProblemCallback func)
+{
+    psMemProblemCallback old = memProblemCallback;
+
+    if (func != NULL) {
+        memProblemCallback = func;
+    } else {
+        memProblemCallback = memProblemCallbackDefault;
+    }
+
+    return old;
+}
+
+/*
+ * And now the I-want-to-be-informed callbacks
+ *
+ * Call the callbacks when these IDs are allocated/freed
+ */
+psMemoryId p_psMemAllocateID = 0;       // notify user this block is allocated
+psMemoryId p_psMemFreeID = 0;   // notify user this block is freed
+
+psMemoryId psMemAllocateCallbackSetID(psMemoryId id)
+{
+    psMemoryId old = p_psMemAllocateID;
+
+    p_psMemAllocateID = id;
+
+    return old;
+}
+
+psMemoryId psMemFreeCallbackSetID(psMemoryId id)
+{
+    psMemoryId old = p_psMemFreeID;
+
+    p_psMemFreeID = id;
+
+    return old;
+}
+
+psMemAllocateCallback psMemAllocateCallbackSet(psMemAllocateCallback func)
+{
+    psMemFreeCallback old = memAllocateCallback;
+
+    if (func != NULL) {
+        memAllocateCallback = func;
+    } else {
+        memAllocateCallback = memAllocateCallbackDefault;
+    }
+
+    return old;
+}
+
+psMemFreeCallback psMemFreeCallbackSet(psMemFreeCallback func)
+{
+    psMemFreeCallback old = memFreeCallback;
+
+    if (func != NULL) {
+        memFreeCallback = func;
+    } else {
+        memFreeCallback = memFreeCallbackDefault;
+    }
+
+    return old;
+}
+
+/*
+ * Return memory ID counter for next block to be allocated
+ */
+psMemoryId psMemGetId(void)
+{
+    psMemoryId id;
+
+    pthread_mutex_lock(&memIdMutex);
+    id = memid + 1;
+    pthread_mutex_unlock(&memIdMutex);
+
+    return id;
+}
+
+psS32 psMemCheckCorruption(psBool abort_on_error)
+{
+    psS32 nbad = 0;               // number of bad blocks
+    psBool failure = false;
+
+    // get exclusive access to the memBlock list to avoid it changing on us while we use it.
+    //    pthread_mutex_lock(&memBlockListMutex);
+
+    for (psMemBlock* iter = lastMemBlockAllocated; iter != NULL; iter = iter->nextBlock) {
+        pthread_mutex_unlock(&memBlockListMutex);
+        failure = checkMemBlock(iter, __func__);
+        pthread_mutex_lock(&memBlockListMutex);
+        if ( failure ) {
+            nbad++;
+
+            memProblemCallback(iter, __func__, __LINE__);
+
+            if (abort_on_error) {
+                // release the lock on the memblock list
+                pthread_mutex_unlock(&memBlockListMutex);
+                psAbort(__func__, "Detected memory corruption");
+                return nbad;
+            }
+        }
+    }
+
+    // release the lock on the memblock list
+    pthread_mutex_unlock(&memBlockListMutex);
+    return nbad;
+}
+
+psPtr p_psAlloc(size_t size, const char *file, psS32 lineno)
+{
+
+    psMemBlock* ptr = NULL;
+
+    size = (size < recycleBinSize[0]) ? recycleBinSize[0] : size; // set the minimum size to allocate
+
+    // memory is of the size I want to bother recycling?
+    if (size < P_PS_LARGE_BLOCK_SIZE) {
+        // find the bin we need.
+        psS32 level = 0;
+
+        while (size > recycleBinSize[level]) {
+            level++;
+        }
+        // Are we in one of the bins
+        if (level < recycleBins) {
+
+            size = recycleBinSize[level];  // round-up size to next sized bin.
+
+            pthread_mutex_lock(&recycleMemBlockListMutex);
+
+            if (recycleMemBlockList[level] != NULL) {
+                ptr = recycleMemBlockList[level];
+                recycleMemBlockList[level] = ptr->nextBlock;
+                if (recycleMemBlockList[level] != NULL) {
+                    recycleMemBlockList[level]->previousBlock = NULL;
+                }
+                size = ptr->userMemorySize;
+            }
+
+            pthread_mutex_unlock(&recycleMemBlockListMutex);
+        }
+    }
+
+    if (ptr == NULL) {
+        ptr = malloc(sizeof(psMemBlock) + size + sizeof(psPtr ));
+
+        if (ptr == NULL) {
+            ptr = memExhaustedCallback(size);
+            if (ptr == NULL) {
+                psAbort(__func__, "Failed to allocate %u bytes at %s:%d", size, file, lineno);
+            }
+        }
+
+        *(psPtr*)&ptr->startblock = P_PS_MEMMAGIC;
+        *(psPtr*)&ptr->endblock = P_PS_MEMMAGIC;
+        ptr->userMemorySize = size;
+        pthread_mutex_init(&ptr->refCounterMutex, NULL);
+    }
+    // increment the memory id safely.
+    pthread_mutex_lock(&memBlockListMutex);
+    *(psMemoryId* ) & ptr->id = ++memid;
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    ptr->file = file;
+    ptr->freeFcn = NULL;
+    ptr->persistent = false;
+    *(psU32 *)&ptr->lineno = lineno;
+    *(psPtr *)((int8_t *) (ptr + 1) + size) = P_PS_MEMMAGIC;
+    ptr->previousBlock = NULL;
+
+    ptr->refCounter = 1;                   // one user so far
+
+    // need exclusive access of the memory block list now...
+    pthread_mutex_lock(&memBlockListMutex);
+
+    // insert the new block to the front of the memBlock linked-list
+    ptr->nextBlock = lastMemBlockAllocated;
+    if (ptr->nextBlock != NULL) {
+        ptr->nextBlock->previousBlock = ptr;
+    }
+    lastMemBlockAllocated = ptr;
+
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    // Did the user ask to be informed about this allocation?
+    if (ptr->id == p_psMemAllocateID) {
+        p_psMemAllocateID += memAllocateCallback(ptr);
+    }
+    // And return the user the memory that they allocated
+    return ptr + 1;                        // user memory
+}
+
+psPtr p_psRealloc(psPtr vptr, size_t size, const char *file, psS32 lineno)
+{
+    size = (size < recycleBinSize[0]) ? recycleBinSize[0] : size; // set the minimum size to allocate
+
+    if (vptr == NULL) {
+        return p_psAlloc(size, file, lineno);
+    } else {
+        psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+        psBool isBlockLast = false;
+
+        if (checkMemBlock(ptr, __func__) != 0) {
+            memProblemCallback(ptr, file, lineno);
+            psAbort(file, "Realloc detected a memory corruption (id %lld @ %s:%d).",
+                    ptr->id, ptr->file, ptr->lineno);
+        }
+
+        pthread_mutex_lock(&memBlockListMutex);
+
+        isBlockLast = (ptr == lastMemBlockAllocated);
+
+        ptr = (psMemBlock* ) realloc(ptr, sizeof(psMemBlock) + size + sizeof(psPtr ));
+
+        if (ptr == NULL) {
+            ptr = memExhaustedCallback(size);
+            if(ptr == NULL) {
+                psAbort(__func__, "Failed to reallocate %ld bytes at %s:%d", size, file, lineno);
+            }
+        }
+
+        ptr->userMemorySize = size;
+        *(psPtr *)((int8_t *) (ptr + 1) + size) = P_PS_MEMMAGIC;
+
+        if (isBlockLast) {
+            lastMemBlockAllocated = ptr;
+        }
+        // the block location may have changed, so fix the linked list addresses.
+        if (ptr->nextBlock != NULL) {
+            ptr->nextBlock->previousBlock = ptr;
+        }
+        if (ptr->previousBlock != NULL) {
+            ptr->previousBlock->nextBlock = ptr;
+        }
+
+        pthread_mutex_unlock(&memBlockListMutex);
+
+        // Did the user ask to be informed about this allocation?
+        if (ptr->id == p_psMemAllocateID) {
+            p_psMemAllocateID += memAllocateCallback(ptr);
+        }
+
+        return ptr + 1;                    // usr memory
+    }
+}
+
+void p_psFree(psPtr vptr, const char *file, psS32 lineno)
+{
+    if (vptr == NULL) {
+        return;
+    }
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+    if (ptr->refCounter < 1) {
+        psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+        psAbort(__func__,PS_ERRORTEXT_psMemory_MULTIPLE_FREE,
+                ptr->id, ptr->file, ptr->lineno, file, lineno);
+    }
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, file, lineno);
+        psAbort(__func__,"Memory Corruption Detected.");
+    }
+
+    (void)p_psMemDecrRefCounter(vptr, file, lineno);    // this handles the free, if required.
+}
+
+/*
+ * Check for memory leaks.
+ */
+psS32 psMemCheckLeaks(psMemoryId id0, psMemBlock* ** arr, FILE * fd, psBool persistence)
+{
+    psS32 nleak = 0;
+    psS32 j = 0;
+    psMemBlock* topBlock = lastMemBlockAllocated;
+
+    pthread_mutex_lock(&memBlockListMutex);
+
+    for (psMemBlock* iter = topBlock; iter != NULL; iter = iter->nextBlock) {
+        if ( (iter->refCounter > 0) &&
+                ( (persistence) || (!persistence && !iter->persistent) ) &&
+                (iter->id >= id0)) {
+
+            nleak++;
+
+            if (fd != NULL) {
+                if (nleak == 1) {
+                    fprintf(fd, "   %20s:line ID\n", "file");
+                }
+
+                fprintf(fd, "   %20s:%-4d %ld\n", iter->file, (int)iter->lineno, (long)iter->id);
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    if (nleak == 0 || arr == NULL) {
+        return nleak;
+    }
+
+    *arr = p_psAlloc(nleak * sizeof(psMemBlock), __FILE__, __LINE__);
+    pthread_mutex_lock(&memBlockListMutex);
+
+    for (psMemBlock* iter = topBlock; iter != NULL; iter = iter->nextBlock) {
+        if ( (iter->refCounter > 0) &&
+                ( (persistence) || (!persistence && !iter->persistent) ) &&
+                (iter->id >= id0)) {
+
+            (*arr)[j++] = iter;
+            if (j == nleak) {              // found them all
+                break;
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&memBlockListMutex);
+
+    return nleak;
+}
+
+/*
+ * Reference counting APIs
+ */
+
+// return refCounter
+psReferenceCount psMemGetRefCounter(const psPtr vptr)
+{
+    psMemBlock* ptr;
+    psU32 refCount;
+
+    if (vptr == NULL) {
+        return 0;
+    }
+
+    ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    pthread_mutex_lock(&ptr->refCounterMutex);
+    refCount = ptr->refCounter;
+    pthread_mutex_unlock(&ptr->refCounterMutex);
+
+    return refCount;
+}
+
+// increment and return refCounter
+psPtr p_psMemIncrRefCounter(const psPtr vptr, const char *file, psS32 lineno)
+{
+    psMemBlock* ptr;
+
+    if (vptr == NULL) {
+        return vptr;
+    }
+
+    ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__)) {
+        memProblemCallback(ptr, file, lineno);
+    }
+
+    pthread_mutex_lock(&ptr->refCounterMutex);
+    ptr->refCounter++;
+    pthread_mutex_unlock(&ptr->refCounterMutex);
+
+    return vptr;
+}
+
+// decrement and return refCounter
+psPtr p_psMemDecrRefCounter(psPtr vptr, const char *file, psS32 lineno)
+{
+    if (vptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, file, lineno);
+        return NULL;
+    }
+
+    // Did the user ask to be informed about this deallocation?
+    if (ptr->id == p_psMemFreeID) {
+        p_psMemFreeID += memFreeCallback(ptr);
+    }
+
+    pthread_mutex_lock(&ptr->refCounterMutex);
+
+    if (ptr->refCounter > 1) {
+        ptr->refCounter--;                 // multiple references, just decrement the count.
+        pthread_mutex_unlock(&ptr->refCounterMutex);
+
+    } else {
+        pthread_mutex_unlock(&ptr->refCounterMutex);
+
+        if (ptr->freeFcn != NULL) {
+            ptr->freeFcn(vptr);
+        }
+
+        pthread_mutex_lock(&memBlockListMutex);
+
+        // cut the memBlock out of the memBlock list
+        if (ptr->nextBlock != NULL) {
+            ptr->nextBlock->previousBlock = ptr->previousBlock;
+        }
+        if (ptr->previousBlock != NULL) {
+            ptr->previousBlock->nextBlock = ptr->nextBlock;
+        }
+        if (lastMemBlockAllocated == ptr) {
+            lastMemBlockAllocated = ptr->nextBlock;
+        }
+
+        pthread_mutex_unlock(&memBlockListMutex);
+
+        // do we need to recycle?
+        if (ptr->userMemorySize < P_PS_LARGE_BLOCK_SIZE) {
+
+            psS32 level = 1;
+
+            while (ptr->userMemorySize >= recycleBinSize[level]) {
+                level++;
+            }
+            level--;
+
+            ptr->refCounter = 0;
+            ptr->previousBlock = NULL;
+
+            pthread_mutex_lock(&recycleMemBlockListMutex);
+            ptr->nextBlock = recycleMemBlockList[level];
+            if (recycleMemBlockList[level] != NULL) {
+                recycleMemBlockList[level]->previousBlock = ptr;
+            }
+            recycleMemBlockList[level] = ptr;
+            pthread_mutex_unlock(&recycleMemBlockListMutex);
+
+        } else {
+            // memory is larger than I want to recycle.
+            #ifdef PS_MEM_DEBUG
+            (void)p_psRealloc(vptr, 0, file, lineno);
+            ptr->previousBlock = NULL;
+            ptr->nextBlock = deadBlockList;
+            if (deadBlockList != NULL) {
+                deadBlockList->previous = ptr;
+            }
+            deadBlockList = ptr;
+            #else
+
+            pthread_mutex_destroy(&ptr->refCounterMutex);
+            free(ptr);
+            #endif
+
+        }
+
+        vptr = NULL;                       // since we freed it, make sure we return NULL.
+    }
+
+    return vptr;
+}
+
+void psMemSetDeallocator(psPtr vptr, psFreeFcn freeFcn)
+{
+    if (vptr == NULL) {
+        return;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    ptr->freeFcn = freeFcn;
+
+}
+psFreeFcn psMemGetDeallocator(psPtr vptr)
+{
+    if (vptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    return ptr->freeFcn;
+}
+
+psBool p_psMemGetPersistent(psPtr vptr)
+{
+    if (vptr == NULL) {
+        return NULL;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    return ptr->persistent;
+}
+
+void p_psMemSetPersistent(psPtr vptr,psBool value)
+{
+    if (vptr == NULL) {
+        return;
+    }
+
+    psMemBlock* ptr = ((psMemBlock* ) vptr) - 1;
+
+    if (checkMemBlock(ptr, __func__) != 0) {
+        memProblemCallback(ptr, __func__, __LINE__);
+    }
+
+    ptr->persistent = value;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psMemory.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psMemory.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psMemory.h	(revision 22271)
@@ -0,0 +1,455 @@
+/** @file  psMemory.h
+ *
+ *  @brief Contains the definitions for the memory management system
+ *
+ *  This is the generic memory management system put inbetween the user's high level code and the OS-level
+ *  memory allocation routines.  This system adds such features as callback routines for memory error events,
+ *  tracing capabilities, and reference counting.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Robert Lupton, Princeton University
+ *
+ *  @ingroup MemoryManagement
+ *
+ *  @version $Revision: 1.36.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:57 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if !defined(PS_MEMORY_H)
+#define PS_MEMORY_H
+
+#include <stdio.h>                     // needed for FILE
+#include <pthread.h>                   // we need a mutex to make this stuff thread safe.
+
+#include "psType.h"
+
+/** @addtogroup MemoryManagement
+ *  @{
+ */
+
+/**
+ *  @addtogroup memCallback Memory Callbacks
+ *
+ *  Routines dealing with the creating and setting of memory management callback functions.
+ */
+
+/**
+ *  @addtogroup memTracing Memory Tracing
+ *
+ *  Routines dealing with memory tracing and corruption checking.
+ */
+
+/**
+ *  @addtogroup memRefCount Reference Count
+ *
+ *  Routines dealing with the reference counting of allocated buffers.
+ */
+
+/// typedef for memory identification numbers.  Guaranteed to be some variety of integer.
+typedef psU64 psMemoryId;
+
+/// typedef for a memory block's reference count. Guaranteed to be some variety of integer.
+typedef psU64 psReferenceCount;
+
+/// typedef for deallocator.
+typedef void (*psFreeFcn) (psPtr ptr);
+
+/** Book-keeping data for storage allocator.
+ *  N.b. sizeof(psMemBlock) must be chosen such that if ptr is a pointer
+ *  returned by malloc, then ((char *)ptr + sizeof(psMemBlock)) is properly
+ *  aligned for all storage types.
+ */
+typedef struct psMemBlock
+{
+    const psPtr startblock;            ///< initialised to p_psMEMMAGIC
+    struct psMemBlock* previousBlock;  ///< previous block in allocation list
+    struct psMemBlock* nextBlock;      ///< next block allocation list
+    psFreeFcn freeFcn;                 ///< deallocator.  If NULL, use generic deallocation.
+    size_t userMemorySize;             ///< the size of the user-portion of the memory block
+    const psMemoryId id;               ///< a unique ID for this allocation
+    const char *file;                  ///< set from __FILE__ in e.g. p_psAlloc
+    const psS32 lineno;                  ///< set from __LINE__ in e.g. p_psAlloc
+    pthread_mutex_t refCounterMutex;   ///< mutex to ensure exclusive access to reference counter
+    psReferenceCount refCounter;       ///< how many times pointer is referenced
+    psBool persistent;                   ///< marks if this non-user persistent data like error stack, etc.
+    const psPtr endblock;              ///< initialised to p_psMEMMAGIC
+}
+psMemBlock;
+
+/** prototype of a basic callback used by memory functions
+ *
+ *  @see psMemAllocateCallbackSet
+ *  @ingroup memCallback
+ */
+typedef psMemoryId(*psMemAllocateCallback) (
+    const psMemBlock* ptr              ///< the psMemBlock just allocated
+);
+
+/** prototype of memory free callback used by memory functions
+ *
+ *  @see psMemFreeCallbackSet
+ *  @ingroup memCallback
+ */
+typedef psMemoryId(*psMemFreeCallback) (
+    const psMemBlock* ptr              ///< the psMemBlock being freed
+);
+
+/** prototype of a callback used in error conditions
+ *
+ *  This callback should not try to call psAlloc or psFree.
+ *
+ *  @see psMemProblemCallbackSet
+ *  @ingroup memCallback
+ */
+typedef void (*psMemProblemCallback) (
+    const psMemBlock* ptr,             ///< the pointer to the problematic memory block.
+    const char *file,                  ///< the file in which the problem originated
+    psS32 lineno                         ///< the line number in which the problem originated
+);
+
+/** prototype of a callback function used when memory runs out
+ *
+ *  @return psPtr pointer to requested buffer of the size size_t, or NULL if memory could not
+ *          be found.
+ *
+ *  @see psMemExhaustedCallbackSet
+ *  @ingroup memCallback
+ */
+typedef psPtr (*psMemExhaustedCallback) (
+    size_t size                        ///< the size of buffer required
+);
+
+/** Memory allocation.  This operates much like malloc(), but is guaranteed to return a non-NULL value.
+ *
+ *  @return psPtr pointer to the allocated buffer. This will not be NULL.
+ *  @see psFree 
+ */
+#ifdef DOXYGEN
+psPtr psAlloc(size_t size       ///< Size required
+             );
+#else
+psPtr p_psAlloc(size_t size,    ///< Size required
+                const char *file,       ///< File of call
+                psS32 lineno      ///< Line number of call
+               );
+
+/// Memory allocation. psAlloc sends file and line number to p_psAlloc.
+#ifndef SWIG
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Set the deallocator routine
+ *
+ *  A deallocator routine can optionally be assigned to a memory block to 
+ *  ensure that associated memory blocks also get freed, e.g., memory buffers
+ *  referenced within a struct.
+ *
+ */
+void psMemSetDeallocator(
+    psPtr ptr,                         ///< the memory block to operate on
+    psFreeFcn freeFcn                  ///< the function to be executed at deallocation
+);
+
+/** Get the deallocator routine
+ *
+ *  This function returns the deallocator for a memory block.  A deallocator 
+ *  routine can optionally be assigned to a memory block to ensure that 
+ *  associated memory blocks also get freed, e.g., memory buffers referenced 
+ *  within a struct.  
+ *
+ *  @return psFreeFcn    the routine to be called at deallocation.
+ */
+psFreeFcn psMemGetDeallocator(
+    psPtr ptr                          ///< the memory block
+);
+
+/** Set the memory as persistent so that it is ignored when detecting memory leaks.
+ *
+ *  Used to mark a memory block as persistent data within the library, 
+ *  i.e., non user-level data used to hold psLib's state or cache data.  Such
+ *  examples of this class of memory is psTrace's trace-levels and dynamic
+ *  error codes.
+ *
+ *  Memory marked as persistent is excluded from memory leak checks.
+ *
+ */
+void p_psMemSetPersistent(
+    psPtr ptr,                         ///< the memory block to operate on
+    psBool value                         ///< true if memory is persistent, otherwise false
+);
+
+/** Get the memory's persistent flag.
+ *
+ *  Checks if a memory block has been marked as persistent by 
+ *  p_psMemSetPresistent.
+ *
+ *  Memory marked as persistent is excluded from memory leak checks.
+ *
+ *  @return psBool    true if memory is marked persistent, otherwise false.
+ */
+psBool p_psMemGetPersistent(
+    psPtr ptr                          ///< the memory block to check.
+);
+
+
+/** Memory re-allocation.  This operates much like realloc(), but is guaranteed to return a non-NULL value.
+ *
+ *  @return psPtr pointer to resized buffer. This will not be NULL.
+ *  @see psAlloc, psFree
+ */
+#ifdef DOXYGEN
+psPtr psRealloc(
+    psPtr ptr,                          ///< Pointer to re-allocate
+    size_t size                         ///< Size required
+);
+#else
+psPtr p_psRealloc(
+    psPtr ptr,                         ///< Pointer to re-allocate
+    size_t size,                       ///< Size required
+    const char *file,                  ///< File of call
+    psS32 lineno                       ///< Line number of call
+);
+
+/// Memory re-allocation.  psRealloc sends file and line number to p_psRealloc.
+#ifndef SWIG
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Free memory.  This operates much like free().
+ *
+ *  @see psAlloc, psRealloc
+ */
+#ifdef DOXYGEN
+void psFree(
+    psPtr ptr                          ///< Pointer to free, if NULL, function returns immediately.
+);
+#else
+void p_psFree(
+    psPtr ptr,                         ///< Pointer to free
+    const char *file,                  ///< File of call
+    psS32 lineno                       ///< Line number of call
+);
+
+/// Free memory.  psFree sends file and line number to p_psFree.
+#ifndef SWIG
+#define psFree(ptr) p_psFree(ptr, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Check for memory leaks.  This scans for allocated memory buffers not freed with an ID not less than id0.
+ *  This is used to check for memory leaks by:
+ *      -# before a block of code to be checked, store the current ID count via psGetMemId
+ *      -# after the block of code to be checked, call this function using the ID stored above.  If all
+ *         memory in the block that was allocated has been freed, this call should output nothing and
+ *         return 0.
+ *
+ *  If memory leaks are found, the Memory Problem callback will be called as well.
+ *
+ *  return psS32  number of memory blocks found as 'leaks', i.e., the number of currently allocated memory
+ *              blocks above id0 that have not been freed.
+ *  @see psAlloc, psFree, psgetMemId, psMemProblemCallbackSet
+ *  @ingroup memTracing
+ */
+psS32 psMemCheckLeaks(
+    psMemoryId id0,                    ///< don't list blocks with id < id0
+    psMemBlock* ** arr,                ///< pointer to array of pointers to leaked blocks, or NULL
+    FILE * fd,                         ///< print list of leaks to fd (or NULL)
+    psBool persistence                 ///< make check across all object even persistent ones
+);
+
+/** Check for memory corruption.  Scans all currently allocated memory buffers and checks for corruptions,
+ *  i.e., invalid markers that signify a buffer under/overflow.
+ *
+ *  @ingroup memTracing
+ */
+psS32 psMemCheckCorruption(
+    psBool abort_on_error                ///< Abort on detecting corruption?
+);
+
+/** Return reference counter
+ *
+ *  @ingroup memRefCount
+ */
+psReferenceCount psMemGetRefCounter(
+    const psPtr vptr   ///< Pointer to get refCounter for
+);
+
+/** Increment reference counter and return the pointer
+ *
+ *  @ingroup memRefCount
+ */
+#ifdef DOXYGEN
+psPtr psMemIncrRefCounter(
+    const psPtr vptr                         ///< Pointer to increment refCounter, and return
+);
+#else
+psPtr p_psMemIncrRefCounter(
+    const psPtr vptr,                        ///< Pointer to increment refCounter, and return
+    const char *file,                  ///< File of call
+    psS32 lineno                         ///< Line number of call
+);
+
+#ifndef SWIG
+#define psMemIncrRefCounter(vptr) p_psMemIncrRefCounter(vptr, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Decrement reference counter and return the pointer
+ *
+ *  @ingroup memRefCount
+ *
+ *  @return psPtr    the pointer deremented in refCount, or NULL if pointer is 
+ *                   fully dereferenced.
+ */
+#ifdef DOXYGEN
+psPtr psMemDecrRefCounter(
+    psPtr vptr                         ///< Pointer to decrement refCounter, and return
+);
+#else
+psPtr p_psMemDecrRefCounter(
+    psPtr vptr,                        ///< Pointer to decrement refCounter, and return
+    const char *file,                  ///< File of call
+    psS32 lineno                         ///< Line number of call
+);
+
+#ifndef SWIG
+#define psMemDecrRefCounter(vptr) p_psMemDecrRefCounter(vptr, __FILE__, __LINE__)
+#endif
+
+#endif
+
+/** Set callback for problems.
+ *
+ *  At various occasions, the memory manager can check the state of the memory 
+ *  stack. If any of these checks discover that the memory stack is corrupted,
+ *  the psMemProblemCallback is called.
+ 
+ *  @ingroup memCallback
+ *
+ *  @return psMemProblemCallback       old psMemProblemCallback function
+ */
+psMemProblemCallback psMemProblemCallbackSet(
+    psMemProblemCallback func          ///< Function to run at memory problem detection
+);
+
+/** Set callback for out-of-memory.
+ *
+ *  If not enough memory is available to satisfy a request by psAlloc or 
+ *  psRealloc, these functions attempt to find an alternative solution by 
+ *  calling the psMemExhaustedCallback, a function which may be set by the 
+ *  programmer in appropriate circumstances, rather than immediately fail. 
+ *  The typical use of such a feature may be when a program needs a large 
+ *  chunk of memory to do an operation, but the exact size is not critical. 
+ *  This feature gives the programmer the opportunity to make a smaller 
+ *  request and try again, limiting the size of the operating buffer.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemExhaustedCallback     old psMemExhaustedCallback function
+ */
+psMemExhaustedCallback psMemExhaustedCallbackSet(
+    psMemExhaustedCallback func        ///< Function to run at memory exhaustion
+);
+
+/** Set call back for when a particular memory block is allocated
+ *
+ *  A private variable, p_psMemAllocateID, can be used to trace the allocation 
+ *  and freeing of specific memory blocks. If p_psMemAllocateID is set and a 
+ *  memory block with that ID is allocated, psMemAllocateCallback is called 
+ *  just before memory is returned to the calling function.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemAllocateCallback      old psMemAllocateCallback function
+ */
+psMemAllocateCallback psMemAllocateCallbackSet(
+    psMemAllocateCallback func       ///< Function to run at memory allocation of specific mem block
+);
+
+/** Set call back for when a particular memory block is freed
+ *
+ *  A private variable, p_psMemFreeID, can be used to trace the freeing of 
+ *  specific memory blocks. If p_psMemFreeID is set and the memory block with 
+ *  the ID is about to be freed, the psMemFreeCallback callback is called just
+ *  before the memory block is freed.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemFreeCallback          old psMemFreeCallback function
+ */
+psMemFreeCallback psMemFreeCallbackSet(
+    psMemFreeCallback func             ///< Function to run at memory free of specific mem block
+);
+
+/** get next memory ID
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemoryId                 the next memory ID to be used
+ */
+psMemoryId psMemGetId(void);
+
+/** set p_psMemAllocateID to specific id
+ *
+ *  A private variable, p_psMemAllocateID, can be used to trace the allocation 
+ *  and freeing of specific memory blocks. If p_psMemAllocateID is set and a 
+ *  memory block with that ID is allocated, psMemAllocateCallback is called 
+ *  just before memory is returned to the calling function.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemoryId       
+ *
+ *  @see psMemAllocateCallbackSet    
+ */
+psMemoryId psMemAllocateCallbackSetID(
+    psMemoryId id                      ///< ID to set
+);
+
+/** set p_psMemFreeID to id
+ *
+ *  A private variable, p_psMemFreeID, can be used to trace the freeing of 
+ *  specific memory blocks. If p_psMemFreeID is set and the memory block with 
+ *  the ID is about to be freed, the psMemFreeCallback callback is called just
+ *  before the memory block is freed.
+ *
+ *  @ingroup memCallback
+ *
+ *  @return psMemoryId                 the old p_psMemFreeID
+ *
+ *  @see psMemFreeCallbackSet
+ */
+psMemoryId psMemFreeCallbackSetID(
+    psMemoryId id                      ///< ID to set
+);
+
+//@} End of Memory Management Functions
+
+#ifndef DOXYGEN
+
+/*
+ * Ensure that any program using malloc/realloc/free will fail to compile
+ */
+#ifndef PS_ALLOW_MALLOC
+#ifdef __GNUC__
+#pragma GCC poison malloc realloc calloc free
+#else
+#define malloc(S)       _Pragma("error Use of malloc is not allowed.  Use psAlloc instead.")
+#define realloc(P,S)    _Pragma("error Use of realloc is not allowed.  Use psRealloc instead.")
+#define calloc(S)       _Pragma("error Use of calloc is not allowed.  Use psAlloc instead.")
+#define free(P)         _Pragma("error Use of free is not allowed.  Use psFree instead.")
+#endif
+#endif
+
+#endif
+// doxygen skip
+
+#endif // end of header file
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psString.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psString.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psString.c	(revision 22271)
@@ -0,0 +1,147 @@
+
+/** @file  psString.c
+ *
+ *  @brief Contains the definition of string utility functions
+ *
+ *  String utility functions defined shall provide basic string copying
+ *  capabilities while using the preferred memory management utilities.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-06 18:15:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "psString.h"
+#include "psMemory.h"
+#include "psError.h"
+
+#include "psSysUtilsErrors.h"
+
+char *psStringCopy(const char *str)
+{
+    // Allocate memory using psAlloc function
+    // Copy input string to memory just allocated
+    // Return the copy
+    return strcpy(psAlloc(strlen(str) + 1), str);
+}
+
+char *psStringNCopy(const char *str, psS32 nChar)
+{
+    char *returnValue = NULL;
+
+    // Check the number of characters to copy is non-negative
+    if (nChar < 0) {
+        // Log error message and return NULL
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psString_NCHAR_NEGATIVE,
+                nChar);
+        return NULL;
+    }
+    // Allocate memory using psAlloc function - nChar bytes
+    // Copy input string to memory allocated up to nChar characters
+    // Return the copy
+    returnValue = strncpy(psAlloc((size_t) nChar + 1), str, (size_t) nChar);
+
+    // Ensure the last byte is NULL character
+    if (nChar > 0) {
+        returnValue[nChar] = '\0';
+    }
+    // Return the string pointer
+    return returnValue;
+}
+
+ssize_t psStringAppend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    size_t          oldLength;          // original string length (sans \0)
+    ssize_t         tailLength;         // length of string to append
+
+    if (!*dest) {
+        *dest = psStringCopy("");
+        oldLength = 0;
+    } else {
+        // size of existing string
+        oldLength = strlen(*dest);
+    }
+
+    // find the size of the string to append
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    tailLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new tail is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (tailLength < 1) {
+        return tailLength == 0 ? oldLength : tailLength;
+    }
+
+    // new string length (sans \0)
+    length = oldLength + tailLength;
+
+    // realloc string to string + tail + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // append tail + \0
+    va_start(args, format);
+    vsnprintf(*dest + oldLength, tailLength + 1, format, args);
+    va_end(args);
+
+    return length;
+}
+
+ssize_t psStringPrepend(char **dest, const char *format, ...)
+{
+    va_list         args;
+    size_t          length;             // complete string length (sans \0)
+    ssize_t         headLength;         // length of string to prepend
+    char            *oldDest;           // copy of original string
+
+    if (!*dest) {
+        // makes the string backup and concatination pointless
+        *dest = psStringCopy("");
+        length = 0;
+    } else {
+        // size of existing string
+        length = strlen(*dest);
+    }
+
+    // find the size of the string to prepend
+    va_start(args, format);
+    // C99 guarentees vsnprintf() to work as expected with size = 0
+    headLength = vsnprintf(*dest, 0, format, args);
+    va_end(args);
+
+    // if the new head is zero length, return the length of the old string.  if
+    // it's a format error, return the error code.
+    if (headLength < 1) {
+        return headLength == 0 ? length : headLength;
+    }
+
+    // backup original string
+    oldDest = psStringCopy(*dest);
+
+    // new string length (sans \0)
+    length += headLength;
+
+    // realloc string to head + string + \0
+    *dest = psRealloc(*dest, length + 1);
+
+    // copy the new head to the beginning of string
+    va_start(args, format);
+    vsnprintf(*dest, length + 1, format, args);
+    va_end(args);
+
+    // append the original string
+    strncat(*dest, oldDest, length + 1);
+
+    psFree(oldDest);
+
+    return length;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psString.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psString.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psString.h	(revision 22271)
@@ -0,0 +1,100 @@
+/** @file  psString.h
+ *
+ *  @brief Contains the declarations of string utility functions
+ *
+ *  @ingroup SysUtils
+ *
+ *  String utility functions defined shall provide basic string copying
+ *  capabilities.
+ *
+ *  @author Eric Van Alst, MHPCC
+ *
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-06 18:15:25 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_STRING_H
+#define PS_STRING_H
+
+#include <sys/types.h>
+#include "psType.h"
+
+/** This macro will convert the argument to a quoted string */
+#define PS_STRING(S)  #S
+
+// Doxygen group tags
+
+/** @addtogroup SysUtils
+ *  @{
+ */
+
+/** Copies the input string
+ *
+ *  This function shall allocate memory to the length of the input string
+ *  plus one and copy the input string to the newly allocated memory.
+ *
+ *  @return char*      Copy of input string
+ *
+ */
+char *psStringCopy(
+    const char *str
+    /**< Input string of characters to copy */
+);
+
+/** Copies the input string up to the specified number of characters
+ *
+ *  This function shall allocate memory to the length specified by nChar
+ *  plus one and copy the input string to the newly allocated memory.
+ *  This function will only copy nChar bytes from the input to new string,
+ *  so if the input string is larger than nChar characters the copied
+ *  string will be a substring of the input string.  If the input string
+ *  is smaller than nChar bytes then the remaining bytes allocated will
+ *  be set to NULL.
+ *
+ *  @return  char* Copy of input string
+ *
+ */
+
+/*@null@*/
+
+char *psStringNCopy(
+    const char *str,
+    /**< Input string of characters to copy */
+
+    psS32 nChar
+    /**< Number of bytes to allocate for string copy */
+);
+
+/** Appends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return The length of the new string (excluding '\0')
+ */
+
+ssize_t psStringAppend(
+    char **dest,                        ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+);
+
+/** Prepends a format onto a string
+ *
+ * This function shall allocate a new string if dest is NULL.  dest shall be
+ * automatically extended to the size of the new string.
+ *
+ * @return The length of the new string (excluding '\0')
+ */
+
+ssize_t psStringPrepend(
+    char **dest,                        ///< existing string
+    const char *format,                 ///< format to append
+    ...                                 ///< format arguments
+);
+
+/* @} */// Doxygen - End of SystemGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psSysUtilsErrors.dat
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psSysUtilsErrors.dat	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psSysUtilsErrors.dat	(revision 22271)
@@ -0,0 +1,50 @@
+#
+#  This file is used to generate psSysUtilsErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERROR_TEXT
+#
+#  N.b. The ERRORNAME is exposed in the code as PS_ERR_ERRORNAME, e.g.,
+#  if ERRORNAME=psMemory_NULL_BLOCK, then use PS_ERR_psMemory_NULL_BLOCK in
+#  the code.
+#
+####################################################################
+#
+# Error Messages from psLogMsg.c:
+#
+psLogMsg_DESTINATION_MALFORMED         The specified destination, %s, is malformed.
+psLogMsg_DEST_LOCATION_INVALID         The location, %s, for protocol 'dest' is invalid.
+psLogMsg_OPEN_FILE_FAILED              Could not open file '%s' for output.
+psLogMsg_UNSUPPORTED_PROTOCOL          Do not know how to handle the protocol '%s'.
+psLogMsg_UNKNOWN_KEY                   Unknown logging keyword %c.
+#
+# Error Messages from psMemory.c:
+#
+psMemory_NULL_BLOCK                    NULL memory block found.
+psMemory_DEREF_BLOCK_USE               Memory block %lld was freed but still being used.
+psMemory_UNDERFLOW                     Memory block %lld is corrupted; buffer underflow detected.
+psMemory_OVERFLOW                      Memory block %lld is corrupted; buffer overflow detected.
+psMemory_MULTIPLE_FREE                 Block %lld, allocated at %s:%d, freed multiple times at %s:%d.
+#
+# Error Messages from psString.c:
+#
+psString_NCHAR_NEGATIVE                Can not copy a negative number of characters (%d).
+#
+# Error Messages from psTrace.c:
+#
+psTrace_NULL_SUBCOMPONENT              Sub-component %d of node %s in trace tree is NULL.
+psTrace_NULL_TRACETREE                 Function %s called on a NULL trace level tree.
+psTrace_ADD_NULL_COMPONENT             Failed to add null component to trace tree.
+psTrace_MALFORMED_COMPONENT_NAME       Failed to add '%s' to the root component tree; component must start with '.'.
+psTrace_FAILED_TO_ADD_COMPONENT        Failed to set trace level (%d) to '%s'.
+#
+# Error Messages from psErrorCodes.c
+#
+psErrorCode_NULL_ERRORDESCRIPTION      Specified psErrorDescription pointer can not be NULL.
+psErrorCode_ERRORCODE_REGISTER_FAILED  Failed to add input psErrorDescription at array index %d.
+#
+# Error Messages from psConfigure.c
+#
+psConfigure_INITIALIZATION_FAILED      Failed to initialize %s.
+psConfigure_FINALIZATION_FAILED        Failed to finalize %s.
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psSysUtilsErrors.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psSysUtilsErrors.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psSysUtilsErrors.h	(revision 22271)
@@ -0,0 +1,52 @@
+/** @file  psSysUtilsErrors.h
+ *
+ *  @brief Contains the error text for the system utility functions
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-02-17 19:26:24 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_SYSUTILS_ERRORS_H
+#define PS_SYSUTILS_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psSysUtilsErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psSysUtilsErrors.dat lines)
+ *     $2  The error text (rest of the line in psSysUtilsErrors.dat)
+ *     $n  The order of the source line in psSysUtilsErrors.dat (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_psLogMsg_DESTINATION_MALFORMED "The specified destination, %s, is malformed."
+#define PS_ERRORTEXT_psLogMsg_DEST_LOCATION_INVALID "The location, %s, for protocol 'dest' is invalid."
+#define PS_ERRORTEXT_psLogMsg_OPEN_FILE_FAILED "Could not open file '%s' for output."
+#define PS_ERRORTEXT_psLogMsg_UNSUPPORTED_PROTOCOL "Do not know how to handle the protocol '%s'."
+#define PS_ERRORTEXT_psLogMsg_UNKNOWN_KEY "Unknown logging keyword %c."
+#define PS_ERRORTEXT_psMemory_NULL_BLOCK "NULL memory block found."
+#define PS_ERRORTEXT_psMemory_DEREF_BLOCK_USE "Memory block %lld was freed but still being used."
+#define PS_ERRORTEXT_psMemory_UNDERFLOW "Memory block %lld is corrupted; buffer underflow detected."
+#define PS_ERRORTEXT_psMemory_OVERFLOW "Memory block %lld is corrupted; buffer overflow detected."
+#define PS_ERRORTEXT_psMemory_MULTIPLE_FREE "Block %lld, allocated at %s:%d, freed multiple times at %s:%d."
+#define PS_ERRORTEXT_psString_NCHAR_NEGATIVE "Can not copy a negative number of characters (%d)."
+#define PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT "Sub-component %d of node %s in trace tree is NULL."
+#define PS_ERRORTEXT_psTrace_NULL_TRACETREE "Function %s called on a NULL trace level tree."
+#define PS_ERRORTEXT_psTrace_ADD_NULL_COMPONENT "Failed to add null component to trace tree."
+#define PS_ERRORTEXT_psTrace_MALFORMED_COMPONENT_NAME "Failed to add '%s' to the root component tree; component must start with '.'."
+#define PS_ERRORTEXT_psTrace_FAILED_TO_ADD_COMPONENT "Failed to set trace level (%d) to '%s'."
+#define PS_ERRORTEXT_psErrorCode_NULL_ERRORDESCRIPTION "Specified psErrorDescription pointer can not be NULL."
+#define PS_ERRORTEXT_psErrorCode_ERRORCODE_REGISTER_FAILED "Failed to add input psErrorDescription at array index %d."
+#define PS_ERRORTEXT_psConfigure_INITIALIZATION_FAILED "Failed to initialize %s."
+#define PS_ERRORTEXT_psConfigure_FINALIZATION_FAILED "Failed to finalize %s."
+//~End
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psTrace.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psTrace.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psTrace.c	(revision 22271)
@@ -0,0 +1,557 @@
+/** @file psTrace.c
+ *  \brief basic run-time trace facilities
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the code for procedures to insert
+ *  trace messages into the code.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.48 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-05 21:24:50 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/*****************************************************************************
+    NOTES:
+ In the SRD, higher trace levels correspond to a numerically lower trace
+ value in the code.  This is a bit confusing.  For example, a high-level
+ message might be something like "Begin Processing".  The module programmer
+ might give that a numerically low trace level, such as 1, so then any
+ non-zero trace level in that code component will display thatmessage.
+ 
+ We build a tree of trace components.  Every node in the tree has a
+ depth, which is it's distance from the root.  However, this is not
+ not the same thing as a node's "level", which corresponds to the
+ trace level of that node.
+ 
+I think the following is the correct behavior, but not sure:
+    PS_UNKNOWN_TRACE_LEVEL: We never set the level of a component to this
+    value.  This value is only used when psTraceGetLevel is called with
+    a bad component name, or if the component root is undefined, I think.
+ 
+    PS_DEFAULT_TRACE_LEVEL: This should only be used when adding the
+    intermediate components of a psS64 name.  Ie. the "B" in .A.B.C
+ 
+ *****************************************************************************/
+
+#ifndef PS_NO_TRACE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psMemory.h"
+#include "psTrace.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLogMsg.h"
+
+#include "psSysUtilsErrors.h"
+
+static p_psComponent* cRoot = NULL; // The root of the trace component
+static FILE *traceFP = NULL;        // File destination for messages.
+
+static void componentFree(p_psComponent* comp);
+static p_psComponent* componentAlloc(const char *name, psS32 level);
+
+/*****************************************************************************
+componentAlloc(): allocate memory for a new node, and initialize members.
+ *****************************************************************************/
+static p_psComponent* componentAlloc(const char *name, psS32 level)
+{
+    p_psComponent* comp = psAlloc(sizeof(p_psComponent));
+
+    p_psMemSetPersistent(comp,true);
+    psMemSetDeallocator(comp, (psFreeFcn) componentFree);
+    comp->name = psStringCopy(name);
+    p_psMemSetPersistent((psPtr)comp->name,true);
+    comp->level = level;
+    comp->n = 0;
+    comp->p_psSpecified = false;
+    comp->subcomp = NULL;
+    return comp;
+}
+
+/*****************************************************************************
+componentFree(): free the current node in the root tree, and all children
+nodes as well.
+ *****************************************************************************/
+static void componentFree(p_psComponent* comp)
+{
+    if (comp == NULL) {
+        return;
+    }
+
+    if (comp->subcomp != NULL) {
+        for (psS32 i = 0; i < comp->n; i++) {
+            p_psMemSetPersistent(comp->subcomp[i],false);
+            psFree(comp->subcomp[i]);
+        }
+        p_psMemSetPersistent(comp->subcomp,false);
+        psFree(comp->subcomp);
+    }
+
+    p_psMemSetPersistent((psPtr)comp->name,false);
+    psFree((psPtr)comp->name);
+}
+
+/*****************************************************************************
+initTrace(): simply initialize the component root tree.
+*****************************************************************************/
+static void initTrace(void)
+{
+    if (cRoot == NULL) {
+        cRoot = componentAlloc(".", PS_DEFAULT_TRACE_LEVEL);
+    }
+}
+
+/*****************************************************************************
+Set all trace levels to zero.
+ 
+XXX: Currently, no function calls this routine.
+ *****************************************************************************/
+void p_psTraceReset(p_psComponent* currentNode)
+{
+    psS32 i = 0;
+
+    if (NULL == currentNode) {
+        return;
+    }
+
+    currentNode->level = 0;
+    for (i = 0; i < currentNode->n; i++) {
+        if (NULL == currentNode->subcomp[i]) {
+            psLogMsg("p_psTraceReset", PS_LOG_WARN,
+                     PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT,
+                     i, currentNode->name);
+        } else {
+            p_psTraceReset(currentNode->subcomp[i]);
+        }
+    }
+    return;
+}
+
+/*****************************************************************************
+Set all trace levels to zero.
+ *****************************************************************************/
+void psTraceReset()
+{
+    psFree(cRoot);
+    cRoot = NULL;
+}
+
+/*****************************************************************************
+componentAdd(): Adds the component named "addNodeName" to the root tree.
+ *****************************************************************************/
+static psBool componentAdd(const char *addNodeName, psS32 level)
+{
+    psS32 i = 0;                        // Loop index variable.
+    char name[strlen(addNodeName) + 1]; // buffer for writeable copy.
+    char *pname = name;
+    char *firstComponent = NULL;        // first component of name
+    p_psComponent* currentNode = cRoot;
+    psS32 nodeExists = 0;
+
+    // XXX: Verify that this is the correct behavior.
+    if (strcmp("", addNodeName) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,
+                PS_ERRORTEXT_psTrace_ADD_NULL_COMPONENT);
+        return false;
+    }
+
+    // Is this the root node? If so, simply set level and return.
+    if (strcmp(".", addNodeName) == 0) {
+        cRoot->level = level;
+        return true;
+    }
+
+    if (addNodeName[0] != '.') {
+        psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                PS_ERRORTEXT_psTrace_MALFORMED_COMPONENT_NAME,
+                addNodeName);
+        return false;
+    }
+
+    strcpy(name, addNodeName);
+    pname = name+1;
+    // Iterate through the components of addNodeName.  Strip off the first
+    // component of the name, find that in the root tree, or add it if it
+    // does not exist, then move to the next component in the name.
+
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        nodeExists = 0;
+        for (i = 0; i < currentNode->n; i++) {
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                nodeExists = 1;
+                if (pname == NULL) {
+                    currentNode->level = level;
+                }
+            }
+        }
+
+        if (nodeExists == 0) {
+            currentNode->subcomp = psRealloc(currentNode->subcomp,
+                                             (currentNode->n + 1) * sizeof(p_psComponent* ));
+            p_psMemSetPersistent(currentNode->subcomp,true);
+
+            currentNode->n = (currentNode->n) + 1;
+
+            if (pname == NULL) {
+                // This is the final component to add.
+                currentNode->subcomp[(currentNode->n) - 1] = componentAlloc(firstComponent, level);
+            } else {
+                // We are adding an intermediate component.  The trace level
+                // is not defined.  An undefined trace level inherits the
+                // trace level of it's parent.  However, we do not set that
+                // specifically here since that would inheritance to be a
+                // static, one-time, type of behavior.
+
+                currentNode->subcomp[(currentNode->n) - 1] = componentAlloc(firstComponent, PS_DEFAULT_TRACE_LEVEL);
+            }
+            currentNode = currentNode->subcomp[(currentNode->n) - 1];
+        }
+    }
+
+    return true;
+}
+
+/*****************************************************************************
+    psSetTraceLevel(): add the component named "comp" to the component tree,
+ if it is not already there, and set it's trace level to "level".
+ 
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+ 
+    Input:
+ comp
+ level
+    Output:
+ none
+    Returns:
+ zero
+*****************************************************************************/
+psBool psTraceSetLevel(const char *comp,   // component of interest
+                       psS32 level)  // desired trace level
+{
+    char *compName = NULL;
+
+    // If the root component tree does not exist, then initialize it.
+    if (cRoot == NULL) {
+        initTrace();
+    }
+
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+
+    // If the component name has no leading dot, then supply it.
+    if (comp[0] != '.') {
+        compName = (char *) psAlloc(10 + strlen(comp));
+        strcpy(compName, ".");
+        compName = strcat(compName, comp);
+    } else {
+        compName = (char *) comp;
+    }
+
+    // Add the new component to the component tree.
+    if ( !componentAdd(compName, level) ) {
+        psError(PS_ERR_UNKNOWN, false,
+                PS_ERRORTEXT_psTrace_FAILED_TO_ADD_COMPONENT,
+                level,
+                compName);
+
+        if (comp[0] != '.') {
+            psFree(compName);
+        }
+        return false;
+    }
+
+    if (comp[0] != '.') {
+        psFree(compName);
+    }
+
+    return true;
+}
+
+/*****************************************************************************
+    doGetTraceLevel()
+ This function recursively searches the root component tree for the
+ component named "name", which is supplied by a parameter.  If it
+ finds that component, it returns the level of that component.
+ Otherwise, it returns ???.
+ 
+    NOTE: We modified this so that the user may omit the leading "," in a
+    component name.  Since the code was already implemented assuming the "."
+    was required, rather than change all that code, in this function, I
+    simply add a leading "." to the component name if there is none.
+ 
+    Inputs:
+ name:
+    Outputs:
+ none
+    Returns:
+ The trace level of the "name" component.
+ *****************************************************************************/
+static psS32 doGetTraceLevel(const char *aname)
+{
+    char name[strlen(aname) + 1];       // need a writeable copy: for strsep()
+    char *pname = name;
+    char *firstComponent = NULL;        // first component of name
+    p_psComponent* currentNode = cRoot;
+    psS32 i = 0;
+    psS32 defaultLevel = 0;
+
+    if (NULL == currentNode) {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    if (strcmp(".", aname) == 0) {
+        return (cRoot->level);
+    }
+
+    if (aname[0] != '.') {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    defaultLevel = cRoot->level;
+    strcpy(name, aname);
+    pname = name+1;
+    while (pname != NULL) {
+        firstComponent = pname;
+        pname = strchr(firstComponent, '.');
+        if (pname != NULL) {
+            *pname = '\0';
+            pname++;
+        }
+        for (i = 0; i < currentNode->n; i++) {
+            if (NULL == currentNode->subcomp[i]) {
+                psLogMsg("p_psTraceReset", PS_LOG_WARN,
+                         PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT,
+                         i, currentNode->name);
+            }
+
+            if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
+                currentNode = currentNode->subcomp[i];
+                // For level inheritance purpose, we save the level of this
+                // component if it is not DEFAULT.
+                if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
+                    defaultLevel = currentNode->level;
+                }
+                // Determine if this is the last component:
+                if (pname == NULL) {
+                    if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
+                        return (currentNode->level);
+                    } else {
+                        return(defaultLevel);
+                    }
+                }
+            }
+        }
+    }
+    return(defaultLevel);
+}
+
+/*****************************************************************************
+    psTraceLevelGet()
+ Return a trace level of "name" in the root component tree.  If the
+ exact string of components in "name" does not exist in the root
+ tree, we return the deepest level of the match.
+    Input:
+ name
+    Output:
+ none
+    Return:
+ The level of "name" in the root component tree.
+ *****************************************************************************/
+psS32 psTraceGetLevel(const char *name)
+{
+    char *compName = NULL;
+    psS32 traceLevel;
+
+    if (cRoot == NULL) {
+        return (PS_UNKNOWN_TRACE_LEVEL);
+    }
+
+    // If the component name has no leading dot, then supply it.
+    if (name[0] != '.') {
+        compName = (char *) psAlloc(10 + strlen(name));
+        strcpy(compName, ".");
+        compName = strcat(compName, name);
+        traceLevel = doGetTraceLevel(compName);
+        psFree(compName);
+    } else {
+        // Search the component root tree, determine the trace level.
+        traceLevel = doGetTraceLevel(name);
+    }
+
+    // XXX: The default trace level is currently set at -1, which is not a
+    // valid trace level.  This is convenient in determining whether or not
+    // a component should inherit the trace level from parent nodes.  However,
+    // it's not clear that -1 should ever be returned by this function.
+    // The SDR is unclear on this point and we should probably request IfA
+    // comment.
+    if (traceLevel == PS_DEFAULT_TRACE_LEVEL) {
+        traceLevel = PS_THE_OTHER_DEFAULT_TRACE_LEVEL;
+    }
+
+    return(traceLevel);
+}
+
+/*****************************************************************************
+    doPrintTraceLevels()
+ This function recursively searches the component tree supplied by the
+ parameter "comp" and prints the name and level of each component.
+    Inputs:
+ comp: a node in the component tree.
+ level: the level of that node
+    Outputs:
+ none
+    Returns:
+ null
+ *****************************************************************************/
+static void doPrintTraceLevels(const p_psComponent* comp,
+                               psS32 depth,
+                               psS32 defLevel)
+{
+    psS32 i = 0;
+
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+
+    if (comp->name[0] == '\0') {
+        return;
+    } else {
+        if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
+            fprintf(traceFP,"%*s%-*s %d\n", depth, "", 20 - depth, comp->name, defLevel);
+        } else {
+            fprintf(traceFP, "%*s%-*s %d\n", depth, "", 20 - depth, comp->name, comp->level);
+        }
+    }
+
+    for (i = 0; i < comp->n; i++) {
+        if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
+            doPrintTraceLevels(comp->subcomp[i], depth + 1, defLevel);
+        } else {
+            doPrintTraceLevels(comp->subcomp[i], depth + 1, comp->level);
+        }
+    }
+}
+
+
+/*****************************************************************************
+psPrintTraceLevels(): Simply print all the trace levels in the trace level
+component tree.
+Inputs:
+ none
+Outputs:
+ none
+Returns:
+ null
+*****************************************************************************/
+void psTracePrintLevels(void)
+{
+    if (cRoot == NULL) {
+        return;
+    }
+
+    doPrintTraceLevels(cRoot, 0, PS_THE_OTHER_DEFAULT_TRACE_LEVEL);
+}
+
+/*****************************************************************************
+p_psTrace(): we display the trace message to standard output if the trace
+level of that message, supplied by the parameter "level" is higher than the
+trace level that is currently associated with the component named by the
+parameter "comp".
+Input:
+ comp
+ level
+ ...  a printf-style output string.
+Output:
+ none
+Return:
+ null
+ *****************************************************************************/
+void p_psTrace(const char *comp,        // component being traced
+               psS32 level,       // desired trace level
+               ...)             // arguments
+{
+    char *fmt = NULL;
+    va_list ap;
+    psS32 i = 0;
+
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+
+    if (NULL == comp) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psTrace_NULL_TRACETREE,
+                __func__);
+        return;
+    }
+    // Only display this message if it's trace level is less than the level
+    // of it's associatedcomponent.
+    if (level <= psTraceGetLevel(comp)) {
+        va_start(ap, level);
+
+        // The following functions get the variable list of parameters with
+        // which this function was called, and print them to the standard
+        // output.
+        fmt = va_arg(ap, char *);
+
+        // We indent each message one space for each level of the message.
+        for (i = 0; i < level; i++) {
+            fprintf(traceFP, " ");
+        }
+        vfprintf(traceFP, fmt, ap);
+        va_end(ap);
+    }
+    fflush(traceFP);
+}
+
+// XXX EAM : I've added code to close the old traceFP (safely)
+void psTraceSetDestination(FILE * fp)
+{
+
+    bool special;
+
+    // XXX EAM perhaps return an error?
+    if (fp == NULL) {
+        return;
+    }
+
+    // cannot close traceFP if one of the special FILE ptrs
+    special  = (traceFP == NULL);
+    special |= (traceFP == stdin);
+    special |= (traceFP == stdout);
+    special |= (traceFP == stderr);
+
+    if (!special) {
+        fclose (traceFP);
+    }
+    traceFP = fp;
+}
+
+FILE *psTraceGetDestination()
+{
+    if (traceFP == NULL) {
+        traceFP = stdout;
+    }
+    return traceFP;
+}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psTrace.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psTrace.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psTrace.h	(revision 22271)
@@ -0,0 +1,105 @@
+/** @file psTrace.h
+ *  \brief basic run-time trace facilities
+ *  \ingroup LogTrace
+ *
+ *  This file will hold the prototypes for defining procedures to insert
+ *  trace messages into the code.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-28 23:46:29 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_TRACE_H)
+#define PS_TRACE_H 1
+
+#define PS_UNKNOWN_TRACE_LEVEL -9999   // we don't know this name's level
+#define PS_DEFAULT_TRACE_LEVEL -1
+#define PS_THE_OTHER_DEFAULT_TRACE_LEVEL 0
+
+/** \addtogroup LogTrace
+ *  \{
+ */
+
+/** Functions **************************************************************/
+
+//#define PS_NO_TRACE 1   ///< to turn off all tracing
+
+// XXX EAM : the old 'empty' values of (void) 0 are dangerous
+# if defined(PS_NO_TRACE)
+    #   define psTrace(facil, level, ...)   /* do nothing */
+    #   define p_psTrace(facil, level, ...) /* do nothing */
+    #   define psTraceSetLevel(facil,level) /* do nothing */
+    #   define psTraceGetLevel(facil)       /* do nothing */
+    #   define psTraceReset()               /* do nothing */
+    #   define psTraceFree()                /* do nothing */
+    #   define psTracePrintLevels()         /* do nothing */
+    #   define psTraceSetDestination(fp)    /* do nothing */
+    #   define psTraceSetDestination()      /* do nothing */
+    #   define PS_TRACE_ON 0
+
+    # else /* PS_NO_TRACE */
+        #   define PS_TRACE_ON 1
+
+        /** Basic structure for the component tree.  A component is a string of the
+            form aaa.bbb.ccc, and may itself contain further subcomponents.  The
+            Component structure doesn't in fact contain it's full name, but only the
+            last part. */
+        typedef struct p_psComponent
+        {
+            const char *name;   // last part of name of component
+            psS32 level;   // trace level for this component
+            bool p_psSpecified;
+            psS32 n;    // number of subcomponents
+            struct p_psComponent* *subcomp;     // next level of subcomponents
+        }
+p_psComponent;
+
+#ifdef DOXYGEN
+void psTrace(const char *facil,        ///< facilty of interest
+             psS32 myLevel,            ///< desired trace level
+             ...)                      ///< trace message arguments
+;
+#else
+/// Send a trace message
+void p_psTrace(const char *facil,      ///< facilty of interest
+               psS32 myLevel,          ///< desired trace level
+               ...)                    ///< trace message arguments
+;
+
+#ifndef SWIG
+#define psTrace(facil, level, ...) p_psTrace(facil, level, __VA_ARGS__)
+#endif /* SWIG */
+
+#endif /* DOXYGEN */
+
+/// Set trace level
+psBool psTraceSetLevel(const char *facil,     ///< facilty of interest
+                       psS32 level)     ///< desired trace level
+;
+
+/// Get the trace level
+psS32 psTraceGetLevel(const char *facil) ///< facilty of interest
+;
+
+/// Set all trace levels to zero (do not free nodes in the component tree).
+void psTraceReset();
+
+/// print trace levels
+void psTracePrintLevels(void);
+
+/// Set the destination of future trace messages.
+void psTraceSetDestination(FILE * fp);
+
+/// Get the current destination for trace messages.
+FILE *psTraceGetDestination(void);
+
+/* \} */// End of SystemGroup Functions
+
+#endif /* PS_NO_TRACE */
+
+#endif /* PS_TRACE_H */
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psType.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psType.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/psType.h	(revision 22271)
@@ -0,0 +1,245 @@
+/** @file  psType.h
+*
+*  @brief Contains support for basic types
+*
+*  This file defines common datatypes used throughout psLib.
+*
+*  @ingroup DataContainer
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.32 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-06 01:12:58 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_TYPE_H
+#define PS_TYPE_H
+
+#include <complex.h>
+#include <stdint.h>
+#include <float.h>
+#include <stdbool.h>
+
+/// @addtogroup DataContainer
+/// @{
+
+/******************************************************************************/
+
+/*  TYPE DEFINITIONS                                                          */
+
+/******************************************************************************/
+
+/** Basic data types used by the containers.
+ *
+ * The basic types of the primitives used by psLib are defined within this enum. This enum is in turn used by
+ * the psType struct.
+ *
+ */
+
+typedef uint8_t psU8;                  ///< 8-bit unsigned int
+typedef uint16_t psU16;                ///< 16-bit unsigned int
+typedef uint32_t psU32;                ///< 32-bit unsigned int
+typedef uint64_t psU64;                ///< 64-bit unsigned int
+typedef int8_t psS8;                   ///< 8-bit signed int
+typedef int16_t psS16;                 ///< 16-bit signed int
+typedef int32_t psS32;                 ///< 32-bit signed int
+typedef int64_t psS64;                 ///< 64-bit signed int
+typedef float psF32;                   ///< 32-bit floating point
+typedef double psF64;                  ///< 64-bit floating point
+
+#ifdef SWIG
+typedef struct
+{
+    float re, im;
+}
+psC32;
+typedef struct
+{
+    double re,im;
+}
+psC64;
+#else
+typedef float _Complex psC32;          ///< complex with 32-bit floating point Real and Imagary numbers
+typedef double _Complex psC64;         ///< complex with 64-bit floating point Real and Imagary numbers
+#endif
+
+typedef void* psPtr;                   ///< void pointer
+typedef bool psBool;                   ///< boolean value
+
+typedef enum {
+    PS_TYPE_S8   = 0x0101,             ///< Character.
+    PS_TYPE_S16  = 0x0102,             ///< Short integer.
+    PS_TYPE_S32  = 0x0104,             ///< Integer.
+    PS_TYPE_S64  = 0x0108,             ///< Long integer.
+    PS_TYPE_U8   = 0x0301,             ///< Unsigned character.
+    PS_TYPE_U16  = 0x0302,             ///< Unsigned psS16 integer.
+    PS_TYPE_U32  = 0x0304,             ///< Unsigned integer.
+    PS_TYPE_U64  = 0x0308,             ///< Unsigned psS64 integer.
+    PS_TYPE_F32  = 0x0404,             ///< Single-precision Floating point.
+    PS_TYPE_F64  = 0x0408,             ///< Double-precision floating point.
+    PS_TYPE_C32  = 0x0808,             ///< Complex numbers consisting of single-precision floating point.
+    PS_TYPE_C64  = 0x0810,             ///< Complex numbers consisting of double-precision floating point.
+    PS_TYPE_BOOL = 0x1301              ///< Boolean.
+} psElemType;
+
+#define PS_TYPE_MASK PS_TYPE_U8        /**< the psElemType to use for mask image */
+#define PS_TYPE_MASK_DATA U8           /**< the data member to use for mask image */
+#define PS_TYPE_MASK_NAME "psU8"       /**< the data type for mask as a string */
+
+typedef psU8 psMaskType;               ///< the C datatype for a mask image
+typedef psBool psBOOL;                 ///< allow psBOOL to be used instead of psBool (for macros)
+
+#define PS_MIN_S8        INT8_MIN      /**< minimum valid psS8 value */
+#define PS_MIN_S16       INT16_MIN     /**< minimum valid psS16 value */
+#define PS_MIN_S32       INT32_MIN     /**< minimum valid psS32 value */
+#define PS_MIN_S64       INT64_MIN     /**< minimum valid psS64 value */
+#define PS_MIN_U8        0             /**< minimum valid psU8 value */
+#define PS_MIN_U16       0             /**< minimum valid psU16 value */
+#define PS_MIN_U32       0             /**< minimum valid psU32 value */
+#define PS_MIN_U64       0             /**< minimum valid psU64 value */
+#define PS_MIN_F32       -FLT_MAX      /**< minimum valid psF32 value */
+#define PS_MIN_F64       -DBL_MAX      /**< minimum valid psF64 value */
+#define PS_MIN_C32       -FLT_MAX      /**< minimum valid real or imaginary psC32 value */
+#define PS_MIN_C64       -DBL_MAX      /**< minimum valid real or imaginary psC32 value */
+
+#define PS_MAX_S8        INT8_MAX      /**< maximum valid psS8 value */
+#define PS_MAX_S16       INT16_MAX     /**< maximum valid psS16 value */
+#define PS_MAX_S32       INT32_MAX     /**< maximum valid psS32 value */
+#define PS_MAX_S64       INT64_MAX     /**< maximum valid psS64 value */
+#define PS_MAX_U8        UINT8_MAX     /**< maximum valid psU8 value */
+#define PS_MAX_U16       UINT16_MAX    /**< maximum valid psU16 value */
+#define PS_MAX_U32       UINT32_MAX    /**< maximum valid psU32 value */
+#define PS_MAX_U64       UINT64_MAX    /**< maximum valid psU64 value */
+#define PS_MAX_F32       FLT_MAX       /**< maximum valid psF32 value */
+#define PS_MAX_F64       DBL_MAX       /**< maximum valid psF64 value */
+#define PS_MAX_C32       FLT_MAX       /**< maximum valid real or imaginary psC32 value */
+#define PS_MAX_C64       DBL_MAX       /**< maximum valid real or imaginary psC32 value */
+
+#define PS_TYPE_BOOL_NAME "psBool"
+#define PS_TYPE_S8_NAME   "psS8"
+#define PS_TYPE_S16_NAME  "psS16"
+#define PS_TYPE_S32_NAME  "psS32"
+#define PS_TYPE_S64_NAME  "psS64"
+#define PS_TYPE_U8_NAME   "psU8"
+#define PS_TYPE_U16_NAME  "psU16"
+#define PS_TYPE_U32_NAME  "psU32"
+#define PS_TYPE_U64_NAME  "psU64"
+#define PS_TYPE_F32_NAME  "psF32"
+#define PS_TYPE_F64_NAME  "psF64"
+#define PS_TYPE_C32_NAME  "psC32"
+#define PS_TYPE_C64_NAME  "psC64"
+
+#define PS_TYPE_NAME(value,type) \
+switch(type) { \
+case PS_TYPE_BOOL: \
+    value = PS_TYPE_BOOL_NAME; \
+    break; \
+case PS_TYPE_S8: \
+    value = PS_TYPE_S8_NAME; \
+    break; \
+case PS_TYPE_S16: \
+    value = PS_TYPE_S16_NAME; \
+    break; \
+case PS_TYPE_S32: \
+    value = PS_TYPE_S32_NAME; \
+    break; \
+case PS_TYPE_S64: \
+    value = PS_TYPE_S64_NAME; \
+    break; \
+case PS_TYPE_U8: \
+    value = PS_TYPE_U8_NAME; \
+    break; \
+case PS_TYPE_U16: \
+    value = PS_TYPE_U16_NAME; \
+    break; \
+case PS_TYPE_U32: \
+    value = PS_TYPE_U32_NAME; \
+    break; \
+case PS_TYPE_U64: \
+    value = PS_TYPE_U64_NAME; \
+    break; \
+case PS_TYPE_F32: \
+    value = PS_TYPE_F32_NAME; \
+    break; \
+case PS_TYPE_F64: \
+    value = PS_TYPE_F64_NAME; \
+    break; \
+case PS_TYPE_C32: \
+    value = PS_TYPE_C32_NAME; \
+    break; \
+case PS_TYPE_C64: \
+    value = PS_TYPE_C64_NAME; \
+    break; \
+default: \
+    value = "unknown"; \
+};
+
+/// Macro to get the bad pixel reason code (stored as part of mask value)
+#define PS_BADPIXEL_BITMASK 0x0f
+#define PS_GET_BADPIXEL(maskValue) (maskValue & PS_BADPIXEL_BITMASK)
+
+#define PS_IS_BADPIXEL(maskValue) (PS_GET_BADPIXEL(maskValue) != 0)
+
+/// Macro to apply a bad pixel reason code to mask image
+#define PS_SET_BADPIXEL(maskValue, reasonCode) \
+{ \
+    maskValue = (psMaskType)((reasonCode & PS_BADPIXEL_BITMASK) | (maskValue & ~PS_BADPIXEL_BITMASK)); \
+}
+
+/// Macro to determine if the psElemType is an integer.
+#define PS_IS_PSELEMTYPE_INT(x) ((x & 0x100) == 0x100)
+/// Macro to determine if the psElemType is unsigned.
+#define PS_IS_PSELEMTYPE_UNSIGNED(x) ((x & 0x200) == 0x200)
+/// Macro to determine if the psElemType is a real (non-complex) floating-point type.
+#define PS_IS_PSELEMTYPE_REAL(x) ((x & 0x400) == 0x400)
+/// Macro to determine if the psElemType is complex number type.
+#define PS_IS_PSELEMTYPE_COMPLEX(x) ((x & 0x800) == 0x800)
+/// Macro to determine if the psElemType is boolean type.
+#define PS_IS_PSELEMTYPE_BOOL(x) ((x & 0x1000) == 0x1000)
+/// Macro to determine the storage size, in bytes, of the psElemType.
+#define PSELEMTYPE_SIZEOF(x) (x & 0xFF)
+
+/** Dimensions of a data type.
+ *
+ * The dimensions of containers used by psLib are defined within this enum. This enum is used by the psType
+struct. *
+ */
+typedef enum {
+    PS_DIMEN_SCALAR,            ///< Scalar.
+    PS_DIMEN_VECTOR,            ///< Vector.
+    PS_DIMEN_TRANSV,            ///< Transposed vector.
+    PS_DIMEN_IMAGE,             ///< Image.
+    PS_DIMEN_OTHER              ///< Something else that's not supported for arithmetic.
+} psDimen;
+
+/** The type of a data type.
+ *
+ * All psLib complex types consist of primitive components. This struct provides the description of those
+ * primitives.
+ *
+ */
+typedef struct
+{
+    psElemType type;            ///< Primitive type.
+    psDimen dimen;              ///< Dimensionality.
+}
+psType;
+
+/** The type of a basic data type
+ *
+ *  All psLib complex types consist of primitive components.  This structure provides the ability to cast
+ *  an unknown data structure to safely test the underlining data type.
+ *
+ */
+typedef struct
+{
+    psType  type;              ///< Data type information
+}
+psMath;
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/sysUtils.i
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/sysUtils.i	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/sysUtils/sysUtils.i	(revision 22271)
@@ -0,0 +1,49 @@
+/* sysUtils */
+%include "psType.h"
+%include "psAbort.h"
+%include "psConfigure.h"
+%include "psErrorCodes.h"
+%include "psError.h"
+%include "psLogMsg.h"
+%include "psMemory.h"
+%include "psString.h"
+%include "psTrace.h"
+
+%inline %{
+
+psErrorCode psError(psErrorCode code, psBool new, const char* msg) {
+    return p_psError("UNKNOWN",0,"SWIG",code,new,msg);
+}
+
+psPtr psAlloc(size_t size) {
+    return p_psAlloc(size,"UNKNOWN",0);
+}
+
+psPtr psRealloc(psPtr ptr, size_t size) {
+    return p_psRealloc(ptr,size,"UNKNOWN",0);
+}
+
+void psFree(psPtr ptr) {
+    p_psFree(ptr,"UNKNOWN",0);
+}
+
+psPtr psMemIncrRefCounter(psPtr vptr) {
+    return p_psMemIncrRefCounter(vptr,"UNKNOWN",0);
+}
+
+psPtr psMemDecrRefCounter(psPtr vptr) {
+    return p_psMemDecrRefCounter(vptr,"UNKNOWN",0);
+}
+
+void psTrace(const char* facil, psS32 myLevel, const char* msg) {
+    p_psTrace(facil, myLevel, msg);
+}
+
+psS32 psMemCheckLeaksToStderr(psMemoryId id0, psMemBlock*** arr, psBool persistence) {
+    return psMemCheckLeaks(id0, arr, stderr, persistence);
+}
+psS32 psMemCheckLeaksToStdout(psMemoryId id0, psMemBlock*** arr, psBool persistence) {
+    return psMemCheckLeaks(id0, arr, stdout, persistence);
+}
+
+%}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psArray.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psArray.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psArray.c	(revision 22271)
@@ -0,0 +1,205 @@
+
+/** @file  psArray.c
+ *
+ *  @brief Contains support for basic vector types
+ *
+ *  This file defines the basic type for a vector struct and functions useful
+ *  in manupulating vectors.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.27.4.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+/******************************************************************************/
+
+/*  INCLUDE FILES                                                             */
+
+/******************************************************************************/
+#include<stdlib.h>                         // for qsort, etc.
+#include<string.h>
+
+#include "psMemory.h"
+#include "psError.h"
+#include "psArray.h"
+#include "psLogMsg.h"
+
+#include "psCollectionsErrors.h"
+
+/*****************************************************************************
+  FUNCTION IMPLEMENTATION - LOCAL
+ *****************************************************************************/
+static void arrayFree(psArray* psArr);
+
+static void arrayFree(psArray* psArr)
+{
+    if (psArr == NULL) {
+        return;
+    }
+
+    p_psArrayElementFree(psArr);
+
+    psFree(psArr->data);
+}
+
+/*****************************************************************************
+  FUNCTION IMPLEMENTATION - PUBLIC
+ *****************************************************************************/
+psArray* psArrayAlloc(psU32 nalloc)
+{
+    psArray* psArr = NULL;
+
+    // Create vector struct
+    psArr = (psArray* ) psAlloc(sizeof(psArray));
+    psMemSetDeallocator(psArr, (psFreeFcn) arrayFree);
+
+    psArr->nalloc = nalloc;
+    psArr->n = nalloc;
+
+    // Create vector data array
+    psArr->data = psAlloc(nalloc * sizeof(psPtr));
+
+    return psArr;
+}
+
+psArray* psArrayRealloc(psArray* in, psU32 nalloc)
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL,true,PS_ERRORTEXT_psArray_REALLOC_NULL);
+        return NULL;
+    } else if (in->nalloc != nalloc) {     // No need to realloc to same size
+        if (nalloc < in->n) {
+            for (psS32 i = nalloc; i < in->n; i++) {      // For reduction in vector size
+                psFree(in->data[i]);
+            }
+            in->n = nalloc;
+        }
+        // Realloc after decrementation to avoid accessing freed array elements
+        in->data = psRealloc(in->data, nalloc * sizeof(psPtr));
+        in->nalloc = nalloc;
+    }
+
+    return in;
+}
+
+psArray* psArrayAdd(psArray* psArr,
+                    int delta,
+                    const psPtr data)
+{
+    if (psArr == NULL) {
+        return psArr;
+    }
+
+    int n = psArr->n;
+
+    if (n >= psArr->nalloc) {
+        // array needs to be expanded to make room for more elements
+        int d = (delta > 0) ? delta : 10; // as spec'ed in SDRS.
+        psArr = psArrayRealloc(psArr, n+d);
+    }
+
+    // add the element to the end of the array.
+    psArr->data[n] = psMemIncrRefCounter(data);
+    psArr->n = n+1;
+
+    return psArr;
+}
+
+psBool psArrayRemove(psArray* psArr,
+                     const psPtr data)
+{
+    psBool success = false;
+
+    if (psArr == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_ARRAY_NULL);
+        return false;
+    }
+
+    psS32 n = psArr->n;
+    psPtr* psArrData = psArr->data;
+    for (psS32 i = n-1; i >= 0; i--) {
+        if (psArrData[i] == data) {
+            memmove(&psArr->data[i],&psArr->data[i+1],(n-i-1)*sizeof(psPtr));
+            n--;
+            success = true;
+        }
+    }
+    psArr->n = n; // reset the array size to indicate the removed item(s)
+
+    return success;
+}
+
+void p_psArrayElementFree(psArray* psArr)
+{
+
+    if (psArr == NULL) {
+        return;
+    }
+
+    for (psS32 i = 0; i < psArr->n; i++) {
+        psFree(psArr->data[i]);
+        psArr->data[i] = NULL;
+    }
+}
+
+psArray* psArraySort(psArray* in, psComparePtrFcn compare)
+{
+    if (in == NULL) {
+        return NULL;
+    }
+
+    qsort(in->data, in->n, sizeof(psPtr), (int (*)(const void* , const void*))compare);
+
+    return in;
+}
+
+/// Set an element in the array.
+psBool psArraySet(psArray* in,                       ///< input array to set element in
+                  psU32 position,                    ///< the element position to set
+                  const void* value) ///< the value to set it to
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_ARRAY_NULL);
+        return false;
+    }
+
+    if (position >= in->nalloc) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
+                position, in->nalloc);
+        return false;
+    }
+
+    psFree(in->data[position]);
+    in->data[position] = value;
+
+    return true;
+}
+
+/// Get an element in the array.
+void* psArrayGet(const psArray* in, psU32 position )
+{
+    if (in == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_ARRAY_NULL);
+        return NULL;
+    }
+
+    if (position >= in->nalloc) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psArray_POSITION_BEYOND_NALLOC,
+                position, in->nalloc);
+        return NULL;
+    }
+
+    return in->data[position];
+}
+
+
+
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psArray.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psArray.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psArray.h	(revision 22271)
@@ -0,0 +1,150 @@
+
+/** @file  psArray.h
+ *
+ *  @brief Contains basic array definitions and operations
+ *
+ *  This file defines the basic type for a array struct and functions useful
+ *  in manupulating arrays.
+ *
+ *  @ingroup Array
+ *
+ *  @author Robert DeSonia, MHPCC
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.22.8.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_ARRAY_H
+#define PS_ARRAY_H
+
+#include "psType.h"
+#include "psCompare.h"
+
+/// @addtogroup Array
+/// @{
+
+/** An array to support primitive types.
+ *
+ * Struct for maintaining an array of frequently used primitive types.
+ *
+ */
+typedef struct
+{
+    psU32 nalloc;        ///< Total number of elements available.
+    psU32 n;             ///< Number of elements in use.
+    psPtr* data;                ///< An Array of pointer elements
+}
+psArray;
+
+/*****************************************************************************/
+
+/* FUNCTION PROTOTYPES                                                       */
+
+/*****************************************************************************/
+
+/** Allocate an array.
+ *
+ * Uses psLib memory allocation functions to create an array collection of 
+ * data
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+psArray* psArrayAlloc(
+    psU32 nalloc                       ///< Total number of elements to make available.
+);
+
+/** Reallocate an array.
+ *
+ * Uses psLib memory allocation functions to reallocate an array collection 
+ * of data. 
+ *
+ * @return psArray* : Pointer to psArray.
+ *
+ */
+psArray* psArrayRealloc(
+    psArray* psArr,                    ///< array to reallocate.
+    psU32 nalloc                       ///< Total number of elements to make available.
+);
+
+/** Add an element to the end the array, expanding the array storage if
+ *  necessary.
+ *
+ *  @return psArray*        The array with the element added
+ */
+psArray* psArrayAdd(
+    psArray* psArr,                    ///< array to operate on
+    int delta,
+    ///< the amount to expand array, if necessary.  If less than one, 10 will be used.
+    const psPtr data   ///< the data pointer to add to psArray
+);
+
+/** Remove an element from the array
+ *
+ *  Finds and removes the specified data pointer from the list.  
+ *
+ * @return bool:  TRUE if the specified data pointer was found and removed, 
+ *                otherwise FALSE.
+ *
+ */
+psBool psArrayRemove(
+    psArray* psArr,                    ///< array to operate on
+    const psPtr data   ///< the data pointer to remove from psArray
+);
+
+/** Deallocate/Dereference elements of an array.
+ *
+ * Uses psLib memory allocation functions to deallocate/dereference elements 
+ * of a array of void pointers.  The array psArr is not freed, and its elements
+ * will all be set to NULL.
+ *
+ */
+void p_psArrayElementFree(
+    psArray* psArr                     ///< Void pointer array to destroy.
+);
+
+/** Sort the array according to an external compare function.
+ *
+ *  Sorts an array via the specification of a comparison function
+ *  to specify how the objects on the array should be sorted.
+ *
+ *  The comparison function must return an integer less than, equal to, or 
+ *  greater than zero if the first argument is considered to be respectively 
+ *  less than, equal to, or greater than the second. 
+ *
+ *  If two members compare as equal, their order in the sorted array is 
+ *  undefined.
+ *
+ *  @return psArray* The sorted array.
+ */
+psArray* psArraySort(
+    psArray* in,                       ///< input array to sort.
+    psComparePtrFcn compare            ///< the compare function
+);
+
+/** Set an element in the array.  If the current element is non-NULL, the old
+ *  element is freed.
+ *
+ *  @return psBool  TRUE if the element was set successfully, otherwise FALSE
+ */
+psBool psArraySet(
+    psArray* in,                       ///< input array to set element in
+    psU32 position,                    ///< the element position to set
+    const void* value   ///< the value to set it to
+);
+
+/** Get an element from the array.
+ *
+ *  @return void*   the element at given position.
+ */
+void* psArrayGet(
+    const psArray* in,   ///< input array to get element from
+    psU32 position                     ///< the element position to get
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psBitSet.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psBitSet.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psBitSet.c	(revision 22271)
@@ -0,0 +1,295 @@
+/** @file  psBitSet.c
+ *
+ *  @brief Creates an array of bytes of arbitrary length for storing individual bits.
+ *
+ *  Bit masks are useful tools for toggling various flags and options. This set of functions module provides
+ *  a mechanism to create an array of bits of arbitrary length and manipulate them with basic binary
+ *  operations. A print function is also provided to display the entire set of bits in binary format as a
+ *  string.
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.24.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "psBitSet.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psString.h"
+
+#include "psCollectionsErrors.h"
+
+enum {
+    UNKNOWN_OP,
+    AND_OP,
+    OR_OP,
+    XOR_OP,
+    NOT_OP
+};
+
+static void bitSetFree(psBitSet* inBitSet);
+
+/** Private function to create a mask.
+ *
+ *  Creates an eight bit mask with the given bit set. All other bits in the byte are zero. The input bit uses
+ *  zero-based indexing, and is the cumulitive index within the array, not the localized byte's bit position.
+ *
+ *  @return  char*: Pointer to byte in which bit is contained.
+ */
+static char mask(psS32 bit)
+{
+    char mask = (char)0x01;
+
+    // Ignore splint warning about negative bit shifts
+    /* @i@ */
+    mask = mask << (bit % 8);
+
+    return mask;
+}
+
+static void bitSetFree(psBitSet* inBitSet)
+{
+    if (inBitSet == NULL) {
+        return;
+    }
+    psFree(inBitSet->bits);
+}
+
+psBitSet* psBitSetAlloc(psS32 n)
+{
+    psS32 numBytes = 0;
+    psBitSet* newObj = NULL;
+
+    if (n < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_ALLOC_NEG_SIZE,
+                n);
+        return NULL;
+    }
+
+    numBytes = ceil(n / 8.0);
+    newObj = psAlloc(sizeof(psBitSet));
+    psMemSetDeallocator(newObj, (psFreeFcn) bitSetFree);
+    newObj->n = numBytes;
+
+    // Ignore splint warning about releasing pointer members, since they've not been allocated yet
+    /* @i@ */
+    newObj->bits = psAlloc(sizeof(char) * numBytes);
+
+    memset(newObj->bits, 0, numBytes);
+
+    return newObj;
+}
+
+psBitSet* psBitSetSet(psBitSet* inBitSet,
+                      psS32 bit)
+{
+    char *byte = NULL;
+
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_SET_NULL);
+        return inBitSet;
+    } else if ( (bit < 0) ||
+                (bit > inBitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE,
+                bit,inBitSet->n * 8 - 1);
+        return inBitSet;
+    }
+    // Variable byte is the byte in the array that contains the bit to be set
+    byte = inBitSet->bits + bit / 8;
+    *byte |= mask(bit);
+
+    return inBitSet;
+}
+
+psBitSet* psBitSetClear(psBitSet* inBitSet,
+                        psS32 bit)
+{
+    char *byte = NULL;
+
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_SET_NULL);
+        return inBitSet;
+    } else if ( (bit < 0) ||
+                (bit > inBitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE,
+                bit,inBitSet->n * 8 - 1);
+        return inBitSet;
+    }
+    // Variable byte is the byte in the array that contains the bit to be set
+    byte = inBitSet->bits + bit / 8;
+    *byte &= ! mask(bit);
+
+    return inBitSet;
+}
+
+psBool psBitSetTest(const psBitSet* inBitSet,
+                    psS32 bit)
+{
+    char *byte = NULL;
+
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_SET_NULL);
+        return false;
+    } else if ( (bit < 0) ||
+                (bit > inBitSet->n * 8 - 1) ) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_BIT_OUTOFRANGE,
+                bit,inBitSet->n * 8 - 1);
+        return false;
+    }
+
+    // Variable byte is the byte in the array that contains the bit to be tested
+    byte = inBitSet->bits + bit / 8;
+    return ((*byte & mask(bit)) != 0);
+}
+
+psBitSet* psBitSetOp(psBitSet* outBitSet,
+                     const psBitSet* inBitSet1,
+                     const char *operator,
+                     const psBitSet* inBitSet2)
+{
+    psS32 i = 0;
+    psS32 n = 0;
+    char* outBits = NULL;
+    char* inBits1 = NULL;
+    char* inBits2 = NULL;
+    psS32 op = UNKNOWN_OP;
+
+    if (inBitSet1 == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_FIRST_OPERAND_NULL);
+        psFree(outBitSet);
+        return NULL;
+    }
+    inBits1 = inBitSet1->bits;
+
+    if (operator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_OPERATOR_NULL);
+        psFree(outBitSet);
+        return NULL;
+    }
+
+    // parse the operator
+    if (strcmp(operator,"AND")==0) {
+        op = AND_OP;
+    } else if (strcmp(operator,"OR")==0) {
+        op = OR_OP;
+    } else if (strcmp(operator,"XOR")==0) {
+        op = XOR_OP;
+    } else if (strcmp(operator,"NOT")==0) {
+        op = NOT_OP;
+    } else {
+        psFree(outBitSet);
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psBitSet_OPERATOR_INVALID,
+                operator);
+        return NULL;
+    }
+
+    if (op != NOT_OP) {
+        if (inBitSet2 == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    PS_ERRORTEXT_psBitSet_SECOND_OPERAND_NULL);
+            psFree(outBitSet);
+            return NULL;
+        }
+
+        if (inBitSet1->n != inBitSet2->n) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                    PS_ERRORTEXT_psBitSet_OPERANDS_SIZE_DIFFER);
+            psFree(outBitSet);
+            return NULL;
+        }
+        inBits2 = inBitSet2->bits;
+    }
+
+    if (outBitSet == NULL) {
+        outBitSet = psBitSetAlloc(inBitSet1->n*8);
+    } else if (outBitSet->n != inBitSet1->n) {
+        outBitSet->n = inBitSet1->n;
+        outBitSet->bits = psRealloc(outBitSet->bits, inBitSet1->n);
+    }
+
+    n = outBitSet->n;
+    outBits = outBitSet->bits;
+
+    switch (op) {
+    case AND_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] & inBits2[i];
+        }
+        break;
+    case OR_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] | inBits2[i];
+        }
+        break;
+    case XOR_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = inBits1[i] ^ inBits2[i];
+        }
+        break;
+    case NOT_OP:
+        for (i = 0; i < n; i++) {
+            outBits[i] = ~inBits1[i];
+        }
+        break;
+    default:
+        psAbort("psBitSetOp",
+                "Unexpected error - operator parsed successfully but not valid?");
+    }
+
+    return outBitSet;
+}
+
+psBitSet* psBitSetNot(psBitSet* outBitSet,
+                      const psBitSet* inBitSet)
+{
+    if (inBitSet == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psBitSet_OPERAND_NULL);
+        psFree(outBitSet);
+        return NULL;
+    }
+
+    outBitSet = psBitSetOp(outBitSet,inBitSet,"NOT",NULL);
+
+    if (outBitSet == NULL) {
+        psError(PS_ERR_UNKNOWN, false,
+                PS_ERRORTEXT_psBitSet_NOT_OP_FAILED);
+    }
+
+    return outBitSet;
+}
+
+char *psBitSetToString(const psBitSet* inBitSet)
+{
+    psS32 i = 0;
+    psS32 numBits = inBitSet->n * 8;
+    char *outString = psAlloc((size_t) numBits + 1);
+
+    for (i = 0; i < numBits; i++) {
+        outString[numBits - i - 1] = psBitSetTest(inBitSet, i) ? '1' : '0';
+    }
+
+    outString[numBits] = 0;
+
+    return outString;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psBitSet.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psBitSet.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psBitSet.h	(revision 22271)
@@ -0,0 +1,145 @@
+/** @file  psBitSet.h
+ *
+ *  @brief Creates an array of bytes of arbitrary length for storing individual bits.
+ *
+ *  Bit masks are useful tools for toggling various flags and options. This set of functions module provides
+ *  a mechanism to create an array of bits of arbitrary length and manipulate them with basic binary
+ *  operations. A print function is also provided to display the entire set of bits in binary format as a
+ *  string.
+ *
+ *  @ingroup BitSet
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.17.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PSBITSET_H
+#define PSBITSET_H
+
+#include "psType.h"
+
+/// @addtogroup BitSet
+/// @{
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+/** Struct containing array of bytes to hold bit data and corresponding array length.
+ *
+ *  The bits in the struct are assembled in as an array of bytes with eight bits per byte. The bits are
+ *  arranged with the LSB in first (right most) position of the first array element.
+ */
+typedef struct
+{
+    psS32 n;                             ///< Number of bytes in the array
+    char *bits;                        ///< Aray of bytes holding bits
+}
+psBitSet;
+
+/*****************************************************************************/
+/* FUNCTION PROTOTYPES                                                       */
+/*****************************************************************************/
+
+/** Allocate a psBitSet.
+ *
+ *  Create a psBitSet with the number of bits specified by the user. All bits are set to zero upon
+ *  allocation.
+ *
+ *  @return  psBitSet* : Pointer to struct containing array of bits and size of array.
+ */
+
+/*@null@*/
+psBitSet* psBitSetAlloc(
+    psS32 n                            ///< Number of bits in psBitSet array
+);
+
+/** Set a bit.
+ *
+ *  Sets a bit at a given bit location. The bit is set based on a zero index with the
+ *  first bit set in the zero bit slot of the zero element of the byte array. As an example, setting bit 3 in
+ *  an array with two elements would result in an psBitSet that looks like 00000000 00001000.
+ *
+ *  @return  psBitSet* : Pointer to struct containing psBitSet.
+ */
+psBitSet* psBitSetSet(
+    /* @returned@ */
+    psBitSet* inMask,                  ///< Pointer to psBitSet to be set.
+    psS32 bit                          ///< Bit to be set.
+);
+
+/** Clear a bit.
+ *
+ *  Clear a bit at a given bit location. The bit is cleared based on a zero 
+ *  index with the first bit set in the zero bit slot of the zero element of 
+ *  the byte array. 
+ *
+ *  @return  psBitSet* : Pointer to struct containing psBitSet.
+ */
+psBitSet* psBitSetClear(
+    /* @returned@ */
+    psBitSet* inMask,                  ///< Pointer to psBitSet to be cleared.
+    psS32 bit                          ///< Bit to be cleared.
+);
+
+/** Test the value of a bit.
+ *
+ *  Prints the value of a bit at a given bit location, either one or zero. The resulting bit is based on a
+ *  zero index format with the first bit set in the zero bit slot of the zero element of the byte array
+ *  As an example, testing bit 3 in a psBitSet with two bytes that looks like 00000000 00001000 would return a
+ *  value of one, since that is the value that was set.
+ *
+ *  @return  int: Value of bit, either one or zero.
+ */
+
+psBool psBitSetTest(
+    const psBitSet* inMask,            ///< Pointer psBitSet to be tested.
+    psS32 bit                          ///< Bit to be tested.
+);
+
+/** Perform a binary operation on two psBitSets
+ *
+ *  Perform an AND, OR, or XOR on two psBitSets. If the BitMasks are not the same size, the operation will not
+ *  be performed and an error message will be logged.
+ *
+ *  @return  psBitSet* : Pointer to struct containing result of binary operation.
+ */
+psBitSet* psBitSetOp(
+    /* @returned@ */
+    psBitSet* outMask,                 ///< Resulting psBitSet from binary operation
+    const psBitSet* inMask1,           ///< First psBitSet on which to operate
+    const char *operator,  ///< Bit operation
+    const psBitSet* inMask2            ///< First psBitSet on which to operate
+);
+
+/** Perform a not operation on a psBitSet
+ *
+ *  Toggles bits in a psBitset. All zero bits are set to one and all one bits are set to zero.
+ *
+ *  @return  psBitSet* : Pointer to struct containing result of operation.
+ */
+
+psBitSet* psBitSetNot(
+    psBitSet* outBitSet,               ///< Resulting psBitSet from operation
+    const psBitSet* inBitSet           ///< Input psBitSet
+);
+
+/** Convert the psBitSet to a string of ones and zeros.
+ *
+ *  Converts the contents of a psBitSet to a string representation of its binary form of ones and zeros. The
+ *  LSB is the right-most chracter. Each set of eight characters represents one byte.
+ *
+ *  @return  char*: Pointer to character array containing string data.
+ */
+
+char *psBitSetToString(
+    const psBitSet* inMask             ///< psBitSet to convert */
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psHash.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psHash.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psHash.c	(revision 22271)
@@ -0,0 +1,466 @@
+
+/** @file  psHash.c
+*
+*  @brief Contains support for basic hashing functions.
+*
+*  This file will hold the functions for defining a hash table with arbitrary
+*  data types, allocating/deallocating that hash table, adding and removing
+*  data from that hash table, and listing all keys defined in the hash table.
+*
+*  @author Robert Lupton, Princeton University
+*  @author George Gusciora, MHPCC
+*  @author Robert DeSonia, MHPCC
+*
+*  @version $Revision: 1.15.2.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:56 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "psHash.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psTrace.h"
+#include "psError.h"
+
+#include "psCollectionsErrors.h"
+
+static psHashBucket* hashBucketAlloc(const char *key, psPtr data, psHashBucket* next);
+static void hashBucketFree(psHashBucket* bucket);
+static psPtr doHashWork(psHash* table, const char *key, psPtr data, psBool remove
+                           );
+static void hashFree(psHash* table);
+
+/******************************************************************************
+psHashKeyList(table): this function creates a linked list with an entry in
+that list for every key in the hash table.
+Inputs:
+    table: a hash table
+Return;
+    The linked list
+ *****************************************************************************/
+psList* psHashKeyList(const psHash* table)
+{
+    psS32 i = 0;                  // Loop index variable
+    psList* myLinkList = NULL;  // The output data structure
+    psHashBucket* ptr = NULL;   // Used to step thru linked list.
+
+    if (table == NULL) {
+        return NULL;
+    }
+    // Create the linked list
+    myLinkList = psListAlloc(NULL);
+
+    // Loop through every bucket in the hash table.  If that bucket is not
+    // NULL, then add the bucket's key to the linked list.
+    for (i = 0; i < table->nbucket; i++) {
+        if (table->buckets[i] != NULL) {
+            // Since a bucket contains a linked list of keys/data, we must
+            // step trough each key in that linked list:
+
+            ptr = table->buckets[i];
+            while (ptr != NULL) {
+                psListAdd(myLinkList, PS_LIST_HEAD, ptr->key);
+                ptr = ptr->next;
+            }
+        }
+    }
+
+    // Return the linked list
+    return (myLinkList);
+}
+
+/******************************************************************************
+hashBucketAlloc(key, data, next): This procedure creates a new hash bucket
+with the specified key, data, and next.
+Inputs:
+    key:  the new bucket's key pointer
+    data: the new bucket's data pointer
+    next: the new bucket's key pointer
+Return:
+    the new hash bucket.
+ *****************************************************************************/
+static psHashBucket* hashBucketAlloc(const char *key,
+                                     psPtr data,
+                                     psHashBucket* next)
+{
+    // Allocate memory for the new hash bucket.
+    psHashBucket* bucket = psAlloc(sizeof(psHashBucket));
+
+    psMemSetDeallocator(bucket, (psFreeFcn) hashBucketFree);
+
+    // Initialize the bucket.
+    bucket->key = psStringCopy(key);
+
+    if (data == NULL) {
+        // NOTE: Should we flag a warning message?
+        bucket->data = NULL;
+    } else {
+        bucket->data = psMemIncrRefCounter(data);
+    }
+
+    bucket->next = next;
+
+    return bucket;
+}
+
+/******************************************************************************
+hashBucketFree(bucket): This procedure deallocates the specified
+hash bucket.
+Inputs:
+    bucket: the hash bucket to be freed.
+Return:
+    NONE
+ *****************************************************************************/
+static void hashBucketFree(psHashBucket* bucket)
+{
+    if (bucket == NULL) {
+        return;
+    }
+
+    psFree(bucket->key);
+
+    psFree(bucket->data);
+}
+
+/******************************************************************************
+psHashAlloc(nbucket): this procedure creates a new hash table with the
+specified number of buckets.
+Inputs:
+    nbucket: initial number of buckets
+Return:
+    The new hash table.
+ *****************************************************************************/
+psHash* psHashAlloc(psS32 nbucket)        // initial number of buckets
+{
+    psS32 i = 0;                  // loop index variable
+
+    // Create the new hash table.
+    psHash* table = psAlloc(sizeof(psHash));
+
+    psMemSetDeallocator(table, (psFreeFcn) hashFree);
+
+    // Allocate memory for the buckets.
+    table->buckets = psAlloc(nbucket * sizeof(psHashBucket* ));
+    table->nbucket = nbucket;
+
+    psTrace("utils.hash", 1, "Creating %d-element hash table\n", nbucket);
+
+    // Initialize all buckets to NULL.
+    for (i = 0; i < nbucket; i++)
+    {
+        table->buckets[i] = NULL;
+    }
+
+    // Return the new hash table.
+    return table;
+}
+
+/******************************************************************************
+hashFree(table): This procedure deallocates the specified hash
+table.  It loops through each bucket, and calls hashBucketFree() on that
+bucket.
+ 
+Inputs:
+    table: a hash table
+Return:
+    NONE
+ *****************************************************************************/
+static void hashFree(psHash* table)
+{
+    psS32 i = 0;                  // Loop index variable.
+
+    if (table == NULL) {
+        return;
+    }
+    // Loop through each bucket in the hash table.  If that bucket is not
+    // NULL, then free the bucket via a function call to hashBucketFree();
+    for (i = 0; i < table->nbucket; i++) {
+
+        // A bucket is composed of a linked list of buckets.
+        while (table->buckets[i] != NULL) {
+            psHashBucket* bucket = table->buckets[i];
+            table->buckets[i] = bucket->next;
+            psFree(bucket);
+        }
+    }
+
+    // Free the bucket structure, then the hash table.
+    psFree(table->buckets);
+}
+
+/******************************************************************************
+doHashWork(table, key, data, remove): This is an internal
+procedure which does the bulk of the work in using the hash table.  Depending
+upon the input parameters, it will either insert a new key/data into the hash
+table, retrieve the data for a specified key, or remove a key/data item.  If
+we try to insert a key that already exists in the hash table, then we deallocate
+the existing data/key item.
+Inputs:
+    table: a hash table
+    key: the key to insert, retrieve, or remove.  Must not be NULL.
+    data: the data to insert, if not NULL
+    remove: set to non-zero if the key/data should be removed from the table.
+Return:
+    NONE
+ 
+NOTE: consider removing this private function and simply putting the code
+into the psHashInsert(), psHashLookup(), and psHashRemove().  Why?  Because
+there is little common code between those functions.
+  *****************************************************************************/
+static psPtr doHashWork(psHash* table,
+                        const char *key,
+                        psPtr data, psBool remove
+                           )
+{
+    psS64 hash = 1;          // This will contain an integer value
+
+    // "hashed" from the key.
+    char *tmpchar = NULL;       // Used in computing the hash function.
+    psHashBucket* ptr = NULL;   // Used to retrieve the hash bucket.
+    psHashBucket* optr = NULL;  // "original pointer": used to step
+
+    // thru the linked list for a bucket.
+
+    // The following condition should never be true, since this is a private
+    // function, but I'm checking it anyway since future coders might change
+    // the way this procedure is called.
+    if ((table == NULL) || (key == NULL)) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return NULL;
+    }
+    // NOTE: This is the originally supplied hash function.
+    // for (psS32 i = 0, len = strlen(key); i < len; i++) {
+    // hash = (hash << 1) ^ key[i];
+    // }
+    // hash &= (table->nbucket - 1);
+
+    // This hash algorithm is from Sedgewick.  NOTE: must reread to ensure that
+    // the size of the hash table is not required to be a prime number.
+    tmpchar = (char *)key;
+    for (hash = 0; *tmpchar != '\0'; tmpchar++) {
+        hash = (64 * hash + *tmpchar) % (table->nbucket);
+    }
+
+    // NOTE: This should not be necessary, but for now, I'm checking bounds
+    // anyway.
+    if ((hash < 0) || (hash >= table->nbucket)) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Internal hash function out of range (%d)", hash);
+    }
+    // ptr will have the correct hash bucket.
+    ptr = table->buckets[hash];
+
+    // We know the correct hash bucket, now we need to know what to do.
+    // If the data parameter is NULL, then, by definition, this is a retrieve
+    // or a remove operation on the hash table.
+
+    if (data == NULL) {
+        if (remove
+           ) {
+            // We search through the linked list for this bucket in
+            // the hash table and look for an entry for this key.
+
+            optr = ptr;
+            while (ptr != NULL) {
+                // Determine if this entry holds the correct key.
+                if (strcmp(key, ptr->key) == 0) {
+                    // The following lines of code are fairly standard ways
+                    // of removing an item from a single-linked list.
+
+                    psPtr data = ptr->data;
+
+                    optr->next = ptr->next;
+                    if (ptr == table->buckets[hash]) {
+                        table->buckets[hash] = ptr->next;
+                    }
+                    psFree(ptr);
+
+                    // By definition, the data associated with that key
+                    // must be returned, not freed.
+                    return data;
+                }
+                optr = ptr;
+                ptr = ptr->next;
+            }
+            return NULL;                   // not in hash
+        }
+        else {
+            // If we get here, then a retrieve operation is requested.  So,
+            // we step trough the linked list at this bucket, and return the
+            // data once we find it, or return NULL if we don't.
+            while (ptr != NULL) {
+                if (strcmp(key, ptr->key) == 0) {
+                    return ptr->data;
+                }
+                ptr = ptr->next;
+            }
+            return NULL;                   // not in hash
+        }
+    } else {
+        // We get here if this procedure was called with non-NULL data.
+        // Therefore, we should insert that data into the hash table.
+        // First, we search through the linked list for this bucket in
+        // the hash table and look for a duplicate entry for this key.
+
+        while (ptr != NULL) {
+            if (strcmp(key, ptr->key) == 0) {
+                // We have found this key in the hash table.
+
+                psTrace("utils.hash.insert", 3, "Replacing data for %s\n", key);
+
+                // NOTE: I have changed this behavior from the originally
+                // supplied code.  Formerly, if itemFree was NULL, then
+                // the new data was not inserted into the hash table.
+
+                psFree(ptr->data);
+
+                ptr->data = psMemIncrRefCounter(data);
+                return data;
+            }
+            ptr = ptr->next;
+        }
+        // We did not found key in the linked list for this bucket of the hash
+        // table.  So, we insert this data at the head of that linked list.
+
+        table->buckets[hash] = hashBucketAlloc(key, data, table->buckets[hash]);
+        return data;
+    }
+}
+
+/******************************************************************************
+psHashAdd(table, key, data): this procedure, which is part of
+the public API, inserts a new key/data pair into the hash table.
+Inputs:
+    table: a hash table
+    key: the key to use
+    data: the data to insert.
+Return:
+    boolean value defining success or failure
+ *****************************************************************************/
+psBool psHashAdd(psHash* table,
+                 const char *key,
+                 const psPtr data)
+{
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return false;
+    }
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return false;
+    }
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_DATA_NULL);
+        return false;
+    }
+
+    return (doHashWork(table, key, data, false) != NULL);
+}
+
+/******************************************************************************
+psHashLookup(table, key): this procedure, which is part of the public API,
+looks up the specified key in the hash table and returns the data associated
+with that key.
+ 
+Inputs:
+    table: a hash table
+    key: the key to use
+Return:
+    The data associated with that key.
+ *****************************************************************************/
+psPtr psHashLookup(const psHash* table, // table to lookup key in
+                   const char *key)     // key to lookup
+{
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return NULL;
+    }
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return NULL;
+    }
+
+    return doHashWork(table, key, NULL, false);
+}
+
+/******************************************************************************
+psHashRemove(table, key): this procedure, which is part of the
+public API, removes the specified key from the hash table.
+Inputs:
+    table: a hash table
+    key: the key to remove
+Return:
+    boolean value defining success or failure
+ *****************************************************************************/
+psBool psHashRemove(psHash* table,
+                    const char *key)
+{
+    psPtr data = NULL;
+    psBool retVal = false;
+
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return false;
+    }
+    if (key == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_KEY_NULL);
+        return false;
+    }
+
+    data = doHashWork(table, key, NULL, true);
+    if (data != NULL) {
+        retVal = true;
+    } else {
+        retVal = false;
+    }
+
+    return retVal;
+}
+
+psArray* psHashToArray(const psHash* table)
+{
+
+    if (table == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psHash_TABLE_NULL);
+        return NULL;
+    }
+
+    // first, let's just count the number of data elements to know what size
+    // psArray we need to allocate.
+    int nElements = 0;
+    int nbucket = table->nbucket;
+    for (int i = 0; i < nbucket; i++) {
+        psHashBucket* tmpBucket = table->buckets[i];
+        while (tmpBucket != NULL) {
+            nElements++;
+            tmpBucket = tmpBucket->next;
+        }
+    }
+
+    psArray* result = psArrayAlloc(nElements);
+    result->n = nElements;
+
+    // now fill in the array with the hash table's data
+    psPtr* data = result->data;
+    for (int i = 0; i < nbucket; i++) {
+        psHashBucket* tmpBucket = table->buckets[i];
+        while (tmpBucket != NULL) {
+            *(data++) = psMemIncrRefCounter(tmpBucket->data);
+            tmpBucket = tmpBucket->next;
+        }
+    }
+
+    return result;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psHash.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psHash.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psHash.h	(revision 22271)
@@ -0,0 +1,86 @@
+
+/** @file  psHash.h
+ *  @brief Contains support for basic hashing functions.
+ *  @ingroup HashTable
+ *
+ *  This file will hold the prototypes for defining a hash table with arbitrary
+ *  data types, allocating/deallocating that has table, adding and removing
+ *  data from that hash table, and listing all keys defined in the hash table.
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author George Gusciora, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.7.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#if !defined(PS_HASH_H)
+#define PS_HASH_H
+
+/** \addtogroup HashTable
+ *  \{
+ */
+
+#include "psList.h"
+
+/** A bucket that holds an item of data. */
+typedef struct psHashBucket
+{
+    char *key;                         ///< key for this item of data
+    psPtr data;                        ///< the data itself
+    struct psHashBucket* next;         ///< list of other possible keys
+}
+psHashBucket;
+
+//typedef struct HashTable psHash; ///< Opaque type for a hash table
+
+/** The hash-table itself. */
+typedef struct psHash
+{
+    psS32 nbucket;                     ///< Number of buckets in hash table.
+    psHashBucket* *buckets;            ///< The bucket data.
+}
+psHash;
+
+/// Allocate hash buckets in table.
+psHash* psHashAlloc(
+    psS32 nbucket                  ///< The number of buckets to allocate.
+);
+
+/// Insert entry into table.
+psBool psHashAdd(
+    psHash* table,                 ///< table to insert in
+    const char *key,               ///< key to use
+    const psPtr data   ///< data to insert
+);
+
+/// Lookup key in table.
+psPtr psHashLookup(
+    const psHash* table,  ///< table to lookup key in
+    const char *key                ///< key to lookup
+);
+
+/// Remove key from table.
+psBool psHashRemove(
+    psHash* table,                 ///< table to lookup key in
+    const char *key                ///< key to lookup
+);
+
+/// List all keys in table.
+psList* psHashKeyList(
+    const psHash* table   ///< table to list keys from.
+);
+
+/** Create a psArray from a psHash contents.
+ *
+ *  @return psArray*       A new psArray with duplicate contents of the input psHash
+ */
+psArray* psHashToArray(
+    const psHash* table   ///< table to convert to psArray
+);
+
+/* \} */// End of DataGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psList.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psList.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psList.c	(revision 22271)
@@ -0,0 +1,639 @@
+/** @file psList.c
+ *  @brief Support for doubly linked lists
+ *  @ingroup LinkedList
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.35.4.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>                       // we need a mutex to make this stuff thread safe.
+
+#include "psError.h"
+#include "psAbort.h"
+#include "psMemory.h"
+#include "psList.h"
+#include "psTrace.h"
+#include "psLogMsg.h"
+
+#include "psCollectionsErrors.h"
+
+#define ITER_INIT_HEAD ((psPtr )1)         // next iteration should return head
+#define ITER_INIT_TAIL ((psPtr )2)         // next iteration should return tail
+
+// private functions.
+static void listFree(psList* list);
+static void listIteratorFree(psListIterator* iter);
+static psBool listIteratorRemove(psListIterator* iterator);
+
+static void listFree(psList* list)
+{
+    if (list == NULL) {
+        return;
+    }
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    // remove the free function of iterators to avoid double removal from list
+    psArray* iterators = list->iterators;
+    for (int i = 0; i < iterators->n; i++) {
+        psMemSetDeallocator(iterators->data[i], NULL);
+    }
+
+    psFree(list->iterators);
+
+    for (psListElem* ptr = list->head; ptr != NULL;) {
+        psListElem* next = ptr->next;
+
+        psFree(ptr->data);
+        psFree(ptr);
+
+        ptr = next;
+    }
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    pthread_mutex_destroy(&list->lock)
+    ;
+
+}
+
+static void listIteratorFree(psListIterator* iter)
+{
+    if (iter == NULL) {
+        return;
+    }
+
+    // remove this iterator from the parent list
+    psArrayRemove(iter->list->iterators,iter);
+
+}
+
+static psBool listIteratorRemove(psListIterator* iterator)
+{
+    if (iterator == NULL || iterator->cursor == NULL) {
+        return false;
+    }
+
+    psListElem* elem = iterator->cursor;
+    psList* list = iterator->list;
+    int index = iterator->index;
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    if (elem == list->head) {        // head of list?
+        list->head = elem->next;
+    } else {
+        elem->prev->next = elem->next;
+    }
+
+    if (elem == list->tail) {        // tail of list?
+        list->tail = elem->prev;
+    } else {
+        elem->next->prev = elem->prev;
+    }
+
+    psArray* iterators = list->iterators;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->cursor == elem) {
+            iter->cursor = NULL;
+        } else if (iter->index > index && iter->index > 0) {
+            iter->index--;
+        }
+    }
+
+    list->size--;
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    // OK, delete orphaned list element and its data
+    psFree(elem->data);
+    psFree(elem);
+
+    return true;
+}
+
+psList* psListAlloc(const psPtr data)
+{
+    psList* list = psAlloc(sizeof(psList));
+
+    psMemSetDeallocator(list, (psFreeFcn) listFree);
+
+    list->size = 0;
+    list->head = list->tail = NULL;
+    list->iterators = psArrayAlloc(16);
+    list->iterators->n = 0;
+
+    // create a default iterator
+    psListIteratorAlloc(list,PS_LIST_HEAD,true);
+
+    pthread_mutex_init(&(list->lock), NULL)
+    ;
+
+    if (data != NULL) {
+        psListAdd(list, PS_LIST_TAIL, data);
+    }
+
+    return list;
+}
+
+psListIterator* psListIteratorAlloc(psList* list, int location, bool mutable)
+{
+    psListIterator* iter = psAlloc(sizeof(psListIterator));
+
+    psMemSetDeallocator(iter, (psFreeFcn) listIteratorFree);
+
+    // initialize the attributes
+    iter->list = list;
+    iter->cursor = NULL;
+    iter->index = 0;
+    iter->offEnd = false;
+    iter->mutable = mutable;
+
+    // add to the list's array of iterators
+    psArray* listIterators = list->iterators;
+    int num = listIterators->n;
+    if ( num >= listIterators->nalloc) {
+        // need to resize the array to make more room for another iterator.
+        list->iterators = psArrayRealloc(listIterators,listIterators->nalloc*2);
+        listIterators = list->iterators;
+    }
+    listIterators->data[num] = iter;
+    listIterators->n = num+1;
+
+    if (! psListIteratorSet(iter,location)) {
+        psFree(iter);
+        iter = NULL;
+    }
+
+    return iter;
+}
+
+psBool psListIteratorSet(psListIterator* iterator,
+                         int location)
+{
+    if (iterator == NULL) {
+        return false;
+    }
+
+    psList* list = iterator->list;
+
+    if (location == PS_LIST_TAIL) {
+        iterator->cursor = list->tail;
+        iterator->index = list->size - 1;
+        iterator->offEnd = false;
+        return true;
+    }
+
+    if (location == PS_LIST_HEAD) {
+        iterator->cursor = list->head;
+        iterator->index = 0;
+        iterator->offEnd = false;
+        return true;
+    }
+
+    if (location < 0) {
+        location = list->size + location;
+    }
+
+    if (location < 0 || location >= (int)list->size) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_LOCATION_INVALID,
+                location);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    int index = iterator->index;
+    if (cursor == NULL) {      // set the cursor to the head if it is NULL
+        if (location > list->size/2) { // closer to tail or head?
+            cursor = list->tail;
+            index = list->size - 1;
+        } else {
+            cursor = list->head;
+            index = 0;
+        }
+    }
+
+    if (location < index) {
+        psS32 diff = index - location;
+
+        for (psS32 count = 0; count < diff; count++) {
+            cursor = cursor->prev; // shouldn't need to check for NULL
+        }
+    } else {
+        psS32 diff = location - index;
+
+        for (psS32 count = 0; count < diff; count++) {
+            cursor = cursor->next; // shouldn't need to check for NULL
+        }
+    }
+    iterator->cursor = cursor;
+    iterator->index = location;
+    iterator->offEnd = false;
+
+    return true;
+}
+
+psBool psListAdd(psList* list, psS32 location, const psPtr data)
+{
+
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return false;
+    }
+
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    if (location > 0 && location >= (int)list->size) {
+        psLogMsg(__func__,PS_LOG_WARN,
+                 "Specified location, %d, is beyond the end of the list.  "
+                 "Adding data item to tail.",
+                 location);
+        location = PS_LIST_TAIL;
+    }
+
+    // move ourselves to the given position
+    if (! psListIteratorSet(list->iterators->data[0],location)) {
+        return false;
+    }
+
+    if (location == PS_LIST_TAIL) {
+        // insert the element at the end of the list
+        return psListAddAfter(list->iterators->data[0],data);
+    } else {
+        return psListAddBefore(list->iterators->data[0],data);
+    }
+}
+
+bool psListAddAfter(psListIterator* iterator, const void* data)
+{
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    if (iterator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NULL);
+        return false;
+    }
+
+    // Check if the list pointed by the iterator can be changed
+    if (!iterator->mutable) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NONMUTABLE);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    psList* list = iterator->list;
+
+    if (cursor == NULL && list->head != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_ITERATOR_INVALID);
+        return false;
+    }
+
+    psListElem* elem = psAlloc(sizeof(psListElem));
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    // set the new list element's attributes
+    if (cursor == NULL) { // must be an empty list
+        elem->prev = NULL;
+        elem->next = NULL;
+        list->head = elem;
+        list->tail = elem;
+    } else {
+        elem->prev = cursor;
+        elem->next = cursor->next;
+        cursor->next = elem;
+        if (elem->next == NULL) {
+            list->tail = elem;
+        } else {
+            elem->next->prev = elem;
+        }
+    }
+
+    elem->data = psMemIncrRefCounter(data);
+
+    list->size++;
+
+    if (cursor == list->tail) {
+        list->tail = elem;
+    }
+
+    psArray* iterators = list->iterators;
+    int index = iterator->index;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->index > index) {
+            iter->index++;
+        }
+    }
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    return true;
+}
+
+bool psListAddBefore(psListIterator* iterator, const void* data)
+{
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    if (iterator == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NULL);
+        return false;
+    }
+
+    // Check if the list pointed by the iterator can be changed
+    if (!iterator->mutable) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_ITERATOR_NONMUTABLE);
+        return false;
+    }
+
+    psListElem* cursor = iterator->cursor;
+    psList* list = iterator->list;
+
+    if (cursor == NULL && list->head != NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_ITERATOR_INVALID);
+        return false;
+    }
+
+    psListElem* elem = psAlloc(sizeof(psListElem));
+
+    pthread_mutex_lock(&list->lock)
+    ;
+
+    // set the new list element's attributes
+    if (cursor == NULL) { // empty list.
+        elem->prev = NULL;
+        elem->next = NULL;
+        list->head = elem;
+        list->tail = elem;
+    } else {
+        elem->prev = cursor->prev;
+        elem->next = cursor;
+        cursor->prev = elem;
+        if (elem->prev == NULL) {
+            list->head = elem;
+        } else {
+            elem->prev->next = elem;
+        }
+    }
+
+    elem->data = psMemIncrRefCounter(data);
+
+    list->size++;
+
+    if (cursor == list->head) {
+        list->head = elem;
+    }
+
+    psArray* iterators = list->iterators;
+    int index = iterator->index;
+    for (int i = 0; i < iterators->n; i++) {
+        psListIterator* iter = (psListIterator*) iterators->data[i];
+        if (iter->index >= index) {
+            iter->index++;
+        }
+    }
+
+    pthread_mutex_unlock(&list->lock)
+    ;
+
+    return true;
+}
+
+psBool psListRemove(psList* list,
+                    psS32 location)
+{
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return false;
+    }
+
+    // move ourselves to the given position
+    psListIterator* defaultIterator = list->iterators->data[0];
+    if (! psListIteratorSet(defaultIterator,location)) {
+        return false;
+    }
+
+    return listIteratorRemove(defaultIterator);
+}
+
+psBool psListRemoveData(psList* list,
+                        const psPtr data)
+{
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return false;
+    }
+
+    if (data == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NULL);
+        return false;
+    }
+
+    psListElem* elem = list->head;
+    int index = 0;
+    while (elem != NULL && elem->data != data) {
+        elem = elem->next;
+        index++;
+    }
+    if (elem == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_DATA_NOT_FOUND);
+        return false;
+    }
+
+    psListIterator* iterator = (psListIterator*)list->iterators->data[0];
+    iterator->index = index;
+    iterator->cursor = elem;
+
+    return listIteratorRemove(iterator);
+}
+
+psPtr psListGet(psList* list, psS32 location)
+{
+    if (list == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psList_LIST_NULL);
+        return NULL;
+    }
+
+    if (list->head == NULL) { // list empty?
+        return NULL;
+    }
+
+    psListIterator* iterator = list->iterators->data[0];
+
+    if (! psListIteratorSet(iterator,location)) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psList_LOCATION_INVALID,
+                location);
+        return NULL;
+    }
+
+    return iterator->cursor->data;
+}
+
+/*
+ * and now return the previous/next element of the list
+ */
+psPtr psListGetAndIncrement(psListIterator* iterator)
+{
+    if (iterator == NULL ) {
+        return NULL;
+    }
+    if (( iterator->cursor == NULL) && (iterator->offEnd)) {
+        return NULL;
+    }
+    if ( (iterator->cursor == NULL) && (!iterator->offEnd)) {
+        iterator->cursor = iterator->list->head;
+        iterator->index = 0;
+        return NULL;
+    }
+
+    psPtr data = iterator->cursor->data;
+
+    iterator->cursor = iterator->cursor->next;
+    iterator->index++;
+    if (iterator->cursor == NULL) {
+        iterator->offEnd = true;
+    }
+
+    return data;
+}
+
+psPtr psListGetAndDecrement(psListIterator* iterator)
+{
+    if (iterator == NULL ) {
+        return NULL;
+    }
+    if ((iterator->cursor == NULL) && (!iterator->offEnd))  {
+        psLogMsg(__func__,PS_LOG_WARN,"Attempt to get previous with itertator cursor NULL and offEnd false");
+        return NULL;
+    }
+    if ( (iterator->cursor == NULL) && (iterator->offEnd) ) {
+        iterator->cursor = iterator->list->tail;
+        iterator->index = iterator->list->size-1;
+        iterator->offEnd = false;
+        return NULL;
+    }
+
+    psPtr data = iterator->cursor->data;
+
+    iterator->cursor = iterator->cursor->prev;
+    iterator->index--;
+
+    return data;
+}
+
+/*
+ * Convert a psList to/from a psVoidPtrArray
+ */
+psArray* psListToArray(const psList* restrict list)
+{
+    psListElem* ptr;
+    psU32 n;
+    psArray* restrict arr;
+
+    if (list == NULL) {
+        return NULL;
+    }
+
+    if (list->size > 0) {
+        arr = psArrayAlloc(list->size);
+    } else {
+        arr = psArrayAlloc(1);
+    }
+
+    arr->n = list->size;
+
+    ptr = list->head;
+    n = list->size;
+    for (psS32 i = 0; i < n; i++) {
+        arr->data[i] = psMemIncrRefCounter(ptr->data);
+        ptr = ptr->next;
+    }
+
+    return arr;
+}
+
+psList* psArrayToList(const psArray* arr)
+{
+    psU32 n;
+    psList* list;               // list of elements
+
+    if (arr == NULL) {
+        return NULL;
+    }
+
+    list = psListAlloc(NULL);
+    n = arr->n;
+    for (psS32 i = 0; i < n; i++) {
+        psListAdd(list, PS_LIST_TAIL, arr->data[i]);
+    }
+
+    return list;
+}
+
+psList* psListSort(psList* list, psComparePtrFcn compare)
+{
+    psArray* arr;
+
+    if (list == NULL) {
+        return NULL;
+    }
+    // convert to indexable vector for use by qsort.
+    arr = psListToArray(list);
+    psArray* iterators = psMemIncrRefCounter(list->iterators);
+    psFree(list);
+
+    arr = psArraySort(arr, compare);
+
+    // convert back to linked list
+    list = psArrayToList(arr);
+    psFree(list->iterators);
+    list->iterators = iterators;
+    psFree(arr);
+
+    // sorting should invalidate all iterator positions.
+    for (int i = 0; i < iterators->n; i++) {
+        ((psListIterator*)iterators->data[i])->cursor = NULL;
+    }
+
+    return list;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psList.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psList.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psList.h	(revision 22271)
@@ -0,0 +1,228 @@
+#if !defined(PS_LIST_H)
+#define PS_LIST_H
+
+/** @file psList.h
+ *  @brief Support for doubly linked lists
+ *
+ *  @author Robert Lupton, Princeton University
+ *  @author Robert Daniel DeSonia, MHPCC
+ *
+ *  @ingroup LinkedList
+ *
+ *  @version $Revision: 1.23.8.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-19 01:09:56 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <pthread.h>                   // we need a mutex to make this stuff thread safe.
+
+#include "psCompare.h"
+#include "psArray.h"
+
+/** @addtogroup LinkedList
+ *  @{
+ */
+
+/** Special values of index into list
+ *
+ *  This list of possible list position values should be contiguous non-positive values ending with
+ *  PS_LIST_UNKNOWN.  Any value less-than-or-equal-to PS_LIST_UNKNOWN is considered a undefined position.
+ *
+ */
+enum {
+    PS_LIST_HEAD = 0,                  ///< at head
+    PS_LIST_TAIL = -1,                 ///< at tail
+};
+
+/** Doubly-linked list element */
+typedef struct psListElem
+{
+    struct psListElem* prev;           ///< previous link in list
+    struct psListElem* next;           ///< next link in list
+    psPtr data;                        ///< real data item
+}
+psListElem;
+
+/** The psList Linked list structure.  User should not allocate this struct
+ *  directly; rather the psListAlloc should be used.
+ *
+ *  @see psListAlloc
+ */
+typedef struct
+{
+    psU32 size;                        ///< number of elements on list
+    psListElem* head;                  ///< first element on list (may be NULL)
+    psListElem* tail;                  ///< last element on list (may be NULL)
+    psArray* iterators;
+    ///< array of all iterators associated with this list.  First iterator is
+    ///< used internally to improve performance when using indexed access, all
+    ///< others are user-level iterators created by psListIteratorAlloc.
+
+    pthread_mutex_t lock;              ///< mutex to lock a node during changes
+}
+psList;
+
+/** The psList iterator structure.  This should be allocated via
+ *  psListIteratorAlloc and not directly.
+ *
+ *  The life span of a psListIterator object is ended by either a psFree
+ *  of this structure OR psFree of the psList in which it operates on.
+ *
+ *  @see psListIteratorAlloc, psListIteratorSet, psListGetAndIncrement, psListGetAndDecrement
+ */
+typedef struct
+{
+psList* list;                      ///< List iterator to works on
+psListElem* cursor;                ///< current cursor position
+int index;                         ///< the index number in the list
+bool offEnd;                       ///< Iterator off the end?
+bool mutable;                      ///< Is it permissible to modify the list?
+}
+psListIterator;
+
+
+/** Creates a psList linked list object.
+ *
+ *  @return psList* A new psList object.
+ */
+psList* psListAlloc(
+    const psPtr data
+    ///< initial data item; may be NULL if no an empty psList is desired
+)
+;
+
+/** Creates a psListIterator object and associates it with a psList.
+ *
+ *  @return psListIterator* A new psListIterator object.
+ */
+psListIterator* psListIteratorAlloc(
+    psList* list,                      ///< the psList to iterate with
+    int location,                      ///< the initial starting point.
+    ///<  This can be a numeric index, PS_LIST_HEAD, or PS_LIST_TAIL.
+    bool mutable                       ///< Is it permissible to modify list?
+);
+
+/** Set the iterator of the list to a given position.  If location is invalid the
+ *  iterator position is not changed.
+ *
+ *  @return psBool        TRUE if iterator successfully set, otherwise FALSE.
+ */
+psBool psListIteratorSet(
+    psListIterator* iterator,            ///< list iterator
+    int location                         ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Adds an element to a psList at position given.
+ *
+ *  @return psBool        TRUE if item was successfully added, otherwise FALSE.
+ */
+psBool psListAdd(
+    psList* list,                      ///< list to add item to
+    psS32 location,                    ///< index, PS_LIST_HEAD, PS_LIST_TAIL, or numbered location.
+    const psPtr data   ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Adds an data item to a psList at position just after the list position given
+ *
+ *  @return psBool        TRUE if item was successfully added, otherwise FALSE.
+ */
+psBool psListAddAfter(
+    psListIterator* list,              ///< list position to add item to
+    const psPtr data   ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Adds an data item to a psList at position just before the list position given
+ *
+ *  @return psBool        TRUE if item was successfully added, otherwise FALSE.
+ */
+psBool psListAddBefore(
+    psListIterator* list,              ///< list position to add item to
+    const psPtr data   ///< data item to add.  If NULL, list is not modified.
+);
+
+/** Remove an item at the specified location from a list.
+ *
+ *  @return psBool        TRUE if element is successfully removed, otherwise FALSE.
+ */
+psBool psListRemove(
+    psList* list,                      ///< list to remove element from
+    psS32 location                     ///< index of item
+);
+
+/** Remove an item from a list.
+ *
+ *  @return psBool        TRUE if element is successfully removed, otherwise FALSE.
+ */
+psBool psListRemoveData(
+    psList* list,                      ///< list to remove element from
+    const psPtr data   ///< data item to find and remove
+);
+
+/** Retrieve an item from a list.
+ *
+ *  @return psPtr       the item corresponding to the location parameter.  If
+ *                      location is invalid (e.g., a numbered index greater
+ *                      than the list size or if the list is empty), a
+ *                      NULL is returned.
+ */
+psPtr psListGet(
+    psList* list,                      ///< list to retrieve element from
+    psS32 location                     ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Position the specified iterator to the next item in list.
+ *
+ *  @return psPtr       the data item at the original iterator position or NULL if the
+ *                      iterator went past the end of the list.
+ */
+psPtr psListGetAndIncrement(
+    psListIterator* iterator           ///< iterator to move
+);
+
+/** Position the specified iterator to the previous item in list.
+ *
+ *  @return psPtr       the data item at the original iterator position or NULL if the
+ *                      iterator went past the beginning of the list.
+ */
+psPtr psListGetAndDecrement(
+    psListIterator* iterator           ///< iterator to move
+);
+
+/** Convert a linked list to an array
+ *
+ *  @return psArray* A new psArray populated with elements from the list,
+ *                      or NULL if the given dlist parameter is NULL.
+ */
+psArray* psListToArray(
+    const psList* dlist   ///< List to convert
+);
+
+/** Convert array to a doubly-linked list
+ *
+ *  @return psList* A new psList populated with elements formt the psArray,
+ *                      or NULL is the given arr parameter is NULL.
+ */
+psList* psArrayToList(
+    const psArray* arr   ///< vector to convert
+);
+
+/** Sort a list via a comparison function.
+ *
+ *  The comparison function must return an integer less than, equal to, or 
+ *  greater than zero if the first argument is considered to be respectively 
+ *  less than, equal to, or greater than the second. 
+ *
+ *  If two members compare as equal, their order in the sorted array is 
+ *  undefined.
+ *
+ *  @return psList*     Sorted list.
+ */
+psList* psListSort(
+    psList* list,                      ///< the list to sort
+    psComparePtrFcn compare            ///< the comparison function
+);
+
+/// @} End of DataGroup Functions
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psLookupTable.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psLookupTable.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psLookupTable.c	(revision 22271)
@@ -0,0 +1,959 @@
+/** @file  psLookupTable.c
+*
+*  @brief This file defines the structure and functions for table lookups.
+*
+*  @ingroup dataIO
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-08 17:58:57 $
+*
+*  Copyright 2004-5 Maui High Performance Computing Center, University of Hawaii
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+//#ifdef DARWIN
+#undef __STRICT_ANSI__
+//#endif
+#include <stdlib.h>
+//#ifdef DARWIN
+#define __STRICT_ANSI__
+//#endif
+#include <math.h>
+#include <stdlib.h>
+
+#include "psMemory.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLookupTable.h"
+#include "psFileUtilsErrors.h"
+#include "psConstants.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 256
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static bool ignoreLine(char *inString);
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+static psU8 parseU8(char *inString, psParseErrorType *status);
+static psS8 parseS8(char *inString, psParseErrorType *status);
+static psU16 parseU16(char *inString, psParseErrorType *status);
+static psS16 parseS16(char *inString, psParseErrorType *status);
+static psU32 parseU32(char *inString, psParseErrorType *status);
+static psS32 parseS32(char *inString, psParseErrorType *status);
+static psU64 parseU64(char *inString, psParseErrorType *status);
+static psS64 parseS64(char *inString, psParseErrorType *status);
+static psF32 parseF32(char *inString, psParseErrorType *status);
+static psF64 parseF64(char *inString, psParseErrorType *status);
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status);
+static void lookupTableFree(psLookupTable* table);
+
+/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+ *  must be null terminated. */
+static bool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+static char *cleanString(char *inString, int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = PS_PARSE_ERROR_GENERAL;
+    }
+
+    return cleanToken;
+}
+
+/** Returns single parsed value as a psU8. The input string must be cleaned and null terminated. */
+static psU8 parseU8(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU8 value = 0.0;
+
+
+    value = (psU8)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS8. The input string must be cleaned and null terminated. */
+static psS8 parseS8(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS8 value = 0.0;
+
+
+    value = (psS8)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU16. The input string must be cleaned and null terminated. */
+static psU16 parseU16(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU16 value = 0.0;
+
+
+    value = (psU16)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS16. The input string must be cleaned and null terminated. */
+static psS16 parseS16(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS16 value = 0.0;
+
+
+    value = (psS16)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU32. The input string must be cleaned and null terminated. */
+static psU32 parseU32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU32 value = 0.0;
+
+
+    value = (psU32)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS32. The input string must be cleaned and null terminated. */
+static psS32 parseS32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS32 value = 0.0;
+
+
+    value = (psS32)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU64. The input string must be cleaned and null terminated. */
+static psU64 parseU64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU64 value = 0.0;
+
+
+    value = (psU64)strtoull(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS64. The input string must be cleaned and null terminated. */
+static psS64 parseS64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS64 value = 0.0;
+
+
+    value = (psS64)strtoll(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psF32. The input string must be cleaned and null terminated. */
+static psF32 parseF32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psF32 value = 0.0;
+
+
+    value = (psF32)strtof(inString, &end);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psF64. The input string must be cleaned and null terminated. */
+static psF64 parseF64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psF64 value = 0.0;
+
+
+    value = (psF64)strtod(inString, &end);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a double precision number. The input string must be cleaned and null
+ * terminated. */
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status)
+{
+    psElemType type;
+
+
+    if(vec == NULL) {
+        *status = 1;
+        return;
+    }
+
+    type = vec->type.type;
+
+    switch(type) {
+    case PS_TYPE_U8:
+        vec->data.U8[index] = parseU8(strValue, status);
+        break;
+    case PS_TYPE_S8:
+        vec->data.S8[index] = parseS8(strValue, status);
+        break;
+    case PS_TYPE_U16:
+        vec->data.U16[index] = parseU16(strValue, status);
+        break;
+    case PS_TYPE_S16:
+        vec->data.S16[index] = parseS16(strValue, status);
+        break;
+    case PS_TYPE_U32:
+        vec->data.U32[index] = parseU32(strValue, status);
+        break;
+    case PS_TYPE_S32:
+        vec->data.S32[index] = parseS32(strValue, status);
+        break;
+    case PS_TYPE_U64:
+        vec->data.U64[index] = parseU64(strValue, status);
+        break;
+    case PS_TYPE_S64:
+        vec->data.S64[index] = parseS64(strValue, status);
+        break;
+    case PS_TYPE_F32:
+        vec->data.F32[index] = parseF32(strValue, status);
+        break;
+    case PS_TYPE_F64:
+        vec->data.F64[index] = parseF64(strValue, status);
+        break;
+    default:
+        *status = PS_PARSE_ERROR_TYPE;
+    }
+
+    return;
+}
+
+static psParseErrorType printError(psU64 lineCount, char* badText, psParseErrorType status)
+{
+    switch(status) {
+    case PS_PARSE_ERROR_VALUE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_VALUE, badText, lineCount);
+        break;
+    case PS_PARSE_ERROR_TYPE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_TYPE, badText, lineCount);
+        break;
+    case PS_PARSE_ERROR_GENERAL:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_GENERAL, badText, lineCount);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_INVALID_TYPE, badText, lineCount);
+    }
+
+    return PS_LOOKUP_SUCCESS;
+}
+
+static void lookupTableFree(psLookupTable* table)
+{
+    if (table == NULL) {
+        return;
+    }
+
+    psFree(table->values);
+    psFree(table->index);
+    psFree((char*)table->fileName);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psLookupTable* psLookupTableAlloc(const char *fileName, psF64 validFrom, psF64 validTo)
+{
+    psLookupTable *outTable = NULL;
+
+
+    // Can't read table if you don't know its name
+    PS_PTR_CHECK_NULL(fileName,NULL);
+
+    // Allocate lookup table
+    outTable = (psLookupTable*)psAlloc(sizeof(psLookupTable));
+
+    // Set deallocator
+    psMemSetDeallocator(outTable, (psFreeFcn)lookupTableFree);
+
+    // Allocate and set metadata item comment
+    outTable->fileName = psStringCopy(fileName);
+
+    // Number of table rows and columns. Automatically resized by table read.
+    outTable->numRows = 0;
+    outTable->numCols = 0;
+
+    // Valid ranges. Automatically set by table read if both zero.
+    outTable->validFrom = validFrom;
+    outTable->validTo = validTo;
+
+    // Vector of independent index values. Filled by table read.
+    outTable->index = NULL;
+
+    // Array of dependent table values corresponding to index values. Filled by table read.
+    outTable->values = NULL;
+
+    return outTable;
+}
+
+#define UPDATE_VALID_TO_FROM(TABLE)                                             \
+switch (TABLE->index->type.type) {                                              \
+case PS_TYPE_U8:                                                                \
+    TABLE->validFrom = (psF64)TABLE->index->data.U8[0];                         \
+    TABLE->validTo   = (psF64)TABLE->index->data.U8[TABLE->index->nalloc-1];    \
+    break;                                                                      \
+case PS_TYPE_S8:                                                                \
+    TABLE->validFrom = (psF64)TABLE->index->data.S8[0];                         \
+    TABLE->validTo   = (psF64)TABLE->index->data.S8[TABLE->index->nalloc-1];    \
+    break;                                                                      \
+case PS_TYPE_U16:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.U16[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.U16[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_S16:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.S16[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.S16[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_U32:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.U32[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.U32[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_S32:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.S32[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.S32[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_U64:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.U64[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.U64[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_S64:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.S64[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.S64[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_F32:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.F32[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.F32[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+case PS_TYPE_F64:                                                               \
+    TABLE->validFrom = (psF64)TABLE->index->data.F64[0];                        \
+    TABLE->validTo   = (psF64)TABLE->index->data.F64[TABLE->index->nalloc-1];   \
+    break;                                                                      \
+default:                                                                        \
+    TABLE->validFrom = (psF64)0;                                                \
+    TABLE->validTo   = (psF64)0;                                                \
+    break;                                                                      \
+}
+
+#define COPY_VECTOR_VALUES(VEC_OUT,INDEX_OUT,VEC_IN,INDEX_IN)                              \
+switch(((psVector*)(VEC_IN))->type.type) {                                                 \
+case PS_TYPE_U8:                                                                           \
+    ((psVector*)VEC_OUT)->data.U8[INDEX_OUT] = ((psVector*)VEC_IN)->data.U8[INDEX_IN];     \
+    break;                                                                                 \
+case PS_TYPE_U16:                                                                          \
+    ((psVector*)VEC_OUT)->data.U16[INDEX_OUT] = ((psVector*)VEC_IN)->data.U16[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_U32:                                                                          \
+    ((psVector*)VEC_OUT)->data.U32[INDEX_OUT] = ((psVector*)VEC_IN)->data.U32[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_U64:                                                                          \
+    ((psVector*)VEC_OUT)->data.U64[INDEX_OUT] = ((psVector*)VEC_IN)->data.U64[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_S8:                                                                           \
+    ((psVector*)VEC_OUT)->data.S8[INDEX_OUT] = ((psVector*)VEC_IN)->data.S8[INDEX_IN];     \
+    break;                                                                                 \
+case PS_TYPE_S16:                                                                          \
+    ((psVector*)VEC_OUT)->data.S16[INDEX_OUT] = ((psVector*)VEC_IN)->data.S16[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_S32:                                                                          \
+    ((psVector*)VEC_OUT)->data.S32[INDEX_OUT] = ((psVector*)VEC_IN)->data.S32[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_S64:                                                                          \
+    ((psVector*)VEC_OUT)->data.S64[INDEX_OUT] = ((psVector*)VEC_IN)->data.S64[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_F32:                                                                          \
+    ((psVector*)VEC_OUT)->data.F32[INDEX_OUT] = ((psVector*)VEC_IN)->data.F32[INDEX_IN];   \
+    break;                                                                                 \
+case PS_TYPE_F64:                                                                          \
+    ((psVector*)VEC_OUT)->data.F64[INDEX_OUT] = ((psVector*)VEC_IN)->data.F64[INDEX_IN];   \
+    break;                                                                                 \
+default:                                                                                   \
+    break;                                                                                 \
+}
+
+
+psLookupTable* psLookupTableRead(psLookupTable *table)
+{
+    bool typeLine = true;
+    char *line = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *linePtr = NULL;
+    psParseErrorType status = PS_PARSE_SUCCESS;
+    psParseErrorType lineStatus = PS_PARSE_SUCCESS;
+    psU64 lineCount = 0;
+    psU64 numRows = 0;
+    psU64 numCols = 0;
+    psU64 failedLines = 0;
+    FILE *fp = NULL;
+    psElemType elemType;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+    psBool sortIndex = false;
+
+    // Check for NULL input table
+    PS_PTR_CHECK_NULL(table,NULL);
+
+    // Check for input table with NULL file name
+    PS_PTR_CHECK_NULL(table->fileName,NULL);
+
+    // Open table file specified by table->fileName
+    if((fp=fopen(table->fileName, "r")) == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_FILE_NOT_FOUND,
+                table->fileName);
+        return table;
+    }
+
+    // Initialize vector pointers
+    indexVec = table->index;
+    values = table->values = psArrayAlloc(10);
+    values->n = 0;
+
+    // Create reusable line for continuous read
+    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+    // Loop through file to get numRows, numCols, and column data types
+    while((fgets(line, MAX_STRING_LENGTH, fp) != NULL) && (lineStatus == PS_PARSE_SUCCESS)) {
+
+        // Initialize variables for new line
+        linePtr = line;
+        lineCount++;
+
+        // If line is not a comment or blank, then extract data
+        if(!ignoreLine(linePtr)) {
+
+            if(typeLine == true) {
+
+                // Determine column types from first line in data file after comments
+                while((strType=getToken(&linePtr," ",&status))) {
+                    numCols++;
+                    typeLine = false;
+                    if(!strncmp(strType, "psU8", 4)) {
+                        elemType = PS_TYPE_U8;
+                    } else if(!strncmp(strType, "psS8", 4)) {
+                        elemType = PS_TYPE_S8;
+                    } else if(!strncmp(strType, "psU16", 5)) {
+                        elemType = PS_TYPE_U16;
+                    } else if(!strncmp(strType, "psS16", 5)) {
+                        elemType = PS_TYPE_S16;
+                    } else if(!strncmp(strType, "psU32", 5)) {
+                        elemType = PS_TYPE_U32;
+                    } else if(!strncmp(strType, "psS32", 5)) {
+                        elemType = PS_TYPE_S32;
+                    } else if(!strncmp(strType, "psU64", 5)) {
+                        elemType = PS_TYPE_U64;
+                    } else if(!strncmp(strType, "psS64", 5)) {
+                        elemType = PS_TYPE_S64;
+                    } else if(!strncmp(strType, "psF32", 5)) {
+                        elemType = PS_TYPE_F32;
+                    } else if(!strncmp(strType, "psF64", 5)) {
+                        elemType = PS_TYPE_F64;
+                    } else {
+                        status = PS_PARSE_ERROR_TYPE;
+                    }
+
+                    // Realloc number of columns as you go
+                    psVector* vec = psVectorAlloc(0, elemType);
+                    if(numCols == 1) {
+                        indexVec = vec;
+                    } else {
+                        psArrayAdd(values,0,vec);
+                        psFree(vec);
+                    }
+
+                    if(status) {
+                        printError(lineCount, strValue, status);
+                        failedLines++;
+                        lineStatus = status;
+                        status = PS_PARSE_SUCCESS;
+                    }
+                    psFree(strType);
+                }
+            } else {
+                // Parse and add values to all columns
+                numRows++;
+                numCols = 0;
+                while((strValue=getToken(&linePtr," ",&status))) {
+                    numCols++;
+
+                    // Realloc number of rows as you go
+                    if(numCols == 1) {
+                        sortIndex = false;
+                        indexVec = psVectorRecycle(indexVec, numRows, indexVec->type.type);
+                        parseValue(indexVec, numRows-1, strValue, &status);
+                    } else {
+                        valuesVec = values->data[numCols-2];
+                        valuesVec = psVectorRecycle(valuesVec, numRows, valuesVec->type.type);
+                        parseValue(valuesVec, numRows-1, strValue, &status);
+                    }
+
+                    if(status) {
+                        printError(lineCount, strValue, status);
+                        failedLines++;
+                        lineStatus = status;
+                        status = PS_PARSE_SUCCESS;
+                    }
+                    psFree(strValue);
+                } // end while
+            } // end else
+        } // if ignoreLine
+    } // end while
+
+    psFree(line);
+
+    // Set table for return
+    table->numRows = numRows;
+    table->numCols = numCols-1;
+    table->index = indexVec;
+    table->values = values;
+
+    // Set table values to indicate error detected during the read
+    if(lineStatus) {
+        table->numRows = 0;
+        table->numCols = 0;
+        table->validFrom = 0;
+        table->validTo = 0;
+        psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psLookupTable_TABLE_INVALID);
+    } else {
+        // Check if index vector needs to be sorted
+        psVector* sortedIndex = psVectorAlloc(table->numRows,PS_TYPE_U32);
+        sortedIndex = psVectorSortIndex(sortedIndex,table->index);
+        for(psS32 i = 0; i < numRows; i++ ) {
+            if(sortedIndex->data.U32[i] != i) {
+                sortIndex = true;
+                break;
+            }
+        }
+        // Check if it is necessary to sort value vectors
+        if(sortIndex) {
+            // Allocate new index vector
+            psVector* newIndexVector = psVectorAlloc(numRows,indexVec->type.type);
+            // Allocate new value vectors
+            psArray* newValueArray = psArrayAlloc(numCols-1);
+            for(psS32 j = 0; j < numCols-1; j++) {
+                psS32 type = ((psVector*)(table->values->data[j]))->type.type;
+                newValueArray->data[j] = psVectorAlloc(numRows,type);
+            }
+            for(psS32 i = 0; i < numRows; i++) {
+                // Populate new index vector
+                psU32 sortIndex = sortedIndex->data.U32[i];
+                COPY_VECTOR_VALUES(newIndexVector,i, indexVec,sortIndex)
+                // For every column populate new value vectors
+                for(psS32 j=0; j < numCols-1; j++) {
+                    COPY_VECTOR_VALUES(newValueArray->data[j],i,table->values->data[j], sortIndex)
+                }
+            }
+            // Free old index vector
+            psFree(table->index);
+            // Free old value vectors
+            psFree(table->values);
+            // Assign new vector value to table
+            table->index = newIndexVector;
+            // Assign new value vectors to table array
+            table->values = newValueArray;
+        }
+        psFree(sortedIndex);
+        // Update the validTo and validFrom
+        UPDATE_VALID_TO_FROM(table)
+    }
+
+    fclose(fp);
+
+    return table;
+}
+
+#define CONVERT_VALUE_TO_F64(VECTOR,INDEX,RESULT)     \
+switch(VECTOR->type.type) {                           \
+case PS_TYPE_U8:                                      \
+    RESULT = (psF64)VECTOR->data.U8[INDEX];           \
+    break;                                            \
+case PS_TYPE_U16:                                     \
+    RESULT = (psF64)VECTOR->data.U16[INDEX];          \
+    break;                                            \
+case PS_TYPE_U32:                                     \
+    RESULT = (psF64)VECTOR->data.U32[INDEX];          \
+    break;                                            \
+case PS_TYPE_U64:                                     \
+    RESULT = (psF64)VECTOR->data.U64[INDEX];          \
+    break;                                            \
+case PS_TYPE_S8:                                      \
+    RESULT = (psF64)VECTOR->data.S8[INDEX];           \
+    break;                                            \
+case PS_TYPE_S16:                                     \
+    RESULT = (psF64)VECTOR->data.S16[INDEX];          \
+    break;                                            \
+case PS_TYPE_S32:                                     \
+    RESULT = (psF64)VECTOR->data.S32[INDEX];          \
+    break;                                            \
+case PS_TYPE_S64:                                     \
+    RESULT = (psF64)VECTOR->data.S64[INDEX];          \
+    break;                                            \
+case PS_TYPE_F32:                                     \
+    RESULT = (psF64)VECTOR->data.F32[INDEX];          \
+    break;                                            \
+case PS_TYPE_F64:                                     \
+    RESULT = VECTOR->data.F64[INDEX];                 \
+    break;                                            \
+default:                                              \
+    RESULT = NAN;                                     \
+    break;                                            \
+}
+
+
+#define CHECK_LOWER_UPPER_BOUND(TABLE,INDEX,COLUMN)                                \
+switch (TABLE->index->type.type) {                                                 \
+case PS_TYPE_U8:                                                                   \
+    if( (psU8)index < TABLE->index->data.U8[0] ) {                                 \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psU8)index > TABLE->index->data.U8[numRows-1]) {                          \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_U16:                                                                  \
+    if( (psU16)index < TABLE->index->data.U16[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psU16)index > TABLE->index->data.U16[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_U32:                                                                  \
+    if( (psU32)index < TABLE->index->data.U32[0]) {                                \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if ( (psU32)index > TABLE->index->data.U32[numRows-1] ) {                      \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_U64:                                                                  \
+    if( (psU64)index < TABLE->index->data.U64[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psU64)index > TABLE->index->data.U64[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S8:                                                                   \
+    if( (psS8)index < TABLE->index->data.S8[0] ) {                                 \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS8)index > TABLE->index->data.S8[numRows-1] ) {                         \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S16:                                                                  \
+    if( (psS16)index < TABLE->index->data.S16[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS16)index > TABLE->index->data.S16[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S32:                                                                  \
+    if( (psS32)index < TABLE->index->data.S32[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS32)index > TABLE->index->data.S32[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_S64:                                                                  \
+    if( (psS64)index < TABLE->index->data.S64[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psS64)index > TABLE->index->data.S64[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_F32:                                                                  \
+    if( (psF32)index < TABLE->index->data.F32[0] ) {                               \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( (psF32)index > TABLE->index->data.F32[numRows-1] ) {                       \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+case PS_TYPE_F64:                                                                  \
+    if( index < TABLE->index->data.F64[0] ) {                                      \
+        *status = PS_LOOKUP_PAST_TOP;                                              \
+    }                                                                              \
+    if( index > TABLE->index->data.F64[numRows-1] ) {                              \
+        *status = PS_LOOKUP_PAST_BOTTOM;                                           \
+    }                                                                              \
+    break;                                                                         \
+default:                                                                           \
+    *status = PS_LOOKUP_ERROR;                                                     \
+    return NAN;                                                                    \
+    break;                                                                         \
+}                                                                                  \
+if(*status == PS_LOOKUP_PAST_TOP) {                                                \
+    CONVERT_VALUE_TO_F64(((psVector*)(TABLE->values->data[COLUMN])),0,out)         \
+    return out;                                                                    \
+} else if (*status == PS_LOOKUP_PAST_BOTTOM) {                                     \
+    CONVERT_VALUE_TO_F64(((psVector*)(TABLE->values->data[COLUMN])),numRows-1,out) \
+    return out;                                                                    \
+}
+
+
+psF64 psLookupTableInterpolate(psLookupTable *table, psF64 index, psU64 column, psLookupStatusType *status)
+{
+    psU64 hiIdx = 0;
+    psU64 loIdx = 0;
+    psU64 numRows = 0;
+    psU64 numCols = 0;
+    psF64 out = 0.0;
+    psF64 denom = 0.0;
+    psF64 convertVal = 0.0;
+    psF64 tempVal = 0.0;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+
+    // Error checks
+    // Set status to error prior to check since if checks fails it will immediately return
+    PS_PTR_CHECK_NULL(status,NAN);
+    *status = PS_LOOKUP_ERROR;
+    PS_PTR_CHECK_NULL(table,NAN);
+    indexVec = table->index;
+    values = table->values;
+    numRows = table->numRows;
+    numCols = table->numCols;
+    PS_PTR_CHECK_NULL(indexVec,NAN);
+    PS_PTR_CHECK_NULL(values,NAN);
+    PS_INT_CHECK_EQUALS(numRows, 0,NAN);
+    PS_INT_CHECK_EQUALS(numCols, 0,NAN);
+    PS_INT_CHECK_RANGE(column, 0, (int)(numCols-1), NAN);
+
+    valuesVec = (psVector*)values->data[column];
+    PS_PTR_CHECK_NULL(indexVec,NAN);
+
+    // Set status to success since it passed all parameter checks
+    *status = PS_LOOKUP_SUCCESS;
+
+    // Verify the index is within the bounds of the table
+    CHECK_LOWER_UPPER_BOUND(table,index,column)
+
+    // Find location in table where specified index is between to entries
+    CONVERT_VALUE_TO_F64(indexVec, 0, convertVal)
+    while(index > convertVal ) {
+        hiIdx++;
+        if(hiIdx >= numRows) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psLookupTable_INTERPOLATE_HIGH, hiIdx);
+            *status = PS_LOOKUP_ERROR;
+            return NAN;
+        }
+        CONVERT_VALUE_TO_F64(indexVec, hiIdx, convertVal)
+    }
+
+    // Check for negative low index and generate error
+    loIdx = hiIdx--;
+    if(loIdx < 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psLookupTable_INTERPOLATE_LOW, loIdx);
+        *status = PS_LOOKUP_ERROR;
+        return NAN;
+    }
+
+    // Perform linear interpolation to calculate return value
+    CONVERT_VALUE_TO_F64(indexVec, hiIdx, denom)
+    CONVERT_VALUE_TO_F64(indexVec, loIdx, convertVal);
+    denom -= convertVal;
+    if(fabs(denom) < FLT_EPSILON) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_DIVIDE_BY_ZERO);
+        *status = PS_LOOKUP_ERROR;
+        return NAN;
+    } else {
+        CONVERT_VALUE_TO_F64(valuesVec,hiIdx,tempVal)
+        CONVERT_VALUE_TO_F64(valuesVec,loIdx,convertVal)
+        tempVal -= convertVal;
+        CONVERT_VALUE_TO_F64(indexVec,loIdx,convertVal)
+        out = tempVal*(index-convertVal)/denom;
+        CONVERT_VALUE_TO_F64(valuesVec,loIdx,convertVal)
+        out += convertVal;
+    }
+
+    return out;
+}
+
+psVector* psLookupTableInterpolateAll(psLookupTable *table, psF64 index, psVector *stats)
+{
+    psU64 i = 0;
+    psU64 numCols = 0;
+    psVector *outVector = NULL;
+    psLookupStatusType status = PS_LOOKUP_SUCCESS;
+
+    // Error checks
+    PS_PTR_CHECK_NULL(table,NULL);
+    PS_PTR_CHECK_NULL(stats,NULL);
+    numCols = table->numCols;
+    PS_INT_CHECK_EQUALS(numCols, 0,NULL);
+
+    outVector = psVectorAlloc(numCols+1, PS_TYPE_F64);
+
+    // Fill vectors with results and status of results
+    for(i=0; i<numCols; i++) {
+        outVector->data.F64[i] = psLookupTableInterpolate(table, index, i, &status);
+        stats->data.U32[i] = status;
+    }
+
+    return outVector;
+}
+
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psLookupTable.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psLookupTable.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psLookupTable.h	(revision 22271)
@@ -0,0 +1,112 @@
+/** @file  psLookupTable.h
+*
+*  @brief This file defines the structure and functions for table lookups.
+*
+*  @ingroup dataIO
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-08 17:58:57 $
+*
+*  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_LOOKUPTABLE_H
+#define PS_LOOKUPTABLE_H
+
+#include "psType.h"
+#include "psVector.h"
+#include "psArray.h"
+
+
+/** Lookup table structure
+ *
+ *  Holds table data read from external data files.
+ *
+ */
+typedef struct
+{
+    const char *fileName;              ///< Name of file with table
+    psU64 numRows;                     ///< Number of table rows
+    psU64 numCols;                     ///< Number of table columns
+    psF64 validFrom;                   ///< Lower bound for rable read
+    psF64 validTo;                     ///< Upper bound for table read
+    psVector *index;                   ///< Vector of independent index values
+    psArray *values;                   ///< Array of dependent table values corresponding to index values
+}
+psLookupTable;
+
+
+/** Lookup table lookup status and error conditions
+ *
+ *  Success, failure, and status conditions for table lookups.
+ */
+typedef enum {
+    PS_LOOKUP_SUCCESS             = 0x0000,        ///< Table lookup succeeded
+    PS_LOOKUP_PAST_TOP            = 0x0101,        ///< Lookup off top of table
+    PS_LOOKUP_PAST_BOTTOM         = 0x0102,        ///< Lookup off bottom of table
+    PS_LOOKUP_ERROR               = 0x0104         ///< Any other type of lookup error
+} psLookupStatusType;
+
+/** Lookup table parse status and error conditions
+ *
+ *  Success, failure, and status conditions for table parsing.
+ */
+typedef enum {
+    PS_PARSE_SUCCESS              = 0x0000,        ///< Table lookup succeeded
+    PS_PARSE_ERROR_TYPE           = 0x0101,        ///< Error parsing type
+    PS_PARSE_ERROR_VALUE          = 0x0102,        ///< Error parsing numerical value
+    PS_PARSE_ERROR_GENERAL        = 0x0104         ///< Any other type of lookup error
+}psParseErrorType;
+
+/** Allocator for psLookupTable struct
+ *
+ *  Allocates a new psLookupTable struct.
+ *
+ *  @return psLookupTable*     New psLookupTable struct.
+ */
+psLookupTable* psLookupTableAlloc(
+    const char *fileName,           ///< Name of file to read
+    psF64 validFrom,                ///< Lower bound for rable read
+    psF64 validTo                   ///< Upper bound for table read
+);
+
+/** Read lookup table
+ *
+ *  Reads a lookup table and fills corresponding psLookupTable struct.
+ *
+ *  @return psLookupTable*     New psLookupTable struct.
+ */
+psLookupTable* psLookupTableRead(
+    psLookupTable *table            ///< Table to read
+);
+
+/** Lookup and interpolate value from table.
+ *
+ *  Interpolates value from table. Sets status bit for success or one of several possible failure
+ *  conditions.
+ *
+ *  @return psLookupTable*     New psLookupTable struct
+ */
+psF64 psLookupTableInterpolate(
+    psLookupTable *table,           ///< Table with data
+    psF64 index,                    ///< Value to be interpolated
+    psU64 column,                   ///< Column in table to be interpolated
+    psLookupStatusType *status      ///< Status of lookup
+);
+
+/** Lookup and interpolate all values from table.
+ *
+ *  Interpolates all values from table. Sets status bit for success or one of several possible failure
+ *  conditions.
+ *
+ *  @return psLookupTable*     New psLookupTable struct
+ */
+psVector* psLookupTableInterpolateAll(
+    psLookupTable *table,           ///< Table with data
+    psF64 index,                    ///< Value to be interpolated
+    psVector *stats                 ///< Vector of status for each lookup
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadata.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadata.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadata.c	(revision 22271)
@@ -0,0 +1,694 @@
+/** @file  psMetadata.c
+*
+*
+*  @brief Contains metadata structures, enumerations and functions prototypes.
+*
+*  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+*  prototypes necessary creating psLib metadata APIs
+*
+*  @ingroup Metadata
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.61.2.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:56 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include<stdio.h>
+#include<stdarg.h>
+#include<string.h>
+
+#include "fitsio.h"
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psAbort.h"
+#include "psList.h"
+#include "psHash.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psLookupTable.h"
+#include "psString.h"
+#include "psAstronomyErrors.h"
+#include "psConstants.h"
+
+static psS32 metadataId = 0;
+
+static psMetadataItem* makeMetaMulti(psHash* table, const char* key, psMetadataItem* existing)
+{
+
+    if (existing != NULL && existing->type == PS_META_MULTI) {
+        return existing;
+    }
+
+
+    psMetadataItem* item = psMetadataItemAlloc(key,
+                           PS_META_MULTI,
+                           "List of Metadata Items",
+                           NULL);
+
+    psListAdd(item->data.list,PS_LIST_TAIL,existing);
+
+    if (existing != NULL) {
+        psHashRemove(table,key); // take out the old entry
+    }
+
+    psHashAdd(table, key, item); // put in the new MULTI list entry
+
+    // free local references of newly allocated item.
+    psFree(item);
+
+    return item;
+}
+
+static void metadataItemFree(psMetadataItem* metadataItem)
+{
+    psMetadataType type;
+
+    type = metadataItem->type;
+
+    if (metadataItem == NULL) {
+        return;
+    }
+
+    psFree(metadataItem->name);
+    psFree(metadataItem->comment);
+    if (! PS_META_IS_PRIMITIVE(type)) {
+        psFree(metadataItem->data.V);
+    }
+}
+
+static void metadataIteratorFree(psMetadataIterator* iter)
+{
+    if (iter == NULL) {
+        return;
+    }
+    psFree(iter->iter);
+
+    if (iter->preg != NULL) {
+        regfree(iter->preg);
+    }
+}
+
+static void metadataFree(psMetadata* metadata)
+{
+    if (metadata == NULL) {
+        return;
+    }
+    psFree(metadata->list);
+    psFree(metadata->table);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psMetadataItem* psMetadataItemAlloc(const char *name, psMetadataType type,
+                                    const char *comment, ...)
+{
+    va_list argPtr;
+    psMetadataItem* metadataItem = NULL;
+
+    // Get the variable list parameters to pass to allocation function
+    va_start(argPtr, comment);
+
+    // Call metadata item allocation
+    metadataItem = psMetadataItemAllocV(name, type, comment, argPtr);
+
+    // Clean up stack after variable arguement has been used
+    va_end(argPtr);
+
+    return metadataItem;
+}
+
+#define METADATAITEM_ALLOC_TYPE(NAME,TYPE,METATYPE) \
+psMetadataItem* psMetadataItemAlloc##NAME(const char* name, \
+        const char* comment, \
+        TYPE value) \
+{ \
+    return psMetadataItemAlloc(name, METATYPE, comment, value); \
+}
+
+METADATAITEM_ALLOC_TYPE(Str,const char*,PS_META_STR)
+METADATAITEM_ALLOC_TYPE(F32,psF32,PS_META_F32)
+METADATAITEM_ALLOC_TYPE(F64,psF64,PS_META_F64)
+METADATAITEM_ALLOC_TYPE(S32,psS32,PS_META_S32)
+METADATAITEM_ALLOC_TYPE(Bool,psBool,PS_META_BOOL)
+
+psMetadataItem* psMetadataItemAllocV(const char *name, psMetadataType type,
+                                     const char *comment, va_list argPtr)
+{
+    psMetadataItem* metadataItem = NULL;
+    char tmp;
+    int nBytes;
+
+    PS_PTR_CHECK_NULL(name,NULL);
+
+    // Allocate metadata item
+    metadataItem = (psMetadataItem*) psAlloc(sizeof(psMetadataItem));
+    metadataItem->data.V = NULL;
+
+    // Set deallocator
+    psMemSetDeallocator(metadataItem, (psFreeFcn) metadataItemFree);
+
+    // set metadata item comment
+    if (comment == NULL) {
+        // Per SDRS, null isn't allowed, must use "" instead
+        metadataItem->comment = psStringCopy("");
+    } else {
+        metadataItem->comment = psStringCopy(comment);
+    }
+
+    // Set metadata item unique id
+    *(psS32 *)(&metadataItem->id) = ++metadataId;
+
+    // Set metadata item type
+    metadataItem->type = type & PS_METADATA_TYPE_MASK;
+
+    // Allocate and set metadata item name
+    nBytes = vsnprintf(&tmp, 0, name, argPtr) + 1;
+    metadataItem->name = (char *)psAlloc(sizeof(char) * nBytes);
+    vsprintf(metadataItem->name, name, argPtr);
+
+    // Set metadata item value
+    switch(metadataItem->type) {
+    case PS_META_BOOL:
+        metadataItem->data.B = (psBool)va_arg(argPtr, psS32);
+        break;
+    case PS_META_S32:
+        metadataItem->data.S32 = (psS32)va_arg(argPtr, psS32);
+        break;
+    case PS_META_F32:
+        metadataItem->data.F32 = (psF32)va_arg(argPtr, psF64);
+        break;
+    case PS_META_F64:
+        metadataItem->data.F64 = (psF64)va_arg(argPtr, psF64);
+        break;
+    case PS_META_STR:
+        // Perform copy of input strings
+        metadataItem->data.V = psStringCopy(va_arg(argPtr, char *));
+        break;
+    case PS_META_MULTI:
+        // MULTI needs to create a psList entry, value must be NULL
+        metadataItem->data.list = psListAlloc(NULL);
+        break;
+    case PS_META_LIST:
+    case PS_META_VEC:
+    case PS_META_HASH:
+    case PS_META_LOOKUPTABLE:
+    case PS_META_JPEG:
+    case PS_META_PNG:
+    case PS_META_ASTROM:
+    case PS_META_UNKNOWN:
+        // Copy of input data not performed due to variability of data types
+        metadataItem->data.V = psMemIncrRefCounter(va_arg(argPtr, psPtr));
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_METATYPE_INVALID, type);
+        psFree(metadataItem);
+        metadataItem = NULL;
+    }
+
+    return metadataItem;
+}
+
+psMetadata* psMetadataAlloc(void)
+{
+    psList* list = NULL;
+    psHash* table = NULL;
+    psMetadata* metadata = NULL;
+
+    // Allocate metadata
+    metadata = (psMetadata*) psAlloc(sizeof(psMetadata));
+    // Set deallocator
+    psMemSetDeallocator(metadata, (psFreeFcn) metadataFree);
+
+    // Allocate metadata's internal containers
+    list = (psList*) psListAlloc(NULL);
+    table = (psHash*) psHashAlloc(10);
+
+    metadata->list = list;
+    metadata->table = table;
+
+    return metadata;
+}
+
+psBool psMetadataAddItem(psMetadata *md, const psMetadataItem *metadataItem, psS32 location, psS32 flags)
+{
+    char * key = NULL;
+    psHash *mdTable = NULL;
+    psList *mdList = NULL;
+    psMetadataItem *existingEntry = NULL;
+
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->table,NULL);
+    PS_PTR_CHECK_NULL(md->list,NULL);
+    PS_PTR_CHECK_NULL(metadataItem,NULL);
+    PS_PTR_CHECK_NULL(metadataItem->name,NULL);
+
+    mdTable = md->table;
+    mdList = md->list;
+    key = metadataItem->name;
+
+    // See if key is already in table
+    existingEntry = psMetadataLookup(md, key);
+
+    // if replace is set, remove any existing items of the same key
+    if (existingEntry != NULL && (flags & PS_META_REPLACE) != 0) {
+        psMetadataRemove(md,0,key);
+        existingEntry = NULL;
+    }
+
+    // if the metadataItem is MULTI, just create a MULTI node.
+    if (metadataItem->type == PS_META_MULTI) {
+        if (metadataItem->data.list == NULL || metadataItem->data.list->size > 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                    "Specified PS_META_MULTI item is not defined properly "
+                    "(list allocated with zero size).");
+            return false;
+        }
+
+        // make sure the existing entry is PS_META_MULTI
+        existingEntry = makeMetaMulti(mdTable,key,existingEntry);
+
+        return true; // all done.
+    }
+
+    // how the item is added to the hash depends on prior existence, flags, etc.
+    if(existingEntry == NULL) { // no prior existence
+        // Node doesn't already exist - Add new metadata item to metadata collection's hash
+        if(!psHashAdd(mdTable, key, metadataItem)) {
+            psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
+            return false;
+        }
+    } else {
+        if (existingEntry->type == PS_META_MULTI || (flags & PS_META_DUPLICATE_OK) != 0) {
+            // duplicate entries allowed - add another entry.
+
+            // make sure the existing hash entry is PS_META_MULTI
+            existingEntry = makeMetaMulti(mdTable,key,existingEntry);
+
+            // add to the hash key's list of entries
+            if (! psListAdd(existingEntry->data.list, PS_LIST_TAIL, metadataItem) ) {
+                psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
+                return false;
+            }
+        } else {
+            // error on duplicate entry.
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psMetadata_DUPLICATE_NOT_ALLOWED);
+            return false;
+        }
+    }
+
+    // finally, add the metadataItem to the metadata's list.
+    if(!psListAdd(mdList, location, metadataItem)) {
+        psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
+        return false;
+    }
+
+    return true;
+}
+
+psBool psMetadataAdd(psMetadata *md, psS32 location, const char *name,
+                     psS32 type, const char *comment, ...)
+{
+    va_list argPtr;
+
+    va_start(argPtr, comment);
+    psBool result = psMetadataAddV(md,location,name,type,comment,argPtr);
+    va_end(argPtr);
+
+    return result;
+}
+
+psBool psMetadataAddV(psMetadata *md, psS32 location, const char *name,
+                      psS32 type, const char *comment, va_list list)
+{
+    psMetadataItem* metadataItem = NULL;
+
+    metadataItem = psMetadataItemAllocV(name, type & PS_METADATA_TYPE_MASK, comment, list);
+
+    if (!psMetadataAddItem(md, metadataItem, location, type & PS_METADATA_FLAGS_MASK)) {
+        psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_FAILED);
+        psFree(metadataItem);
+        return false;
+    }
+    // Decrement reference count, since the metadata item is now in metadata collection and no longer needed
+    psFree(metadataItem);
+
+    return true;
+}
+
+#define METADATA_ADD_TYPE(NAME,TYPE,METATYPE) \
+psBool psMetadataAdd##NAME(psMetadata* md, psS32 where, const char* name, \
+                           const char* comment, const TYPE value) { \
+    return psMetadataAdd(md,where,name, METATYPE,comment,value); \
+}
+
+METADATA_ADD_TYPE(S32,psS32,PS_META_S32)
+METADATA_ADD_TYPE(F32,psF32,PS_META_F32)
+METADATA_ADD_TYPE(F64,psF64,PS_META_F64)
+METADATA_ADD_TYPE(List,psList*,PS_META_LIST)
+METADATA_ADD_TYPE(Str,const char*,PS_META_STR)
+METADATA_ADD_TYPE(Vector,psVector*,PS_META_VEC)
+METADATA_ADD_TYPE(Image,psImage*,PS_META_IMG)
+METADATA_ADD_TYPE(Hash,psHash*,PS_META_HASH)
+METADATA_ADD_TYPE(LookupTable,psLookupTable*,PS_META_LOOKUPTABLE)
+METADATA_ADD_TYPE(Unknown,void*,PS_META_UNKNOWN)
+
+psBool psMetadataRemove(psMetadata *md, psS32 where, const char *key)
+{
+    PS_PTR_CHECK_NULL(md,NULL);
+
+    PS_PTR_CHECK_NULL(md->list,NULL);
+    psList* mdList = md->list;
+
+    PS_PTR_CHECK_NULL(md->table,NULL);
+    psHash* mdTable = md->table;
+
+    // Select removal by key or index
+    if (key != NULL) {
+        // Remove by key name
+        psMetadataItem* entry = psHashLookup(mdTable,key);
+        if (entry == NULL) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED, key);
+            return false;
+        }
+        if (entry->type == PS_META_MULTI) {
+            psMetadataItem* listItem;
+            psListIterator* iter = psListIteratorAlloc(
+                                       entry->data.list,
+                                       PS_LIST_HEAD,true);
+            while ((listItem=psListGetAndIncrement(iter)) != NULL) {
+                psListRemoveData(mdList, listItem);
+            }
+            psFree(iter);
+            psHashRemove(mdTable,key);
+
+        } else {
+            psListRemoveData(mdList, entry);
+            psHashRemove(mdTable, key);
+        }
+    } else {
+        // Remove by index
+        psMetadataItem* entry = psListGet(mdList, where);
+        if (entry == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_FIND_INDEX_FAILED, where);
+            return false;
+        }
+        key = entry->name;
+
+        if (key == NULL) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_REMOVE_LIST_INDEX_FAILED, where);
+            return false;
+        }
+
+        psMetadataItem* tableItem = psHashLookup(mdTable, key);
+        if (tableItem == NULL) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED, key);
+            return false;
+        }
+
+        if (tableItem->type == PS_META_MULTI) {
+            // multiple entries with same key, remove just the specified one
+            psListRemoveData(tableItem->data.list, entry);
+        } else {
+            if (!psHashRemove(mdTable, key)) {
+                psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadata_REMOVE_TABLE_FAILED, key);
+                return false;
+            }
+        }
+        psListRemove(mdList, where);
+    }
+
+    return true;
+}
+
+psMetadataItem* psMetadataLookup(psMetadata *md, const char *key)
+{
+    psHash* mdTable = NULL;
+    psMetadataItem* entry = NULL;
+
+
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->table,NULL);
+    PS_PTR_CHECK_NULL(key,NULL);
+
+    mdTable = md->table;
+    entry = (psMetadataItem*)psHashLookup(mdTable, key);
+
+    return entry;
+}
+
+void* psMetadataLookupPtr(psBool *status, psMetadata *md, const char *key)
+{
+    psMetadataItem *metadataItem = NULL;
+
+    if (status) {
+        *status = true;
+    }
+
+    metadataItem = psMetadataLookup(md, key);
+    if(metadataItem == NULL) {
+        if (status) {
+            *status = false;
+        }
+        return NULL;
+    }
+    if (metadataItem->type == PS_META_MULTI) {
+        // if multiple keys found, use the first.
+        metadataItem = (psMetadataItem*)((metadataItem->data.list)->head);
+    }
+
+    if(PS_META_IS_PRIMITIVE(metadataItem->type)) {
+        if (status) {
+            *status = false;
+        }
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psMetadata_METATYPE_INVALID,
+                metadataItem->type);
+        return NULL;
+    } else {
+        return metadataItem->data.V;
+    }
+}
+
+#define psMetadataLookupNumTYPE(TYPE) \
+ps##TYPE psMetadataLookup##TYPE(psBool *status, psMetadata *md, const char *key) \
+{ \
+    psMetadataItem *metadataItem = NULL; \
+    ps##TYPE value = 0; \
+    \
+    if (status) { \
+        *status = true; \
+    } \
+    \
+    metadataItem = psMetadataLookup(md, key); \
+    if(metadataItem == NULL) { \
+        if (status) { \
+            *status = false; \
+        } \
+        return 0; \
+    } \
+    if (metadataItem->type == PS_META_MULTI) { \
+        /* if multiple keys found, use the first. */ \
+        metadataItem = (psMetadataItem*)((metadataItem->data.list)->head); \
+    } \
+    \
+    switch (metadataItem->type) { \
+    case PS_META_S32: \
+        value = (ps##TYPE)metadataItem->data.S32; \
+        break; \
+    case PS_META_F32: \
+        value = (ps##TYPE)metadataItem->data.F32; \
+        break; \
+    case PS_META_F64: \
+        value = (ps##TYPE)metadataItem->data.F64; \
+        break; \
+    case PS_META_BOOL: \
+        if (metadataItem->data.B) { \
+            value = 1; \
+        } \
+        break; \
+    default: \
+        /* if you get to this point, the value is not a number. */ \
+        if (status) { \
+            *status = false; \
+        } \
+        break; \
+    } \
+    \
+    /* psFree(metadataItem); currently, the lookup doesn't increment the ref count */ \
+    return value; \
+}
+
+psMetadataLookupNumTYPE(F32)
+psMetadataLookupNumTYPE(F64)
+psMetadataLookupNumTYPE(S32)
+psMetadataLookupNumTYPE(Bool)
+
+psMetadataItem* psMetadataGet(psMetadata *md, psS32 where)
+{
+    psMetadataItem* entry = NULL;
+
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->list,NULL);
+
+    entry = (psMetadataItem*) psListGet(md->list, where);
+    if (entry == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_FIND_INDEX_FAILED, where);
+        return NULL;
+    }
+
+    return entry;
+}
+
+psMetadataIterator* psMetadataIteratorAlloc(psMetadata* md,
+        int location,
+        const char* regex)
+{
+    PS_PTR_CHECK_NULL(md,NULL);
+    PS_PTR_CHECK_NULL(md->list,NULL);
+
+    psMetadataIterator* newIter = psAlloc(sizeof(psMetadataIterator));
+    newIter->preg = NULL;
+    newIter->iter = NULL;
+
+    // Set deallocator
+    psMemSetDeallocator(newIter, (psFreeFcn) metadataIteratorFree);
+
+    if (regex == NULL) {
+        newIter->iter = psListIteratorAlloc(md->list, location, false);
+        return newIter;
+    } else {
+        int regRtn = regcomp(newIter->preg,regex,0);
+        if (regRtn != 0) {
+            char errMsg[256];
+            regerror(regRtn, newIter->preg, errMsg, 256);
+            regfree(newIter->preg);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psMetadata_REGEX_INVALID,
+                    errMsg);
+            psFree(newIter);
+            return NULL;
+        }
+    }
+
+    psMetadataIteratorSet(newIter, location); // XXX: do we error if no match is found?
+
+    return newIter;
+}
+
+psBool psMetadataIteratorSet(psMetadataIterator* iterator,
+                             int location)
+{
+    int match;
+    psMetadataItem* cursor;
+
+    PS_PTR_CHECK_NULL(iterator,NULL);
+
+    psListIterator* iter = iterator->iter;
+    PS_PTR_CHECK_NULL(iterator->iter,NULL);
+
+    regex_t* preg = iterator->preg;
+
+    // handle trivial case where no regex subsetting is required.
+    if (preg == NULL) {
+        return psListIteratorSet(iter,location);
+    }
+
+    if (location < 0) {
+        // match from the tail
+        match = 0;
+        psListIteratorSet(iter,PS_LIST_TAIL);
+        while ( (cursor=(psMetadataItem*)iter->cursor) != NULL) {
+            if (regexec(preg, cursor->name, 0, NULL, 0) == 0) {
+                // this key is a match
+                match--;
+                if (match == location) {
+                    break;
+                }
+            }
+            (void)psListGetAndDecrement(iter);
+        }
+        return (match == location);
+    }
+
+    // find the n-th match from the head
+    match = -1;
+    psListIteratorSet(iter,PS_LIST_HEAD);
+    while ( (cursor=(psMetadataItem*)iter->cursor) != NULL) {
+        if (regexec(preg, cursor->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            match++;
+            if (match == location) {
+                break;
+            }
+        }
+        (void)psListGetAndIncrement(iter);
+    }
+    return (match == location);
+}
+
+psMetadataItem* psMetadataGetAndIncrement(psMetadataIterator* iterator)
+{
+    psMetadataItem* oldValue;
+
+    PS_PTR_CHECK_NULL(iterator,NULL);
+
+    psListIterator* iter = iterator->iter;
+    PS_PTR_CHECK_NULL(iterator->iter,NULL);
+
+    regex_t* preg = iterator->preg;
+
+    // handle trivial case where no regex subsetting is required.
+    if (preg == NULL) {
+        return (psMetadataItem*)psListGetAndIncrement(iter);
+    }
+
+    oldValue = (psMetadataItem*)iter->cursor;
+
+    while (psListGetAndIncrement(iter) != NULL) {
+        if (iter->cursor != NULL &&
+                regexec(preg, ((psMetadataItem*)iter->cursor)->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            break;
+        }
+    }
+    return oldValue;
+}
+
+psMetadataItem* psMetadataGetAndDecrement(psMetadataIterator* iterator)
+{
+    psMetadataItem* oldValue;
+
+    PS_PTR_CHECK_NULL(iterator,NULL);
+
+    psListIterator* iter = iterator->iter;
+    PS_PTR_CHECK_NULL(iterator->iter,NULL);
+
+    regex_t* preg = iterator->preg;
+
+    // handle trivial case where no regex subsetting is required.
+    if (preg == NULL) {
+        return (psMetadataItem*)psListGetAndDecrement(iter);
+    }
+
+    oldValue = (psMetadataItem*)iter->cursor;
+
+    while (psListGetAndDecrement(iter) != NULL) {
+        if (iter->cursor != NULL &&
+                regexec(preg, ((psMetadataItem*)iter->cursor)->name, 0, NULL, 0) == 0) {
+            // this key is a match
+            break;
+        }
+    }
+    return oldValue;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadata.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadata.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadata.h	(revision 22271)
@@ -0,0 +1,479 @@
+/** @file  psMetadata.h
+*
+*  @brief Contains metadata struuctures, enumerations and functions prototypes
+*
+*  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
+*  prototypes necessary creating psLib metadata APIs
+*
+*  @ingroup Metadata
+*
+*  @author Robert DeSonia, MHPCC
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.44.4.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-19 01:09:56 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+#ifndef PS_METADATA_H
+#define PS_METADATA_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#include "psHash.h"
+#include "psList.h"
+#include "psImage.h"
+#include "psLookupTable.h"
+
+/// @addtogroup Metadata
+/// @{
+
+/** Metadata item type.
+ *
+ * Enumeration for maintaining metadata item types.
+ */
+typedef enum {
+    PS_META_S32 = PS_TYPE_S32,         ///< psS32 primitive data.
+    PS_META_F32 = PS_TYPE_F32,         ///< psF32 primitive data.
+    PS_META_F64 = PS_TYPE_F64,         ///< psF64 primitive data.
+    PS_META_BOOL = PS_TYPE_BOOL,       ///< psBool primitive data.
+    PS_META_LIST = 0x10000,            ///< List data (Stored as item.data.list).
+    PS_META_STR,                       ///< String data (Stored as item.data.V).
+    PS_META_VEC,                       ///< Vector data (Stored as item.data.V).
+    PS_META_IMG,                       ///< Image data (Stored as item.data.V).
+    PS_META_HASH,                      ///< Hash data (Stored as item.data.V).
+    PS_META_LOOKUPTABLE,               ///< Lookup table data (Stored as item.data.V).
+    PS_META_JPEG,                      ///< JPEG data (Stored as item.data.V).
+    PS_META_PNG,                       ///< PNG data (Stored as item.data.V).
+    PS_META_ASTROM,                    ///< Astrometric coefficients (Stored as item.data.V).
+    PS_META_UNKNOWN,                   ///< Other data (Stored as item.data.V).
+    PS_META_MULTI                      ///< Used internally, do not create an metadata item of this type.
+} psMetadataType;
+
+#define PS_META_IS_PRIMITIVE(TYPE) \
+(TYPE == PS_META_S32 || \
+ TYPE == PS_META_F32 || \
+ TYPE == PS_META_F64 || \
+ TYPE == PS_META_BOOL)
+
+#define PS_META_PRIMITIVE_TYPE(METATYPE) ( \
+        (METATYPE==PS_META_S32) ? PS_TYPE_S32 : \
+        (METATYPE==PS_META_F32) ? PS_TYPE_F32 : \
+        (METATYPE==PS_META_F64) ? PS_TYPE_F64 : \
+        (METATYPE==PS_META_BOOL) ? PS_TYPE_BOOL : 0 )
+
+/** Option flags for psMetadata functions
+ *
+ *  Enumeration for the modification of the behaviour in psMetadataAddItem.
+ * 
+ *  @see psMetadataAddItem
+ */
+typedef enum {
+    PS_META_DEFAULT = 0,               ///< default behaviour (duplicate entry is an error)
+    PS_META_REPLACE = 0x1000000,       ///< allow entry to be replaced
+    PS_META_DUPLICATE_OK = 0x2000000   ///< allow duplicate entries
+} psMetadataFlags;
+
+#define PS_METADATA_FLAGS_MASK 0xFF000000
+#define PS_METADATA_TYPE_MASK 0x00FFFFFF
+
+/** Metadata data structure.
+ *
+ *  Struct for holding metadata items. Metadata items are held in two
+ *  containers. The first employs a doubly-linked list to preserve the order
+ *  of the metadata. The second container employs a hash table which
+ *  allows fast lookup when given a metadata keyword.
+ */
+typedef struct psMetadata
+{
+    psList*  list;                     ///< Metadata in linked-list
+    psHash*  table;                    ///< Metadata in a hash table
+}
+psMetadata;
+
+/** Metadata iterator
+ *
+ *  Iterator for metadata.
+ */
+typedef struct
+{
+    psListIterator* iter;              ///< iterator for the psMetadata's psList
+    regex_t* preg;                     ///< the subsetting regular expression
+}
+psMetadataIterator;
+
+
+/** Metadata item data structure.
+ *
+ * Struct for maintaining metadata items of varying types. It also contains
+ * information about the item name, flags, comments, and other items with the same name.
+ */
+typedef struct psMetadataItem
+{
+    const psS32 id;                    ///< Unique ID for metadata item.
+    char *name;                        ///< Name of metadata item.
+    psMetadataType type;               ///< Type of metadata item.
+    union {
+        psBool B;                      ///< boolean data
+        psS32 S32;                     ///< Signed 32-bit integer data.
+        psF32 F32;                     ///< Single-precision float data.
+        psF64 F64;                     ///< Double-precision float data.
+        psList *list;                  ///< List data.
+        psMetadata *md;                ///< Metadata data.
+        psPtr V;                       ///< Pointer to other type of data.
+    } data;                            ///< Union for data types.
+    char *comment;                     ///< Optional comment ("", not NULL).
+}
+psMetadataItem;
+
+/** Create a metadata item.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct. The name argument specifies the name to use for this item, and
+ *  may include sprintf formatting codes. The format entry specifies both
+ *  the metadata type and optional flags and is created by bit-wise or of the
+ *  appropriate type and flag. The comment argument is a fixed string used to
+ *  comment the metadata item. The arguments to the name formatting codes and
+ *  the metadata itself are passed as arguments following the comment string.
+ *  The data must be a pointer for any of the elements stored in data.void.
+ *  The argument list must be interpreted appropriately by the va_list
+ *  operators in the function specified size and type.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAlloc(
+    const char *name,                  ///< Name of metadata item.
+    psMetadataType type,               ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+);
+
+/** Create a metadata item with specified string data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocStr(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    const char* value                  ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psF32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocF32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psF32 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psF64 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocF64(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psF64 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psS32 data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocS32(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psS32 value                        ///< the value of the metadata item.
+);
+
+/** Create a metadata item with specified psBool data.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocBool(
+    const char* name,                  ///< Name of metadata item.
+    const char* comment,               ///< Comment for metadata item.
+    psBool value                       ///< the value of the metadata item.
+);
+
+#ifndef SWIG
+/** Create a metadata item with va_list.
+ *
+ *  Returns a fill psMetadataItem ready for insertion into the psMetadata
+ *  struct. The name argument specifies the name to use for this item, and
+ *  may include sprintf formatting codes. The format entry specifies both
+ *  the metadata type and optional flags and is created by bit-wise or of the
+ *  appropriate type and flag. The comment argument is a fixed string used to
+ *  comment the metadata item. The arguments to the name formatting codes and
+ *  the metadata itself are passed as arguments following the comment string.
+ *  The data must be a pointer for any of the elements stored in data.void.
+ *  The argument list must be interpreted appropriately by the va_list
+ *  operators in the function specified size and type.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataItemAllocV(
+    const char *name,                  ///< Name of metadata item.
+    psMetadataType type,               ///< Type of metadata item.
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#endif
+
+/** Create a metadata collection.
+ *
+ *  Returns an empty metadata container with fully allocated internal metadata
+ *  containers.
+ *
+ *  @return psMetadata* : Pointer metadata.
+ */
+psMetadata* psMetadataAlloc(void);
+
+/** Add existing metadata item to metadata collection.
+ *
+ *  Add a metadata item that has already been created to the metadata
+ *  collection.
+ *
+ *  @return bool: True for success, false for failure.
+ */
+psBool psMetadataAddItem(
+    psMetadata*  md,                   ///< Metadata collection to insert metadat item.
+    const psMetadataItem*  item, ///< Metadata item to be added.
+    psS32 location,                    ///< Location to be added.
+    psS32 flags                        ///< Options flag mask, see psMetadataFlags enum
+);
+
+/** Create and add a metadata item to metadata collection.
+ *
+ * Creates a new metadata item add to the metadata collection.
+ *
+ * @return bool: True for success, false for failure.
+ */
+psBool psMetadataAdd(
+    psMetadata* md,                    ///< Metadata collection to insert metadata item.
+    psS32 location,                    ///< Location to be added.
+    const char *name,                  ///< Name of metadata item.
+    int type,                          ///< Type of metadata item (psMetadataType) and options (psMetadataFlags)
+    const char *comment,               ///< Comment for metadata item.
+    ...                                ///< Arguments for name formatting and metadata item data.
+);
+
+#ifndef SWIG
+/** Create and add a metadata item to metadata collection.
+ *
+ * Creates a new metadata item add to the metadata collection.
+ *
+ * @return bool: True for success, false for failure.
+ */
+psBool psMetadataAddV(
+    psMetadata* md,                    ///< Metadata collection to insert metadat item.
+    psS32 location,                    ///< Location to be added.
+    const char *name,                  ///< Name of metadata item.
+    int type,                          ///< Type of metadata item (psMetadataType) and options (psMetadataFlags)
+    const char *comment,               ///< Comment for metadata item.
+    va_list list                       ///< Arguments for name formatting and metadata item data.
+);
+#endif
+
+psBool psMetadataAddS32(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, psS32 value);
+psBool psMetadataAddF32(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, psF32 value);
+psBool psMetadataAddF64(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, psF64 value);
+psBool psMetadataAddList(psMetadata* md, psS32 location, const char* name,
+                         const char* comment, const psList* value);
+psBool psMetadataAddStr(psMetadata* md, psS32 location, const char* name,
+                        const char* comment, const char* value);
+psBool psMetadataAddVector(psMetadata* md, psS32 location, const char* name,
+                           const char* comment, const psVector* value);
+psBool psMetadataAddImage(psMetadata* md, psS32 location, const char* name,
+                          const char* comment, const psImage* value);
+psBool psMetadataAddHash(psMetadata* md, psS32 location, const char* name,
+                         const char* comment, const psHash* value);
+psBool psMetadataAddLookupTable(psMetadata* md, psS32 location, const char* name,
+                                const char* comment, const psLookupTable* value);
+psBool psMetadataAddUnknown(psMetadata* md, psS32 location, const char* name,
+                            const char* comment, const psPtr value);
+
+/** Remove an item from metadata collection.
+ *
+ *  Items may be removed from metadata by specifing a key or location. If the
+ *  name is null, the where argument is used instead. If name is not null,
+ *  where is set to PS_LIST_UNKNOWN. If the item is found, it is removed from
+ *  the metadata and true is returned.  If the key is not unique, then all
+ *  items corresponding to it are removed.
+ *
+ * @return bool: True for success, false for failure.
+ */
+psBool psMetadataRemove(
+    psMetadata*  md,           ///< Metadata collection to remove metadata item.
+    psS32 where,               ///< Location to be removed.
+    const char * key           ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the first item is returned. If the item is not found, null is
+ *  returned.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataLookup(
+    psMetadata * md,                   ///< Metadata collection to lookup metadata item.
+    const char * key                   ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its double precision value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psF64 : Value of metadata item.
+ */
+psF64 psMetadataLookupF64(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its single precision value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psF32 : Value of metadata item.
+ */
+psF32 psMetadataLookupF32(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psS32 : Value of metadata item.
+ */
+psS32 psMetadataLookupS32(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its boolean value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return psBool : Value of metadata item.
+ */
+psBool psMetadataLookupBool(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata *md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on key name and return its integer value.
+ *
+ *  Items may be found in the metadata by providing a key. If the key is
+ *  non-unique, the value of the first item is returned. If the item is not found, zero is
+ *  returned.
+ *
+ * @return void* : Value of metadata item.
+ */
+psPtr psMetadataLookupPtr(
+    psBool *status,                    ///< Status of lookup.
+    psMetadata* md,                    ///< Metadata collection to lookup metadata item.
+    const char *key                    ///< Name of metadata key.
+);
+
+/** Find an item in the metadata collection based on list index.
+ *
+ *  Items may be found in the metadata by their entry position in the list
+ *  container.
+ *
+ *  @return psMetadataItem* : Pointer metadata item.
+ */
+psMetadataItem* psMetadataGet(
+    psMetadata*  md,                   ///< Metadata collection to insert metadat item.
+    psS32 location                     ///< Location to be retrieved.
+);
+
+/** Creates a psMetadataIterator to iterate over the specified psMetadata.
+ *
+ *  Supports the subsetting of the metadata via keyword using regular
+ *  expression.  If no regular expression is specified, iteration
+ *  over the entire psMetadata is performed.
+ *
+ *  @return psMetadataIterator*        a new psMetadataIterator, of NULL if error occurred
+ */
+psMetadataIterator* psMetadataIteratorAlloc(
+    psMetadata* md,                    ///< the psMetadata to iterate with
+    int location,                      ///< the initial starting point (after subsetting).
+    const char* regex
+    ///< A regular expression for subsetting the psMetadata.  If NULL, no
+    ///< subsetting is performed.
+);
+
+/** Set the iterator of the psMetadat to a given position.  If location is
+ *  invalid the iterator position is not changed.
+ *
+ *  @return psBool        TRUE if iterator successfully set, otherwise FALSE.
+*/
+psBool psMetadataIteratorSet(
+    psMetadataIterator* iterator,      ///< psMetadata iterator
+    int location                       ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
+);
+
+/** Position the specified iterator to the next matching item in psMetadata,
+ *  given the regular expression of the iterator
+ *
+ *  @return psPtr       the psMetadataItem at the original iterator position
+ *                      or NULL if the iterator went past the end of the list.
+ */
+psMetadataItem* psMetadataGetAndIncrement(
+    psMetadataIterator* iterator           ///< iterator to move
+);
+
+/** Position the specified iterator to the previous matching item in psMetadata,
+ *  given the regular expression of the iterator
+ *
+ *  @return psPtr       the psMetadataItem at the original iterator position
+ *                      or NULL if the iterator went past the beginning of the
+ *                      list.
+ */
+psMetadataItem* psMetadataGetAndDecrement(
+    psMetadataIterator* iterator           ///< iterator to move
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadataConfig.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadataConfig.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadataConfig.c	(revision 22271)
@@ -0,0 +1,1168 @@
+/** @file  psMetadataIO.c
+*
+*  @brief Contains metadata input/output functions.
+*
+*  This file defines functions to read and write metadata to/from an external file.
+*
+*  @ingroup Metadata
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-26 19:53:30 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <libxml/parser.h>
+#include <fitsio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "psAbort.h"
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psString.h"
+#include "psList.h"
+#include "psHash.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psMetadataIO.h"
+#include "psConstants.h"
+#include "psAstronomyErrors.h"
+
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Check for FITS errors */
+#define FITS_ERROR(STRING,PS_ERROR)                                                                          \
+fits_get_errstatus(status, fitsErr);                                                                         \
+psError(PS_ERR_IO,true, STRING, PS_ERROR, fitsErr);                                                          \
+status = 0;                                                                                                  \
+fits_close_file(fd, &status);                                                                                \
+if(status){                                                                                                  \
+    fits_get_errstatus(status, fitsErr);                                                                     \
+    psError(PS_ERR_IO,true, "Couldn't close FITS file. FITS error: %s", fitsErr);                            \
+}                                                                                                            \
+status = 0;                                                                                                  \
+psFree(output);                                                                                              \
+return NULL;
+
+/** Free and null temporary variables used by config file parser */
+#define CLEAR_TEMPS()                                                                                        \
+if(strName) {                                                                                                \
+    psFree(strName);                                                                                         \
+    strName = NULL;                                                                                          \
+}                                                                                                            \
+if(strType) {                                                                                                \
+    psFree(strType);                                                                                         \
+    strType = NULL;                                                                                          \
+}                                                                                                            \
+if(strValue) {                                                                                               \
+    psFree(strValue);                                                                                        \
+    strValue = NULL;                                                                                         \
+}                                                                                                            \
+if(strComment) {                                                                                             \
+    psFree(strComment);                                                                                      \
+    strComment = NULL;                                                                                       \
+}
+
+/** Maximum size of a FITS line */
+#define FITS_LINE_SIZE 80
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 256
+
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static void saxEndElement(void *ctx, const xmlChar *tagName);
+static void initVectorXml(void *ctx, char *tagName);
+static void initMetadataItemXml(void *ctx, char *tagName);
+static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts);
+
+/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+ *  must be null terminated. */
+psBool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+char *cleanString(char *inString, psS32 sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+/** Count repeat occurances of a single character within a line. The input string must be null terminated. */
+psS32 repeatedChars(char *inString, char ch)
+{
+    psS32 count = 0;
+
+
+    while(*inString!='\0') {
+        if(*inString == ch) {
+            count++;
+        }
+        inString++;
+    }
+
+    return count;
+}
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+char* getToken(char **inString, char *delimiter, psS32 *status)
+{
+    char *cleanToken = NULL;
+    psS32 sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = 1;
+    }
+
+    return cleanToken;
+}
+
+/** Returns single parsed value as a double precision number. The input string must be cleaned and null
+ * terminated. */
+double parseValue(char *inString, psS32 *status)
+{
+    char *end = NULL;
+    double value = 0.0;
+
+
+    value = strtod(inString, &end);
+    if(*end != '\0') {
+        *status = 1;
+    } else if(inString==end) {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns true or false. 'T', 't', '1', 'F', 'f', and '0' are acceptable, parsable variations. */
+psBool parseBool(char *inString, psS32 *status)
+{
+    psBool value = false;
+
+
+    if(*inString=='T' || *inString=='t' || *inString=='1') {
+        value = true;
+    } else if(*inString=='F' || *inString=='f' || *inString=='0') {
+        value = false;
+    } else {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns parsed vector filled with with data. The input string must be null terminated. */
+psVector* parseVector(char *inString, psElemType elemType, psS32 *status)
+{
+    char *end = NULL;
+    char *saveValue = NULL;
+    psS32 i = 0;
+    psS32 numValues = 0;
+    double value = 0.0;
+    psVector *vec = NULL;
+
+
+    // Cycle through string and count entries
+    saveValue = inString;
+    while(*inString!='\0') {
+        strtod(inString, &end);
+        if(inString==end) {
+            *status = 1;
+            return NULL;
+        }
+        while(*end==' ' || *end==',') { // Commas or spaces may be used as delimiters for vector values
+            end++;
+        }
+        inString=end;
+        numValues++;
+    }
+
+    // Cycle through string and convert string values to values
+    if(numValues) {
+        inString = saveValue;
+        end = NULL;
+        vec = psVectorAlloc(numValues, elemType);
+
+        while(*inString!='\0') {
+            value = strtod(inString, &end);
+            if(inString==end) {
+                *status = 1;
+                return vec;
+            }
+            switch(elemType) {
+            case PS_TYPE_U8:
+                vec->data.U8[i++] = (psU8)value;
+                break;
+            case PS_TYPE_S32:
+                vec->data.S32[i++] = (psS32)value;
+                break;
+            case PS_TYPE_F32:
+                vec->data.F32[i++] = (psF32)value;
+                break;
+            case PS_TYPE_F64:
+                vec->data.F64[i++] = (psF64)value;
+                break;
+            default:
+                *status = 1;
+                psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                        PS_ERRORTEXT_psMetadataIO_TYPE_INVALID,
+                        elemType);
+            }
+
+            while(*end==' ' || *end==',') {
+                end++;
+            }
+            inString=end;
+        }
+    }
+
+    return vec;
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+bool psMetadataItemPrint(FILE * fd, const char *format, const psMetadataItem* metadataItem)
+{
+    psMetadataType type;
+    psBool success = true;
+
+    PS_PTR_CHECK_NULL(fd, success);
+    PS_PTR_CHECK_NULL(format, success);
+    PS_PTR_CHECK_NULL(metadataItem, success);
+
+    type = metadataItem->type;
+
+    // determining the format type
+    char* fType = strchr(format,'%');
+    if (fType == NULL) {
+        // well, the format contains no reference to the metadataItem's data:
+        // that is truly trival to do!
+        fprintf(fd,format);
+        return success;
+    }
+
+    // skip over any format modifiers
+    const char* formatEnd = format+strlen(format);
+    while ( (fType < formatEnd) &&
+        (strchr(" +-01234567890.$#, hlL",*(++fType)) != NULL) ) {}
+
+    #define METADATAITEM_NUMERIC_CAST(FORMAT_TYPE) { \
+        switch(type) { \
+        case PS_META_BOOL: \
+            fprintf(fd, format, (FORMAT_TYPE) metadataItem->data.B); \
+            break; \
+        case PS_META_S32: \
+            fprintf(fd,format,(FORMAT_TYPE)  metadataItem->data.S32); \
+            break; \
+        case PS_META_F32: \
+            fprintf(fd, format,(FORMAT_TYPE)  metadataItem->data.F32); \
+            break; \
+        case PS_META_F64: \
+            fprintf(fd, format,(FORMAT_TYPE) metadataItem->data.F64); \
+            break; \
+        default: \
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true, \
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, (int)type); \
+            success = false; \
+        } \
+    }
+
+    switch(*fType) {
+    case 'd':
+    case 'i':
+    case 'c':
+        METADATAITEM_NUMERIC_CAST(int)
+        break;
+    case 'o':
+    case 'u':
+    case 'x':
+    case 'X':
+        METADATAITEM_NUMERIC_CAST(unsigned int)
+        break;
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+    case 'a':
+    case 'A':
+        METADATAITEM_NUMERIC_CAST(double)
+        break;
+    case 's':
+        if (type == PS_META_STR) {
+            fprintf(fd,format,(char*)metadataItem->data.V);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true,
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, (int)type);
+            success = false;
+        }
+        break;
+    case 'p':
+        fprintf(fd,format,metadataItem->data.V);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psMetadata_FORMAT_INVALID, *fType);
+        break;
+    }
+
+    return success;
+}
+
+
+psMetadata* psMetadataReadHeader(psMetadata* output, char *extName, psS32 extNum, char *fileName)
+{
+    psBool tempBool;
+    psBool success;
+    char keyType;
+    char keyName[FITS_LINE_SIZE];
+    char keyValue[FITS_LINE_SIZE];
+    char keyComment[FITS_LINE_SIZE];
+    char fitsErr[MAX_STRING_LENGTH];
+    psS32 i;
+    psS32 hduType = 0;
+    psS32 status = 0;
+    psS32 numKeys = 0;
+    psS32 keyNum = 0;
+    fitsfile *fd = NULL;
+
+    PS_PTR_CHECK_NULL(fileName,NULL);
+
+    fits_open_file(&fd, fileName, READONLY, &status);
+    if(fd == NULL || status != 0) {
+        FITS_ERROR("FITS error while opening file: %s %s", fileName);
+        return NULL;
+    }
+
+    if (extName == NULL && extNum < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psMetadataIO_EXTNUM_NOTPOSITIVE,
+                extNum);
+        return NULL;
+    }
+
+    // Allocate metadata if user didn't
+    if (output == NULL) {
+        output = psMetadataAlloc();
+    }
+
+    // Move to user designated HDU number or HDU name in FITS file. HDU numbers starts at one.
+    if (extName != NULL) {
+        if (fits_movnam_hdu(fd, ANY_HDU, extName, 0, &status) != 0) {
+            FITS_ERROR("FITS error while locating header %s: %s", extName);
+        }
+    } else {
+        if (fits_movabs_hdu(fd, extNum, &hduType, &status) != 0) {
+            FITS_ERROR("FITS error while locating header %d: %s", extNum);
+        }
+    }
+
+    // Get number of key names
+    if (fits_get_hdrpos(fd, &numKeys, &keyNum, &status) != 0) {
+        FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+    }
+
+    // Get each key name. Keywords start at one.
+    for (i = 1; i <= numKeys; i++) {
+        if (fits_read_keyn(fd, i, keyName, keyValue, keyComment, &status) != 0) {
+            FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+        }
+        if (fits_get_keytype(keyValue, &keyType, &status) != 0) {
+            fits_get_errstatus(status, fitsErr);
+            if (status != VALUE_UNDEFINED) {
+                FITS_ERROR("FITS error while determining key %d type: %s", keyNum);
+            } else {
+                // Some keywords are still valid if they don't have a type (like COMMENTS and HISTORY)
+                keyType = 'C';
+                status = 0;
+            }
+        }
+
+        switch (keyType) {
+        case 'I':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_S32 | PS_META_DUPLICATE_OK,
+                                    keyComment, atoi(keyValue));
+            break;
+        case 'F':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_F64 | PS_META_DUPLICATE_OK,
+                                    keyComment, atof(keyValue));
+            break;
+        case 'C':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_STR | PS_META_DUPLICATE_OK,
+                                    keyComment, keyValue);
+            break;
+        case 'L':
+            tempBool = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_BOOL | PS_META_DUPLICATE_OK,
+                                    keyComment, tempBool);
+            break;
+        case 'U':
+        case 'X':
+        default:
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FITS_METATYPE_INVALID, keyType);
+            return output;
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadataIO_ADD_FAILED, keyName);
+            return output;
+        }
+    }
+
+    return output;
+}
+
+psMetadata* psMetadataParseConfig(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
+{
+    psBool tempBool;
+    char *line = NULL;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *strComment = NULL;
+    char *linePtr = NULL;
+    psElemType vecType = 0;
+    psS32 status = 0;
+    psU32 lineCount = 0;
+    psF64 tempDbl = 0.0;
+    psS32 tempInt = 0.0;
+    psVector *tempVec = NULL;
+    FILE *fp = NULL;
+    psMetadataType mdType;
+    psMetadataFlags flags;
+    psBool addStatus;
+
+    // Check for nulls
+    PS_PTR_CHECK_NULL(fileName,NULL);
+    if((fp=fopen(fileName, "r")) == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
+        return NULL;
+    }
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        md = psMetadataAlloc();
+    }
+
+    // Create reusable line for continuous read
+    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+    // While loop to parse the file
+    while(fgets(line, MAX_STRING_LENGTH, fp) != NULL) {
+
+        // Initialize variables for new line
+        linePtr = line;
+        lineCount++;
+        CLEAR_TEMPS();
+
+        // If line is not a comment or blank, then extract data
+        if(!ignoreLine(linePtr)) {
+
+            // Check for more than one '*' or '@' in a line
+            if(repeatedChars(linePtr, '@') > 1) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
+                continue;
+            } else if(repeatedChars(linePtr, '*') > 1) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '*', lineCount, fileName);
+                continue;
+            } else if(repeatedChars(linePtr, '~') > 0) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '~', lineCount, fileName);
+                continue;
+            }
+
+            // Get metadata item name
+            strName = getToken(&linePtr, " ", &status);
+            if(strName==NULL || status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "name", lineCount, fileName);
+                continue;
+            }
+
+            flags = (overwrite) ? PS_META_REPLACE : PS_META_DEFAULT;
+
+            // Get the metadata item type
+            strType = getToken(&linePtr, " ", &status);
+            if(strType==NULL) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
+                        fileName);
+                continue;
+            } else {
+                char* tempStrType = strType;
+                if(*strType == '*') {
+                    flags = PS_META_DUPLICATE_OK;
+                    tempStrType = strType+1;
+                }
+
+                if(!strncmp(tempStrType, "STR", 3)) {
+                    mdType = PS_META_STR;
+                } else if(!strncmp(tempStrType, "BOOL", 4)) {
+                    mdType = PS_META_BOOL;
+                } else if(!strncmp(tempStrType, "S32", 3)) {
+                    mdType = PS_META_S32;
+                } else if(!strncmp(tempStrType, "F32", 3)) {
+                    mdType = PS_META_F32;
+                } else if(!strncmp(tempStrType, "F64", 3)) {
+                    mdType = PS_META_F64;
+                } else {
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineCount,
+                            fileName);
+                    continue;
+                }
+            }
+
+            if(*strName == '@') {
+                vecType = PS_META_PRIMITIVE_TYPE(mdType);
+                mdType = PS_META_VEC;
+            }
+
+            // Get the metadata item value if there is one.
+            strValue = getToken(&linePtr, "#", &status);
+            if(status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                        fileName);
+                continue;
+            }
+            if(strValue==NULL) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                        fileName);
+                continue;
+            }
+
+            // Not all lines will have comments, so NULL is ok.
+            strComment = getToken(&linePtr,"~", &status);
+            if(status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                        fileName);
+                continue;
+            }
+
+            // Create and add metadata item to metadata and parse values
+            switch (mdType) {
+            case PS_META_STR:
+                addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                          mdType | flags,
+                                          strComment, strValue);
+                break;
+            case PS_META_VEC:
+                tempVec = parseVector(strValue, vecType, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName+1,
+                                              mdType | flags,
+                                              strComment, tempVec);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                psFree(tempVec);
+                break;
+            case PS_META_BOOL:
+                tempBool = parseBool(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempBool);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                break;
+            case PS_META_S32:
+                tempInt = (psS32)parseValue(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempInt);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                break;
+            case PS_META_F32:
+            case PS_META_F64:
+                tempDbl = parseValue(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempDbl);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true,
+                            PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType, lineCount,
+                            fileName);
+                    continue;
+                }
+                break;
+            default:
+                (*nFail)++;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, mdType, lineCount,
+                        fileName);
+                continue;
+            } // switch
+            if (! addStatus) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineCount,
+                        fileName);
+            }
+
+        } // if ignoreLine
+    } // while loop
+
+    psFree(line);
+
+    return md;
+}
+
+static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts)
+{
+    psU64 i = 0;
+    char* psTagName = NULL;
+    char *psAttName = NULL;
+    char *psAttValue = NULL;
+    const xmlChar *attName = NULL;
+    const xmlChar *attValue = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+
+    // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+    psTagName = psStringCopy((const char*)tagName);
+
+    // Metadata containter for housing element attributes used by other SAX events
+    htAtts = psHashAlloc(10);
+
+
+    // Get tag name
+    if(psTagName != NULL) {
+        psHashAdd(htAtts, "tagName", psTagName);
+    } else {
+        PS_PTR_CHECK_NULL_GENERAL(psTagName, return);
+        psFree(htAtts);
+        psFree(psTagName);
+        return;
+    }
+
+    // Get all attribute names and attribute values
+    if(atts != NULL) {
+        attName = atts[i++];
+        attValue = atts[i++];
+        while(attName != NULL) {
+            if(attValue != NULL) {
+
+                // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+                psAttName = psStringCopy((const char*)attName);
+                psAttValue = psStringCopy((const char*)attValue);
+                psHashAdd(htAtts, psAttName, psAttValue);
+                psFree(psAttName);
+                psFree(psAttValue);
+            } else {
+                PS_PTR_CHECK_NULL_GENERAL(psAttValue, return);
+                psFree(htAtts);
+                psFree(psTagName);
+                return;
+            }
+            attName = atts[i++];
+            attValue = atts[i++];
+        }
+    }
+
+    // Add attributes to metadata
+
+    psMetadataAdd(md, PS_LIST_TAIL, "htAtts",
+                  PS_META_HASH | PS_META_DUPLICATE_OK,
+                  NULL, htAtts);
+
+    psFree(psTagName);
+    psFree(htAtts);
+
+    return;
+}
+
+static void initMetadataItemXml(void *ctx, char *tagName)
+{
+    psBool overwrite = false;
+    psBool tempBool = false;
+    psS32 status = 0;
+    psU32 lineNumber = 0;
+    psF64 tempDbl = 0.0;
+    psS32 tempInt = 0.0;
+    psMetadataType mdType = PS_META_UNKNOWN;
+    char *fileName = NULL;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    psMetadataItem *metadataItem = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    metadataItem = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(metadataItem, return);
+    PS_PTR_CHECK_NULL_GENERAL(metadataItem->data.list, return);
+    metadataItem = (psMetadataItem*)psListGet(metadataItem->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)metadataItem->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+    fileName = (char*)input->filename;
+    PS_PTR_CHECK_NULL_GENERAL(fileName, return);
+    lineNumber = input->line;
+
+    // Get attribute name
+    strName = psHashLookup(htAtts, "name");
+    if(strName == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_NO_NAME, lineNumber, fileName);
+        return;
+    }
+
+    // Get attribute type, if there is one
+    strType = psHashLookup(htAtts, "psType");
+    if(strType!= NULL) {
+        if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psString")) {
+            mdType = PS_META_STR;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psBool")) {
+            mdType = PS_META_BOOL;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psS32")) {
+            mdType = PS_META_S32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF32")) {
+            mdType = PS_META_F32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF64")) {
+            mdType = PS_META_F64;
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE, strType, lineNumber,
+                    fileName);
+            return;
+        }
+    }
+
+    // Get attribute value, if there is one
+    strValue = psHashLookup(htAtts, "value");
+
+    /* If metadata item is found, and is not a folder node, and overwrite is allowed, then remove
+    existing and allow switch/case below to add new item. If overwrite is false, then report error. If
+    found item is folder node, then psMetadataAdd will automatically add a new child. */
+    metadataItem = psMetadataLookup(md, "overwrite");
+    if(metadataItem != NULL) {
+        overwrite = parseBool((char*)strValue, &status);
+        if(status) {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        metadataItem = psMetadataLookup(md, strName);
+        if(metadataItem != NULL) {
+            if(metadataItem->type != PS_META_LIST) {
+                if(overwrite) {
+                    psMetadataRemove(md, INT_MIN, strName);
+                } else {
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineNumber,
+                            fileName);
+                    return;
+                }
+            }
+        }
+    }
+
+    // Create metadata item and add to metadata
+    switch(mdType) {
+    case PS_META_LIST:
+        psMetadataAdd(md, PS_LIST_TAIL, strName,
+                      mdType | PS_META_DUPLICATE_OK,
+                      NULL, NULL);
+        break;
+    case PS_META_STR:
+        psMetadataAdd(md, PS_LIST_TAIL, strName,
+                      mdType | PS_META_DUPLICATE_OK,
+                      NULL, strValue);
+        break;
+    case PS_META_BOOL:
+        tempBool = parseBool((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempBool);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    case PS_META_S32:
+        tempInt = (psS32)parseValue((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempInt);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    case PS_META_F32:
+    case PS_META_F64:
+        tempDbl = parseValue((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempDbl);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    default:
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineNumber, fileName);
+    } // End switch
+
+    return;
+}
+
+
+static void initVectorXml(void *ctx, char *tagName)
+{
+    bool overwrite = false;
+    psS32 status = 0;
+    psU32 lineNumber = 0;
+    psElemType pType = 0;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *fileName = NULL;
+    psMetadataItem *table = NULL;
+    psMetadataItem *tables = NULL;
+    psMetadataItem *metadataItem = NULL;
+    psVector *vec = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    tables = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(tables, return);
+    PS_PTR_CHECK_NULL_GENERAL(tables->data.list, return);
+    table = (psMetadataItem*)psListGet(tables->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)table->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+    fileName = (char*)input->filename;
+    PS_PTR_CHECK_NULL_GENERAL(fileName, return);
+    lineNumber = input->line;
+
+    // Get attribute name
+    strName = psHashLookup(htAtts, "name");
+    if(strName == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_NO_NAME, lineNumber, fileName);
+        return;
+    }
+
+    // Get attribute type, if there is one
+    strType = psHashLookup(htAtts, "psType");
+    if(strType!= NULL) {
+        if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psBool")) {
+            pType = PS_TYPE_U8;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psS32")) {
+            pType = PS_TYPE_S32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF32")) {
+            pType = PS_TYPE_F32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF64")) {
+            pType = PS_TYPE_F64;
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE, strName, lineNumber,
+                    fileName);
+            return;
+        }
+    }
+
+    strValue = psHashLookup(htAtts, "value");
+    PS_PTR_CHECK_NULL_GENERAL(strValue, return);
+
+
+    /* If metadata item is found, and is not a folder node, and overwrite is allowed, then remove
+    existing and allow switch/case below to add new item. If overwrite is false, then report error. If
+    found item is folder node, then psMetadataAdd will automatically add a new child. */
+    metadataItem = psMetadataLookup(md, "overwrite");
+    if(metadataItem != NULL) {
+        overwrite = parseBool((char*)strValue, &status);
+        if(status) {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    input->line, input->filename);
+        }
+        metadataItem = psMetadataLookup(md, strName);
+        if(metadataItem != NULL) {
+            if(metadataItem->type != PS_META_LIST) {
+                if(overwrite) {
+                    psMetadataRemove(md, INT_MIN, strName);
+                } else {
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineNumber,
+                            fileName);
+                    return;
+                }
+            }
+        }
+    }
+
+    // Get value
+    vec = parseVector((char*)strValue, pType, &status);
+    if(!status) {
+        psMetadataAdd(md, PS_LIST_TAIL, strName+1,
+                      PS_META_VEC | PS_META_DUPLICATE_OK,
+                      NULL, vec);
+    } else {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                lineNumber, fileName);
+    }
+    psFree(vec);
+}
+
+static void saxEndElement(void *ctx, const xmlChar *tagName)
+{
+    char *psStartTagName = NULL;
+    char *psEndTagName = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    psMetadataItem *table = NULL;
+    psMetadataItem *tables = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    tables = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(tables, return);
+    PS_PTR_CHECK_NULL_GENERAL(tables->data.list, return);
+    table = (psMetadataItem*)psListGet(tables->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)table->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+
+    // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+    psEndTagName = psStringCopy((const char*)tagName);
+
+    // Compare start and end tag names
+    psStartTagName = psHashLookup(htAtts, "tagName");
+    PS_PTR_CHECK_NULL_GENERAL(psStartTagName, return);
+    if(strcmp(psEndTagName, psStartTagName)) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TAG_MISMATCH, psStartTagName, psEndTagName);
+    }
+
+    // Initialize psLib structs
+    if(!strcmp(psEndTagName, "psMetadataItem")) {
+        initMetadataItemXml(ctx, psEndTagName);
+    } else if(!strcmp(psEndTagName, "psVector")) {
+        initVectorXml(ctx, psEndTagName);
+    } else if(strcmp(psEndTagName, "psRoot")) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TAG_UNKNOWN, psEndTagName);
+    }
+
+    // Free temporary metadata item and its hash table
+    psListRemove(tables->data.list, PS_LIST_TAIL);
+
+    psFree(psEndTagName);
+
+    return;
+}
+
+psMetadata*  psMetadataParseConfigXml(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
+{
+    xmlSAXHandler saxHandler;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(fileName, NULL);
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        md = psMetadataAlloc();
+    }
+
+    // Sax handler initializations
+    saxHandler.internalSubset           = NULL;
+    saxHandler.isStandalone             = NULL;
+    saxHandler.hasInternalSubset        = NULL;
+    saxHandler.hasExternalSubset        = NULL;
+    saxHandler.resolveEntity            = NULL;
+    saxHandler.getEntity                = NULL;
+    saxHandler.entityDecl               = NULL;
+    saxHandler.notationDecl             = NULL;
+    saxHandler.attributeDecl            = NULL;
+    saxHandler.elementDecl              = NULL;
+    saxHandler.unparsedEntityDecl       = NULL;
+    saxHandler.setDocumentLocator       = NULL;
+    saxHandler.startDocument            = NULL;
+    saxHandler.endDocument              = NULL;
+    saxHandler.startElement             = saxStartElement;
+    saxHandler.endElement               = saxEndElement;
+    saxHandler.reference                = NULL;
+    saxHandler.characters               = NULL;
+    saxHandler.ignorableWhitespace      = NULL;
+    saxHandler.processingInstruction    = NULL;
+    saxHandler.comment                  = NULL;
+    saxHandler.warning                  = xmlParserError;
+    saxHandler.error                    = xmlParserError;
+    saxHandler.fatalError               = xmlParserError;
+    saxHandler.getParameterEntity       = NULL;
+    saxHandler.cdataBlock               = NULL;
+    saxHandler.externalSubset           = NULL;
+    saxHandler.initialized              = 1;
+    saxHandler._private                 = md;
+    saxHandler.startElementNs           = NULL;
+    saxHandler.endElementNs             = NULL;
+    saxHandler.serror                   = NULL;
+
+    // Parse XML file
+    if (xmlSAXUserParseFile(&saxHandler, NULL, fileName)) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
+        return NULL;
+    }
+
+    // Parser and memory cleanups for libxml2
+    xmlCleanupParser();
+    xmlMemoryDump();
+
+    return md;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadataConfig.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadataConfig.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psMetadataConfig.h	(revision 22271)
@@ -0,0 +1,85 @@
+/** @file  psMetadataIO.h
+ *
+ *  @brief Contains metadata input/output functions.
+ *
+ *  This file defines functions to read and write metadata to/from an external file.
+ *
+ *  @ingroup Metadata
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-07 20:58:50 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_METADATAIO_H
+#define PS_METADATAIO_H
+
+/// @addtogroup Metadata
+/// @{
+
+
+/** Print metadata item to file.
+ *
+ *  Metadata items may be printed to an open file descriptor based on a
+ *  provided format. The format is a sprintf format statement with exactly
+ *  one % formatting command. If the metadata item type is a numeric type,
+ *  this formatting command must also be numeric, and the type conversion
+ *  performed to the value to match the format type. If the metadata type is
+ *  a string, the fromatting command must also be for a string. If the
+ *  metadata type is any other data type, printing is not allowed.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+bool psMetadataItemPrint(
+    FILE * fd,                         ///< Pointer to file to write metadata item.
+    const char *format,                ///< Format to print metadata item.
+    const psMetadataItem* metadataItem ///< Metadata item to print.
+);
+
+/** Read metadata header.
+ *
+ *  Read a metadata header from file. If the file is not found, an error is
+ *  reported.
+ *
+ *  @return psMetadata* : Pointer to resulting metadata.
+ */
+psMetadata* psMetadataReadHeader(
+    psMetadata* output,                ///< Resulting metadata from read.
+    char *extName,                     ///< File name extension string.
+    psS32 extNum,                      ///< File name extension number. Starts at 1.
+    char *fileName                     ///< Name of file to read.
+);
+
+/** Read metadata configuration file.
+ *
+ *  Loads pre-defined settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+psMetadata* psMetadataParseConfig(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    psU32 *nFail,                      ///< Number of failed lines.
+    const char *fileName,              ///< Name of file to read.
+    psBool overwrite                   ///< Allow overwrite of duplicate specifications.
+);
+
+/** Read XML metadata configuration file.
+ *
+ *  Loads pre-defined XML settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+
+psMetadata*  psMetadataParseConfigXml(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    psU32 *nFail,                      ///< Number of failed lines.
+    const char *fileName,              ///< Name of file to read.
+    psBool overwrite                   ///< Allow overwrite of duplicate specifications.
+);
+
+/// @}
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psPixels.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psPixels.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psPixels.c	(revision 22271)
@@ -0,0 +1,262 @@
+/** @file  psPixels.c
+ *
+ *  @brief Contains psPixel related functions
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-23 00:10:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "psPixels.h"
+#include "psMemory.h"
+
+typedef int(*qsortCompareFcn)(const void *, const void *);
+
+static void pixelsFree(psPixels* pixels)
+{
+    if (pixels != NULL) {
+        psFree(pixels->data);
+    }
+}
+
+// for use by qsort, etc.
+static int comparePixelCoord(psPixelCoord* coord1, psPixelCoord* coord2)
+{
+    // check row first
+    if (coord1->y < coord2->y) {
+        return -1;
+    }
+
+    if (coord1->y > coord2->y) {
+        return 1;
+    }
+
+    // rows are the same, so check column
+    if (coord1->x < coord2->x) {
+        return -1;
+    }
+
+    if (coord1->x > coord2->x) {
+        return 1;
+    }
+
+    return 0;
+}
+
+psPixels* psPixelsAlloc(int size)
+{
+    psPixels* out = psAlloc(sizeof(psPixels));
+
+    if (size > 0) {
+        out->data = psAlloc(sizeof(psPixelCoord)*size);
+    } else {
+        out->data = NULL;
+    }
+    out->n = 0;
+    out->nalloc = size;
+
+    psMemSetDeallocator(out, (psFreeFcn)pixelsFree);
+
+    return NULL;
+}
+
+psPixels* psPixelsRealloc(psPixels* pixels, int size)
+{
+    if (pixels == NULL) {
+        return psPixelsAlloc(size);
+    }
+
+    pixels->data = psRealloc(pixels->data, sizeof(psPixelCoord)*size);
+
+    pixels->nalloc = size;
+    if (pixels->n > pixels->nalloc) {
+        pixels->n = pixels->nalloc;
+    }
+
+    return pixels;
+}
+
+psPixels* psPixelsCopy(psPixels* out, const psPixels* in)
+{
+    if (in == NULL) {
+        return NULL;
+    }
+
+    out = psPixelsRealloc(out, in->n);
+
+    memcpy(in->data,out->data, in->n*sizeof(psPixelCoord));
+    out->n = in->n;
+
+    return out;
+}
+
+psImage *psPixelsToMask(psImage *out, const psPixels *pixels, const psRegion *region, unsigned int maskVal)
+{
+    // check that the input pixel vector is valid
+    if (pixels == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    psPixelCoord* data = pixels->data;
+    if (data == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+
+    // check if the input region is valid
+    if (region == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    int x0 = region->x0;
+    int x1 = region->x1;
+    int y0 = region->y0;
+    int y1 = region->y1;
+
+    // determine the output image size
+    int numRows = x1-x0;
+    int numCols = y1-y0;
+    if (numRows < 1 || numCols < 1) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+
+    //  allocate the output image
+    out = psImageRecycle(out, numCols, numRows, PS_TYPE_MASK);
+    if (out == NULL) {
+        // XXX: Error message
+        return NULL;
+    }
+    *(psS32*)&out->row0 = x0;
+    *(psS32*)&out->col0 = y0;
+
+    // initialize image to all zeros
+    int columnByteSize = sizeof(PS_TYPE_MASK)*numCols;
+    for (int row = 0; row < numRows; row++) {
+        memset(out->data.U8[row],0,columnByteSize);
+    }
+
+    // determine the length of the pixel vector
+    int length = pixels->n;
+
+    // cycle through the vector of pixels and insert pixels into image
+    psMaskType** outData = out->data.PS_TYPE_MASK_DATA;
+    for (int p = 0; p < length; p++) {
+        psS32 x = data[p].x;
+        psS32 y = data[p].y;
+        // pixel in region?
+        if (x >= x0 && x < x1 && y >= y0 && y < y1) {
+            outData[x-x0][y-y0] |= maskVal;
+        }
+    }
+
+    return out;
+}
+
+psPixels *psMaskToPixels(psPixels *out, const psImage *mask, unsigned int maskVal)
+{
+    if (mask == NULL) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    if (mask->type.type != PS_TYPE_MASK) {
+        // XXX: Error message
+        psFree(out);
+        return NULL;
+    }
+    int numRows = mask->numRows;
+    int numCols = mask->numCols;
+
+    // assumption: number of masked pixels is relatively small compared to
+    // total pixels, so it is best to just start with a guess and resize if
+    // necessary
+    int minPixels = numRows*numCols/100; // initial guess, 1% of pixels masked
+    if (minPixels < 32) { // enforce a minimum size
+        minPixels = 32;
+    }
+
+    // check out's validity (allocated/minimum size)
+    if (out == NULL || out->data == NULL || out->nalloc < minPixels) {
+        out = psPixelsRealloc(out,minPixels);
+    }
+
+    // start with a blank list of pixels
+    out->n = 0;
+
+    // find the mask pixels in the image
+    int numPixels = 0;
+    psPixelCoord* data = out->data;
+    int nalloc = out->nalloc;
+    for (int row=0; row<numRows; row++) {
+        psMaskType* maskRow = mask->data.PS_TYPE_MASK_DATA[row];
+        for (int col=0; col<numCols; col++) {
+            if ( (maskRow[col] | maskVal) != 0 ) {
+                // check the vector sizes, and expand if necessary
+                if (nalloc >= numPixels) {
+                    out = psPixelsRealloc(out, 2*nalloc);
+                    nalloc = out->nalloc;
+                    data = out->data;
+                }
+
+                data[numPixels].x = col;
+                data[numPixels].y = row;
+                numPixels++;
+            }
+        }
+    }
+
+    return out;
+}
+
+psPixels* psPixelsConcatenate(psPixels *out,const psPixels *pixels)
+{
+    if (pixels == NULL) {
+        // XXX: Error message
+        return NULL;
+    }
+    int pixelsN = pixels->n;
+    psPixelCoord* pixelsData = pixels->data;
+
+    if (out == NULL) {
+        // simple copy pixels
+        out = psPixelsCopy(out,pixels);
+        return out;
+    }
+
+    // make sure the out is large enough to fit the result
+    int outN = out->n;
+    out = psPixelsRealloc(out,outN + pixelsN);
+    psPixelCoord* outData = out->data;
+
+    // sort the OUT array to help in searching for duplicates later
+    qsort(outData, sizeof(psPixelCoord), outN,
+          (qsortCompareFcn)comparePixelCoord);
+
+    // add non-duplicate values in pixels to out
+    psPixelCoord pCoord;
+    int end = outN;
+    for (int n = 0; n < pixelsN; n++) {
+        pCoord = pixelsData[n];
+        if (bsearch(&pCoord, outData, sizeof(psPixelCoord), outN,
+                    (qsortCompareFcn)comparePixelCoord) == NULL) {
+            // no match in OUT array of this value
+            outData[end++] = pCoord;
+        }
+    }
+    out->n = end; // set number of elements to reflect added data
+
+    return out;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/types/psPixels.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/types/psPixels.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/types/psPixels.h	(revision 22271)
@@ -0,0 +1,127 @@
+/** @file  psPixels.h
+ *
+ *  @brief Contains psPixel related functions
+ *
+ *  @ingroup Image
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-04-23 00:10:19 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_PIXELS_H
+#define PS_PIXELS_H
+
+#include "psImage.h"
+#include "psVector.h"
+
+/// @addtogroup Image
+/// @{
+
+typedef struct
+{
+    psS32 x;
+    psS32 y;
+}
+psPixelCoord;
+
+/** list of pixel coordinates
+ *
+ *  Usually an image mask is the best way to carry information about what
+ *  pixels mean what. However, in the case where the number of pixels in which
+ *  we are interested is limited, it is more efï¬cient to simply carry a list
+ *  of pixels. An example of this is in the image combination code, where we
+ *  want to perform an operation on a relatively small fraction of pixels, and
+ *  it is inefï¬cient to go through an entire mask image checking each pixel.
+ *
+ */
+typedef struct
+{
+    int n;
+    int nalloc;
+    psPixelCoord* data;
+}
+psPixels;
+
+
+/** Allocates a new psPixels structure
+ *
+ *  @return psPixels*   new psPixels
+ */
+psPixels* psPixelsAlloc(
+    int size                           ///< the size of the coordinate vectors
+);
+
+/** resizes a psPixels structure
+ *
+ *  @return psPixels*   resized psPixels
+ */
+psPixels* psPixelsRealloc(
+    psPixels* pixels,                  ///< psPixels to resize, or NULL to create new psPixels
+    int size                           ///< the size of the coordinate vectors
+);
+
+/** Copies a psPixels object
+ *
+ *  Makes a deep copy of the data in a psPixels object.  Any data in the OUT
+ *  parameter will be destroyed and OUT will be resized, if necessary.
+ *
+ *  @return psPixels*   a new psPixels that is a duplicate to IN
+ */
+psPixels* psPixelsCopy(
+    psPixels* out,                     ///< psPixels struct to recycle, or NULL
+    const psPixels* in                 ///< psPixels struct to copy
+);
+
+/** Generate a psImage from a psPixels
+ *
+ *  psPixelsToMask shall return an image of type U8 with the pixels lying
+ *  within the speciï¬ed region set to the maskVal. The out image shall be
+ *  modiï¬ed if supplied, or allocated and returned if NULL. The size of the
+ *  output image shall be region->x1 - region->x0 by region->y1 - region->y0,
+ *  with out->x0 = region->x0 and out->y0 = region->y0. In the event that
+ *  either of pixels or region are NULL, the function shall generate an
+ *  error and return NULL.
+ *
+ *  @return psImage*    generated mask image
+ */
+psImage* psPixelsToMask(
+    psImage* out,                      ///< psImage to recycle, or NULL
+    const psPixels* pixels,            ///< list of pixels to use
+    const psRegion* region,            ///< region to define the output mask image
+    unsigned int maskVal               ///< the mask bit-values to act upon
+);
+
+/** Generate a psPixels from a mask psImage
+ *
+ *  psMaskToPixels shall return a psPixels consisting of the coordinates in
+ *  the mask that match the maskVal. The out pixel list shall be modiï¬ed if
+ *  supplied, or allocated and returned if NULL. In hte event that mask is
+ *  NULL, the function shall generate an error and return NULL.
+ *
+ *  @return psPixels*   generated psPixels pixel list
+ */
+psPixels* psMaskToPixels(
+    psPixels *out,                     ///< psPixels to recycle, or NULL
+    const psImage *mask,               ///< the input mask psImage
+    unsigned int maskVal               ///< the mask bit-values to act upon
+);
+
+/** Concatenates two psPixels
+ *
+ *  psPixelsConcatenate shall concatenate pixels onto out. In the event that
+ *  out is NULL, a new psPixels shall be allocated, and the contents of
+ *  pixels simply copied in. If pixels is NULL, the function shall generate
+ *  an error and return NULL. The function shall take care to ensure that
+ *  there are no duplicate pixels in out.
+ *
+ *  @return psPixels         Concatenated psPixel list
+ */
+psPixels* psPixelsConcatenate(
+    psPixels *out,                     ///< psPixels to recycle, or NULL
+    const psPixels *pixels             ///< psPixels to append to OUT
+);
+
+#endif
Index: /tags/pap_tags/pap_branch_050518/psLib/src/xml/psXML.c
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/xml/psXML.c	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/xml/psXML.c	(revision 22271)
@@ -0,0 +1,1168 @@
+/** @file  psMetadataIO.c
+*
+*  @brief Contains metadata input/output functions.
+*
+*  This file defines functions to read and write metadata to/from an external file.
+*
+*  @ingroup Metadata
+*
+*  @author Ross Harman, MHPCC
+*
+*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-04-26 19:53:30 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#include <libxml/parser.h>
+#include <fitsio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "psAbort.h"
+#include "psType.h"
+#include "psMemory.h"
+#include "psError.h"
+#include "psString.h"
+#include "psList.h"
+#include "psHash.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psMetadataIO.h"
+#include "psConstants.h"
+#include "psAstronomyErrors.h"
+
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Check for FITS errors */
+#define FITS_ERROR(STRING,PS_ERROR)                                                                          \
+fits_get_errstatus(status, fitsErr);                                                                         \
+psError(PS_ERR_IO,true, STRING, PS_ERROR, fitsErr);                                                          \
+status = 0;                                                                                                  \
+fits_close_file(fd, &status);                                                                                \
+if(status){                                                                                                  \
+    fits_get_errstatus(status, fitsErr);                                                                     \
+    psError(PS_ERR_IO,true, "Couldn't close FITS file. FITS error: %s", fitsErr);                            \
+}                                                                                                            \
+status = 0;                                                                                                  \
+psFree(output);                                                                                              \
+return NULL;
+
+/** Free and null temporary variables used by config file parser */
+#define CLEAR_TEMPS()                                                                                        \
+if(strName) {                                                                                                \
+    psFree(strName);                                                                                         \
+    strName = NULL;                                                                                          \
+}                                                                                                            \
+if(strType) {                                                                                                \
+    psFree(strType);                                                                                         \
+    strType = NULL;                                                                                          \
+}                                                                                                            \
+if(strValue) {                                                                                               \
+    psFree(strValue);                                                                                        \
+    strValue = NULL;                                                                                         \
+}                                                                                                            \
+if(strComment) {                                                                                             \
+    psFree(strComment);                                                                                      \
+    strComment = NULL;                                                                                       \
+}
+
+/** Maximum size of a FITS line */
+#define FITS_LINE_SIZE 80
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 256
+
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static void saxEndElement(void *ctx, const xmlChar *tagName);
+static void initVectorXml(void *ctx, char *tagName);
+static void initMetadataItemXml(void *ctx, char *tagName);
+static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts);
+
+/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+ *  must be null terminated. */
+psBool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+char *cleanString(char *inString, psS32 sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+/** Count repeat occurances of a single character within a line. The input string must be null terminated. */
+psS32 repeatedChars(char *inString, char ch)
+{
+    psS32 count = 0;
+
+
+    while(*inString!='\0') {
+        if(*inString == ch) {
+            count++;
+        }
+        inString++;
+    }
+
+    return count;
+}
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+char* getToken(char **inString, char *delimiter, psS32 *status)
+{
+    char *cleanToken = NULL;
+    psS32 sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = 1;
+    }
+
+    return cleanToken;
+}
+
+/** Returns single parsed value as a double precision number. The input string must be cleaned and null
+ * terminated. */
+double parseValue(char *inString, psS32 *status)
+{
+    char *end = NULL;
+    double value = 0.0;
+
+
+    value = strtod(inString, &end);
+    if(*end != '\0') {
+        *status = 1;
+    } else if(inString==end) {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns true or false. 'T', 't', '1', 'F', 'f', and '0' are acceptable, parsable variations. */
+psBool parseBool(char *inString, psS32 *status)
+{
+    psBool value = false;
+
+
+    if(*inString=='T' || *inString=='t' || *inString=='1') {
+        value = true;
+    } else if(*inString=='F' || *inString=='f' || *inString=='0') {
+        value = false;
+    } else {
+        *status = 1;
+    }
+
+    return value;
+}
+
+/** Returns parsed vector filled with with data. The input string must be null terminated. */
+psVector* parseVector(char *inString, psElemType elemType, psS32 *status)
+{
+    char *end = NULL;
+    char *saveValue = NULL;
+    psS32 i = 0;
+    psS32 numValues = 0;
+    double value = 0.0;
+    psVector *vec = NULL;
+
+
+    // Cycle through string and count entries
+    saveValue = inString;
+    while(*inString!='\0') {
+        strtod(inString, &end);
+        if(inString==end) {
+            *status = 1;
+            return NULL;
+        }
+        while(*end==' ' || *end==',') { // Commas or spaces may be used as delimiters for vector values
+            end++;
+        }
+        inString=end;
+        numValues++;
+    }
+
+    // Cycle through string and convert string values to values
+    if(numValues) {
+        inString = saveValue;
+        end = NULL;
+        vec = psVectorAlloc(numValues, elemType);
+
+        while(*inString!='\0') {
+            value = strtod(inString, &end);
+            if(inString==end) {
+                *status = 1;
+                return vec;
+            }
+            switch(elemType) {
+            case PS_TYPE_U8:
+                vec->data.U8[i++] = (psU8)value;
+                break;
+            case PS_TYPE_S32:
+                vec->data.S32[i++] = (psS32)value;
+                break;
+            case PS_TYPE_F32:
+                vec->data.F32[i++] = (psF32)value;
+                break;
+            case PS_TYPE_F64:
+                vec->data.F64[i++] = (psF64)value;
+                break;
+            default:
+                *status = 1;
+                psError(PS_ERR_BAD_PARAMETER_VALUE,true,
+                        PS_ERRORTEXT_psMetadataIO_TYPE_INVALID,
+                        elemType);
+            }
+
+            while(*end==' ' || *end==',') {
+                end++;
+            }
+            inString=end;
+        }
+    }
+
+    return vec;
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+bool psMetadataItemPrint(FILE * fd, const char *format, const psMetadataItem* metadataItem)
+{
+    psMetadataType type;
+    psBool success = true;
+
+    PS_PTR_CHECK_NULL(fd, success);
+    PS_PTR_CHECK_NULL(format, success);
+    PS_PTR_CHECK_NULL(metadataItem, success);
+
+    type = metadataItem->type;
+
+    // determining the format type
+    char* fType = strchr(format,'%');
+    if (fType == NULL) {
+        // well, the format contains no reference to the metadataItem's data:
+        // that is truly trival to do!
+        fprintf(fd,format);
+        return success;
+    }
+
+    // skip over any format modifiers
+    const char* formatEnd = format+strlen(format);
+    while ( (fType < formatEnd) &&
+        (strchr(" +-01234567890.$#, hlL",*(++fType)) != NULL) ) {}
+
+    #define METADATAITEM_NUMERIC_CAST(FORMAT_TYPE) { \
+        switch(type) { \
+        case PS_META_BOOL: \
+            fprintf(fd, format, (FORMAT_TYPE) metadataItem->data.B); \
+            break; \
+        case PS_META_S32: \
+            fprintf(fd,format,(FORMAT_TYPE)  metadataItem->data.S32); \
+            break; \
+        case PS_META_F32: \
+            fprintf(fd, format,(FORMAT_TYPE)  metadataItem->data.F32); \
+            break; \
+        case PS_META_F64: \
+            fprintf(fd, format,(FORMAT_TYPE) metadataItem->data.F64); \
+            break; \
+        default: \
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true, \
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, (int)type); \
+            success = false; \
+        } \
+    }
+
+    switch(*fType) {
+    case 'd':
+    case 'i':
+    case 'c':
+        METADATAITEM_NUMERIC_CAST(int)
+        break;
+    case 'o':
+    case 'u':
+    case 'x':
+    case 'X':
+        METADATAITEM_NUMERIC_CAST(unsigned int)
+        break;
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+    case 'a':
+    case 'A':
+        METADATAITEM_NUMERIC_CAST(double)
+        break;
+    case 's':
+        if (type == PS_META_STR) {
+            fprintf(fd,format,(char*)metadataItem->data.V);
+        } else {
+            psError(PS_ERR_BAD_PARAMETER_TYPE,true,
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, (int)type);
+            success = false;
+        }
+        break;
+    case 'p':
+        fprintf(fd,format,metadataItem->data.V);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                PS_ERRORTEXT_psMetadata_FORMAT_INVALID, *fType);
+        break;
+    }
+
+    return success;
+}
+
+
+psMetadata* psMetadataReadHeader(psMetadata* output, char *extName, psS32 extNum, char *fileName)
+{
+    psBool tempBool;
+    psBool success;
+    char keyType;
+    char keyName[FITS_LINE_SIZE];
+    char keyValue[FITS_LINE_SIZE];
+    char keyComment[FITS_LINE_SIZE];
+    char fitsErr[MAX_STRING_LENGTH];
+    psS32 i;
+    psS32 hduType = 0;
+    psS32 status = 0;
+    psS32 numKeys = 0;
+    psS32 keyNum = 0;
+    fitsfile *fd = NULL;
+
+    PS_PTR_CHECK_NULL(fileName,NULL);
+
+    fits_open_file(&fd, fileName, READONLY, &status);
+    if(fd == NULL || status != 0) {
+        FITS_ERROR("FITS error while opening file: %s %s", fileName);
+        return NULL;
+    }
+
+    if (extName == NULL && extNum < 1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psMetadataIO_EXTNUM_NOTPOSITIVE,
+                extNum);
+        return NULL;
+    }
+
+    // Allocate metadata if user didn't
+    if (output == NULL) {
+        output = psMetadataAlloc();
+    }
+
+    // Move to user designated HDU number or HDU name in FITS file. HDU numbers starts at one.
+    if (extName != NULL) {
+        if (fits_movnam_hdu(fd, ANY_HDU, extName, 0, &status) != 0) {
+            FITS_ERROR("FITS error while locating header %s: %s", extName);
+        }
+    } else {
+        if (fits_movabs_hdu(fd, extNum, &hduType, &status) != 0) {
+            FITS_ERROR("FITS error while locating header %d: %s", extNum);
+        }
+    }
+
+    // Get number of key names
+    if (fits_get_hdrpos(fd, &numKeys, &keyNum, &status) != 0) {
+        FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+    }
+
+    // Get each key name. Keywords start at one.
+    for (i = 1; i <= numKeys; i++) {
+        if (fits_read_keyn(fd, i, keyName, keyValue, keyComment, &status) != 0) {
+            FITS_ERROR("FITS error while reading key %d: %s", keyNum);
+        }
+        if (fits_get_keytype(keyValue, &keyType, &status) != 0) {
+            fits_get_errstatus(status, fitsErr);
+            if (status != VALUE_UNDEFINED) {
+                FITS_ERROR("FITS error while determining key %d type: %s", keyNum);
+            } else {
+                // Some keywords are still valid if they don't have a type (like COMMENTS and HISTORY)
+                keyType = 'C';
+                status = 0;
+            }
+        }
+
+        switch (keyType) {
+        case 'I':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_S32 | PS_META_DUPLICATE_OK,
+                                    keyComment, atoi(keyValue));
+            break;
+        case 'F':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_F64 | PS_META_DUPLICATE_OK,
+                                    keyComment, atof(keyValue));
+            break;
+        case 'C':
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_STR | PS_META_DUPLICATE_OK,
+                                    keyComment, keyValue);
+            break;
+        case 'L':
+            tempBool = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAdd(output, PS_LIST_TAIL, keyName,
+                                    PS_META_BOOL | PS_META_DUPLICATE_OK,
+                                    keyComment, tempBool);
+            break;
+        case 'U':
+        case 'X':
+        default:
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FITS_METATYPE_INVALID, keyType);
+            return output;
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false, PS_ERRORTEXT_psMetadataIO_ADD_FAILED, keyName);
+            return output;
+        }
+    }
+
+    return output;
+}
+
+psMetadata* psMetadataParseConfig(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
+{
+    psBool tempBool;
+    char *line = NULL;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *strComment = NULL;
+    char *linePtr = NULL;
+    psElemType vecType = 0;
+    psS32 status = 0;
+    psU32 lineCount = 0;
+    psF64 tempDbl = 0.0;
+    psS32 tempInt = 0.0;
+    psVector *tempVec = NULL;
+    FILE *fp = NULL;
+    psMetadataType mdType;
+    psMetadataFlags flags;
+    psBool addStatus;
+
+    // Check for nulls
+    PS_PTR_CHECK_NULL(fileName,NULL);
+    if((fp=fopen(fileName, "r")) == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
+        return NULL;
+    }
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        md = psMetadataAlloc();
+    }
+
+    // Create reusable line for continuous read
+    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+    // While loop to parse the file
+    while(fgets(line, MAX_STRING_LENGTH, fp) != NULL) {
+
+        // Initialize variables for new line
+        linePtr = line;
+        lineCount++;
+        CLEAR_TEMPS();
+
+        // If line is not a comment or blank, then extract data
+        if(!ignoreLine(linePtr)) {
+
+            // Check for more than one '*' or '@' in a line
+            if(repeatedChars(linePtr, '@') > 1) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
+                continue;
+            } else if(repeatedChars(linePtr, '*') > 1) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '*', lineCount, fileName);
+                continue;
+            } else if(repeatedChars(linePtr, '~') > 0) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '~', lineCount, fileName);
+                continue;
+            }
+
+            // Get metadata item name
+            strName = getToken(&linePtr, " ", &status);
+            if(strName==NULL || status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true,
+                        PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "name", lineCount, fileName);
+                continue;
+            }
+
+            flags = (overwrite) ? PS_META_REPLACE : PS_META_DEFAULT;
+
+            // Get the metadata item type
+            strType = getToken(&linePtr, " ", &status);
+            if(strType==NULL) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
+                        fileName);
+                continue;
+            } else {
+                char* tempStrType = strType;
+                if(*strType == '*') {
+                    flags = PS_META_DUPLICATE_OK;
+                    tempStrType = strType+1;
+                }
+
+                if(!strncmp(tempStrType, "STR", 3)) {
+                    mdType = PS_META_STR;
+                } else if(!strncmp(tempStrType, "BOOL", 4)) {
+                    mdType = PS_META_BOOL;
+                } else if(!strncmp(tempStrType, "S32", 3)) {
+                    mdType = PS_META_S32;
+                } else if(!strncmp(tempStrType, "F32", 3)) {
+                    mdType = PS_META_F32;
+                } else if(!strncmp(tempStrType, "F64", 3)) {
+                    mdType = PS_META_F64;
+                } else {
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineCount,
+                            fileName);
+                    continue;
+                }
+            }
+
+            if(*strName == '@') {
+                vecType = PS_META_PRIMITIVE_TYPE(mdType);
+                mdType = PS_META_VEC;
+            }
+
+            // Get the metadata item value if there is one.
+            strValue = getToken(&linePtr, "#", &status);
+            if(status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                        fileName);
+                continue;
+            }
+            if(strValue==NULL) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                        fileName);
+                continue;
+            }
+
+            // Not all lines will have comments, so NULL is ok.
+            strComment = getToken(&linePtr,"~", &status);
+            if(status) {
+                (*nFail)++;
+                status = 0;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                        fileName);
+                continue;
+            }
+
+            // Create and add metadata item to metadata and parse values
+            switch (mdType) {
+            case PS_META_STR:
+                addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                          mdType | flags,
+                                          strComment, strValue);
+                break;
+            case PS_META_VEC:
+                tempVec = parseVector(strValue, vecType, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName+1,
+                                              mdType | flags,
+                                              strComment, tempVec);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                psFree(tempVec);
+                break;
+            case PS_META_BOOL:
+                tempBool = parseBool(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempBool);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                break;
+            case PS_META_S32:
+                tempInt = (psS32)parseValue(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempInt);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
+                            strType, lineCount, fileName);
+                    continue;
+                }
+                break;
+            case PS_META_F32:
+            case PS_META_F64:
+                tempDbl = parseValue(strValue, &status);
+                if(!status) {
+                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
+                                              mdType | flags,
+                                              strComment, tempDbl);
+                } else {
+                    status = 0;
+                    (*nFail)++;
+                    psError(PS_ERR_IO, true,
+                            PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType, lineCount,
+                            fileName);
+                    continue;
+                }
+                break;
+            default:
+                (*nFail)++;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, mdType, lineCount,
+                        fileName);
+                continue;
+            } // switch
+            if (! addStatus) {
+                (*nFail)++;
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineCount,
+                        fileName);
+            }
+
+        } // if ignoreLine
+    } // while loop
+
+    psFree(line);
+
+    return md;
+}
+
+static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts)
+{
+    psU64 i = 0;
+    char* psTagName = NULL;
+    char *psAttName = NULL;
+    char *psAttValue = NULL;
+    const xmlChar *attName = NULL;
+    const xmlChar *attValue = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+
+    // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+    psTagName = psStringCopy((const char*)tagName);
+
+    // Metadata containter for housing element attributes used by other SAX events
+    htAtts = psHashAlloc(10);
+
+
+    // Get tag name
+    if(psTagName != NULL) {
+        psHashAdd(htAtts, "tagName", psTagName);
+    } else {
+        PS_PTR_CHECK_NULL_GENERAL(psTagName, return);
+        psFree(htAtts);
+        psFree(psTagName);
+        return;
+    }
+
+    // Get all attribute names and attribute values
+    if(atts != NULL) {
+        attName = atts[i++];
+        attValue = atts[i++];
+        while(attName != NULL) {
+            if(attValue != NULL) {
+
+                // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+                psAttName = psStringCopy((const char*)attName);
+                psAttValue = psStringCopy((const char*)attValue);
+                psHashAdd(htAtts, psAttName, psAttValue);
+                psFree(psAttName);
+                psFree(psAttValue);
+            } else {
+                PS_PTR_CHECK_NULL_GENERAL(psAttValue, return);
+                psFree(htAtts);
+                psFree(psTagName);
+                return;
+            }
+            attName = atts[i++];
+            attValue = atts[i++];
+        }
+    }
+
+    // Add attributes to metadata
+
+    psMetadataAdd(md, PS_LIST_TAIL, "htAtts",
+                  PS_META_HASH | PS_META_DUPLICATE_OK,
+                  NULL, htAtts);
+
+    psFree(psTagName);
+    psFree(htAtts);
+
+    return;
+}
+
+static void initMetadataItemXml(void *ctx, char *tagName)
+{
+    psBool overwrite = false;
+    psBool tempBool = false;
+    psS32 status = 0;
+    psU32 lineNumber = 0;
+    psF64 tempDbl = 0.0;
+    psS32 tempInt = 0.0;
+    psMetadataType mdType = PS_META_UNKNOWN;
+    char *fileName = NULL;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    psMetadataItem *metadataItem = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    metadataItem = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(metadataItem, return);
+    PS_PTR_CHECK_NULL_GENERAL(metadataItem->data.list, return);
+    metadataItem = (psMetadataItem*)psListGet(metadataItem->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)metadataItem->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+    fileName = (char*)input->filename;
+    PS_PTR_CHECK_NULL_GENERAL(fileName, return);
+    lineNumber = input->line;
+
+    // Get attribute name
+    strName = psHashLookup(htAtts, "name");
+    if(strName == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_NO_NAME, lineNumber, fileName);
+        return;
+    }
+
+    // Get attribute type, if there is one
+    strType = psHashLookup(htAtts, "psType");
+    if(strType!= NULL) {
+        if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psString")) {
+            mdType = PS_META_STR;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psBool")) {
+            mdType = PS_META_BOOL;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psS32")) {
+            mdType = PS_META_S32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF32")) {
+            mdType = PS_META_F32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF64")) {
+            mdType = PS_META_F64;
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE, strType, lineNumber,
+                    fileName);
+            return;
+        }
+    }
+
+    // Get attribute value, if there is one
+    strValue = psHashLookup(htAtts, "value");
+
+    /* If metadata item is found, and is not a folder node, and overwrite is allowed, then remove
+    existing and allow switch/case below to add new item. If overwrite is false, then report error. If
+    found item is folder node, then psMetadataAdd will automatically add a new child. */
+    metadataItem = psMetadataLookup(md, "overwrite");
+    if(metadataItem != NULL) {
+        overwrite = parseBool((char*)strValue, &status);
+        if(status) {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        metadataItem = psMetadataLookup(md, strName);
+        if(metadataItem != NULL) {
+            if(metadataItem->type != PS_META_LIST) {
+                if(overwrite) {
+                    psMetadataRemove(md, INT_MIN, strName);
+                } else {
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineNumber,
+                            fileName);
+                    return;
+                }
+            }
+        }
+    }
+
+    // Create metadata item and add to metadata
+    switch(mdType) {
+    case PS_META_LIST:
+        psMetadataAdd(md, PS_LIST_TAIL, strName,
+                      mdType | PS_META_DUPLICATE_OK,
+                      NULL, NULL);
+        break;
+    case PS_META_STR:
+        psMetadataAdd(md, PS_LIST_TAIL, strName,
+                      mdType | PS_META_DUPLICATE_OK,
+                      NULL, strValue);
+        break;
+    case PS_META_BOOL:
+        tempBool = parseBool((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempBool);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    case PS_META_S32:
+        tempInt = (psS32)parseValue((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempInt);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    case PS_META_F32:
+    case PS_META_F64:
+        tempDbl = parseValue((char*)strValue, &status);
+        if(!status) {
+            psMetadataAdd(md, PS_LIST_TAIL, strName,
+                          mdType | PS_META_DUPLICATE_OK,
+                          NULL, tempDbl);
+        } else {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    lineNumber, fileName);
+        }
+        break;
+    default:
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineNumber, fileName);
+    } // End switch
+
+    return;
+}
+
+
+static void initVectorXml(void *ctx, char *tagName)
+{
+    bool overwrite = false;
+    psS32 status = 0;
+    psU32 lineNumber = 0;
+    psElemType pType = 0;
+    char *strName = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *fileName = NULL;
+    psMetadataItem *table = NULL;
+    psMetadataItem *tables = NULL;
+    psMetadataItem *metadataItem = NULL;
+    psVector *vec = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    tables = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(tables, return);
+    PS_PTR_CHECK_NULL_GENERAL(tables->data.list, return);
+    table = (psMetadataItem*)psListGet(tables->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)table->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+    fileName = (char*)input->filename;
+    PS_PTR_CHECK_NULL_GENERAL(fileName, return);
+    lineNumber = input->line;
+
+    // Get attribute name
+    strName = psHashLookup(htAtts, "name");
+    if(strName == NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_NO_NAME, lineNumber, fileName);
+        return;
+    }
+
+    // Get attribute type, if there is one
+    strType = psHashLookup(htAtts, "psType");
+    if(strType!= NULL) {
+        if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psBool")) {
+            pType = PS_TYPE_U8;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psS32")) {
+            pType = PS_TYPE_S32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF32")) {
+            pType = PS_TYPE_F32;
+        } else if(xmlStrEqual(BAD_CAST strType, BAD_CAST "psF64")) {
+            pType = PS_TYPE_F64;
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TYPE_INVALID_LINE_FILE, strName, lineNumber,
+                    fileName);
+            return;
+        }
+    }
+
+    strValue = psHashLookup(htAtts, "value");
+    PS_PTR_CHECK_NULL_GENERAL(strValue, return);
+
+
+    /* If metadata item is found, and is not a folder node, and overwrite is allowed, then remove
+    existing and allow switch/case below to add new item. If overwrite is false, then report error. If
+    found item is folder node, then psMetadataAdd will automatically add a new child. */
+    metadataItem = psMetadataLookup(md, "overwrite");
+    if(metadataItem != NULL) {
+        overwrite = parseBool((char*)strValue, &status);
+        if(status) {
+            status = 0;
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                    input->line, input->filename);
+        }
+        metadataItem = psMetadataLookup(md, strName);
+        if(metadataItem != NULL) {
+            if(metadataItem->type != PS_META_LIST) {
+                if(overwrite) {
+                    psMetadataRemove(md, INT_MIN, strName);
+                } else {
+                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineNumber,
+                            fileName);
+                    return;
+                }
+            }
+        }
+    }
+
+    // Get value
+    vec = parseVector((char*)strValue, pType, &status);
+    if(!status) {
+        psMetadataAdd(md, PS_LIST_TAIL, strName+1,
+                      PS_META_VEC | PS_META_DUPLICATE_OK,
+                      NULL, vec);
+    } else {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType,
+                lineNumber, fileName);
+    }
+    psFree(vec);
+}
+
+static void saxEndElement(void *ctx, const xmlChar *tagName)
+{
+    char *psStartTagName = NULL;
+    char *psEndTagName = NULL;
+    psMetadata* md = NULL;
+    psHash* htAtts = NULL;
+    psMetadataItem *table = NULL;
+    psMetadataItem *tables = NULL;
+    xmlParserCtxtPtr ctxt = NULL;
+    xmlParserInputPtr input = NULL;
+
+
+    // Get and check initial data pointers
+    ctxt = (xmlParserCtxtPtr)ctx;
+    PS_PTR_CHECK_NULL_GENERAL(ctxt, return);
+    md = (psMetadata*)ctxt->sax->_private;
+    PS_PTR_CHECK_NULL_GENERAL(md, return);
+    input = (xmlParserInputPtr)ctxt->input;
+    PS_PTR_CHECK_NULL_GENERAL(input, return);
+    tables = psMetadataLookup(md, "htAtts");
+    PS_PTR_CHECK_NULL_GENERAL(tables, return);
+    PS_PTR_CHECK_NULL_GENERAL(tables->data.list, return);
+    table = (psMetadataItem*)psListGet(tables->data.list,PS_LIST_TAIL);
+    htAtts = (psHash*)table->data.list;
+    PS_PTR_CHECK_NULL_GENERAL(htAtts, return);
+
+    // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
+    psEndTagName = psStringCopy((const char*)tagName);
+
+    // Compare start and end tag names
+    psStartTagName = psHashLookup(htAtts, "tagName");
+    PS_PTR_CHECK_NULL_GENERAL(psStartTagName, return);
+    if(strcmp(psEndTagName, psStartTagName)) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TAG_MISMATCH, psStartTagName, psEndTagName);
+    }
+
+    // Initialize psLib structs
+    if(!strcmp(psEndTagName, "psMetadataItem")) {
+        initMetadataItemXml(ctx, psEndTagName);
+    } else if(!strcmp(psEndTagName, "psVector")) {
+        initVectorXml(ctx, psEndTagName);
+    } else if(strcmp(psEndTagName, "psRoot")) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_TAG_UNKNOWN, psEndTagName);
+    }
+
+    // Free temporary metadata item and its hash table
+    psListRemove(tables->data.list, PS_LIST_TAIL);
+
+    psFree(psEndTagName);
+
+    return;
+}
+
+psMetadata*  psMetadataParseConfigXml(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
+{
+    xmlSAXHandler saxHandler;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(fileName, NULL);
+
+    // Allocate metadata if necessary
+    if (md == NULL) {
+        md = psMetadataAlloc();
+    }
+
+    // Sax handler initializations
+    saxHandler.internalSubset           = NULL;
+    saxHandler.isStandalone             = NULL;
+    saxHandler.hasInternalSubset        = NULL;
+    saxHandler.hasExternalSubset        = NULL;
+    saxHandler.resolveEntity            = NULL;
+    saxHandler.getEntity                = NULL;
+    saxHandler.entityDecl               = NULL;
+    saxHandler.notationDecl             = NULL;
+    saxHandler.attributeDecl            = NULL;
+    saxHandler.elementDecl              = NULL;
+    saxHandler.unparsedEntityDecl       = NULL;
+    saxHandler.setDocumentLocator       = NULL;
+    saxHandler.startDocument            = NULL;
+    saxHandler.endDocument              = NULL;
+    saxHandler.startElement             = saxStartElement;
+    saxHandler.endElement               = saxEndElement;
+    saxHandler.reference                = NULL;
+    saxHandler.characters               = NULL;
+    saxHandler.ignorableWhitespace      = NULL;
+    saxHandler.processingInstruction    = NULL;
+    saxHandler.comment                  = NULL;
+    saxHandler.warning                  = xmlParserError;
+    saxHandler.error                    = xmlParserError;
+    saxHandler.fatalError               = xmlParserError;
+    saxHandler.getParameterEntity       = NULL;
+    saxHandler.cdataBlock               = NULL;
+    saxHandler.externalSubset           = NULL;
+    saxHandler.initialized              = 1;
+    saxHandler._private                 = md;
+    saxHandler.startElementNs           = NULL;
+    saxHandler.endElementNs             = NULL;
+    saxHandler.serror                   = NULL;
+
+    // Parse XML file
+    if (xmlSAXUserParseFile(&saxHandler, NULL, fileName)) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
+        return NULL;
+    }
+
+    // Parser and memory cleanups for libxml2
+    xmlCleanupParser();
+    xmlMemoryDump();
+
+    return md;
+}
Index: /tags/pap_tags/pap_branch_050518/psLib/src/xml/psXML.h
===================================================================
--- /tags/pap_tags/pap_branch_050518/psLib/src/xml/psXML.h	(revision 22271)
+++ /tags/pap_tags/pap_branch_050518/psLib/src/xml/psXML.h	(revision 22271)
@@ -0,0 +1,85 @@
+/** @file  psMetadataIO.h
+ *
+ *  @brief Contains metadata input/output functions.
+ *
+ *  This file defines functions to read and write metadata to/from an external file.
+ *
+ *  @ingroup Metadata
+ *
+ *  @author Ross Harman, MHPCC
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-03-07 20:58:50 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#ifndef PS_METADATAIO_H
+#define PS_METADATAIO_H
+
+/// @addtogroup Metadata
+/// @{
+
+
+/** Print metadata item to file.
+ *
+ *  Metadata items may be printed to an open file descriptor based on a
+ *  provided format. The format is a sprintf format statement with exactly
+ *  one % formatting command. If the metadata item type is a numeric type,
+ *  this formatting command must also be numeric, and the type conversion
+ *  performed to the value to match the format type. If the metadata type is
+ *  a string, the fromatting command must also be for a string. If the
+ *  metadata type is any other data type, printing is not allowed.
+ *
+ * @return psMetadataItem* : Pointer metadata item.
+ */
+bool psMetadataItemPrint(
+    FILE * fd,                         ///< Pointer to file to write metadata item.
+    const char *format,                ///< Format to print metadata item.
+    const psMetadataItem* metadataItem ///< Metadata item to print.
+);
+
+/** Read metadata header.
+ *
+ *  Read a metadata header from file. If the file is not found, an error is
+ *  reported.
+ *
+ *  @return psMetadata* : Pointer to resulting metadata.
+ */
+psMetadata* psMetadataReadHeader(
+    psMetadata* output,                ///< Resulting metadata from read.
+    char *extName,                     ///< File name extension string.
+    psS32 extNum,                      ///< File name extension number. Starts at 1.
+    char *fileName                     ///< Name of file to read.
+);
+
+/** Read metadata configuration file.
+ *
+ *  Loads pre-defined settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+psMetadata* psMetadataParseConfig(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    psU32 *nFail,                      ///< Number of failed lines.
+    const char *fileName,              ///< Name of file to read.
+    psBool overwrite                   ///< Allow overwrite of duplicate specifications.
+);
+
+/** Read XML metadata configuration file.
+ *
+ *  Loads pre-defined XML settings by parsing a configuration file into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from read.
+ */
+
+psMetadata*  psMetadataParseConfigXml(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    psU32 *nFail,                      ///< Number of failed lines.
+    const char *fileName,              ///< Name of file to read.
+    psBool overwrite                   ///< Allow overwrite of duplicate specifications.
+);
+
+/// @}
+
+#endif
