/*============================================================================

  WCSLIB 5.10 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2015, Mark Calabretta

  This file is part of WCSLIB.

  WCSLIB is free software: you can redistribute it and/or modify it under the
  terms of the GNU Lesser General Public License as published by the Free
  Software Foundation, either version 3 of the License, or (at your option)
  any later version.

  WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  more details.

  You should have received a copy of the GNU Lesser General Public License
  along with WCSLIB.  If not, see http://www.gnu.org/licenses.

  Direct correspondence concerning WCSLIB to mark@calabretta.id.au

  Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
  http://www.atnf.csiro.au/people/Mark.Calabretta
  $Id: lin.h,v 5.10 2015/10/09 08:19:15 mcalabre Exp $
*=============================================================================
*
* WCSLIB 5.10 - C routines that implement the FITS World Coordinate System
* (WCS) standard.  Refer to the README file provided with WCSLIB for an
* overview of the library.
*
*
* Summary of the lin routines
* ---------------------------
* Routines in this suite apply the linear transformation defined by the FITS
* World Coordinate System (WCS) standard, as described in
*
=   "Representations of world coordinates in FITS",
=   Greisen, E.W., & Calabretta, M.R. 2002, A&A, 395, 1061 (WCS Paper I)
*
* These routines are based on the linprm struct which contains all information
* needed for the computations.  The struct contains some members that must be
* set by the user, and others that are maintained by these routines, somewhat
* like a C++ class but with no encapsulation.
*
* Four routines, linini(), lindis(), lincpy(), and linfree() are provided to
* manage the linprm struct, and another, linprt(), prints its contents.
*
* linperr() prints the error message(s) (if any) stored in a linprm struct,
* and the disprm structs that it may contain.
*
* A setup routine, linset(), computes intermediate values in the linprm struct
* from parameters in it that were supplied by the user.  The struct always
* needs to be set up by linset() but need not be called explicitly - refer to
* the explanation of linprm::flag.
*
* linp2x() and linx2p() implement the WCS linear transformations.
*
* An auxiliary routine, linwarp(), computes various measures of the distortion
* over a specified range of pixel coordinates.
*
* An auxiliary matrix inversion routine, matinv(), is included.  It uses
* LU-triangular factorization with scaled partial pivoting.
*
*
* linini() - Default constructor for the linprm struct
* ----------------------------------------------------
* linini() allocates memory for arrays in a linprm struct and sets all members
* of the struct to default values.
*
* PLEASE NOTE: every linprm struct must be initialized by linini(), possibly
* repeatedly.  On the first invokation, and only the first invokation,
* linprm::flag must be set to -1 to initialize memory management, regardless
* of whether linini() will actually be used to allocate memory.
*
* Given:
*   alloc     int       If true, allocate memory unconditionally for arrays in
*                       the linprm struct.
*
*                       If false, it is assumed that pointers to these arrays
*                       have been set by the user except if they are null
*                       pointers in which case memory will be allocated for
*                       them regardless.  (In other words, setting alloc true
*                       saves having to initalize these pointers to zero.)
*
*   naxis     int       The number of world coordinate axes, used to determine
*                       array sizes.
*
* Given and returned:
*   lin       struct linprm*
*                       Linear transformation parameters.  Note that, in order
*                       to initialize memory management linprm::flag should be
*                       set to -1 when lin is initialized for the first time
*                       (memory leaks may result if it had already been
*                       initialized).
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         2: Memory allocation failed.
*
*                       For returns > 1, a detailed error message is set in
*                       linprm::err if enabled, see wcserr_enable().
*
*
* lindis() - Assign a distortion to a linprm struct
* -------------------------------------------------
* lindis() may be used to assign the address of a disprm struct to
* linprm::dispre or linprm::disseq.  The linprm struct must already have been
* initialized by linini().
*
* The disprm struct must have been allocated from the heap (e.g. using
* malloc(), calloc(), etc.).  lindis() will immediately initialize it via a
* call to disini() using the value of linprm::naxis.  Subsequently, it will be
* reinitialized by calls to linini(), and freed by linfree(), neither of which
* would happen if the disprm struct was assigned directly.
*
* If the disprm struct had previously been assigned via lindis(), it will be
* freed before reassignment.  It is also permissable for a null disprm pointer
* to be assigned to disable the distortion correction.
*
* Given:
*   sequence  int       Is it a prior or sequent distortion?
*                         1: Prior,   the assignment is to linprm::dispre.
*                         2: Sequent, the assignment is to linprm::disseq.
*
*                       Anything else is an error.
*
* Given and returned:
*   lin       struct linprm*
*                       Linear transformation parameters.
*
*   dis       struct disprm*
*                       Distortion function parameters.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         4: Invalid sequence.
*
*
* lincpy() - Copy routine for the linprm struct
* ---------------------------------------------
* lincpy() does a deep copy of one linprm struct to another, using linini() to
* allocate memory for its arrays if required.  Only the "information to be
* provided" part of the struct is copied; a call to linset() is required to
* initialize the remainder.
*
* Given:
*   alloc     int       If true, allocate memory for the crpix, pc, and cdelt
*                       arrays in the destination.  Otherwise, it is assumed
*                       that pointers to these arrays have been set by the
*                       user except if they are null pointers in which case
*                       memory will be allocated for them regardless.
*
*   linsrc    const struct linprm*
*                       Struct to copy from.
*
* Given and returned:
*   lindst    struct linprm*
*                       Struct to copy to.  linprm::flag should be set to -1
*                       if lindst was not previously initialized (memory leaks
*                       may result if it was previously initialized).
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         2: Memory allocation failed.
*
*                       For returns > 1, a detailed error message is set in
*                       linprm::err if enabled, see wcserr_enable().
*
*
* linfree() - Destructor for the linprm struct
* --------------------------------------------
* linfree() frees memory allocated for the linprm arrays by linini() and/or
* linset().  linini() keeps a record of the memory it allocates and linfree()
* will only attempt to free this.
*
* PLEASE NOTE: linfree() must not be invoked on a linprm struct that was not
* initialized by linini().
*
* Given:
*   lin       struct linprm*
*                       Linear transformation parameters.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*
*
* linprt() - Print routine for the linprm struct
* ----------------------------------------------
* linprt() prints the contents of a linprm struct using wcsprintf().  Mainly
* intended for diagnostic purposes.
*
* Given:
*   lin       const struct linprm*
*                       Linear transformation parameters.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*
*
* linperr() - Print error messages from a linprm struct
* -----------------------------------------------------
* linperr() prints the error message(s) (if any) stored in a linprm struct,
* and the disprm structs that it may contain.  If there are no errors then
* nothing is printed.  It uses wcserr_prt(), q.v.
*
* Given:
*   lin       const struct linprm*
*                       Coordinate transformation parameters.
*
*   prefix    const char *
*                       If non-NULL, each output line will be prefixed with
*                       this string.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*
*
* linset() - Setup routine for the linprm struct
* ----------------------------------------------
* linset(), if necessary, allocates memory for the linprm::piximg and
* linprm::imgpix arrays and sets up the linprm struct according to information
* supplied within it - refer to the explanation of linprm::flag.
*
* Note that this routine need not be called directly; it will be invoked by
* linp2x() and linx2p() if the linprm::flag is anything other than a
* predefined magic value.
*
* Given and returned:
*   lin       struct linprm*
*                       Linear transformation parameters.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         2: Memory allocation failed.
*                         3: PCi_ja matrix is singular.
*
*                       For returns > 1, a detailed error message is set in
*                       linprm::err if enabled, see wcserr_enable().
*
*
* linp2x() - Pixel-to-world linear transformation
* -----------------------------------------------
* linp2x() transforms pixel coordinates to intermediate world coordinates.
*
* Given and returned:
*   lin       struct linprm*
*                       Linear transformation parameters.
*
* Given:
*   ncoord,
*   nelem     int       The number of coordinates, each of vector length nelem
*                       but containing lin.naxis coordinate elements.
*
*   pixcrd    const double[ncoord][nelem]
*                       Array of pixel coordinates.
*
* Returned:
*   imgcrd    double[ncoord][nelem]
*                       Array of intermediate world coordinates.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         2: Memory allocation failed.
*                         3: PCi_ja matrix is singular.
*
*                       For returns > 1, a detailed error message is set in
*                       linprm::err if enabled, see wcserr_enable().
*
*
* linx2p() - World-to-pixel linear transformation
* -----------------------------------------------
* linx2p() transforms intermediate world coordinates to pixel coordinates.
*
* Given and returned:
*   lin       struct linprm*
*                       Linear transformation parameters.
*
* Given:
*   ncoord,
*   nelem     int       The number of coordinates, each of vector length nelem
*                       but containing lin.naxis coordinate elements.
*
*   imgcrd   const double[ncoord][nelem]
*                       Array of intermediate world coordinates.
*
* Returned:
*   pixcrd    double[ncoord][nelem]
*                       Array of pixel coordinates.
*
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         2: Memory allocation failed.
*                         3: PCi_ja matrix is singular.
*
*                       For returns > 1, a detailed error message is set in
*                       linprm::err if enabled, see wcserr_enable().
*
*
* linwarp() - Compute measures of distortion
* ------------------------------------------
* linwarp() computes various measures of the distortion over a specified range
* of pixel coordinates.
*
* All distortion measures are specified as an offset in pixel coordinates,
* as given directly by prior distortions.  The offset in intermediate pixel
* coordinates given by sequent distortions is translated back to pixel
* coordinates by applying the inverse of the linear transformation matrix
* (PCi_ja or CDi_ja).  The difference may be significant if the matrix
* introduced a scaling.
*
* If all distortions are prior, then linwarp() uses diswarp(), q.v.
*
* Given and returned:
*   lin       struct linprm*
*                       Linear transformation parameters plus distortions.
*
* Given:
*   pixblc    const double[naxis]
*                       Start of the range of pixel coordinates (i.e. "bottom
*                       left-hand corner" in the conventional FITS image
*                       display orientation).  May be specified as a NULL
*                       pointer which is interpreted as (1,1,...).
*
*   pixtrc    const double[naxis]
*                       End of the range of pixel coordinates (i.e. "top
*                       right-hand corner" in the conventional FITS image
*                       display orientation).
*
*   pixsamp   const double[naxis]
*                       If positive or zero, the increment on the particular
*                       axis, starting at pixblc[].  Zero is interpreted as a
*                       unit increment.  pixsamp may also be specified as a
*                       NULL pointer which is interpreted as all zeroes, i.e.
*                       unit increments on all axes.
*
*                       If negative, the grid size on the particular axis (the
*                       absolute value being rounded to the nearest integer).
*                       For example, if pixsamp is (-128.0,-128.0,...) then
*                       each axis will be sampled at 128 points between
*                       pixblc[] and pixtrc[] inclusive.  Use caution when
*                       using this option on non-square images.
*
* Returned:
*   nsamp     int*      The number of pixel coordinates sampled.
*
*                       Can be specified as a NULL pointer if not required.
*
*   maxdis    double[naxis]
*                       For each individual distortion function, the
*                       maximum absolute value of the distortion.
*
*                       Can be specified as a NULL pointer if not required.
*
*   maxtot    double*   For the combination of all distortion functions, the
*                       maximum absolute value of the distortion.
*
*                       Can be specified as a NULL pointer if not required.
*
*   avgdis    double[naxis]
*                       For each individual distortion function, the
*                       mean value of the distortion.
*
*                       Can be specified as a NULL pointer if not required.
*
*   avgtot    double*   For the combination of all distortion functions, the
*                       mean value of the distortion.
*
*                       Can be specified as a NULL pointer if not required.
*
*   rmsdis    double[naxis]
*                       For each individual distortion function, the
*                       root mean square deviation of the distortion.
*
*                       Can be specified as a NULL pointer if not required.
*
*   rmstot    double*   For the combination of all distortion functions, the
*                       root mean square deviation of the distortion.
*
*                       Can be specified as a NULL pointer if not required.
*
* Function return value:
*             int       Status return value:
*                         0: Success.
*                         1: Null linprm pointer passed.
*                         2: Memory allocation failed.
*                         3: Invalid parameter.
*                         4: Distort error.
*
*
* linprm struct - Linear transformation parameters
* ------------------------------------------------
* The linprm struct contains all of the information required to perform a
* linear transformation.  It consists of certain members that must be set by
* the user ("given") and others that are set by the WCSLIB routines
* ("returned").
*
*   int flag
*     (Given and returned) This flag must be set to zero whenever any of the
*     following members of the linprm struct are set or modified:
*
*       - linprm::naxis (q.v., not normally set by the user),
*       - linprm::pc,
*       - linprm::cdelt,
*       - linprm::dispre.
*       - linprm::disseq.
*
*     This signals the initialization routine, linset(), to recompute the
*     returned members of the linprm struct.  linset() will reset flag to
*     indicate that this has been done.
*
*     PLEASE NOTE: flag should be set to -1 when linini() is called for the
*     first time for a particular linprm struct in order to initialize memory
*     management.  It must ONLY be used on the first initialization otherwise
*     memory leaks may result.
*
*   int naxis
*     (Given or returned) Number of pixel and world coordinate elements.
*
*     If linini() is used to initialize the linprm struct (as would normally
*     be the case) then it will set naxis from the value passed to it as a
*     function argument.  The user should not subsequently modify it.
*
*   double *crpix
*     (Given) Pointer to the first element of an array of double containing
*     the coordinate reference pixel, CRPIXja.
*
*     It is not necessary to reset the linprm struct (via linset()) when
*     linprm::crpix is changed.
*
*   double *pc
*     (Given) Pointer to the first element of the PCi_ja (pixel coordinate)
*     transformation matrix.  The expected order is
*
=       struct linprm lin;
=       lin.pc = {PC1_1, PC1_2, PC2_1, PC2_2};
*
*     This may be constructed conveniently from a 2-D array via
*
=       double m[2][2] = {{PC1_1, PC1_2},
=                         {PC2_1, PC2_2}};
*
*     which is equivalent to
*
=       double m[2][2];
=       m[0][0] = PC1_1;
=       m[0][1] = PC1_2;
=       m[1][0] = PC2_1;
=       m[1][1] = PC2_2;
*
*     The storage order for this 2-D array is the same as for the 1-D array,
*     whence
*
=       lin.pc = *m;
*
*     would be legitimate.
*
*   double *cdelt
*     (Given) Pointer to the first element of an array of double containing
*     the coordinate increments, CDELTia.
*
*   struct disprm *dispre
*     (Given) Pointer to a disprm struct holding parameters for prior
*     distortion functions, or a null (0x0) pointer if there are none.
*
*     Function lindis() may be used to assign a disprm pointer to a linprm
*     struct, allowing it to take control of any memory allocated for it, as
*     in the following example:
*
=       void add_distortion(struct linprm *lin)
=       {
=         struct disprm *dispre;
=
=         dispre = malloc(sizeof(struct disprm);
=         dispre->flag = -1;
=         lindis(1, lin, dispre);
=           :
=          (Set up dispre.)
=           :
=
=         return;
=       }
*
*     Here, after the distortion function parameters etc. are copied into
*     dispre, dispre is assigned using lindis() which takes control of the
*     allocated memory.  It will be free'd later when linfree() is invoked on
*     the linprm struct.
*
*     Consider also the following erroneous code:
*
=       void bad_code(struct linprm *lin)
=       {
=         struct disprm dispre;
=
=         dispre.flag = -1;
=         lindis(1, lin, &dispre);   // WRONG.
=           :
=
=         return;
=       }
*
*     Here, dispre is declared as a struct, rather than a pointer.  When the
*     function returns, dispre will go out of scope and its memory will most
*     likely be reused, thereby trashing its contents.  Later, a segfault will
*     occur when linfree() tries to free dispre's stale address.
*
*   struct disprm *disseq
*     (Given) Pointer to a disprm struct holding parameters for sequent
*     distortion functions, or a null (0x0) pointer if there are none.
*
*     Refer to the comments and examples given for disprm::dispre.
*
*   double *piximg
*     (Returned) Pointer to the first element of the matrix containing the
*     product of the CDELTia diagonal matrix and the PCi_ja matrix.
*
*   double *imgpix
*     (Returned) Pointer to the first element of the inverse of the
*     linprm::piximg matrix.
*
*   int i_naxis
*     (Returned) The dimension of linprm::piximg and linprm::imgpix (normally
*     equal to naxis).
*
*   int unity
*     (Returned) True if the linear transformation matrix is unity.
*
*   int affine
*     (Returned) True if there are no distortions.
*
*   int simple
*     (Returned) True if unity and no distortions.
*
*   struct wcserr *err
*     (Returned) If enabled, when an error status is returned, this struct
*     contains detailed information about the error, see wcserr_enable().
*
*   double *tmpcrd
*     (For internal use only.)
*   int m_flag
*     (For internal use only.)
*   int m_naxis
*     (For internal use only.)
*   double *m_crpix
*     (For internal use only.)
*   double *m_pc
*     (For internal use only.)
*   double *m_cdelt
*     (For internal use only.)
*   struct disprm *m_dispre
*     (For internal use only.)
*   struct disprm *m_disseq
*     (For internal use only.)
*
*
* Global variable: const char *lin_errmsg[] - Status return messages
* ------------------------------------------------------------------
* Error messages to match the status value returned from each function.
*
*===========================================================================*/

