Index: trunk/doc/pslib/psLibSDRS.tex
===================================================================
--- trunk/doc/pslib/psLibSDRS.tex	(revision 275)
+++ trunk/doc/pslib/psLibSDRS.tex	(revision 276)
@@ -4,5 +4,5 @@
 % basic document variables
 \title{Pan-STARRS IPP Library Design}
-\author{}
+\author{Paul Price, Eugene Magnier, Robert Lupton}
 \shorttitle{PSLib Design}
 \group{Pan-STARRS Algorithm Group}
@@ -67,8 +67,622 @@
 \section{System Utilities}
 
-\note{INSERT HERE STUFF FROM RHL'S \file{utils.pdf}}
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Memory Management}
+\hlabel{psMemBlock}
+
+\subsubsection{Introduction}
+
+The \PS{} software system will need a level of memory management
+placed between the operating system (malloc/free) and the high level
+routines (e.g. \code{psMetaDataAlloc}).
+
+This layer is in addition to the possibility that specific heavily
+used data types may need their own special-purpose memory managers;
+but as we have specified that all user-level objects be allocated via
+\code{typeAlloc/typeFree} functions, we will easily be able to
+implement such functionality without impacting on the facilities
+described here.
+
+All of the memory management APIs should be provided in a header file
+\file{psMemory.h} which is included by \file{psUtils.h}.
+
+\subsubsection{Rationale}
+
+We wish to insert our own layer of memory management for a number of
+reasons:
+
+\begin{itemize}
+\item
+  We wish to insulate ourselves from the details of the system-provided
+  \code{malloc}.  There is no guarantee that the goals of the system
+  architect align with those of the \PS{} processing
+
+\item
+  We need at least a wrapper layer which handles failed memory
+  requests without requiring the application programmer to check
+  every attempted allocation.
+
+\item
+  We need to provide a mechanism for tracking and fixing memory
+  leaks.  While it is possible to do this by linking with external
+  libraries (\tbd{reference}), it is convenient to do so within the
+  \PS{} framework.
+
+\item
+  Similarly, we wish to provide convenient hooks to detect and diagnose
+  memory corruption.
+
+\item
+  While debugging complex scientific code, it is very useful to be
+  able to trace a given data structure as it passed through the
+  processing pipeline.
+
+\item
+  There may be other features that we wish to add in the future (e.g.
+  associating a type with every allocation).
+\end{itemize}
+
+\subsubsection{A Minimal Specification}
+
+The previous section laid out a number of desiderata for a memory
+management system.  Rather than implement our own heap-manager at this
+stage of the project, I propose that we put in place a sufficient
+set of data structures and APIs, but initially implement only
+a simple subset (e.g. that we not implement a multi-bucket memory
+manager that takes in 100Mb chunks from the system and performs
+sophisticated defragmentation and garbage collection).
+
+Subject to agreement with the IfA, some of the functions proposed
+here may be omitted from the initial implementation; in particular
+the routines \code{psMemCheckCorruption} and \code{psMemCheckLeaks}
+may be hard to build efficiently in a simple implementation based
+upon adding extra fields to allocation requests.
+
+\paragraph{Extra information to be saved by Memory Allocation Functions}
+
+It is required that a table of all allocated memory blocks be
+maintained by the \PS{} memory system.
+
+Each allocated block should preserve at least the information present
+in the following struct definition:
+\begin{verbatim}
+  typedef struct {
+      const unsigned long id;           // a unique ID for this allocation
+      const char *file;                 // set from __FILE__ in e.g. p_psAlloc
+      const int lineno;                 // set from __LINE__ in e.g. p_psAlloc
+      int refcntr;                      // how many times pointer is referenced
+      const void *magic;                // initialised to P_PS_MEMMAGIC
+  } psMemBlock;
+\end{verbatim}
+
+The value of \code{P_PS_MEMMAGIC} shall be \code{(void *)0xdeadbeef}%
+\footnote{Why this choice? Tradition, and because it's easy to notice
+  in a hex dump.}
+
+\paragraph{APIs for using Memory Allocation Functions}
+
+\begin{table}
+\begin{verbatim}
+void *psAlloc(size_t size);
+void *psRealloc(void *ptr, size_t size);
+void psFree(void *ptr);
+
+void *p_psAlloc(size_t size, const char *file, int lineno);
+void *p_psRealloc(void *ptr, size_t size, const char *file, int lineno);
+void p_psFree(void *ptr, const char *file, int lineno);
+
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+#define psFree(size) p_psFree(size, __FILE__, __LINE__)
+\end{verbatim}
+\begin{caption}{The APIs required to use the \PS{} memory allocation routines}
+  \hlabel{tabUsageAPI}
+  The APIs required to use the \PS{} memory allocation routines, and as
+  provided by \file{psMemory.h}.
+\end{caption}
+\end{table}
+
+The types and function prototypes for the part of the memory API
+concerned with allocating and freeing memory are given in table
+\ref{tabUsageAPI}).
+
+N.b.
+\begin{itemize}
+\item
+  The functions \code{psAlloc}, \code{psRealloc}, and
+  \code{psFree} are defined, and are required to be equivalent to
+  \code{p_psAlloc}, \code{p_psRealloc}, and \code{p_psFree}
+  with the final two arguments \code{"(unknown)"} and \code{0}.
+
+  In the descriptions that follow, I shall not distinguish between the
+  functions with and without the \code{p_} prefix.
+
+\item
+  Except as specified below, the functions \code{psAlloc},
+  \code{psRealloc}, and \code{psFree} have identical semantics
+  to the standard C library functins \code{malloc}, \code{realloc},
+  and \code{free}.
+  
+\item
+  The file \file{psMemory.h} shall take steps to ensure that
+  code calling the functions \code{malloc}, \code{calloc}, \code{realloc},
+  or \code{free} shall not compile (\eg{} \code{#define malloc(S) for})
+  unless the symbol \code{PS_ALLOC_MALLOC} is defined.
+
+\item
+  In all cases, application code will call
+  \code{p_\{psAlloc,psRealloc,psFree\}} via the macros defined above.
+  
+\item
+  The functions \code{psAlloc} and \code{psRealloc} shall never
+  return a \code{NULL} pointer. If they are unable to provide
+  the requested memory they should attempt to obtain the desired
+  memory by calling the routine registered by \code{psMemExhaustedSetCB} (see
+  subsubsection \ref{secMemAdvanced}), and if still unsuccessful,
+  call \code{abort()}.
+
+\item
+  The memory management routines shall maintain the field
+  \code{psMemBlock.refcntr}. It should be set to \code{1}
+  when a block is returned to the user. It is an error to
+  attempt to free a block with \code{psMemBlock.refcntr != 1}
+  (see subsubsection \ref{secMemRefcounter}).
+  
+\item
+  Where practical and efficient, the memory manager shall call
+  the routine registered using the \code{psMemProblemSetCB}
+  (see section \ref{secMemAdvanced})
+  whenever a corrupted block of memory is discovered. For example,
+  doubly-freed blocks can be detected by checking \code{psMemBlock.refcntr}.
+
+\item
+  There is no \code{psCalloc} function. Initialisation of data is
+  almost always more complex than setting all bytes to 0.
+\end{itemize}
+
+\paragraph{APIs for Tracing and Debugging Memory}
+\hlabel{secMemAdvanced}
+
+\begin{table}
+\begin{verbatim}
+typedef int (*psMemCallback)(const psMemBlock *ptr);
+typedef void (*psMemProblemCallback)(const psMemBlock *ptr,
+                                     const char *file, int lineno);
+typedef void *(*psMemExhaustedCallback)(size_t size);
+
+psMemProblemCallback psMemProblemSetCB(psMemProblemCallback func);
+psMemExhaustedCallback psMemExhaustedSetCB(psMemExhaustedCallback func);
+
+psMemCallback psMemAllocateSetCB(psMemCallback func);
+psMemCallback psMemFreeSetCB(psMemCallback func);
+
+int psMemGetId(void);                   // get next memory ID
+
+long psMemSetAllocateID(long id);       // set p_psMemAllocateID to id
+long psMemSetFreeID(long id);           // set p_psMemFreeID to id
+
+int psMemCheckLeaks(
+    int 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)
+int psMemCheckCorruption(int abort_on_error);
+\end{verbatim}
+\begin{caption}{The APIs required to use the \PS{} memory allocation routines}
+  \hlabel{tabCallbackAPI}
+  The APIs required to use the callback and tracing facilities
+  in \PS{} memory allocation routines, as defined in \file{psMemory.h}.
+\end{caption}
+\end{table}
+
+The types and function prototypes for this part of the memory API
+are given in table \ref{tabCallbackAPI}.
+
+\paragraph{Callback Routines}
+
+The four `\code{SetCB}' routines are:
+
+\begin{tabular}{ll|l}
+\textbf{type} &  \textbf{Name} & \textbf{Function of callback} \\
+psMemProblemCallback & psMemProblemSetCB &
+Called when a problem is detected with data being managed on the heap \\
+psMemExhaustedCallback & psMemExhaustedSetCB &
+Called when \code{psAlloc} is unable to satisfy a memory request. \\
+psMemCallback & psMemAllocateSetCB &
+Callback is called when the \code{psMemBlock} with a specified ID is
+allocated. \\
+psMemCallback & psMemFreeSetCB &
+Callback is called when the \code{psMemBlock} with a specified ID is
+freed. \\
+\end{tabular}
+
+N.b.
+\begin{itemize}
+\item
+  In all cases, the `\code{SetCB}' routine takes a pointer to the
+  desired callback function, and returns a pointer to the one that was
+  previously installed. If the function pointer is \code{NULL}, the
+  default callback function is reinstalled.
+
+\item
+  The routine installed by \code{psMemExhaustedSetCB} should return
+  a pointer to the desired memory or \code{NULL}; in the latter case
+  \code{psAlloc} will call \code{abort()}
+
+\item
+  \code{psMemGetId} returns the current value of \code{psMemBlock.id}.
+
+\item
+  The handler specified by \code{psMemAllocateSetCB} is called just
+  before the pointer with \code{psMemBlock.id} equal to
+  \code{p_psMemAllocateID} is returned to the user. The variable
+  \code{p_psMemAllocateID} should not be set directly in any
+  delivered code, \code{psMemSetAllocateID} should be used instead.
+
+\item
+  The handler specified by \code{psMemFreeSetCB} is called just
+  before the pointer with \code{psMemBlock.id} equal to
+  \code{p_psMemFreeID} is freed. The variable
+  \code{p_psMemFreeID} should not be set directly in any delivered
+  code, \code{psMemSetFreeID} should be used instead.
+
+\item
+  The routines \code{psMemSetFreeID} and \code{psMemSetAllocateID}
+  accept the desired ID value (see previous two items) and return
+  the old value to the user.
+
+\item
+  The return values of the handlers installed by \code{psMemAllocateSetCB}
+  and \code{psMemFreeSetCB} are used to increment the values of
+  \code{p_psMemAllocateID} and \code{p_psMemFreeID} respectively.
+
+  For example, the return value \code{0} implies that the value is unchanged;
+  if the value is \code{2} the callback will be called again when the
+  memory ID counter has increased by two.
+\end{itemize}
+
+\paragraph{Memory Tracing and Corruption Routines}
+
+The routine \code{psMemCheckLeaks} may be used to check for memory
+leaks. The return value is the number of blocks that have been
+allocated but not freed.
+
+Only blocks with \code{psMemBlock.id} greater than \code{id0}
+are checked; this allows the user to ignore blocks allocated
+by initialisation routines.
+
+If the argument \code{arr} is non-\code{NULL}, then upon entering
+the call \code{**arr} should be \code{NULL}. Upon return it is set
+to an array of \code{psMemBlock *} pointers, one for each block
+allocated but not freed.  It is the caller's responsibility to free
+this array with \code{psFree}.
+
+If the argument \code{fd} is non\code{NULL}, a one-line summary
+of each block that has been allocated but not freed is written to that
+file descriptor.
+
+The routine \code{psMemCheckCorruption} checks the entire heap for
+corruption, calling the routine registered with
+\code{psMemProblemSetCB} for each block detected as being corrupted.
+The return value is the number of corrupted blocks detected. If the
+argument \code{abort_on_error} is true,
+\code{psMemCheckCorruption} shall call \code{abort()} as soon as
+memory corruption is detected.
+
+\paragraph{Reference Counting}
+\hlabel{secMemRefcounter}
+
+The memory management routines include a field
+\code{psMemBlock.refcntr} which must equal \code{1} whenever
+a pointer is presented to \code{psFree}.
+
+The API for this field is:
+\begin{verbatim}
+int psMemGetRefCounter(void *vptr);        // return refcounter
+void *psMemIncrRefCounter(void *vptr);     // increment refcounter and return vptr
+void *psMemDecrRefCounter(void *vptr);     // decrement refcounter and return vptr
+\end{verbatim}
+
+The functions \code{psMemIncrRefCounter} and \code{psMemDecrRefCounter} shall
+return \code{NULL} if passed a \code{NULL} pointer.
+
+\subsubsubsection{Rationale}
+
+\begin{table}
+\begin{verbatim}
+typedef struct {
+    char *name;
+    int value;
+} psSimple;
+
+psSimple *psSimpleAlloc(const char *name, int val)
+{
+    psSimple *simp = psAlloc(sizeof(psSimple));
+    simp->name = strcpy(psAlloc(strlen(name) + 1), name);
+    simp->value = val;
+
+    return simp;
+}
+
+void psSimpleFree(psSimple *simp)
+{
+    if (simp == NULL) { return; }
+
+    if (psMemGetRefCounter(simp) > 1) {
+        (void)psMemDecrRefCounter(simp);
+        return;
+    }
+}
+\end{verbatim}
+\begin{caption}{An example of reference counting}
+  \hlabel{tabReferenceCounting}
+  An example of using reference counting
+\end{caption}
+\end{table}
+
+The \code{psMemBlock.refcounter} is clearly useful for detecting
+attempts to free memory that is already free.  A more complex
+application is for allowing pointers to complex data-objects (e.g.
+images) to appear in more than one data structure simultaneously
+(see table \ref{tabReferenceCounting}).
+
+Because of the use of the \code{refcounter} field, we can safely put items of
+this type onto many lists:
+\goodbreak
+\begin{verbatim}
+simp = psSimpleAlloc("RHL", 0);
+psDlistAppend(list1, psMemIncrRefCounter(simp));
+psDlistAppend(list2, psMemIncrRefCounter(simp));
+psSimpleFree(simp);
+\end{verbatim}
+
+(Note: in fact there is no need to explicitly increment the counter
+in this case, as the \code{psDlistAppend} (section \ref{psDlist})
+API specifies that it
+does it for you.)
+
+\subsection{Tracing and Logging}
+
+This document defines the \PS{} Tracing and Logging APIs; the former
+refers to debug information that we wish to be able to turn on and off
+without recompiling (although it will \emph{not} be available in
+production code); the latter means information about the processing
+that must be collected and saved, even in the production system.
+
+We envision that we will make extensive use of \code{psTrace} throughout
+the \PS{} code.
+
+\subsubsection{Tracing APIs}
+\hlabel{psTrace}
+
+\begin{table}
+  \begin{verbatim}
+#if defined(PS_NTRACE)
+#  define psTrace(facil, level, ...) /* do nothing */
+#else
+#  define psTrace(facil, level, ...) \
+          p_psTrace(facil, level, __VA_ARGS__)
+#endif
+
+void p_psTrace(const char *facil, int level, ...);
+
+int psSetTraceLevel(const char *facil,  // facilty of interest
+                    int level);         // desired trace level
+int psGetTraceLevel(const char *name);  // facilty of interest
+
+void psTraceReset(void);		// turn off all tracing, and free trace's allocated memory
+
+void psPrintTraceLevels(void);          // print trace levels
+  \end{verbatim}
+  \begin{caption}{The public API for the trace facility
+      from \file{psTrace.h}.}
+    \hlabel{tabTraceAPI}
+  \end{caption}
+\end{table}
+
+The public API for the trace facility (table \ref{tabTraceAPI})
+should be provided in a header file \file{psTrace.h} which
+is included by \file{psUtils.h}.
+
+\begin{itemize}
+  \item
+    Logging is provided by the function \code{psTrace},
+    which is actually a macro.  When the macro \code{PS_NTRACE}
+    is defined, all occurrences of  \code{psTrace} shall
+    be replaced by whitespace.
+
+  \item
+    The first argument to \code{psTrace} is a name of the form
+    \code{aaa.bbb.ccc}. The second is an integer,
+    \code{level}. The remaining arguments are a printf-style format
+    and a (possibly empty) set of values for that formatting
+    string. When this trace is active, \code{psTrace} shall generate
+    the requested output on \code{stdout}, preceded by \code{level}
+    spaces.
+
+  \item
+    The call \code{psSetTraceLevel(name, level)} shall set the trace
+    level for \code{name} to \code{level}.
+
+  \item
+    The call \code{psGetTraceLevel(name)} shall return the level
+    associated with \code{name}.  If \code{name} has not been
+    associated with a level, the routine shall remove the \textit{last}
+    element of \code{name} and attempt to look up its level; this
+    procedure shall be applied recursively until the name is reduced
+    to \code{""}; if this name has no level associated with
+    it, then the value \code{0} shall be returned (see examples
+    below).
+
+    \item
+      The call \code{psTrace(name, level, ...)}
+      shall print the message if and only if
+      \code{psGetTraceLevel(name)} returns a value greater than
+      or equal to \code{level}.
+
+    \item
+      \code{psPrintTraceLevels} shall print a listing of all
+      declared levels, displayed as a hierarchy with sub-components
+      sorted within each component (see examples below).
+
+      Note in particular that the root of the tree, the name \code{""}
+      should print as \code{(root)}, and nodes which have not
+      been assigned a value should list their level as \code{.}.
+
+    \item
+      All of the tracing facilities shall be SWIGed, with the exception
+      of \code{p_psTrace}.
+
+    \item
+      There is no requirement that \code{psTrace} be usable from
+      within the functions that implement \file{psTrace.h}
+      or \file{psMemory.h} systems.
+
+    \item
+      \code{psTraceReset} shall reset all tracing to the state that it
+      had at program initiation, including freeing any memory that the
+      tracing subsystem may have allocated.
+\end{itemize}
+
+\paragraph{Examples of the use of Tracing Facilities}
+
+For example, after the commands:
+\begin{verbatim}
+    psSetTraceLevel("utils.dlist.add.head", 9);
+    psSetTraceLevel("utils.dlist.add", 3);
+    psSetTraceLevel("utils.dlist.remove", 4);
+    psSetTraceLevel("coadd", 2);
+    psSetTraceLevel("coadd.CR.remove.morphology", 5);
+    psSetTraceLevel("utils.hash", 2);
+    psSetTraceLevel("utils.dlist.add", 9);
+    psSetTraceLevel("utils", 1);
+\end{verbatim}
+the command \code{psPrintTraceLevels()} should print:
+\begin{verbatim}
+(root)               0
+ utils               1
+  hash               2
+  dlist              .
+   remove            4
+   add               9
+    head             9
+ coadd               2
+  CR                 .
+   remove            .
+    morphology       5
+\end{verbatim}
+where \code{.} means that the trace level should be inherited from its parent.
+
+After this set of \code{psSetTraceLevel} commands, and if
+\code{PS_NTRACE} is not defined, the following commands
+\begin{verbatim}
+    psTrace("utils.dlist.remove", 2, "Removing psDList key \"%s\"\n", "my_key");
+    psTrace("utils", 2, "Initialising utilities library\n");
+    psTrace("", 2, "This is turned on by trace component \"\"");
+    psTrace("utils.dlist", 2, "Initialising psDList\n");
+    psTrace("utils.dlist.remove", 4, "Removing psDList key \"%s\" (value: \"%d\")\n", "my_key", 12345);
+    psTrace("utils.hash.remove", 4, "Removing hash key \"%s\" (value: \"%d\")\n", "my_key", 12345);
+    psTrace("utils.dlist.add", 1, "Adding psDList key \"%s\"\n", "your_key");
+    psTrace("utils.dlist.find", 2, "Looking up psDList key \"%s\"\n", "some_key");
+    psTrace("coadd.CR.remove", 4, "Removing CRs\n");
+    psTrace("coadd.CR.remove.morphology", 4, "CRs are not fuzzy\n");
+\end{verbatim}
+should produce this output:
+\begin{verbatim}
+  Removing psDList key "my_key"
+    Removing psDList key "my_key" (value: "12345")
+ Adding psDList key "your_key"
+    CRs are not fuzzy
+\end{verbatim}
+
+Note that
+\begin{description}
+\item
+  \code{utils.dlist} messages are at level 1, inherited from \code{utils}, so the
+  \code{Initialising utilities library}, \code{Initialising psDList}, and
+  \code{Looking up psDList key} messages are \emph{not} printed (the traces are at level 2).
+
+\item
+  \code{utils.dlist.remove} messages are at level 4, and are printed.
+
+\item
+  \code{utils.hash} messages are at level 2, and are not printed (the traces are at level 4)
+
+\item
+  \code{coadd.CR.remove} is at level 2 (inherited from \code{coadd}) so \code{Removing CRs}
+  isn't printed.  \code{coadd.CR.remove.morphology} is at level 4, so \code{CRs are not fuzzy} is printed.
+\end{description}
+
+\subsubsection{Message Logging}
+\hlabel{psLogMsg}
+
+\begin{table}
+  \begin{verbatim}
+enum { PS_LOG_ABORT = 0, PS_LOG_ERROR, PS_LOG_WARN, PS_LOG_INFO };
+
+enum { PS_LOG_TO_STDERR, PS_LOG_TO_STDOUT };
+
+void psLogMsg(const char *name, int level, const char *fmt, ...);
+void p_psVLogMsg(const char *name, int level, const char *fmt, va_list ap);
+
+int psSetLogDestination(int dest);
+void psSetLogFormat(const char *fmt);
+
+int psSetLogLevel(int level);
+  \end{verbatim}
+  \begin{caption}{API for message logging}
+    \hlabel{tabLogMsgAPI}
+    The API for message logging.
+  \end{caption}
+\end{table}
+
+The public API for the logging facility (table \ref{tabLogMsgAPI})
+should be provided in a header file \file{psLogMsg.h} which
+is included by \file{psUtils.h}.
+
+The function \code{psSetLogLevel} may be used to set the current
+level of logging; the previous value is returned.
+The default value is \code{PS_LOG_INFO}. Valid values are 0---9
+inclusive (note that only the first four are required to have
+symbolic names).
+
+A call to \code{psLogMsg(name, level, msg, ...)} shall generate
+a log message if \code{level} is less than or equal to the
+value most recently set using \code{psSetLogLevel}. The function
+\code{p_psLogMsg} is identical, except that it expects a
+final \code{va_list} argument.
+
+The format of the log message shall be of the form:
+\begin{verbatim}
+YYYY:MM:DD hh:mm:ssZ|hostname|l|name            |msg
+\end{verbatim}
+\code{YYYY}, \code{MM}, \code{DD}, \code{hh}, \code{mm}, and \code{ss}
+are the year, month (Jan == 1), day of the month, hours (0--23),
+minutes, and seconds when the log message was received.  Note that the
+timestamp is in ISO order, and that the timezone is GMT (hence the
+\code{Z}).
+
+The \code{hostname} is returned by \code{gethostname}, \code{l} is a
+letter associated with the level (\code{A}, \code{E}, \code{W}, and
+\code{I} for \code{PS_LOG_ABORT}, \code{PS_LOG_ERROR}, \code{PS_LOG_WARN},
+and \code{PS_LOG_INFO} respectively. Other levels are represented
+numerically (\code{5} etc.). The other two field, \code{name} and
+\code{msg} are arguments to \code{psLogMsg}; note that \code{name} has
+a fixed width of 15 characters. If \code{msg} doesn't end in a newline,
+a single newline is emitted to terminate the message.
+
+An example message is:
+\begin{verbatim}
+2004:02:24 20:14:18Z|alibaba.IfA.Hawaii.Edu|I|utils          |Hello World
+\end{verbatim}
+
+Log messages are sent to the destination most recently set using
+\code{psSetLogDestination}.  The only two values that are initially
+defined are \code{PS_LOG_TO_STDERR} and \code{PS_LOG_TO_STDOUT} to
+write to \code{stderr} and \code{stdout} respectively.
+
+The fields included in the log message may be controlled using \code{psSetLogFormat} which
+expects a string consisting of the letters \code{H} (host), \code{L} (level), \code{M} (message),
+\code{N} (name), and \code{T} (time).  The default is \code{HLMNT}.
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
@@ -84,5 +698,358 @@
 \end{itemize}
 
-\note{INSERT HERE STUFF FROM RHL'S \file{utils.pdf}}
+\subsection{The Pan-STARRS \texttt{psDlist} doubly-linked list type}
+\hlabel{psDlist}
+
+Pan-STARRS supports doubly linked lists through a type \code{psDlist}.
+The type is defined in the header file \file{psDlist.h}, and consists
+of the following definitions:
+
+\begin{verbatim}
+typedef struct psDlistElem {
+    struct psDlistElem *prev;           // previous link in list
+    struct psDlistElem *next;           // next link in list
+    void *data;                         // real data item
+} psDlistElem;
+
+typedef struct {
+    int n;                              // number of elements on list
+    psDlistElem *head;                  // first element on list (may be NULL)
+    psDlistElem *tail;                  // last element on list (may be NULL)
+    psDlistElem *iter;                  // iteration cursor; private
+} psDlist;
+
+enum {                                  // Special values of index into list
+    psDlistHead = 0,                    // at head
+    psDlistTail = -1,                   // at tail
+    psDlistUnknown = -2,                // unknown position
+    psDlistPrev = -3,                   // previous element
+    psDlistNext = -4                    // next element
+};
+\end{verbatim}
+
+The API is:
+
+\begin{verbatim}
+psDlist *psDlistAlloc(void *data);        // initial data item; may be NULL
+
+void psDlistFree(psDlist *list,          // list to destroy
+                void (*elemFree)(void *)); // destructor for list data, or NULL
+
+/*
+ * List maintainence functions
+ */
+psDlist *psDlistAdd(psDlist *list,      // list to add to (may be NULL)
+                    void *data,         // data item to add
+                    int where);         // index, psDlistHead, or psDlistTail
+psDlist *psDlistAppend(psDlist *list,   // list to append to (may be NULL)
+                       void *data);     // data item to add
+void *psDlistRemove(psDlist *list,      // list to remove element from
+                    void *data,         // data item to remove
+                    int which);         // index of item, or psDlistUnknown,
+                                        // or psDlistNext, or psDlistPrev
+void *psDlistGet(const psDlist *list,   // list to retrieve element from
+                 int which);            // index of item, or psDlistNext,
+                                        // or psDlistPrev
+/*
+ * Conversions to/from arrays
+ */
+psVoidPtrArray *psDlistToArray(psDlist *dlist);
+psDlist *psArrayToDlist(psVoidPtrArray *arr);
+\end{verbatim}
+
+All data items placed onto lists (e.g. with \code{psDlistAdd})
+shall have their reference counters (section \ref{secMemRefcounter}) incremented.
+When elements
+are removed from a list with \code{psDlistRemove}, they shall
+have their reference counters decremented. The action of retrieving
+data from a list (with \code{psDlistGet}) shall not affect
+their reference counter.
+
+If \code{psDlistFree}'s argument \code{elemFree} is NULL, the
+list should be deleted, but not the elements on it (although their
+\code{refcounter}'s should be decremented).
+
+Iteration over all elements of the list is provided by the functions:
+\begin{verbatim}
+void psDlistSetIterator(psDlist *list, int where, int which);
+void *psDlistGetNext(psDlist *list, int which);
+void *psDlistGetPrev(psDlist *list, int which);
+\end{verbatim}
+in which the \code{where} argument must be \code{PS_DLIST_HEAD} or \code{PS_DLIST_TAIL},
+to start at the head or tail of the list, and \code{psDlistGetNext} and \code{psDlistGetPrev}
+return the next/previous element. The argument \code{which} identifies which of potentially
+many iteration cursors should be used; it must currently always be \code{0}.
+
+Explicit traversal of the list using the \code{psDlistElem}'s
+\code{prev} and \code{next} pointers is also supported.
+
+The routines to convert to and from \code{psVoidPtrArray}s,
+\code{psDlistToArray} and \code{psArrayToDlist} shall ensure that the
+objects on the arrays and lists have had their reference pointers
+correctly incremented (see section \ref{secArrayVoidPtr}) (\eg{} that
+\code{psArrayToDlist(psDlistToArray(list))} returns a properly-formed
+\code{psDlist}).
+
+\subsection{The \PS{} Array types}
+
+\begin{table}
+  \begin{verbatim}
+  \end{verbatim}
+  \begin{caption}{The array creation macros defined in \file{psArray.h}}
+    \hlabel{tabPsArray}
+  \end{caption}
+\end{table}
+
+\subsubsection{Arrays of Simple Types}
+
+Any \PS/ datatype \code{psType} may be associated with an array type
+\code{psTypeArray}:
+\begin{verbatim}
+typedef struct {
+  int size;
+  int n;
+  psType *arr;
+} psTypeArray;
+\end{verbatim}
+with associated constructors and a destructor:
+\begin{verbatim}
+psTypeArray *psTypeAlloc(int n, int size);
+psTypeArray *psTypeRealloc(psTypeArray *arr, int n);
+void psTypeFree(psTypeArray *arr);  
+\end{verbatim}
+
+The argument \code{n} is the dimension of the array; \code{size}
+is the number of elements allocated ($s \ge n$).
+
+This type and functions may be declared and defined using two macros
+from \file{psArray.h} (table \ref{tabPsArray}),
+\code{PS_DECLARE_ARRAY_TYPE(psType)} and
+\code{PS_CREATE_ARRAY_TYPE(psType)}.  The former defines the typedef
+and declares the prototypes (and is thus suitable for use in a
+header file); the latter generates the code for the three functions
+\code{psType(Alloc|Realloc|Free} (and should thus appear in exactly one
+source file for a given type).
+
+The \code{psType} should be a single word (e.g. \code{psXY}); in particular,
+there is no requirement to support a pointer type (\eg{} \code{psXY *});
+see next section.
+
+\subsubsection{Arrays of Pointer Types}
+
+The data type created with \code{PS_CREATE_ARRAY_TYPE} (\code{psType})
+contains an array of \code{psType}s not
+pointers to \code{psType}s; this means that the individual elements are
+not allocated using \code{psTypeAlloc}, are not correctly initialized,
+and shouldn't be individually deleted with \code{psTypeFree};
+
+If you wish to use arrays of pointers, use the macros
+\code{PS_DECLARE_ARRAY_PTR_TYPE(psType)} and
+\code{PS_CREATE_ARRAY_PTR_TYPE(psType)} (table \ref{tabPsArray}). These
+create types \code{typedef psType *psTypePtr} and \code{psTypePtrArray}:
+\begin{verbatim}
+typedef struct {
+  int size;
+  int n;
+  psTypePtr *arr;
+} psTypePtrArray;
+\end{verbatim}
+with associated constructors and a destructor:
+\begin{verbatim}
+psTypePtrArray *psTypePtrAlloc(int n, int size);
+psTypePtrArray *psTypePtrRealloc(psTypePtrArray *arr, int n);
+void psTypePtrArrayFree(psTypePtrArray *arr);  
+\end{verbatim}
+
+These constructors create arrays of \code{psType *} and call
+\code{psTypeAlloc} and \code{psTypeFree} to allocate and free the
+elements. As for the simple arrays, The former defines the typedef and
+declares the prototypes (and is thus suitable for use in a header
+file) and the latter generates the code for the three functions
+\code{psType(Alloc|Realloc|Free} (and should thus appear in exactly one
+source file for a given type).
+
+The objects pointed to by these types have had their \code{refCounter}s
+incremented (see \ref{secMemRefcounter}); to remove an element from the array you
+need to say something like:
+\begin{verbatim}
+  psTypePtrArray *pt = psTypePtrArrayAlloc(10, 10);
+  psType *xy = psMemDecrRefCounter(pt->arr[0]);
+  pt->arr[0] = NULL;
+\end{verbatim}
+
+\subsubsection{Arrays of \texttt{void *}}
+\hlabel{secArrayVoidPtr}
+
+Arrays of \code{void *} are different, as the need an explicitly-specified
+destructor.
+
+\file{psArray.h} shall specify a type \code{psVoidPtrArray} that
+behaves in all respects as if it had been created with:
+\begin{verbatim}
+typedef void *psVoidPtr;
+PS_DECLARE_ARRAY_TYPE(psVoidPtr);
+PS_CREATE_ARRAY_TYPE(psVoidPtr);
+\end{verbatim}
+except that its destructor is specified as:
+\begin{verbatim}
+void psVoidPtrArrayFree(psVoidPtrArray *arr,      // array to destroy
+                       void (*elemFree)(void *)); // destructor for array data
+\end{verbatim}
+
+The routine \code{psVoidPtrArrayFree} assumes that all pointers
+had their reference counters incremented
+when they were inserted onto the array.\footnote{%
+  \eg{} \code{va->arr[i] = psMemIncrRefCounter(ptr);}}
+
+If \code{psVoidPtrArrayFree}'s argument \code{elemFree} is NULL, the
+list should be deleted, but not the elements on it (although their
+\code{refcounter}'s should be decremented).
+
+\subsubsection{Examples of Array Types}
+
+The following is a complete C program that illustrates the use of
+\code{array}s.
+\begin{verbatim}
+#include "psUtils.h"
+
+typedef struct {
+    int x, y;
+} psXY;
+
+psXY *psXYAlloc(void)
+{
+    return psAlloc(sizeof(psXY));
+}
+
+void psXYFree(psXY *xy)
+{
+    psFree(xy);
+}
+
+PS_DECLARE_ARRAY_TYPE(psXY);
+PS_CREATE_ARRAY_TYPE(psXY);
+
+PS_DECLARE_ARRAY_PTR_TYPE(psXY);
+PS_CREATE_ARRAY_PTR_TYPE(psXY);
+
+int main(void)
+{
+    psXYArray *t = psXYArrayAlloc(10, 15);
+    psXYPtrArray *pt = psXYPtrArrayAlloc(10, 10);
+
+    for (int i = 0; i < t->n; i++) {
+	t->arr[i].x = i;
+	pt->arr[i]->y = 10*i;
+    }
+
+    t = psXYArrayRealloc(t, 5);
+    t = psXYArrayRealloc(t, 8);
+
+    for (int i = 0; i < t->n; i++) {
+	printf("%d %d  ", t->arr[i].x, pt->arr[i]->y);
+    }
+    printf("\n");
+    
+    psXYArrayFree(t);
+
+    psXY *xy = psMemDecrRefCounter(pt->arr[0]);
+    pt->arr[0] = NULL;
+    psXYFree(xy);    
+    
+    psXYPtrArrayFree(pt);
+
+    psMemCheckLeaks(0, NULL, stderr);
+
+    return 0;
+}
+\end{verbatim}
+
+\subsection{Hash Tables}
+\hlabel{psHash}
+
+\begin{table}
+  \begin{verbatim}
+typedef struct HashTable psHash;
+
+psHash *psHashAlloc(int nbucket);       // initial number of buckets
+void psHashFree(psHash *table,          // hash table to be freed
+               void (*itemFree)(void *item)); // how to free hashed data;
+                                        // or NULL
+
+void *psHashInsert(psHash *table,       // table to insert in
+                   const char *key,     // key to use
+                   void *data,          // data to insert
+                   void (*itemFree)(void *item)); // how to free hashed data;
+                                        // or NULL
+void *psHashLookup(psHash *table,       // table to lookup key in
+                   const char *key);    // key to lookup
+
+void *psHashRemove(psHash *table,       // table to lookup key in
+                   const char *key);    // key to lookup
+  \end{verbatim}
+  \begin{caption}{The public API for hash tables from \file{psHash.h}}
+    \hlabel{tabPsHash}
+  \end{caption}
+\end{table}
+
+The public API for the hash table (table \ref{tabPsHash})
+should be provided in a header file \file{psHash.h} which
+is included by \file{psUtils.h}.
+\footnote{
+  We choose not to use the posix function \code{hcreate},
+  \code{hdestroy}, and \code{hsearch} as they only support
+  a single hash table at any one time.}
+
+A hash table is an abstract type \code{psHash}.  The argument
+\code{nbucket} to \code{psHashAlloc} is a non-binding suggestion
+from the user for the initial size of the hash table.
+
+If the \code{itemFree} argument to \code{psHashFree} is non-NULL,
+it will be used to delete the data items that have been stored
+in the hash table; if it is NULL this is the responsibility of
+the caller.
+
+The routine \code{psHashInsert} must provide a non-NULL \code{itemFree}
+argument if it wishes to change the value previously inserted keys;
+if \code{itemFree} is NULL attempting to insert a pre-existing key
+is an error, and the routine will return NULL.  If  \code{psHashInsert}
+succeeds it returns \code{data}.
+
+\code{psHashLookup} returns the \code{data} associated with the
+key, or NULL if the key's invalid.
+
+\code{psHashRemove} removes the entry associated with the
+key from the table, and returns the \code{data}; if the key's invalid it returns NULL.
+
+\subsection{Miscellaneous Utilities}
+
+The API for miscellaneous \PS{} utilities is provided by \file{psMisc.h}
+which shall be included by \file{psUtils.h}.
+
+\begin{table}
+  \begin{verbatim}
+#define PS_CONCAT(A, B) A ## B          // Expands to AB
+#define PS_CONCAT2(A, B) PS_CONCAT(A, B) // Also expands to AB
+#define PS_CONCAT3(A, B, C) A ## B ## C // Expands to ABC
+
+#define PS_STRING(S) #S                 // converts argument S to string
+    
+void psAbort(const char *name, const char *fmt, ...);
+void psError(const char *name, const char *fmt, ...);
+char *psStringCopy(const char *str);
+  \end{verbatim}
+  \begin{caption}{The utilities provided by \file{psMisc.h}}
+      \hlabel{psMisc}
+  \end{caption}
+\end{table}
+
+\code{psAbort} shall call \code{psMsgLog} with a level of \code{PS_LOG_ABORT},
+and then call \code{abort}.
+\code{psError} shall call \code{psMsgLog} with a level of \code{PS_LOG_ERROR},
+and then return.
+In cases of doubt, a good choice for
+\code{name} is \code{__func__}.
+
+\code{psStringCopy} shall allocate and return a copy of the input string.
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -641,4 +1608,379 @@
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Image handling}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Basic Images}
+
+The most important data product produced by the telescope is an image.
+The simplest image is a 2-D collection of pixels, each with some
+value.  We require a basic image data type:
+
+\begin{verbatim}
+/// basic image data structure.
+typedef struct psImage {
+    psType type;                        ///< image data type and dimension
+    int nx, ny;                         ///< size of image 
+    int x0, y0;                         ///< data region relative to parent 
+    union {
+        psF32 **rows;                   ///< == rows_f32 
+        psS8  **rows_s8;                ///< pointers to psS8 data 
+        psS16 **rows_s16;               ///< pointers to psS16 data 
+        psS32 **rows_s32;               ///< pointers to psS32 data 
+        psU8  **rows_u8;                ///< pointers to psU8 data 
+        psU16 **rows_u16;               ///< pointers to psU16 data 
+        psU32 **rows_u32;               ///< pointers to psU32 data 
+        psF32 **rows_f32;               ///< pointers to psF32 data 
+        psF64 **rows_f64;               ///< pointers to psF64 data 
+    } rows;
+    struct psImage *parent;             ///< parent, if a subimage 
+    struct psImage *children;           ///< children of this region 
+    int Nchildren;                      ///< number of subimages 
+} psImage;
+\end{verbatim}
+
+This structure represents an image consisting of a 2-D array of
+pixels.  The size of this array is given by the elements \code{(nx,
+ny)}.  The data type of the pixel is defined by the \code{psType type}
+entry (see \ref{TBD}).  (n.b. that for FITS images, these values are
+restricted to the datatypes equivalent to the valid BITPIX values 8,
+16, 32, -32, -64).  The image represented in the data structure may
+represent a subset of the pixels in a complete array.  The offset of
+the \code{(0,0)} pixel in this array relative to the parent array is
+given by the elements \code{(x0,y0)}.  The structure may include
+references to subrasters (\code{children, Nchildren}) and/or to a
+containing array (\code{parent}).
+
+We require a variety of functions to manipulate these image
+structures, including creation, destruction, input, output, and
+various manipulations of the pixels.  The required functions are
+listed below.
+
+Create an image of a specified width, height, and data type.  This
+function must allow any of the valid image data types and not restrict
+to the valid FITS BITPIX types.
+\begin{verbatim}
+psImage *
+psImageAlloc (int nx,                   ///< image width 
+              int ny,                   ///< image height 
+              psType type)              ///< image data type 
+\end{verbatim}
+
+Define a subimage of the specified area of the given image.  This
+function must return an error if the requested subset area lies
+outside of the parent image.
+\begin{verbatim}
+psImage *
+psImageSubset (psImage *image,          ///< parent image 
+               int nx,                  ///< subimage width (<= image.nx - x0)  
+               int ny,                  ///< subimage width (<= image.ny - y0)  
+               int x0,                  ///< subimage x-offset (0 <= x0 < nx)   
+               int y0)                  ///< subimage y-offset (0 <= y0 < ny)   
+\end{verbatim}
+
+Free the memory associated with a specific image.  \note{does this
+  free the input structure or just the allocated elements?}  Free the
+  children of the image if they exist.
+\begin{verbatim}
+void 
+psImageFree (psImage *image)            ///< free this image
+\end{verbatim}
+
+Free the memory associated with the children of a specific image.  
+\begin{verbatim}
+int 
+psImageFreeChildren (psImage *image)    ///< free children of this image
+\end{verbatim}
+
+Create a copy of the specified image.  If the output target pointer is
+not NULL, place the result in the specified structure.
+\begin{verbatim}
+psImage *
+psImageCopy (psImage *output,           ///< target structure for output image data
+             psImage *input)            ///< copy this image 
+\end{verbatim}
+
+Extract pixels from rectlinear region to a vector (array of floats).
+The output vector contains either \code{nx} or \code{ny} elements,
+based on the value of the direction: e.g., if \code{direction} is
+\tbd{+x}, there are \code{nx} elements.  The input region is collapsed
+in the perpendicular 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 \code{psStats stats}.
+\begin{verbatim}
+psFloatArray *
+psImageSlice (psImage *input,           ///< extract slice from this image
+              int x,                    ///< starting x coord of region to slice
+              int y,                    ///< starting y coord of region to slice
+              int nx,                   ///< width of region in x
+              int ny,                   ///< width of region in y
+              int direction,            ///< direction of vector along slice
+              psStats *stats)           ///< defines statistics used to find output values
+\end{verbatim}
+
+Extract pixels from an image along a line to a vector (array of
+floats).  The vector \code{xs,ys} - \code{xe,ye} forms the basis of
+the output vector.  Pixels are considered in a rectangular region of
+width \code{dw} about this vector.  The input region is collapsed in
+the perpendicular direction, and each element of the output vector
+represents a pixel-sized boxes, where the value is derived from the
+statistics of the pixels interpolated along the perpendicular
+direction.  The statistic used to derive the output vector value is
+specified by \code{psStats stats}.
+\begin{verbatim}
+psFloatArray *
+psImageCut (psImage *input,             ///< extract cut from this image
+            float xs,                   ///< starting x coord of cut
+            float ys,                   ///< starting y coord of cut
+            float xe,                   ///< ending x coord of cut
+            float ye,                   ///< ending y coord of cut
+            float dw,                   ///< width of cut
+            psStats *stats)             ///< defines statistics used to find output values
+ \end{verbatim}
+
+
+Extract radial annulii data to a vector.  A vector is constructed
+where each vector elements is derived from the statistics of the
+pixels which land in one of a sequence of annulii.  The annulii are
+centered on the image pixel coordinate \code{x,y}, and have width
+\code{dr}.  The number of annulii is $radius / dr$.  The statistic
+used to derive the output vector value is specified by \code{psStats
+stats}
+\begin{verbatim}
+psFloatArray *
+psImageRadialCut (psImage *input,       ///< extract profile from this image
+                  float x,              ///< center x coord of annulii
+                  float y,              ///< center y coord of annulii
+                  float radius,         ///< outer radius of annulii
+                  float dr,             ///< radial step size of annulii
+                  psStats *stats)       ///< defines statistics used to find output values 
+\end{verbatim}
+
+%/// Extract a 2-d contour from an image at the given threshold.
+%\begin{verbatim}
+%psFloatArray *
+%psImageContour (psImage *input,        ///< create contour for this image
+%               float threshold,        ///< contour image at this threshold
+%               int binning)            ///< bin image by this value for contour calculation
+%\end{verbatim}
+
+Rebin image to new scale.  A new image is constructed in which the
+dimensions are reduced by a factor of \code{scale}.  The output image
+represents a one-to-one mapping of the pixels in the input image,
+except for edge effects.  Each pixel in the output image is derived
+from the statistics of the corresponding input image pixels based on
+the statistics specified by \code{psStats stats}.
+\tbd{interpolation?}
+\begin{verbatim}
+psImage *
+psImageRebin (psImage *input,           ///< rebin this image
+              float scale,              ///< rebinning scale: doutput = scale*dinput
+              psStats *stats)           ///< defines statistics used to find output values
+\end{verbatim}
+
+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 of \tbd{value}.  The center of rotation is always
+the center pixel of the image.  The rotation is specified in the sense
+that a positive value is a clock-wise rotation.  
+\begin{verbatim}
+psImage *
+psImageRotate (psImage *input,          ///< rotate this image
+               float angle)             ///< rotate by this amount (degrees)
+\end{verbatim}
+
+Shift image by an arbitrary number of pixels (\code{dx,dy}) in either
+direction.  If the shift values are fractional, the output pixel
+values shoul 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 loast.  Newly exposed pixels are
+set to the value given by \code{exposed}.  
+\begin{verbatim}
+psImage *
+psImageShift (psImage *input,           ///< shift this image
+              float dx,                 ///< shift by this amount in x
+              float dy,                 ///< shift by this amount in y
+              float exposed)            ///< set exposed pixels to this value
+\end{verbatim}
+
+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).
+\begin{verbatim}
+psImage *
+psImageRoll (psImage *input,            ///< roll this image
+             int dx,                    ///< roll this amount in x
+             int dy)                    ///< roll this amount in y
+\end{verbatim}
+
+Determine statistics for image (or subimage).  The statistics to be
+determined are specified by \code{psStats stats}.
+\begin{verbatim}
+psStats *
+psImageGetStats (psImage *input,        ///< image (or subimage) to calculate stats
+                 psStats *stats)        ///< defines statistics to be calculated
+\end{verbatim}
+
+Construct a histogram from an image (or subimage).  The histogram to
+generate is specified by \code{psHistogram hist}.
+\begin{verbatim}
+psHistogram *
+psImageHistogram (psHistogram *hist,    ///< input histogram description & target
+                  psImage *input)       ///< determine histogram of this image
+\end{verbatim}
+
+Fit a 2-D polynomial surface to an image.  The input structure
+\code{coeffs} contains the desired order and terms of interest.
+\tbd{how do we specify the renomalization?}
+\begin{verbatim}
+psPolynomial2D *
+psImageFitPolynomial (psImage *input,   ///< image to fit
+                      psPolynomial2D *coeffs) ///< coefficient structure carries in desired terms
+\end{verbatim}
+
+Evaluate a 2-D polynomial surface to image pixels.  Given the input
+polynomial coefficients, return an image generated on the basis of the
+input image pixels which evaluates the polynomial for all pixels in
+the image.a
+\begin{verbatim}
+int
+psImageEvalPolynomial (psImage *input,  ///< image to fit
+                       psPolynomial2D *coeffs) ///< coefficient structure carries in desired terms
+\end{verbatim}
+
+Read an image or subimage from a named file.  This function is a
+wrapper to the FITS library function.  The input parameters allow or a
+subimage to be read.  The starting pixel of the region is specified by
+\code{x,y}, while the dimensions of the requested region are specified
+by \code{nx,ny}.  A value of -1 for these two parameters specifies the
+full array of the requested image.  If the native image is a cube, the
+value of z specifies the requested slice of the image.  The data is
+read from the extension specified by extname (matching the EXTNAME
+keyword) or by the extnum value (with -1 representing the PHU, 0 the
+first extension, etc).  This function must return an error if any of
+the specified parameters are out of range for the data in the image
+file, if the specified image file does not exist.  \tbd{what do we do
+with a 0D or 1D image?}
+\begin{verbatim}
+psImage *
+psImageReadSection (psImage *output,    ///< place data in this structure for output 
+                    int x,              ///< starting x coord of region
+                    int y,              ///< starting y coord of region
+                    int nx,             ///< x size of region (-1 for full range)
+                    int ny,             ///< y size of region (-1 for full range)
+                    int z,              ///< plane of interest
+                    char *extname,      ///< MEF extension name ("PHU" for primary header)
+                    int extnum,         ///< MEF extension sequence number (-1 for PHU)
+                    char *filename)     ///< file to read data from
+\end{verbatim}
+ 
+Read an image or subimage from file descriptor.  The input parameters
+and their behavior for this function are identical with those in
+\code{psImageReadSection}.
+\begin{verbatim}
+psImage *
+psImageFReadSection (psImage *output,   ///< place data in this structure for output 
+                     int x,             ///< starting x coord of region            
+                     int y,             ///< starting y coord of region            
+                     int dx,            ///< x size of region (-1 for full range)          
+                     int dy,            ///< y size of region (-1 for full range)          
+                     int z,             ///< plane of interest                     
+                     char *extname,     ///< MEF extension name ("PHU" for primary header)                         
+                     FILE *f)           ///< file descriptor to read data from             
+\end{verbatim}
+
+Write an image section to named file, which may exist.  This
+operatation may write a portion of an image over the existing bytes of
+an existing image.  If the file does not exist, it should be created.
+If the specified extention does not exist, it should be created.  If
+an extension is specified and no PHU exists, a basic PHU should be
+created.  
+\begin{verbatim}
+psImage *
+psImageWriteSection (psImage *input,    ///< image to write out
+                     int x,             ///< starting x coord of region            
+                     int y,             ///< starting y coord of region            
+                     int z,             ///< plane of interest                     
+                     char *extname,     ///< MEF extension name ("PHU" for primary header)                         
+                     char *filename)    ///< file to write data to                 
+\end{verbatim}
+
+Write an image section to file descriptor.
+\begin{verbatim}
+psImage *
+psImageFWriteSection(psImage *input,    ///< image to write out
+                     int x,             ///< starting x coord of region            
+                     int y,             ///< starting y coord of region            
+                     int z,             ///< plane of interest                     
+                     char *extname,     ///< MEF extension name                    
+                     FILE *f)           ///< file descriptor to write data to              
+\end{verbatim}
+
+Read header data from a FITS image file into a \code{psMetaData}
+structure.  If the named extension does not exist, the function should
+return an error.  
+\begin{verbatim}
+struct psMetadata *
+psImageReadHeader(struct psMetadata *output,    ///< read data to this structure
+                  char *extname,        ///< MEF extension name ("PHU" for primary header)
+                  int extnum,           ///< MEF extension number (-1 for "PHU", 0 : Nextend - 1)
+                  char *filename)       ///< file to read from
+\end{verbatim}
+
+Read header data from a FITS image file descriptor into a \code{psMetaData}
+structure.  
+\begin{verbatim}
+struct psMetadata *
+psImageFReadHeader (struct psMetadata *output, ///< read data to this structure
+                   char *extname,       ///< MEF extension name ("PHU" for primary header)
+                   int extnum,          ///< MEF extension number (-1 for "PHU", 0 : Nextend - 1)
+                   FILE *f)             ///< file descriptor to read from
+\end{verbatim}
+
+Perform a 2-D FFT on the specified image. 
+\begin{verbatim}
+psImage *
+psImageFFT (psImage *input,             ///< image to FFT
+            int direction)              ///< FFT direction 
+\end{verbatim}
+
+Clip image values outside of range to given values.  All pixels with
+values $<$ min are set to the value vmin. All pixels with values $>$
+max are set to the value vmax.
+\begin{verbatim}
+int
+psImageClip (psImage *input,            ///< clip this image
+             float min,                 ///< clip pixels with values < min
+             float vmin,                ///< set min-clipped pixels to vmin
+             float max,                 ///< clip pixels with values > max
+             float vmax)                ///< set max-clipped pixels to vmax
+\end{verbatim}
+
+Clip NaN image pixels to given value.  Pixels with NaN or Inf values
+are set to the specified value.
+\begin{verbatim}
+int
+psImageClipNaN (psImage *input,         ///< clip this image
+                float value)            ///< set nan pixels to this value
+\end{verbatim}
+
+Overlay subregion of image with another image.  Replace the pixels in
+the \code{image} which correspond to the pixels in \code{overlay} with
+values derived from the values in \code{image} and \code{overlay}
+based on the given operator.  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).  
+\begin{verbatim}
+int 
+psImageOverlaySection (psImage *image,          ///< input image 
+                psImage *overlay,       ///< image to overlay 
+                int x0,                 ///< x offset of overlay subimage 
+                int y0,                 ///< y offset of overlay subimage 
+                char *operator)         ///< overlay operation 
+\end{verbatim}
 
 \subsection{Astrometry}
@@ -1181,4 +2523,5 @@
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
 \subsection{Dates and times}
 
@@ -1186,11 +2529,196 @@
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-\subsection{Image handling}
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-Gene --- done.
 
 \subsection{Metadata}
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-Gene --- done.
+
+This section addresses the question of how \PS{} metadata should be
+represented in memory. We do not (yet) address how it should be
+represented on disk.
+
+\subsubsection{Metadata Representation}
+
+We propose that an item of metadata be represented as a C structure with at least the following
+fields:
+\begin{verbatim}
+/*
+ * A struct to define a single item of metadata
+ */
+typedef struct {
+    const int id;                       // unique ID for this item
+    
+    char *name;                         // Name of item
+    psMetaDataType type;                // type of this item
+    const union {
+        float f;                        // floating value
+        int i;                          // integer value
+        void *v;                        // other type
+    } val;                              // value of metadata
+    char *comment;                      // optional comment ("", not NULL)
+} psMetaDataItem;
+\end{verbatim}
+
+The \code{id} is a unique identifier for this item of metadata; experience
+shows that such tags are useful.
+
+The \code{psMetaDataType type} entry specifies the type of the data
+being represented; the possibilities are listed in section \ref{metadataTypes}.
+
+\paragraph{Possible Types of Metadata}
+\label{metadataTypes}
+
+The possible types of metadata are identified by the enumerated type
+\code{psMetaDataType} (see also table \ref{tabMetaDataTypes}); the initial defined values are:
+\begin{verbatim}
+/*
+ * Possible types of metadata.  
+ */
+typedef enum {                          // type of val is:
+    psMetaFloat,                        // float (.f)
+    psMetaInt,                          // int (.i)
+    psMetaStr,                          // string (.v)
+    psMetaImg,                          // image (.v)
+    psMetaJPEG,                         // JPEG (.v)
+    psMetaPNG,                          // PNG (.v)
+    psMetaAstrom,                       // astrometric coefficients (.v)
+    psMetaUnknown,                      // other (.v)
+    psMetaNType                         // Number of types; must be last
+} psMetaDataType;
+\end{verbatim}
+
+\begin{table}
+\begin{tabular}{llll}
+\textbf{Value} & \textbf{Type} & \textbf{member of union} & \textbf{Comments}\\
+\hline
+psMetaFloat & float & f & value, not pointer, is stored \\
+psMetaInt & int & i &  value, not pointer, is stored \\
+psMetaStr & string & v &  value, not pointer to original, is stored \\
+psMetaImg & psImage & v &  \\
+psMetaJPEG & JPEG & v &  \\
+psMetaPNG & PNG & v &  \\
+psMetaAstrom & psAstrom & v &  \\
+psMetaUnknown & other & v &  \\
+psMetaNType & (none) & & The number of types defined
+\end{tabular}
+\begin{caption}{Supported Metadata Types}
+\label{tabMetaDataTypes}
+  Possible types of metadata
+\end{caption}
+\end{table}
+
+\subsubsection{Collections of Metadata}
+
+\begin{verbatim}
+typedef struct {
+    psDlist *list;			// list of psMetaDataItem
+    psHash *table;			// hash table of the same metadata
+} psMetaDataSet;
+\end{verbatim}
+
+The type \code{psMetaDataSet} is a container class for metadata. Note that there are
+in fact \emph{two} representations of the metadata (each \code{psMetaDataItem} appears
+on both).
+
+We are using the standard \PS{} doubly-linked list types \code{psDlist} and  \code{psHash}
+(see \href{file:utils#psDlist}{utils.pdf:psDlist} and \href{file:utils#psHash}{utils.pdf:psHash} for details).
+For example:
+\begin{verbatim}
+    for (int i = 0; i < 10; i += 5) {
+        float sqrti = sqrt(i);
+        psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_INT, &i, NULL, "const.%d", i));
+        psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_FLOAT, &sqrti, "square root", "const.sqrt%d", i));
+    }
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR, "Bonjour", "French", "lang.hello"));
+    /*
+     * Remove a key
+     */
+    psMetaDataItemFree(psMetaDataRemove(ms, "lang.hello"));
+    /*
+     * Print all metadata
+     */
+    fprintf(stdout, "------\n");
+    psMetaDataSetIterator(ms);
+    while ((meta = psMetaDataGetNext(ms, NULL)) != NULL) {
+        psMetaDataItemPrint(stdout, meta, "");
+    }
+    /*
+     * Look up a key by name
+     */
+    fprintf(stdout, "------\n");
+    fprintf(stdout, "Looking up by name:\n");
+    meta = psMetaDataLookup(ms, "const.5");
+    if (meta != NULL) {
+        psMetaDataItemPrint(stdout, meta, "");
+    }
+\end{verbatim}
+
+\subsubsection{Naming convention for MetaData}
+
+The \code{psMetaDataItem} struct includes a name.  This name should be of
+the form \code{name1.name2.name3$\cdots$}, e.g.\hfil\break
+\null\qquad\qquad\code{IPP.phase1.ota12.biassec}.
+
+We shall set in place a system for assigning the top-level `domains'
+to responsible individuals, and for gathering a complete list of
+all metadata names in use throughout the project.
+
+\paragraph{Support for Multiple Values for a Given Name}
+
+\begin{verbatim}
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR | PS_META_NON_UNIQUE,
+                                           "Bonjour", "French", "lang.hello"));
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR | PS_META_NON_UNIQUE,
+                                           "Aloha", "Hawaiian", "lang.hello"));
+    psMetaDataAppend(ms, psMetaDataItemAlloc(PS_META_STR | PS_META_NON_UNIQUE,
+                                           "Good Morning", "English", "lang.hello"));
+    /*
+     * Print all metadata starting "lang"
+     */
+    psMetaDataSetIterator(ms);
+    while ((meta = psMetaDataGetNext(ms, "lang")) != NULL) {
+        psMetaDataItemPrint(stdout, meta, "");
+    }
+\end{verbatim}
+
+It is an unfortunate fact that certain metadata keywords (such as \code{COMMENT} in a FITS header)
+may be repeated with different values.  The \code{psMetaDataAppend} routine is required
+to check that all metadata names are unique unless the type is qualified as \code{PS_META_NON_UNIQUE};
+in this case a unique integer will be added to each name that you specify. In this case,
+you may either delete individual element separately or as a complete set:
+\begin{verbatim}
+psMetaDataItemFree(psMetaDataRemove(ms, "lang.hello.0"));
+psMetaDataItemFree(psMetaDataRemove(ms, "lang.hello"));
+\end{verbatim}
+
+\subsubsection{MetaData APIs}
+
+\begin{verbatim}
+psMetaDataItem *psMetaDataItemAlloc(
+    psMetaDataType type,		// type of this piece of metadata
+    const void *val,			// value of new item
+    					// N.b. a pointer even if the item
+    					// is of type e.g. int
+    const char *comment,		// comment associated with item
+    const char *name,			// name of new item of metadata (may be an sprintf format)
+    ...);				// possible arguments for name format
+
+void psMetaDataItemFree(psMetaDataItem *ms); // piece of metadata to destroy
+
+psMetaDataSet *psMetaDataSetAlloc(void);	  // make a new set of metadata
+void psMetaDataSetDel(psMetaDataSet *ms); // destroy a set of metadata
+
+/*****************************************************************************/
+/*
+ * Utilities
+ */
+psMetaDataItem *psMetaDataAppend(psMetaDataSet *restrict ms, psMetaDataItem *restrict item);
+psMetaDataItem *psMetaDataRemove(psMetaDataSet *restrict ms, const char *restrict key);
+
+void psMetaDataSetIterator(psMetaDataSet *ms);
+psMetaDataItem *psMetaDataGetNext(psMetaDataSet *ms);
+psMetaDataItem *psMetaDataLookup(const psMetaDataSet *ms, const char *key);
+
+void psMetaDataItemPrint(FILE *fd,		// file descriptor to write to
+			 const psMetaDataItem *ms); // item of metadata to print
+\end{verbatim}
 
 \subsection{Detector and sky positions}
@@ -1375,5 +2903,66 @@
 \subsection{Photometry}
 
-Gene --- done.
+
+Photometric observations are performed in a photometric system, and
+are must be related to other photometric systems.  We require a data
+structure which defines a photometric system, as well as a structure
+to define the transformation between photometric systems.  
+
+The photometric system is defined by the psPhotSystem structure.  
+A photometric system is identified by a human-readable \code{name}
+(ie, SDSS.g, Landolt92.B, GPC1.OTA32.r).  Each photometric system is
+given a unique identifier \code{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.
+\begin{verbatim}
+typedef struct {
+    int ID;
+    char *name;
+    char *camera;
+    char *filter;
+    char *detector;
+} psPhotSystem;
+\end{verbatim}
+
+The following structure defines the transformation between two
+photometric systems.
+\begin{verbatim}
+typedef struct {
+    psPhotSystem src;
+    psPhotSystem dst;
+    psPhotSystem pP, pM;	///< Colour reference
+    psPhotSystem sP, sM;	///< Colour reference
+    float pA, sA;		///< 
+    psPolynomial3D transform;   
+} psPhotTransform;
+\end{verbatim}
+
+The transformation between two photometric systems may depend on the
+airmass of the observation and on the colors of the object of
+interest.  For a specific observation, such a transformations can be
+defined as a polynomial function of the color the star and the airmass
+of the observations.  If sufficient data exists, the transformation
+between the photometric systems may include more than one color,
+constraining the curvature of the stellar spectral energy
+distributions.  This latter term may be significant for stars which
+are highly reddened, for example.  Derived photometric quantities may
+have been corrected for airmass variations, in which case only color
+terms may be measureable.  The structure defines the transformation
+between a source photometric system (\code{src}) and a target
+photometric system (\code{dst}).  The photometric system of a primary
+color is defined by \code{pP, pM} such that the color is constructed
+as $pP - pM$.  A secondary color is defined by \code{sP, sM}.  For
+both, a reference color is specified (\code{pA, sA}): the polynomial
+transformation terms refer to colors in the form $pP - pM - pA$.  The
+transformation is specified as a 3D polynomial.  For a star of
+magnitude $M_{\rm src}$ in the source photometric system, with
+additional magnitude information in the other systems $M_{\rm pP}$,
+$M_{\rm pM}$, $M_{\rm sP}$, $M_{\rm sM}$, observed at an airmass of
+$z$, the magnitude of the star in the target system $M_{\rm dst}$ is
+given by: 
+$M_{\rm dst} = M_{\rm src} + transform(z, M_{\rm pP} - M_{\rm pM} - pA, M_{\rm sP} - M_{\rm sM} - sA)$
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1381,4 +2970,415 @@
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
+\appendix
+
+\section{API Summary: all functions}
+
+\subsection{System Utilities}
+\begin{CompactItemize}
+\item 
+int {\bf ps\-Set\-Log\-Destination} (int dest)
+\begin{CompactList}\small\item\em Sets the log destination.\item\end{CompactList}\item 
+int {\bf ps\-Set\-Log\-Level} (int level)
+\begin{CompactList}\small\item\em Sets the log level.\item\end{CompactList}\item 
+void {\bf ps\-Set\-Log\-Format} (const char $\ast$fmt)
+\begin{CompactList}\small\item\em sets the log format\item\end{CompactList}\item 
+void {\bf ps\-Log\-Msg} (const char $\ast$name, int level, const char $\ast$fmt,...)
+\begin{CompactList}\small\item\em Logs a message.\item\end{CompactList}\item 
+void {\bf p\_\-ps\-VLog\-Msg} (const char $\ast$name, int level, const char $\ast$fmt, va\_\-list ap)
+\begin{CompactList}\small\item\em Logs a message from varargs.\item\end{CompactList}\item 
+void $\ast$ {\bf p\_\-ps\-Alloc} (size\_\-t size, const char $\ast$file, int lineno)
+\begin{CompactList}\small\item\em Memory allocation. Underlying private function called by macro ps\-Alloc.\item\end{CompactList}\item 
+void $\ast$ {\bf p\_\-ps\-Realloc} (void $\ast$ptr, size\_\-t size, const char $\ast$file, int lineno)
+\begin{CompactList}\small\item\em Memory re-allocation. Underlying private function called by macro ps\-Realloc.\item\end{CompactList}\item 
+void {\bf p\_\-ps\-Free} (void $\ast$ptr, const char $\ast$file, int lineno)
+\begin{CompactList}\small\item\em Free memory. Underlying private function called by macro ps\-Free.\item\end{CompactList}\item 
+int {\bf ps\-Mem\-Check\-Leaks} (int id0, {\bf ps\-Mem\-Block} $\ast$$\ast$$\ast$arr, FILE $\ast$fd)
+\begin{CompactList}\small\item\em Check for memory leaks.\item\end{CompactList}\item 
+int {\bf ps\-Mem\-Check\-Corruption} (int abort\_\-on\_\-error)
+\begin{CompactList}\small\item\em Check for memory corruption.\item\end{CompactList}\item 
+int {\bf ps\-Mem\-Get\-Ref\-Counter} (void $\ast$vptr)
+\begin{CompactList}\small\item\em Return reference counter.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Mem\-Incr\-Ref\-Counter} (void $\ast$vptr)
+\begin{CompactList}\small\item\em Increment reference counter and return the pointer.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Mem\-Decr\-Ref\-Counter} (void $\ast$vptr)
+\begin{CompactList}\small\item\em Decrement reference counter and return the pointer.\item\end{CompactList}\item 
+{\bf ps\-Mem\-Problem\-Callback} {\bf ps\-Mem\-Problem\-Set\-CB} ({\bf ps\-Mem\-Problem\-Callback} func)
+\begin{CompactList}\small\item\em Set callback for problems.\item\end{CompactList}\item 
+{\bf ps\-Mem\-Exhausted\-Callback} {\bf ps\-Mem\-Exhausted\-Set\-CB} ({\bf ps\-Mem\-Exhausted\-Callback} func)
+\begin{CompactList}\small\item\em Set callback for out-of-memory.\item\end{CompactList}\item 
+{\bf ps\-Mem\-Callback} {\bf ps\-Mem\-Allocate\-Set\-CB} ({\bf ps\-Mem\-Callback} func)
+\begin{CompactList}\small\item\em Set call back for when a particular memory block is allocated.\item\end{CompactList}\item 
+{\bf ps\-Mem\-Callback} {\bf ps\-Mem\-Free\-Set\-CB} ({\bf ps\-Mem\-Callback} func)
+\begin{CompactList}\small\item\em Set call back for when a particular memory block is freed.\item\end{CompactList}\item 
+int {\bf ps\-Mem\-Get\-Id} (void)
+\begin{CompactList}\small\item\em get next memory ID\item\end{CompactList}\item 
+long {\bf ps\-Mem\-Set\-Allocate\-ID} (long id)
+\begin{CompactList}\small\item\em set p\_\-ps\-Mem\-Allocate\-ID to id\item\end{CompactList}\item 
+long {\bf ps\-Mem\-Set\-Free\-ID} (long id)
+\begin{CompactList}\small\item\em set p\_\-ps\-Mem\-Free\-ID to id\item\end{CompactList}\item 
+void {\bf ps\-Abort} (const char $\ast$name, const char $\ast$fmt,...)
+\begin{CompactList}\small\item\em Prints an error message and aborts.\item\end{CompactList}\item 
+void {\bf ps\-Error} (const char $\ast$name, const char $\ast$fmt,...)
+\begin{CompactList}\small\item\em Prints an error message and doesn't abort.\item\end{CompactList}\item 
+char $\ast$ {\bf ps\-String\-Copy} (const char $\ast$str)
+\begin{CompactList}\small\item\em Allocates and returns a copy of a string.\item\end{CompactList}\item 
+void {\bf p\_\-ps\-Trace} (const char $\ast$facil, int level,...)
+\begin{CompactList}\small\item\em Send a trace message.\item\end{CompactList}\item 
+int {\bf ps\-Set\-Trace\-Level} (const char $\ast$facil, int level)
+\begin{CompactList}\small\item\em Set trace level.\item\end{CompactList}\item 
+int {\bf ps\-Get\-Trace\-Level} (const char $\ast$name)
+\begin{CompactList}\small\item\em Get the trace level.\item\end{CompactList}\item 
+void {\bf ps\-Trace\-Reset} (void)
+\begin{CompactList}\small\item\em turn off all tracing, and free trace's allocated memory\item\end{CompactList}\item 
+void {\bf ps\-Print\-Trace\-Levels} (void)
+\begin{CompactList}\small\item\em print trace levels\item\end{CompactList}\end{CompactItemize}
+
+\subsection{Data Containers}
+\begin{CompactItemize}
+\item 
+{\bf ps\-Dlist} $\ast$ {\bf ps\-Dlist\-Alloc} (void $\ast$data)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+void {\bf ps\-Dlist\-Free} ({\bf ps\-Dlist} $\ast$list, void($\ast$elem\-Free)(void $\ast$))
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Dlist} $\ast$ {\bf ps\-Dlist\-Add} ({\bf ps\-Dlist} $\ast$list, void $\ast$data, int where)
+\begin{CompactList}\small\item\em Add to list.\item\end{CompactList}\item 
+{\bf ps\-Dlist} $\ast$ {\bf ps\-Dlist\-Append} ({\bf ps\-Dlist} $\ast$list, void $\ast$data)
+\begin{CompactList}\small\item\em Append to a list.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Dlist\-Remove} ({\bf ps\-Dlist} $\ast$list, void $\ast$data, int which)
+\begin{CompactList}\small\item\em PS\_\-DLIST\_\-PREV.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Dlist\-Get} (const {\bf ps\-Dlist} $\ast$list, int which)
+\begin{CompactList}\small\item\em Retrieve from a list.\item\end{CompactList}\item 
+void {\bf ps\-Dlist\-Set\-Iterator} ({\bf ps\-Dlist} $\ast$list, int where, int which)
+\begin{CompactList}\small\item\em PS\_\-DLIST\_\-PREV.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Dlist\-Get\-Next} ({\bf ps\-Dlist} $\ast$list, int which)
+\begin{CompactList}\small\item\em PS\_\-DLIST\_\-PREV.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Dlist\-Get\-Prev} ({\bf ps\-Dlist} $\ast$list, int which)
+\begin{CompactList}\small\item\em PS\_\-DLIST\_\-PREV.\item\end{CompactList}\item 
+{\bf ps\-Void\-Ptr\-Array} $\ast$ {\bf ps\-Dlist\-To\-Array} ({\bf ps\-Dlist} $\ast$dlist)
+\begin{CompactList}\small\item\em Convert doubly-linked list to an array.\item\end{CompactList}\item 
+{\bf ps\-Dlist} $\ast$ {\bf ps\-Array\-To\-Dlist} ({\bf ps\-Void\-Ptr\-Array} $\ast$arr)
+\begin{CompactList}\small\item\em Convert array to a doubly-linked list.\item\end{CompactList}\item 
+{\bf ps\-Hash} $\ast$ {\bf ps\-Hash\-Alloc} (int nbucket)
+\begin{CompactList}\small\item\em Allocate hash buckets in table.\item\end{CompactList}\item 
+void {\bf ps\-Hash\-Free} ({\bf ps\-Hash} $\ast$table, void($\ast$item\-Free)(void $\ast$item))
+\begin{CompactList}\small\item\em Free hash buckets from table.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Hash\-Insert} ({\bf ps\-Hash} $\ast$table, const char $\ast$key, void $\ast$data, void($\ast$item\-Free)(void $\ast$item))
+\begin{CompactList}\small\item\em Insert entry into table.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Hash\-Lookup} ({\bf ps\-Hash} $\ast$table, const char $\ast$key)
+\begin{CompactList}\small\item\em Lookup key in table.\item\end{CompactList}\item 
+void $\ast$ {\bf ps\-Hash\-Remove} ({\bf ps\-Hash} $\ast$table, const char $\ast$key)
+\begin{CompactList}\small\item\em Remove key from table.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Float\-Array\-Alloc} (int s, int n)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Float\-Array\-Realloc} ({\bf ps\-Float\-Array} $\ast$my\-Array, int s)
+\begin{CompactList}\small\item\em Reallocator.\item\end{CompactList}\item 
+void {\bf ps\-Float\-Array\-Free} ({\bf ps\-Float\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Complex\-Array} $\ast$ {\bf ps\-Complex\-Array\-Alloc} (int s, int n)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Complex\-Array} $\ast$ {\bf ps\-Complex\-Array\-Realloc} ({\bf ps\-Complex\-Array} $\ast$my\-Array, int s)
+\begin{CompactList}\small\item\em Reallocator.\item\end{CompactList}\item 
+void {\bf ps\-Complex\-Array\-Free} ({\bf ps\-Complex\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Int\-Array} $\ast$ {\bf ps\-Int\-Array\-Alloc} (int s, int n)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Int\-Array} $\ast$ {\bf ps\-Int\-Array\-Realloc} ({\bf ps\-Int\-Array} $\ast$my\-Array, int s)
+\begin{CompactList}\small\item\em Reallocator.\item\end{CompactList}\item 
+void {\bf ps\-Int\-Array\-Free} ({\bf ps\-Int\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Double\-Array} $\ast$ {\bf ps\-Double\-Array\-Alloc} (int s, int n)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Double\-Array} $\ast$ {\bf ps\-Double\-Array\-Realloc} ({\bf ps\-Double\-Array} $\ast$my\-Array, int s)
+\begin{CompactList}\small\item\em Reallocator.\item\end{CompactList}\item 
+void {\bf ps\-Double\-Array\-Free} ({\bf ps\-Double\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Vector\-Array} $\ast$ {\bf ps\-Vector\-Array\-Alloc} (int s, int n)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Vector\-Array} $\ast$ {\bf ps\-Vector\-Array\-Realloc} ({\bf ps\-Vector\-Array} $\ast$my\-Array, int s)
+\begin{CompactList}\small\item\em Reallocator.\item\end{CompactList}\item 
+void {\bf ps\-Vector\-Array\-Free} ({\bf ps\-Vector\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Void\-Ptr\-Array} $\ast$ {\bf ps\-Void\-Ptr\-Array\-Alloc} (int n, int s)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Void\-Ptr\-Array} $\ast$ {\bf ps\-Void\-Ptr\-Array\-Realloc} ({\bf ps\-Void\-Ptr\-Array} $\ast$arr, int n)
+\begin{CompactList}\small\item\em Reallocate.\item\end{CompactList}\item 
+void {\bf ps\-Void\-Ptr\-Array\-Free} ({\bf ps\-Void\-Ptr\-Array} $\ast$arr, void($\ast$elem\-Free)(void $\ast$))
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\end{CompactItemize}
+
+\subsection{Math Utilities}
+\begin{CompactItemize}
+\item 
+{\bf ps\-Bit\-Mask} $\ast$ {\bf ps\-Bit\-Mask\-Alloc} (int n)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+void {\bf ps\-Bit\-Mask\-Free} ({\bf ps\-Bit\-Mask} $\ast$restrict my\-Mask)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Bit\-Mask} $\ast$ {\bf ps\-Bit\-Mask\-Set} ({\bf ps\-Bit\-Mask} $\ast$out\-Mask, const {\bf ps\-Bit\-Mask} $\ast$my\-Mask, int bit)
+\begin{CompactList}\small\item\em Set a bit mask.\item\end{CompactList}\item 
+int {\bf ps\-Bit\-Mask\-Test} (const {\bf ps\-Bit\-Mask} $\ast$check\-Mask, int bit)
+\begin{CompactList}\small\item\em Check a bit mask.\item\end{CompactList}\item 
+{\bf ps\-Bit\-Mask} $\ast$ {\bf ps\-Bit\-Mask\-Op} ({\bf ps\-Bit\-Mask} $\ast$out\-Mask, const {\bf ps\-Bit\-Mask} $\ast$restrict in\-Mask1, char $\ast$operator, const {\bf ps\-Bit\-Mask} $\ast$restrict in\-Mask2)
+\begin{CompactList}\small\item\em apply the given operator to two bit masks\item\end{CompactList}\item 
+{\bf ps\-Complex\-Array} $\ast$ {\bf ps\-Real\-FFT} ({\bf ps\-Complex\-Array} $\ast$restrict out, const {\bf ps\-Float\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Return Fourier Transform of an array.\item\end{CompactList}\item 
+{\bf ps\-Complex\-Array} $\ast$ {\bf ps\-Complex\-FFT} ({\bf ps\-Complex\-Array} $\ast$restrict out, const {\bf ps\-Complex\-Array} $\ast$restrict my\-Array, int sign)
+\begin{CompactList}\small\item\em Return [inverse?] Fourier Transform of a complex array.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Power\-Spec} ({\bf ps\-Float\-Array} $\ast$restrict out, const {\bf ps\-Float\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Return Power spectrum of a array.\item\end{CompactList}\item 
+{\bf ps\-Polynomial1D} $\ast$ {\bf ps\-Polynomial1DAlloc} (int n)
+\begin{CompactList}\small\item\em Constructors.\item\end{CompactList}\item 
+{\bf ps\-Polynomial2D} $\ast$ {\bf ps\-Polynomial2DAlloc} (int n\-X, int n\-Y)
+\item 
+{\bf ps\-Polynomial3D} $\ast$ {\bf ps\-Polynomial3DAlloc} (int n\-X, int n\-Y, int n\-Z)
+\item 
+{\bf ps\-Polynomial4D} $\ast$ {\bf ps\-Polynomial4DAlloc} (int n\-W, int n\-X, int n\-Y, int n\-Z)
+\item 
+void {\bf ps\-Polynomial1DFree} ({\bf ps\-Polynomial1D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Destructors.\item\end{CompactList}\item 
+void {\bf ps\-Polynomial2DFree} ({\bf ps\-Polynomial2D} $\ast$restrict my\-Poly)
+\item 
+void {\bf ps\-Polynomial3DFree} ({\bf ps\-Polynomial3D} $\ast$restrict my\-Poly)
+\item 
+void {\bf ps\-Polynomial4DFree} ({\bf ps\-Polynomial4D} $\ast$restrict my\-Poly)
+\item 
+float {\bf ps\-Eval\-Polynomial1D} (float x, const {\bf ps\-Polynomial1D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 1D polynomial.\item\end{CompactList}\item 
+float {\bf ps\-Eval\-Polynomial2D} (float x, float y, const {\bf ps\-Polynomial2D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 2D polynomial.\item\end{CompactList}\item 
+float {\bf ps\-Eval\-Polynomial3D} (float x, float y, float z, const {\bf ps\-Polynomial3D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 3D polynomial.\item\end{CompactList}\item 
+float {\bf ps\-Eval\-Polynomial4D} (float w, float x, float y, float z, const {\bf ps\-Polynomial4D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 4D polynomial.\item\end{CompactList}\item 
+{\bf ps\-DPolynomial1D} $\ast$ {\bf ps\-DPolynomial1DAlloc} (int n)
+\begin{CompactList}\small\item\em Constructors.\item\end{CompactList}\item 
+{\bf ps\-DPolynomial2D} $\ast$ {\bf ps\-DPolynomial2DAlloc} (int n\-X, int n\-Y)
+\item 
+{\bf ps\-DPolynomial3D} $\ast$ {\bf ps\-DPolynomial3DAlloc} (int n\-X, int n\-Y, int n\-Z)
+\item 
+{\bf ps\-DPolynomial4D} $\ast$ {\bf ps\-DPolynomial4DAlloc} (int n\-W, int n\-X, int n\-Y, int n\-Z)
+\item 
+void {\bf ps\-DPolynomial1DFree} ({\bf ps\-DPolynomial1D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Destructors.\item\end{CompactList}\item 
+void {\bf ps\-DPolynomial2DFree} ({\bf ps\-DPolynomial2D} $\ast$restrict my\-Poly)
+\item 
+void {\bf ps\-DPolynomial3DFree} ({\bf ps\-DPolynomial3D} $\ast$restrict my\-Poly)
+\item 
+void {\bf ps\-DPolynomial4DFree} ({\bf ps\-DPolynomial4D} $\ast$restrict my\-Poly)
+\item 
+double {\bf ps\-Eval\-DPolynomial1D} (double x, const {\bf ps\-DPolynomial1D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 1D polynomial (double precision).\item\end{CompactList}\item 
+double {\bf ps\-Eval\-DPolynomial2D} (double x, double y, const {\bf ps\-DPolynomial2D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 2D polynomial (double precision).\item\end{CompactList}\item 
+double {\bf ps\-Eval\-DPolynomial3D} (double x, double y, double z, const {\bf ps\-DPolynomial3D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 3D polynomial (double precision).\item\end{CompactList}\item 
+double {\bf ps\-Eval\-DPolynomial4D} (double w, double x, double y, double z, const {\bf ps\-DPolynomial4D} $\ast$restrict my\-Poly)
+\begin{CompactList}\small\item\em Evaluate 4D polynomial (double precision).\item\end{CompactList}\item 
+{\bf ps\-Type} $\ast$ {\bf ps\-Binary\-Op} (void $\ast$out, void $\ast$in1, char $\ast$operator, void $\ast$in2)
+\begin{CompactList}\small\item\em Perform a binary operation on two data items ({\bf ps\-Image} {\rm (p.\,\pageref{structpsImage})}, ps\-Vector, ps\-Scalar).\item\end{CompactList}\item 
+{\bf ps\-Type} $\ast$ {\bf ps\-Unary\-Op} (void $\ast$out, void $\ast$in, char $\ast$operator)
+\begin{CompactList}\small\item\em Perform a binary operation on two data items ({\bf ps\-Image} {\rm (p.\,\pageref{structpsImage})}, ps\-Vector, ps\-Scalar).\item\end{CompactList}\item 
+{\bf p\_\-ps\-Scalar} $\ast$ {\bf ps\-Scalar} (double value)
+\begin{CompactList}\small\item\em create a {\bf ps\-Type} {\rm (p.\,\pageref{structpsType})}-ed structure from a constant double value.\item\end{CompactList}\item 
+{\bf p\_\-ps\-Scalar} $\ast$ {\bf ps\-Scalar\-Type} (char $\ast$mode,...)
+\begin{CompactList}\small\item\em create a {\bf ps\-Type} {\rm (p.\,\pageref{structpsType})}-ed structure from a specified type\item\end{CompactList}\item 
+{\bf ps\-Matrix} $\ast$ {\bf ps\-Matrix\-Alloc} (int Xdimen, int Ydimen)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+void {\bf ps\-Matrix\-Free} ({\bf ps\-Matrix} $\ast$restrict my\-Matrix)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Matrix} $\ast$ {\bf ps\-Matrix\-Invert} ({\bf ps\-Matrix} $\ast$out, const {\bf ps\-Matrix} $\ast$my\-Matrix, float $\ast$restrict determinant)
+\begin{CompactList}\small\item\em Invert matrix.\item\end{CompactList}\item 
+float {\bf ps\-Matrix\-Determinant} (const {\bf ps\-Matrix} $\ast$restrict my\-Matrix)
+\begin{CompactList}\small\item\em Matrix determinant.\item\end{CompactList}\item 
+{\bf ps\-Matrix} $\ast$ {\bf ps\-Matrix\-Op} ({\bf ps\-Matrix} $\ast$out, const {\bf ps\-Matrix} $\ast$matrix1, const char $\ast$op, const {\bf ps\-Matrix} $\ast$matrix2)
+\begin{CompactList}\small\item\em Matrix operations.\item\end{CompactList}\item 
+{\bf ps\-Matrix} $\ast$ {\bf ps\-Matrix\-Transpose} ({\bf ps\-Matrix} $\ast$out, const {\bf ps\-Matrix} $\ast$my\-Matrix)
+\begin{CompactList}\small\item\em Transpose Matrix.\item\end{CompactList}\item 
+{\bf ps\-Vector} $\ast$ {\bf ps\-Matrix\-To\-Vector} ({\bf ps\-Matrix} $\ast$my\-Matrix)
+\begin{CompactList}\small\item\em Convert matrix to vector.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Minimize} (float($\ast$my\-Function)(const {\bf ps\-Float\-Array} $\ast$restrict), {\bf ps\-Float\-Array} $\ast$restrict initial\-Guess)
+\begin{CompactList}\small\item\em Minimize a particular function.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Minimize\-Chi2} (float($\ast$eval\-Model)(const {\bf ps\-Float\-Array} $\ast$restrict, const {\bf ps\-Float\-Array} $\ast$restrict), const {\bf ps\-Float\-Array} $\ast$restrict domain, const {\bf ps\-Float\-Array} $\ast$restrict data, const {\bf ps\-Float\-Array} $\ast$restrict errors, {\bf ps\-Float\-Array} $\ast$restrict initial\-Guess, const {\bf ps\-Int\-Array} $\ast$restrict guess\-Mask)
+\begin{CompactList}\small\item\em Minimize chi$^\wedge$2 for input data.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Get\-Array\-Polynomial} (const {\bf ps\-Float\-Array} $\ast$restrict ord, const {\bf ps\-Float\-Array} $\ast$restrict coord)
+\begin{CompactList}\small\item\em Derive a polynomial that goes through the points --- can be done analytically.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Sort} ({\bf ps\-Float\-Array} $\ast$out, const {\bf ps\-Float\-Array} $\ast$my\-Array)
+\begin{CompactList}\small\item\em Sort an array.\item\end{CompactList}\item 
+{\bf ps\-Int\-Array} $\ast$ {\bf ps\-Sort\-Index} ({\bf ps\-Int\-Array} $\ast$restrict out, const {\bf ps\-Float\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Sort an array, along with some other stuff.\item\end{CompactList}\item 
+{\bf ps\-Stats} $\ast$ {\bf ps\-Array\-Stats} (const {\bf ps\-Float\-Array} $\ast$restrict my\-Array, const {\bf ps\-Int\-Array} $\ast$restrict mask\-Array, unsigned int mask\-Val, {\bf ps\-Stats} $\ast$stats)
+\begin{CompactList}\small\item\em Do Statistics on an array.\item\end{CompactList}\item 
+{\bf ps\-Histogram} $\ast$ {\bf ps\-Histogram\-Alloc} (float lower, float upper, float size)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+{\bf ps\-Histogram} $\ast$ {\bf ps\-Histogram\-Alloc\-Generic} (const {\bf ps\-Float\-Array} $\ast$restrict lower, const {\bf ps\-Float\-Array} $\ast$restrict upper, float min\-Val, float max\-Val)
+\begin{CompactList}\small\item\em Generic constructor.\item\end{CompactList}\item 
+void {\bf ps\-Histogram\-Free} ({\bf ps\-Histogram} $\ast$restrict my\-Hist)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Histogram} $\ast$ {\bf ps\-Get\-Array\-Histogram} ({\bf ps\-Histogram} $\ast$restrict my\-Hist, const {\bf ps\-Float\-Array} $\ast$restrict my\-Array)
+\begin{CompactList}\small\item\em Calculate a histogram.\item\end{CompactList}\end{CompactItemize}
+
+\subsection{Astronomy Functions}
+\begin{CompactItemize}
+\item 
+{\bf ps\-Chip} $\ast$ {\bf ps\-Chip\-In\-FPA} ({\bf ps\-FPA} $\ast$fpa, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em returns Chip in FPA which contains the given FPA coordinate\item\end{CompactList}\item 
+{\bf ps\-Cell} $\ast$ {\bf ps\-Cell\-In\-Chip} ({\bf ps\-Chip} $\ast$chip, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em returns Cell in Chip which contains the given chip coordinate\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-Sky\-To\-TP} ({\bf ps\-Exposure} $\ast$exp, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em Convert (RA,Dec) to tangent plane coords.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-TPto\-FPA} ({\bf ps\-FPA} $\ast$fpa, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em Convert tangent plane coords to focal plane coordinates.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-FPAto\-Chip} ({\bf ps\-FPA} $\ast$fpa, {\bf ps\-Chip} $\ast$chip, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em converts the specified FPA coord to the coord on the given Chip\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-Chipto\-Cell} ({\bf ps\-Chip} $\ast$chip, {\bf ps\-Cell} $\ast$cell, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em converts the specified Chip coord to the coord on the given Cell\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-Cellto\-Chip} ({\bf ps\-Cell} $\ast$cell, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em converts the specified Cell coord to the coord on the parent Chip\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-Chipto\-FPA} ({\bf ps\-Chip} $\ast$chip, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em converts the specified Chip coord to the coord on the parent FPA\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-FPATo\-TP} ({\bf ps\-FPA} $\ast$fpa, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em Convert focal plane coords to tangent plane coordinates.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-TPto\-Sky} ({\bf ps\-Exposure} $\ast$exp, {\bf ps\-Coord} $\ast$coord)
+\begin{CompactList}\small\item\em Convert tangent plane coords to (RA,Dec).\item\end{CompactList}\item 
+float {\bf ps\-Get\-Airmass} ({\bf ps\-Coord} $\ast$coord, double sidereal\-Time)
+\begin{CompactList}\small\item\em Get the airmass for a given position and sidereal time.\item\end{CompactList}\item 
+float {\bf ps\-Get\-Parallactic} ({\bf ps\-Coord} $\ast$coord, double sidereal\-Time)
+\begin{CompactList}\small\item\em Get the parallactic angle for a given position and sidereal time.\item\end{CompactList}\item 
+float {\bf ps\-Get\-Refraction} (float colour, {\bf ps\-Phot\-System} color\-Plus, {\bf ps\-Phot\-System} color\-Minus, {\bf ps\-Exposure} $\ast$exp)
+\begin{CompactList}\small\item\em Estimate atmospheric refraction, along the parallactic.\item\end{CompactList}\item 
+{\bf ps\-Exposure} $\ast$ {\bf ps\-Exposure\-Alloc} (double ra, double dec, double ha, double zd, double az, double lst, float mjd, float rot\-Angle, float temp, float pressure, float humidity, float exptime)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+void {\bf ps\-Exposure\-Free} ({\bf ps\-Exposure} $\ast$restrict my\-Exp)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+double {\bf ps\-Get\-MJD} (void)
+\begin{CompactList}\small\item\em Get current MJD, for a timestamp.\item\end{CompactList}\item 
+double {\bf ps\-Get\-Sidereal} (float mjd, float longitude)
+\begin{CompactList}\small\item\em Get current sidereal time at longitude.\item\end{CompactList}\item 
+char $\ast$ {\bf ps\-Time\-To\-ISOTime} (ps\-Time time)
+\begin{CompactList}\small\item\em Convert ps\-Time to ISOTime (Human-readable date/time string YYYY/MM/DD,HH:MM:SS.SSS).\item\end{CompactList}\item 
+double {\bf ps\-Time\-To\-UTC} (ps\-Time time)
+\begin{CompactList}\small\item\em Convert ps\-Time to UTC.\item\end{CompactList}\item 
+double {\bf ps\-Time\-To\-MJD} (ps\-Time time)
+\begin{CompactList}\small\item\em Convert ps\-Time to MJD.\item\end{CompactList}\item 
+double {\bf ps\-Time\-To\-JD} (ps\-Time time)
+\begin{CompactList}\small\item\em Convert ps\-Time to JD.\item\end{CompactList}\item 
+timeval $\ast$ {\bf ps\-Time\-To\-Timeval} (ps\-Time time)
+\begin{CompactList}\small\item\em Convert ps\-Time to timeval (struct timeval).\item\end{CompactList}\item 
+tm $\ast$ {\bf ps\-Time\-To\-Tm} (ps\-Time time)
+\begin{CompactList}\small\item\em Convert ps\-Time to broken-down time (struct tm).\item\end{CompactList}\item 
+ps\-Time $\ast$ {\bf ps\-ISOTime\-To\-Time} (char $\ast$input)
+\begin{CompactList}\small\item\em Convert ISOTime (Human-readable date/time string YYYY/MM/DD,HH:MM:SS.SSS) to ps\-Time.\item\end{CompactList}\item 
+ps\-Time $\ast$ {\bf ps\-UTCTo\-Time} (double input)
+\begin{CompactList}\small\item\em Convert UTC to ps\-Time.\item\end{CompactList}\item 
+ps\-Time $\ast$ {\bf ps\-MJDTo\-Time} (double input)
+\begin{CompactList}\small\item\em Convert MJD to ps\-Time.\item\end{CompactList}\item 
+ps\-Time $\ast$ {\bf ps\-JDTo\-Time} (double input)
+\begin{CompactList}\small\item\em Convert JD to ps\-Time.\item\end{CompactList}\item 
+ps\-Time $\ast$ {\bf ps\-Timeval\-To\-Time} (struct timeval $\ast$input)
+\begin{CompactList}\small\item\em Convert timeval to ps\-Time (struct timeval).\item\end{CompactList}\item 
+ps\-Time $\ast$ {\bf ps\-TMto\-Time} (struct tm $\ast$input)
+\begin{CompactList}\small\item\em Convert broken-to ps\-Time down time (struct tm).\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Alloc} (int nx, int ny, {\bf ps\-Type} type)
+\begin{CompactList}\small\item\em Create an image of the specified size and type.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Subset} ({\bf ps\-Image} $\ast$image, int nx, int ny, int x0, int y0)
+\begin{CompactList}\small\item\em Create a subimage of the specified area.\item\end{CompactList}\item 
+void {\bf ps\-Image\-Free} ({\bf ps\-Image} $\ast$image)
+\begin{CompactList}\small\item\em Destroy the specified image (destroy children if they exist).\item\end{CompactList}\item 
+int {\bf ps\-Image\-Free\-Children} ({\bf ps\-Image} $\ast$image)
+\begin{CompactList}\small\item\em Destroy the children of the specified image.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Copy} ({\bf ps\-Image} $\ast$output, {\bf ps\-Image} $\ast$input)
+\begin{CompactList}\small\item\em Create a copy of the specified image.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Image\-Slice} ({\bf ps\-Image} $\ast$input, int x, int y, int nx, int ny, int direction, {\bf ps\-Stats} $\ast$stats)
+\begin{CompactList}\small\item\em Extract pixels from rectlinear region to a vector.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Image\-Cut} ({\bf ps\-Image} $\ast$input, float xs, float ys, float xe, float ye, float dw, {\bf ps\-Stats} $\ast$stats)
+\begin{CompactList}\small\item\em Extract pixels along a line to a vector.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Image\-Radial\-Cut} ({\bf ps\-Image} $\ast$input, float x, float y, float radius, float dr, {\bf ps\-Stats} $\ast$stats)
+\begin{CompactList}\small\item\em Extract radial annulii data to a vector.\item\end{CompactList}\item 
+{\bf ps\-Float\-Array} $\ast$ {\bf ps\-Image\-Contour} ({\bf ps\-Image} $\ast$input, float threshold, int binning)
+\begin{CompactList}\small\item\em Extract a 2-d contour from an image at the given threshold.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Rebin} ({\bf ps\-Image} $\ast$input, float scale, {\bf ps\-Stats} $\ast$stats)
+\begin{CompactList}\small\item\em Rebin image to new scale.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Rotate} ({\bf ps\-Image} $\ast$input, float angle)
+\begin{CompactList}\small\item\em Rotate image by given angle.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Shift} ({\bf ps\-Image} $\ast$input, float dx, float dy, float exposed)
+\begin{CompactList}\small\item\em Shift image by an arbitrary number of pixels in either direction.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Roll} ({\bf ps\-Image} $\ast$input, int dx, int dy)
+\begin{CompactList}\small\item\em Roll image by an integer number of pixels in either direction.\item\end{CompactList}\item 
+{\bf ps\-Stats} $\ast$ {\bf ps\-Image\-Get\-Stats} ({\bf ps\-Image} $\ast$input, {\bf ps\-Stats} $\ast$stats)
+\begin{CompactList}\small\item\em defines statistics to be calculated\item\end{CompactList}\item 
+{\bf ps\-Histogram} $\ast$ {\bf ps\-Image\-Histogram} ({\bf ps\-Histogram} $\ast$hist, {\bf ps\-Image} $\ast$input)
+\begin{CompactList}\small\item\em Construct a histogram from an image (or subimage).\item\end{CompactList}\item 
+{\bf ps\-Polynomial2D} $\ast$ {\bf ps\-Image\-Fit\-Polynomial} ({\bf ps\-Image} $\ast$input, {\bf ps\-Polynomial2D} $\ast$coeffs)
+\begin{CompactList}\small\item\em Fit a 2-D polynomial surface to an image.\item\end{CompactList}\item 
+int {\bf ps\-Image\-Eval\-Polynomial} ({\bf ps\-Image} $\ast$input, {\bf ps\-Polynomial2D} $\ast$coeffs)
+\begin{CompactList}\small\item\em Evaluate a 2-D polynomial surface to image pixels.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Read\-Section} ({\bf ps\-Image} $\ast$output, int x, int y, int dx, int dy, int z, char $\ast$extname, char $\ast$filename)
+\begin{CompactList}\small\item\em Read an image or subimage from a named file.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-FRead\-Section} ({\bf ps\-Image} $\ast$output, int x, int y, int dx, int dy, int z, char $\ast$extname, FILE $\ast$f)
+\begin{CompactList}\small\item\em Read an image or subimage from file descriptor.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Write\-Section} ({\bf ps\-Image} $\ast$input, int x, int y, int z, char $\ast$extname, char $\ast$filename)
+\begin{CompactList}\small\item\em Write an image section to named file (which may exist).\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-FWrite\-Section} ({\bf ps\-Image} $\ast$input, int x, int y, int z, char $\ast$extname, FILE $\ast$f)
+\begin{CompactList}\small\item\em Write an image section to named file (which may exist).\item\end{CompactList}\item 
+ps\-Metadata $\ast$ {\bf ps\-Image\-Read\-Header} (struct ps\-Metadata $\ast$output, char $\ast$extname, char $\ast$filename)
+\begin{CompactList}\small\item\em Read only header from image file.\item\end{CompactList}\item 
+ps\-Metadata $\ast$ {\bf ps\-Image\-FRead\-Header} (struct ps\-Metadata $\ast$output, char $\ast$extname, FILE $\ast$f)
+\begin{CompactList}\small\item\em Read only header from image file descriptor.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-FFT} ({\bf ps\-Image} $\ast$input, int direction)
+\begin{CompactList}\small\item\em Perform an FFT on the image.\item\end{CompactList}\item 
+int {\bf ps\-Image\-Clip} ({\bf ps\-Image} $\ast$input, float min, float vmin, float max, float vmax)
+\begin{CompactList}\small\item\em Clip image values outside of range to given values.\item\end{CompactList}\item 
+int {\bf ps\-Image\-Clip\-Na\-N} ({\bf ps\-Image} $\ast$input, float value)
+\begin{CompactList}\small\item\em Clip Na\-N image pixels to given value.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Binary\-Op} ({\bf ps\-Image} $\ast$out, {\bf ps\-Image} $\ast$in1, char $\ast$operator, {\bf ps\-Image} $\ast$in2)
+\begin{CompactList}\small\item\em Perform a binary operation on two images.\item\end{CompactList}\item 
+{\bf ps\-Image} $\ast$ {\bf ps\-Image\-Unary\-Op} ({\bf ps\-Image} $\ast$out, {\bf ps\-Image} $\ast$in1, char $\ast$operator)
+\begin{CompactList}\small\item\em Perform a unary operation on an image.\item\end{CompactList}\item 
+int {\bf ps\-Image\-Overlay\-Section} ({\bf ps\-Image} $\ast$image, {\bf ps\-Image} $\ast$overlay, int x0, int y0, char $\ast$operator)
+\begin{CompactList}\small\item\em Overlay subregion of image with another image.\item\end{CompactList}\item 
+{\bf ps\-Meta\-Data\-Item} $\ast$ {\bf ps\-Meta\-Data\-Item\-Alloc} (int type\-Flags, const void $\ast$val, const char $\ast$comment, const char $\ast$name,...)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+void {\bf ps\-Meta\-Data\-Item\-Free} ({\bf ps\-Meta\-Data\-Item} $\ast$ms)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Meta\-Data\-Set} $\ast$ {\bf ps\-Meta\-Data\-Set\-Alloc} (void)
+\begin{CompactList}\small\item\em Constructor.\item\end{CompactList}\item 
+void {\bf ps\-Meta\-Data\-Set\-Free} ({\bf ps\-Meta\-Data\-Set} $\ast$ms)
+\begin{CompactList}\small\item\em Destructor.\item\end{CompactList}\item 
+{\bf ps\-Meta\-Data\-Item} $\ast$ {\bf ps\-Meta\-Data\-Append} ({\bf ps\-Meta\-Data\-Set} $\ast$restrict ms, {\bf ps\-Meta\-Data\-Item} $\ast$restrict item)
+\item 
+{\bf ps\-Meta\-Data\-Item} $\ast$ {\bf ps\-Meta\-Data\-Remove} ({\bf ps\-Meta\-Data\-Set} $\ast$restrict ms, const char $\ast$restrict key)
+\item 
+void {\bf ps\-Meta\-Data\-Set\-Iterator} ({\bf ps\-Meta\-Data\-Set} $\ast$ms)
+\item 
+{\bf ps\-Meta\-Data\-Item} $\ast$ {\bf ps\-Meta\-Data\-Get\-Next} ({\bf ps\-Meta\-Data\-Set} $\ast$restrict ms, const char $\ast$restrict match)
+\item 
+{\bf ps\-Meta\-Data\-Item} $\ast$ {\bf ps\-Meta\-Data\-Lookup} (const {\bf ps\-Meta\-Data\-Set} $\ast$restrict ms, const char $\ast$restrict key)
+\item 
+void {\bf ps\-Meta\-Data\-Item\-Print} (FILE $\ast$fd, const {\bf ps\-Meta\-Data\-Item} $\ast$restrict ms, const char $\ast$prefix)
+\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coord\-Xform\-Apply} ({\bf ps\-Coord\-Xform} $\ast$frame, {\bf ps\-Coord} $\ast$coords)
+\begin{CompactList}\small\item\em apply the coordinate transformation to the given coordinate\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Distortion\-Apply} ({\bf ps\-Distortion} $\ast$pattern, {\bf ps\-Coord} $\ast$coords, float mag, float color)
+\begin{CompactList}\small\item\em apply the optical distortion to the given coordinate, magnitude, color\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Get\-Offset} (const {\bf ps\-Coord} $\ast$restrict position1, const {\bf ps\-Coord} $\ast$restrict position2, char $\ast$system)
+\begin{CompactList}\small\item\em Get offset (RA,Dec) on the sky between two positions position1 and position2 may not be identical.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Apply\-Offset} (const {\bf ps\-Coord} $\ast$restrict position, const {\bf ps\-Coord} $\ast$restrict offset, char $\ast$system)
+\begin{CompactList}\small\item\em Apply an offset to a position.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Get\-Sun\-Pos} (float mjd)
+\begin{CompactList}\small\item\em Get Sun Position.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Get\-Moon\-Pos} (float mjd, double latitude, double longitude)
+\begin{CompactList}\small\item\em Get Moon position.\item\end{CompactList}\item 
+float {\bf ps\-Get\-Moon\-Phase} (float mjd)
+\begin{CompactList}\small\item\em Get Moon phase.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Get\-Solar\-System\-Pos} (char $\ast$solar\-System\-Object, float mjd)
+\begin{CompactList}\small\item\em Get Planet positions.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coordinates\-Ito\-E} (const {\bf ps\-Coord} $\ast$restrict coordinates)
+\begin{CompactList}\small\item\em Convert ICRS to Ecliptic.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coordinates\-Eto\-I} (const {\bf ps\-Coord} $\ast$restrict coordinates)
+\begin{CompactList}\small\item\em Convert Ecliptic to ICRS.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coordinates\-Ito\-G} (const {\bf ps\-Coord} $\ast$restrict coordinates)
+\begin{CompactList}\small\item\em Convert ICRS to Galactic.\item\end{CompactList}\item 
+{\bf ps\-Coord} $\ast$ {\bf ps\-Coordinates\-Gto\-I} (const {\bf ps\-Coord} $\ast$restrict coordinates)
+\begin{CompactList}\small\item\em Convert Galactic to ICRS.\item\end{CompactList}\end{CompactItemize}
+
+
 \bibliographystyle{plain} \bibliography{panstarrs}
 