#ifndef WCSLIB_LIN
#define WCSLIB_LIN

#ifdef __cplusplus
extern "C" {
#endif


extern const char *lin_errmsg[];

enum lin_errmsg_enum {
  LINERR_SUCCESS      = 0,	/* Success. */
  LINERR_NULL_POINTER = 1,	/* Null linprm pointer passed. */
  LINERR_MEMORY       = 2,	/* Memory allocation failed. */
  LINERR_SINGULAR_MTX = 3,	/* PCi_ja matrix is singular. */
  LINERR_DISTORT_INIT = 4,	/* Failed to initialise distortions. */
  LINERR_DISTORT      = 5,	/* Distort error. */
  LINERR_DEDISTORT    = 6	/* De-distort error. */
};

struct linprm {
  /* Initialization flag (see the prologue above).                          */
  /*------------------------------------------------------------------------*/
  int flag;			/* Set to zero to force initialization.     */

  /* Parameters to be provided (see the prologue above).                    */
  /*------------------------------------------------------------------------*/
  int naxis;			/* The number of axes, given by NAXIS.      */
  double *crpix;		/* CRPIXja keywords for each pixel axis.    */
  double *pc;			/* PCi_ja  linear transformation matrix.    */
  double *cdelt;		/* CDELTia keywords for each coord axis.    */
  struct disprm *dispre;	/* Prior   distortion parameters, if any.   */
  struct disprm *disseq;	/* Sequent distortion parameters, if any.   */

  /* Information derived from the parameters supplied.                      */
  /*------------------------------------------------------------------------*/
  double *piximg;		/* Product of CDELTia and PCi_ja matrices.  */
  double *imgpix;		/* Inverse of the piximg matrix.            */
  int    i_naxis;		/* Dimension of piximg and imgpix.          */
  int    unity;			/* True if the PCi_ja matrix is unity.      */
  int    affine;		/* True if there are no distortions.        */
  int    simple;		/* True if unity and no distortions.        */

  /* Error handling, if enabled.                                            */
  /*------------------------------------------------------------------------*/
  struct wcserr *err;

  /* Private - the remainder are for internal use.                          */
  /*------------------------------------------------------------------------*/
  double *tmpcrd;

  int    m_flag, m_naxis;
  double *m_crpix, *m_pc, *m_cdelt;
  struct disprm *m_dispre, *m_disseq;
};

/* Size of the linprm struct in int units, used by the Fortran wrappers. */
#define LINLEN (sizeof(struct linprm)/sizeof(int))


int linini(int alloc, int naxis, struct linprm *lin);

int lindis(int sequence, struct linprm *lin, struct disprm *dis);

int lincpy(int alloc, const struct linprm *linsrc, struct linprm *lindst);

int linfree(struct linprm *lin);

int linprt(const struct linprm *lin);

int linperr(const struct linprm *lin, const char *prefix);

int linset(struct linprm *lin);

int linp2x(struct linprm *lin, int ncoord, int nelem, const double pixcrd[],
           double imgcrd[]);

int linx2p(struct linprm *lin, int ncoord, int nelem, const double imgcrd[],
           double pixcrd[]);

int linwarp(struct linprm *lin, const double pixblc[], const double pixtrc[],
            const double pixsamp[], int *nsamp,
            double maxdis[], double *maxtot,
            double avgdis[], double *avgtot,
            double rmsdis[], double *rmstot);

int matinv(int n, const double mat[], double inv[]);


/* Deprecated. */
#define linini_errmsg lin_errmsg
#define lincpy_errmsg lin_errmsg
#define linfree_errmsg lin_errmsg
#define linprt_errmsg lin_errmsg
#define linset_errmsg lin_errmsg
#define linp2x_errmsg lin_errmsg
#define linx2p_errmsg lin_errmsg

#ifdef __cplusplus
}
#endif

#endif /* WCSLIB_LIN */
