/*
*class++
*  Name:
*     Moc

*  Purpose:
*     An arbitrary region of the sky expressed as a collection of HEALPix
*     cells.

*  Constructor Function:
c     astMoc
f     AST_MOC

*  Description:
*     The Moc class uses the IVOA MOC (Multi-Order Coverage) recommendation
*     to describe a region on the sky. The region is made up of an
*     arbitrary collection of cells from the HEALPix sky tessellation,
*     and thus may represent any area on the sky, subject to the
*     constraint that the edges of the area correspond to edges of the
*     HEALPix cells. See the MOC recommendation for further information
*     (http://www.ivoa.net/documents/MOC/).
*
*     The Moc class describes an arbitrary collection of cells on the sky,
*     whereas other subclasses of Region describe exact geometric shapes
*     in any arbitrary domain. This results in some differences between
*     Mocs and other types of Region, the main one being that Mocs have
*     no associated uncertainty.
*
*     The MOC recommendation requires that a MOC always describes a sky
*     area using the ICRS coordinate system. However, the Moc class, like
*     other subclasses of Region, allows its attributes to be changed so
*     that it represents the equivalent area in any celestial coordinate
*     system that can be mapped to ICRS. See attribute Adaptive.
*
*     In practice, to use this class an empty Moc object (i.e. a Moc
*     describing a null area of the sky) should first be created using the
c     astMoc
f     AST_MOC
*     constructor. Areas of the sky should then be added into the empty
*     Moc using one or more of the class methods.
*
*     If it is required to write a Moc out to a FITS binary table, the
*     data value and headers to put in the table can be obtained using
*     methods
c     astGetMocData and astGetMocHeader
f     AST_GETMOCDATA and AST_GETMOCHEADER.
*     The MOC described by an existing FITS binary table can be added
*     into a Moc object using the
c     astAddMocData method.
f     AST_ADDMOCDATA method.
*
*     Note, this class is limited to MOCs for which the number of cells
*     in the normalised MOC can be represented in a four byte signed integer.

*  Inheritance:
*     The Moc class inherits from the Region class.

*  Attributes:
*     In addition to those attributes common to all Regions, every
*     Moc also has the following attributes:
*     - MaxOrder: the highest HEALPix order used in the MOC
*     - MaxRes: the best resolution of the MOC, in arc-seconds
*     - MinOrder: the lowest HEALPix order used in the MOC
*     - MinRes: the worst resolution of the MOC, in arc-seconds
*     - MocArea: the area of the MOC
*     - MocLength: the table length used to describe a MOC in FITS
*     - MocType: the data type used to describe a MOC in FITS

*  Functions:
c     In addition to those functions applicable to all Regions, the
c     following functions may also be applied to all Mocs:
f     In addition to those routines applicable to all Regions, the
f     following routines may also be applied to all Mocs:
*
c     - astAddCell: Adds a single HEALPix cell into an existing Moc
f     - AST_ADDCELL: Adds a single HEALPix cell into an existing Moc
c     - astAddMocData: Adds a FITS binary table into an existing Moc
f     - ADT_ADDMOCDATA: Adds a FITS binary table into an existing Moc
c     - astAddMocString: Adds a JSON or string-encoded MOC into an existing Moc
f     - ADT_ADDMOCSTRING: Adds a JSON or string-encoded MOC into an existing Moc
c     - astAddPixelMask<X>: Adds a pixel mask to an existing Moc
f     - AST_ADDPIXELMASK<X>: Adds a pixel mask to an existing Moc
c     - astAddRegion: Adds a Region to an existing Moc
f     - AST_ADDREGION: Adds a Region to an existing Moc
c     - astGetCell: Identify the next cell included in a Moc
f     - AST_GETCELL: Identify the next cell included in a Moc
c     - astGetMocData: Get the FITS binary table data describing a Moc
f     - AST_GETMOCDATA: Get the FITS binary table data describing a Moc
c     - astGetMocHeader: Get the FITS binary table headers describing a Moc
f     - AST_GETMOCHEADER: Get the FITS binary table headers describing a Moc
c     - astGetMocString: Get the JSON or string-encoded form of a Moc
f     - AST_GETMOCSTRING: Get the JSON or string-encoded form of a Moc
c     - astTestCell: Test if a single HEALPix cell is included in a Moc
f     - AST_TESTCELL: Test if a single HEALPix cell is included in a Moc

*  Copyright:
*     Copyright (C) 2018 East Asian Observatory
*     All Rights Reserved.

*  Licence:
*     This program 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.
*
*     This program is distributed in the hope that it will be useful,
*     but WITHOUT ANY WARRANTY; without even the implied warranty of
*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*     GNU Lesser General Public License for more details.
*
*     You should have received a copy of the GNU Lesser General
*     License along with this program.  If not, see
*     <http://www.gnu.org/licenses/>.

*  Authors:
*     DSB: David S. Berry (Starlink)

*  History:
*     10-OCT-2018 (DSB):
*        Original version.
*     6-MAY-2019 (DSB):
*        Added methods astAddMocString and astGetMocString.
*     7-MAY-2019 (DSB):
*        Modify astAddMocString and astGetMocString so that they can
*        handle JSON encoding as well as string encodiing.
*class--
*/

/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
   the header files that define class interfaces that they should make
   "protected" symbols available. */
#define astCLASS Moc

/* Date type identification codes */
#define LONG_DOUBLE 0
#define DOUBLE 1
#define LONG_INT 2
#define UNSIGNED_LONG_INT 3
#define INT 4
#define UNSIGNED_INT 5
#define SHORT_INT 6
#define UNSIGNED_SHORT_INT 7
#define SIGNED_CHAR 8
#define UNSIGNED_CHAR 9
#define FLOAT 10

/* The declination (rads) at the transition between the polar and equatorial
   regions of the HEALPix projection - arcsin(2/3). */
#define THETAX 0.729727656226966

/* A value that is slightly less than to 0.5. Used as increments to be
   added to the grid coords at a cell centre, in order to get the grid
   coords at the cell edges. Using exactly 0.5 causes problems for cells
   that are adjacent to a discontinuity. */
#define HALF 0.49999

/* The maximum "npix" value for a given order. */
#define MaxNpix(order) (12*(1L<<(2*(order)))-1)


/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */

#include "channel.h"             /* I/O channels */
#include "cmpmap.h"              /* Compound mappings */
#include "cmpregion.h"           /* AST__AND/OR/XOR */
#include "error.h"               /* Error reporting facilities */
#include "fitschan.h"            /* Converts FITS headers to FrameSets */
#include "frame.h"               /* Coordinate system description */
#include "frameset.h"            /* Collections of inter-related Frames */
#include "globals.h"             /* Thread-safe global data access */
#include "mapping.h"             /* Position mappings */
#include "matrixmap.h"           /* Matrix Mappings */
#include "memory.h"              /* Memory allocation facilities */
#include "moc.h"                 /* Interface definition for this class */
#include "object.h"              /* Base Object class */
#include "permmap.h"             /* Axis permutations */
#include "polygon.h"             /* AST__LE/GT etc */
#include "region.h"              /* Frame regions (parent class) */
#include "shiftmap.h"            /* Shift of origin Mappings */
#include "skyframe.h"            /* Celestial coordinate systems */
#include "unitmap.h"             /* Unit Mappings */
#include "wcsmap.h"              /* AST__DR2D */
#include "winmap.h"              /* Mappings between windows */
#include "xphmap.h"              /* Mappings between HPX projection types */
#include "circle.h"              /* Circles */

/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h"             /* AST error codes */

/* C header files. */
/* --------------- */
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>

/* Type definitions. */
/* ================= */
typedef struct PixelMask {
   const void *data;
   void *value;
   void *bad;
   int type;
   int nx;
   int ny;
   int oper;
} PixelMask;

typedef struct CellList {
   int maxorder;
   AstXphMap *maps[ AST__MXORDHPX + 1 ];
   double *(ix[ AST__MXORDHPX + 1 ]);
   double *(iy[ AST__MXORDHPX + 1 ]);
   int len[ AST__MXORDHPX + 1 ];
} CellList;


struct Cell;
struct Corner;

typedef struct Cell {
   struct Corner *bl;
   struct Corner *tl;
   struct Corner *tr;
   struct Corner *br;
   int ix;
   int iy;
   char interior;
   struct Cell *prev;
} Cell;

typedef struct Corner {
   double ra;
   double dec;
   double cosdec;
   struct Cell *cells[4];
   int ncell;
   char interior;
   int dist;
   struct Corner *prev;
} Corner;

typedef struct SinkData {
   char *string;
   size_t mxsize;
} SinkData;

typedef struct SourceData {
   const char *string;
   size_t mxsize;
} SourceData;

typedef struct List {
   int64_t nval;
   size_t *values;
} List;



/* Module Variables. */
/* ================= */

/* Table used by log2_64 function. */
static const int Tab64[64] = {
    63,  0, 58,  1, 59, 47, 53,  2,
    60, 39, 48, 27, 54, 33, 42,  3,
    61, 51, 37, 40, 49, 18, 28, 20,
    55, 30, 34, 11, 43, 14, 22,  4,
    62, 57, 46, 52, 38, 26, 32, 41,
    50, 36, 17, 19, 29, 10, 13, 21,
    56, 45, 25, 31, 35, 16,  9, 12,
    44, 24, 15,  8, 23,  7,  6,  5};

/* Table used by log2_32 function. */
static const int Tab32[32] = {
     0,  9,  1, 10, 13, 21,  2, 29,
    11, 14, 16, 18, 22, 25,  3, 30,
     8, 12, 20, 28, 15, 17, 24,  7,
    19, 27, 23,  6, 26,  5,  4, 31};

/* Address of this static variable is used as a unique identifier for
   member of this class. */
static int class_check;

/* Pointers to parent class methods which are extended by this class. */
static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static const char *(* parent_getattrib)( AstObject *, const char *, int * );
static int (* parent_testattrib)( AstObject *, const char *, int * );
static void (* parent_clearattrib)( AstObject *, const char *, int * );
static void (* parent_setattrib)( AstObject *, const char *, int * );
static int (* parent_equal)( AstObject *, AstObject *, int * );


#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
   globals->Comp_Corner_tol = 0.0; \
   globals->Comp_Corner_exact = 0; \
   globals->Comp_Corner_loop = 0; \
   globals->Comp_Decra_ptr1 = NULL; \
   globals->Comp_Decra_ptr2 = NULL; \
   globals->Class_Init = 0; \
   globals->GetAttrib_Buff[ 0 ] = 0;

/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(Moc)

/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(Moc,Class_Init)
#define Comp_Corner_Tol astGLOBAL(Moc,Comp_Corner_tol)
#define Comp_Corner_Exact astGLOBAL(Moc,Comp_Corner_exact)
#define Comp_Corner_Loop astGLOBAL(Moc,Comp_Corner_loop)
#define class_vtab astGLOBAL(Moc,Class_Vtab)
#define getattrib_buff astGLOBAL(Moc,GetAttrib_Buff)
#define Comp_Decra_Ptr1 astGLOBAL(Moc,Comp_Decra_ptr1)
#define Comp_Decra_Ptr2 astGLOBAL(Moc,Comp_Decra_ptr2)


#include <pthread.h>


#else

static char getattrib_buff[ 51 ];

/* Define the class virtual function table and its initialisation flag
   as static variables. */
static AstMocVtab class_vtab;    /* Virtual function table */
static int class_init = 0;       /* Virtual function table initialised? */

/* Used to pass data to qsort comparison functions */
static double Comp_Corner_Tol = 0.0;
static int Comp_Corner_Exact = 0;
static int Comp_Corner_Loop = 0;
static double *Comp_Decra_Ptr1 = NULL;
static double *Comp_Decra_Ptr2 = NULL;

#endif

/* External Interface Function Prototypes. */
/* ======================================= */
/* The following functions have public prototypes only (i.e. no
   protected prototypes), so we must provide local prototypes for use
   within this module. */
AstMoc *astMocId_( const char *, ... );

/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstFitsChan *GetMocHeader( AstMoc *, int * );
static AstFrame *FindSkyAxes( AstFrame *, const char *, int * );
static AstFrameSet *GetHPX12FrameSet( AstMoc *, int, int * );
static AstMapping *GetCachedMapping( AstMoc *, int, const char *, int * );
static AstPointSet *RegBaseMesh( AstRegion *, int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static AstRegion *GetDefUnc( AstRegion *, int * );
static Cell *MakeCell( int, int, int, Cell **, int * );
static double GetMocArea( AstMoc *, int * );
static double GetPixelArea( AstFrameSet *, const int *, int * );
static double OrderToRes( int order );
static int Comp_corner( const void *, const void * );
static int Comp_decra( const void *, const void * );
static int Comp_range( const void *, const void * );
static int Comp_int64( const void *, const void * );
static int Equal( AstObject *, AstObject *, int * );
static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * );
static int RegTrace( AstRegion *, int, double *, double **, int * );
static int ResToOrder( double );
static int log2_32( int );
static int log2_64( int64_t );
static int TestCell( AstMoc *, int, int64_t, int, int * );
static int64_t XyToNested( int, int, int );
static void AddCell( AstMoc *, int, int, int64_t, int * );
static void AddMocData( AstMoc *, int, int, int, int, int, const void *, int * );
static void AddMocString( AstMoc *, int, int, int, size_t, const char *, int *, int * );
static void AddRegion( AstMoc *, int, AstRegion *, int * );
static void AppendChildren( AstMoc *, Cell *, int, Cell **, int *);
static void ClearCache( AstMoc *, int * );
static void CombineRanges( AstMoc *, int, const char *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void GetCell( AstMoc *, int, int *, int64_t *, int * );
static void GetMocData( AstMoc *, size_t, void *, int * );
static void GetMocString( AstMoc *, int, size_t, char *, size_t *, int * );
static void GetNorm( AstMoc *, const char *, int * );
static void IncorporateCells( AstMoc *, CellList *, int, int, const char *, int * );
static void MakeCorners( AstMoc *, int, Cell *, Corner **, int, int * );
static void MergeRanges( AstMoc *, int, int * );
static void NegateRanges( AstMoc *, int, int, int * );
static void NestedToXy( int64_t, int, int *, int * );
static void PutCell( AstMoc *, AstMapping **, int, int, int, CellList *, int, void *, int, const char *, int * );
static void RegBaseBox( AstRegion *, double *, double *, int * );
static void Sink1( void *, size_t, const char *, int * );
static void Sink2( void *, size_t, const char *, int * );
static const char *Source1( void *, size_t *, int * );
static void TestPixels( PixelMask *, int *, AstPointSet *, int[9], int *);

/* For debugging of astRegBaseMesh and astRegTrace......
static void dump_cell( AstMoc *, Cell *, int );
static void dump_corner( Corner *this, int );
static void dump_moc( AstMoc *, const char *, int *); */

static const char *GetAttrib( AstObject *, const char *, int * );
static void ClearAttrib( AstObject *, const char *, int * );
static void SetAttrib( AstObject *, const char *, int * );
static int TestAttrib( AstObject *, const char *, int * );

static int GetMaxOrder( AstMoc *, int * );
static int TestMaxOrder( AstMoc *, int * );
static void ClearMaxOrder( AstMoc *, int * );
static void SetMaxOrder( AstMoc *, int, int * );

static int GetMinOrder( AstMoc *, int * );
static int TestMinOrder( AstMoc *, int * );
static void ClearMinOrder( AstMoc *, int * );
static void SetMinOrder( AstMoc *, int, int * );

static int GetMocType( AstMoc *, int * );
static int GetMocLength( AstMoc *, int * );



/* Define a macro that expands to a single prototype for function
   AddPixelMask for a given data type. Then use it to define all
   AddPixelMask prototypes for all data types. */
#define ADDPIXELMASK_PROTO(X,Xtype) \
static void AddPixelMask##X( AstMoc *, int, AstFrameSet *, Xtype, int, \
                             int, Xtype, const Xtype[], const int[2], int * );

#if HAVE_LONG_DOUBLE     /* Not normally implemented */
ADDPIXELMASK_PROTO(LD,long double)
#endif
ADDPIXELMASK_PROTO(D,double)
ADDPIXELMASK_PROTO(L,long int)
ADDPIXELMASK_PROTO(UL,unsigned long int)
ADDPIXELMASK_PROTO(I,int)
ADDPIXELMASK_PROTO(UI,unsigned int)
ADDPIXELMASK_PROTO(S,short int)
ADDPIXELMASK_PROTO(US,unsigned short int)
ADDPIXELMASK_PROTO(B,signed char)
ADDPIXELMASK_PROTO(UB,unsigned char)
ADDPIXELMASK_PROTO(F,float)

/* Define a macro that expands to a single prototype for function
   GetSelectionBounds for a given data type and operation. */
#define GETSELECTIONBOUNDS_PROTO0(X,Xtype,Oper) \
static int GetSelectionBounds##Oper##X( Xtype, const Xtype[], const int[ 2 ], int *, int *, int *, int *, int * );

/* Define a macro that expands to a set of prototypes for all operations
   for function GetSelectionBounds for a given data type. */
#define GETSELECTIONBOUNDS_PROTO(X,Xtype) \
GETSELECTIONBOUNDS_PROTO0(X,Xtype,LT) \
GETSELECTIONBOUNDS_PROTO0(X,Xtype,LE) \
GETSELECTIONBOUNDS_PROTO0(X,Xtype,EQ) \
GETSELECTIONBOUNDS_PROTO0(X,Xtype,GE) \
GETSELECTIONBOUNDS_PROTO0(X,Xtype,GT) \
GETSELECTIONBOUNDS_PROTO0(X,Xtype,NE)

/* Use the above macros to define all GetSelectionBounds prototypes for all
   data types and operations. */
#if HAVE_LONG_DOUBLE     /* Not normally implemented */
GETSELECTIONBOUNDS_PROTO(LD,long double)
#endif
GETSELECTIONBOUNDS_PROTO(D,double)
GETSELECTIONBOUNDS_PROTO(L,long int)
GETSELECTIONBOUNDS_PROTO(UL,unsigned long int)
GETSELECTIONBOUNDS_PROTO(I,int)
GETSELECTIONBOUNDS_PROTO(UI,unsigned int)
GETSELECTIONBOUNDS_PROTO(S,short int)
GETSELECTIONBOUNDS_PROTO(US,unsigned short int)
GETSELECTIONBOUNDS_PROTO(B,signed char)
GETSELECTIONBOUNDS_PROTO(UB,unsigned char)
GETSELECTIONBOUNDS_PROTO(F,float)



/* Member functions. */
/* ================= */
static void AddCell( AstMoc *this, int cmode, int order, int64_t npix,
                     int *status ) {
/*
*++
*  Name:
c     astAddCell
f     AST_ADDCELL

*  Purpose:
*     Adds a single HEALPix cell into an existing Moc.

*  Type:
*     Public virtual function.

*  Synopsis:
c     #include "moc.h"
c     void astAddCell( AstMoc *this, int cmode, int order, int64_t npix )
f     CALL AST_ADDCELL( THIS, CMODE, ORDER, NPIX, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function modifies a Moc by combining it with a single
*     specified HEALPix cell. The way in which they are combined is
*     determined by the
c     "cmode" parameter.
f     CMODE parameter.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     cmode
f     CMODE = INTEGER (Given)
*        Indicates how the Moc and specified cell are to be combined. Any
*        of the following values may be supplied:
*        - AST__AND: If the specified cell is included in the Moc, it is
*        removed. Otherwise the Moc is left unchanged.
*        - AST__OR: If the specified cell is not included in the Moc, it is
*        added. Otherwise the Moc is left unchanged.
*        - AST__XOR: The specified cell is toggled - it is removed from
*        the Moc if originally present, and it is added to the Moc if not
*        originally present.
c     order
f     ORDER = INTEGER (Given)
*        The HEALPix order of the cell. An error is reported if this is
*        higher than the maximum order allowed in the Moc (as given by its
*        MaxOrder attribute). If no value has been set for the MaxOrder
*        attribute, calling this method causes it to be set to the supplied
*        order value. So the highest order cells should usually be added
*        first.
c     npix
f     NPIX = INTEGER*8 (Given)
*        The "npix" value identifying the required cell (see the MOC
*        recommendation for more details).
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*--
*/

/* Local Variables: */
   int irange;
   int maxorder;
   int shift;
   int64_t *pr;
   int64_t ihigh;
   int64_t ilow;

/* Check the global error status. */
   if ( !astOK ) return;

/* Validate */
   if( order < 0 || order > AST__MXORDHPX ) {
      astError( AST__INVAR, "astAddCell(%s): Invalid value (%d) "
                "supplied for parameter 'order' - must be no greater "
                "than %d.", status, astGetClass( this ), order,
                AST__MXORDHPX );

   } else if( npix < 0 || npix > MaxNpix( order ) ) {
      astError( AST__INVAR, "astAddCell(%s): Invalid value (%zu) "
                "supplied for parameter 'npix' - must be greater "
                "than 0 and less than %zu.", status, astGetClass( this ),
                npix, MaxNpix( order ) + 1 );

/* Get MaxOrder, if set, and check "order" is no greater than MaxOrder. */
   } else if( astTestMaxOrder( this ) ) {
      maxorder = astGetMaxOrder( this );
      if( maxorder < order && astOK ) {
         astError( AST__INVAR, "astAddCell(%s): Invalid value (%d) "
                   "supplied for parameter 'order' - must be no "
                   "greater than the Moc's MaxOrder attribute (%d).",
                   status, astGetClass( this ), order, maxorder );
      }

/* If MaxOrder is not set, set it to "order". */
   } else {
      maxorder = order;
      astSetMaxOrder( this, order );
   }

/* Get the upper and lower bounds of the range of cells at MaxOrder
   that corresponds to the specified cell. */
   if( astOK ) {
      shift = 2*( maxorder - order );
      ilow = ( npix << shift );
      ihigh = ( (npix + 1 ) << shift ) - 1;

/*  Add this range to the array of ranges in the Moc. */
      irange = this->nrange++;
      this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
      if( astOK ) {
         pr = this->range + 2*irange;
         pr[ 0 ] = ilow;
         pr[ 1 ] = ihigh;
      }

/* Normalise the Moc. */
      astMocNorm( this, cmode == AST__AND, cmode, irange, maxorder,
                  "astAddCell" );
   }
}

static void AddMocData( AstMoc *this, int cmode, int negate, int maxorder,
                        int len, int nbyte, const void *data, int *status ) {
/*
*++
*  Name:
c     astAddMocData
f     AST_ADDMOCDATA

*  Purpose:
*     Adds a FITS binary table into an existing Moc.

*  Type:
*     Public virtual function.

*  Synopsis:
c     #include "moc.h"
c     void astAddMocData( AstMoc *this, int cmode, int negate, int maxorder,
c                         int len, int nbyte, const void *data );
f     CALL AST_ADDMOCDATA( THIS, CMODE, NEGATE, MAXORDER, LEN, NBYTE, DATA,
f                          STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function modifies a Moc by combining it with a binary data array
*     describing a MOC read from a FITS binary table. The way in which they
*     are combined is determined by the
c     "cmode" parameter.
f     CMODE parameter.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     cmode
f     CMODE = INTEGER (Given)
*        Indicates how the Moc and data are to be combined. Any of the
*        following values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the data.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the data.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the data.
c     negate
f     NEGATE = LOGICAL (Given)
*        If
c        non-zero,
f        .FALSE.,
*        the cells added to the Moc will be those included in the
*        supplied data array.
*        If
c        zero,
f        .TRUE.,
*        the cells added to the Moc will be those not included in the
*        supplied data array.
c     maxorder
f     MAXORDER = INTEGER (Given)
*        The maximum HEALPix order to use. If a negative value is supplied,
*        the maximum order will be determined by searching the data array
*        (this will take extra time). In either case, if a value has already
*        been set for the MaxOrder attribute in the Moc, then the attribute
*        value is used in preference to the value supplied for this parameter.
*        Any HEALPix cells in the data array that refer to an order greater
*        than
c        "maxorder"
f        MAXORDER
*        are ignored.
c     len
f     LEN = INTEGER (Given)
*        The length of the supplied array (i.e. the number of 4 or 8 byte
*        integer values it contains). Note, this class only supports binary
*        MOCs with lengths that can be represented in a 4 byte signed
*        integer.
c     nbyte
f     NBYTE = INTEGER (Given)
*        The number of bytes in each integer value stored in the supplied
*        array. Must be 4 or 8.
c     data
f     DATA( * ) = BYTE (Given)
c        Pointer to the
f        The
*        data array holding a description of a MOC in the form used by
*        FITS binary tables. See the IVOA MOC recommendation for details.
*        The values in this array are signed integers, each with the
*        number of bytes specified by parameter
c        "nbyte".
f        NBYTE.
*        The number of bytes in this array should be at least
c        "len*nbyte".
f        LEN*NBYTE.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*  Notes:
*     - If no value has yet been set for attribute MaxOrder, then this
*     function will automatically set it to the value supplied for
c     "Maxorder",
f     MAXORDER,
*     or to the largest order present in the supplied binary MOC if
c     "Maxorder" is negative.
f     MAXORDER  is negative.
*--
*/

/* Local Variables: */
   const int *pni;
   const int64_t *pnk;
   int icell;
   int irange;
   int nold;
   int order;
   int shift;
   int64_t *pr;
   int64_t ihigh;
   int64_t ilow;
   int64_t npix;

/* Check the global error status. */
   if ( !astOK ) return;

/* Validate */
   if( nbyte != 4 && nbyte != 8 ) {
      astError( AST__INVAR, "astAddMocData(%s): Invalid value (%d) "
                "supplied for parameter 'nbyte' - must be 4 or 8",
                status, astGetClass( this ), nbyte );

   } else if( maxorder > AST__MXORDHPX ) {
      astError( AST__INVAR, "astAddMocData(%s): Invalid value (%d) "
                "supplied for parameter 'maxorder' - must be no greater "
                "than %d.", status, astGetClass( this ), maxorder,
                AST__MXORDHPX );

   } else if( len < 0 ) {
      astError( AST__INVAR, "astAddMocData(%s): Invalid value (%d) "
                "supplied for parameter 'len' - must be greater "
                "than 0.", status, astGetClass( this ), len );

/* If the supplied MOC is empty (i.e. len==0) then the resulting Moc
   will be unchanged unless "cmode" is AST__AND, in which case the
   resulting Moc will be empty. */
   } else if( len == 0 ) {
      if( cmode == AST__AND ) {
         this->nrange = 0;
         this->range = astFree( this->range );
         ClearCache( this, status );
      }

/* Otherwise, read a MOC from the data array and combine it with the
   supplied Moc. */
   } else {

/* If the MaxOrder attribute is set in the Moc, use it in preference to
   the value supplied for parameter "maxorder". */
      if( astTestMaxOrder( this ) ) {
         maxorder = astGetMaxOrder( this );

/* Otherwise, we use the supplied "maxorder" value. If "maxorder" was
   not supplied (i.e. is negative), make an initial pass through the array
   to determine the maximum order. */
      } else {
         if( maxorder < 0 ) {
            if( nbyte == 4 ) {
               pni = data;
               for( icell = 0; icell < len; icell++ ) {
                  order = log2_32( *(pni++) / 4 ) / 2;
                  if( order > maxorder ) maxorder = order;
               }
            } else {
               pnk = data;
               for( icell = 0; icell < len; icell++ ) {
                  order = log2_64( *(pnk++) / 4 ) / 2;
                  if( order > maxorder ) maxorder = order;
               }
            }
         }

/* Use this value as the Moc's MaxOrder attribute value from now on. */
         astSetMaxOrder( this, maxorder );
      }

/* Record the orginal number of ranges in the Moc. */
      nold =this->nrange;

/* Convert the supplied MOC data to a list of ranges of cells at
  "maxorder" and append to the end of the ranges currently in the Moc. */
      if( nbyte == 4 ) {
         pni = data;
      } else {
         pnk = data;
      }
      for( icell = 0; icell < len; icell++ ) {

/* Decode the data value (a "nuniq" value) to get the order and npix, using
   a fast log2 function. */
         if( nbyte == 4 ) {
            order = log2_32( *pni / 4 ) / 2;
            npix = *(pni++) - ( 1 << (2 + 2*order) );
         } else {
            order = log2_64( *pnk / 4 ) / 2;
            npix = *(pnk++) - ( 1L << (2 + 2*order) );
         }

/* Ignore cells at orders higher than maxorder. */
         if( order <= maxorder ) {

/* Get the upper and lower bounds of the cells at maxorder contained
   within this cell at order. */
            shift = 2*( maxorder - order );
            ilow = ( npix << shift );
            ihigh = ( (npix + 1 ) << shift ) - 1;

/* Append this as a new range to the Moc. */
            irange = this->nrange++;
            this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
            if( astOK ) {
               pr = this->range + 2*irange;
               pr[ 0 ] = ilow;
               pr[ 1 ] = ihigh;
            } else {
               break;
            }
         }
      }

/* Normalise the Moc. */
      astMocNorm( this, negate, cmode, nold, maxorder, "astAddMocData" );
   }
}

static void AddMocString( AstMoc *this, int cmode, int negate, int maxorder,
                          size_t len, const char *string, int *json,
                          int *status ) {
/*
*++
*  Name:
c     astAddMocString
f     AST_ADDMOCSTRING

*  Purpose:
*     Adds a JSON or string-encoded MOC into an existing Moc.

*  Type:
*     Public virtual function.

*  Synopsis:
c     #include "moc.h"
c     int astAddMocString( AstMoc *this, int cmode, int negate, int maxorder,
c                          size_t len, const char*string, int *json );
f     CALL AST_ADDMOCSTRING( THIS, CMODE, NEGATE, MAXORDER, LEN, STRING,
f                            JSON, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function modifies a Moc by combining it with the MOC described
*     by the supplied string - assumed to be encoded using either the string
*     or JSON serialisation described in the MOC recommendation. The way in
*     which they are combined is determined by the
c     "cmode" parameter.
f     CMODE parameter.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     cmode
f     CMODE = INTEGER (Given)
*        Indicates how the supplied MOC is to be combined with the
*        existing Moc. Any of the following values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the sipplied MOC.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the supplied MOC.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the supplied MOC.
c     negate
f     NEGATE = LOGICAL (Given)
*        If
c        non-zero,
f        .FALSE.,
*        the cells added to the existing Moc will be those included in the
*        supplied MOC.
*        If
c        zero,
f        .TRUE.,
*        the cells added to the existing Moc will be those not included in the
*        supplied MOC.
c     maxorder
f     MAXORDER = INTEGER (Given)
*        The maximum HEALPix order to use. If a negative value is supplied,
*        the maximum order will be determined by searching the supplied MOC
*        (this will take extra time). In either case, if a value has already
*        been set for the MaxOrder attribute in the Moc, then the attribute
*        value is used in preference to the value supplied for this parameter.
*        Any HEALPix cells in the supplied MOC that refer to an order greater
*        than
c        "maxorder"
f        MAXORDER
*        are ignored.
c     len
f     LEN = INTEGER (Given)
*        The number of characters to read from the supplied string. If
*        this is greater than the length of the string, it is ignored and the
*        whole string is read.
c     string
f     STRING = CHARACTER * ( * ) (Given)
c        Pointer to the
f        The
*        array of characters holding the supplied MOC. It should be
*        encoded using either the string or JSON serialisation described
*        in the MOC recommendation. The used serialisation is determined
*        from the first non-blank character, which should be either a
*        curly brace ('{'- JSON serialisation) or a digit (string
*        serialisation).
c     json
f     JSON = LOGICAL (Returned)
c        Pointer to an int in which to return a
f        A
*        boolean flag indicating if the supplied string was interpreted
c        using the JSON (non-zero) or string (zero) serialisation.
f        using the JSON (.TRUE.) or string (.FALSE.) serialisation.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*  Notes:
*     - If no value has yet been set for attribute MaxOrder, then this
*     function will automatically set it to the value supplied for
c     "Maxorder",
f     MAXORDER,
*     or to the largest order present in the supplied string MOC if
c     "Maxorder" is negative.
f     MAXORDER  is negative.
*--
*/

/* Local Variables: */
   int nold;
   SourceData data;

/* Check the global error status. */
   if ( !astOK ) return;

/* Validate */
   if( maxorder > AST__MXORDHPX ) {
      astError( AST__INVAR, "astAddMocString(%s): Invalid value (%d) "
                "supplied for parameter 'maxorder' - must be no greater "
                "than %d.", status, astGetClass( this ), maxorder,
                AST__MXORDHPX );

/* If the supplied MOC is empty (i.e. len==0) then the resulting Moc
   will be unchanged unless "cmode" is AST__AND, in which case the
   resulting Moc will be empty. */
   } else if( len == 0 ) {
      if( cmode == AST__AND ) {
         this->nrange = 0;
         this->range = astFree( this->range );
         ClearCache( this, status );
      }

/* Otherwise, read a MOC from the string and combine it with the supplied
   Moc. */
   } else {
      nold = this->nrange;
      data.string = string;
      data.mxsize = len;
      astAddMocText( this, maxorder, Source1, &data, "astAddMocString", json );
      astMocNorm( this, negate, cmode, nold, astGetMaxOrder( this ),
                  "astAddMocString" );
   }
}

void astAddMocText_( AstMoc *this, int maxorder,
                     const char *(*source)( void *, size_t *nc, int * ),
                     void *data, const char *method, int *json, int *status ){
/*
*+
*  Name:
*     astAddMocText

*  Purpose:
*     Adds a JSON or string-encoded MOC into an existing Moc but does not
*     normalise.

*  Type:
*     Protected function.

*  Synopsis:
*     #include "moc.h"
*     void astAddMocText_( AstMoc *this, int maxorder,
*                          const char *(*source)( void *, size_t *, int * ),
*                          void *data, const char *method, int *json )

*  Class Membership:
*     Moc method.

*  Description:
*     This function identifies the ranges of cells at "maxorder" that are
*     included in the supplied JSON or string-encoded MOC, and appends them
*     to the cell ranges stored in the supplied Moc. It does not normalise
*     the Moc (astMocNorm should be called to do this one all ranges have
*     been added to the Moc).

*  Parameters:
*     this
*        Pointer to the Moc to be modified.
*     maxorder
*        The maximum HEALPix order to use. If a negative value is supplied,
*        the maximum order will be determined by searching the supplied MOC.
*        In either case, if a value has already been set for the MaxOrder
*        attribute in the Moc, then the attribute value is used in preference
*        to the value supplied for this parameter. Any HEALPix cells in the
*        supplied MOC that refer to an order greater than "maxorder" are
*        ignored.
*     source
*        A function to call to read in each section of the MOC's string
*        representation. It should have the following synopsis:
*
*        const char *source( void *data, size_t *nc, int *status )
*
*        The source function should return a pointer to a string holding the
*        next set of characters within the JSON or string encoded MOC. It
*        should also return the maximum number of characters (nc) to be
*        used from the string. If a null character is found in the string
*        before 'nc' characters have been read, parsing of the string
*        ends at that point. The "data" pointer can be used for any purpose.
*        A NULL pointer should be returned if there are no more characters
*        to return.
*     data
*        A pointer to an arbitrary structure to be passed to the source
*        function. Can be NULL.
*     method
*        Method name to include in error messages.
*     json
*        Pointer to an int in which to return a boolean flag indicating if
*        the supplied string was interpreted using the JSON (non-zero) or
*        string (zero) serialisation.

*  Notes:
*     - If no value has yet been set for attribute MaxOrder, then this
*     function will automatically set it to the value supplied for "Maxorder",
*     or to the largest order present in the supplied MOC if "Maxorder"
*     is negative.
*-
*/

/* Local Variables: */
   List orders[ 1 + AST__MXORDHPX ];
   const char *text;
   const char *pt;
   const char *pend;
   int first;
   int irange;
   int isrange;
   int mxord;
   int order;
   int shift;
   int state;
   int64_t *pr;
   int64_t ihigh;
   int64_t ilow;
   int64_t ipix;
   int64_t nadd;
   int64_t npix0;
   int64_t npix;
   int64_t nval;
   size_t *values;
   size_t nbyte;
   size_t nc;

/* Initialise */
   *json = 0;

/* Check the global error status. */
   if ( !astOK ) return;

/* Validate */
   if( maxorder > AST__MXORDHPX ) {
      astError( AST__INVAR, "%s(%s): Invalid value (%d) supplied for parameter"
                " 'maxorder' - must be no greater than %d.", status, method,
                astGetClass( this ), maxorder, AST__MXORDHPX );

/* Otherwise, read in and process the text. */
   } else {

/* Initialise the maximum order found in the text. */
      mxord = -1;

/* Initialise the current order and npix value. */
      order = -1;
      npix = 0;

/* Indicate we are not in a string-format range. */
      isrange = 0;
      npix0 = 0;

/* Indicate we are currently looking for the first non-space character,
   from which we determine the format of the supplied string. */
      state = 0;

/* Use the source function to get a pointer to a null-terminated string
   holding the first characters to read. */
      text = (*source)( data, &nc, status );

/* Initialise the list of NPIX values at each order. */
      for( order = 0; order <= AST__MXORDHPX; order++ ) {
         orders[ order ].nval = 0;
         orders[ order ].values = NULL;
      }

/* Loop to parse all available text. This loop populates the above array
   of "Order" structures, which hold the orders used, and the NPIX values
   at each order. It also determines the maximum order. But it does not
   modifiy the supplied Moc structure. */
      while( text && astOK ) {

/* Process each character in the current text string, Stop when 'nc'
   characters have been read or a null character is encountered, which ever
   happens first.  */
         pend = text + nc;
         pt = text;
         while( *pt && pt < pend ){

/* If we are currently looking for the first non-space character... */
            if( state == 0 ) {

/* If the first non-space character is an opening curly brace, assume the
   text uses JSON-serialisation, and indicate we are now looking for an
   order value. */
               if( *pt == '{' ) {
                  *json = 1;
                  state = 1;

/* If the first non-space character is a digit, assume the text uses
   string-serialisation, and indicate we are now looking for an order
   value. Backup by one character so that the digit is re-read on the
   next pass. */
               } else if( isdigit( *pt ) ) {
                  *json = 0;
                  state = 1;
                  pt--;

/* If the first non-space character is anything else, report an error. */
               } else if( !isspace( *pt ) ) {
                  astError( AST__INMOC, "%s(%s): Invalid textual MOC supplied: '%.30s...'",
                            status, method, astGetClass( this ), pt );
                  astError( AST__INMOC, "Cannot determine the serialisation from "
                            "the first character.", status );
                  break;
               }

/* First deal with JSON-serialisation. */
            } else if( *json ) {

/* If we are looking for the double quote starting an order value, skip
   spaces until we find a double quote... */
               if( state == 1 ) {
                  if( *pt == '"' ) {
                     order = 0;
                     state = 2;
                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected double quote at '%.15s'.",
                               status, pt );
                     break;
                  }

/* If we are looking for the digits in an order value, record digits until
   we find a double quote... */
               } else if( state == 2 ) {
                  if( isdigit( *pt ) ) {
                     order = ( *pt - '0' ) + 10*order;
                  } else if( *pt == '"' ) {
                     state = 3;
                  } else {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected double quote or digit "
                               "at '%.15s'.", status, pt );
                     break;
                  }

/* If we are looking for the colon following an order value, skip spaces
   until we find a colon. */
               } else if( state == 3 ) {
                  if( *pt == ':' ) {
                     state = 4;
                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected colon at '%.15s'.",
                               status, pt );
                     break;
                  }

/* If we are looking for the opening square bracket that marks the start
   of a list of NPIX values, skip spaces until we find an opening square
   bracket. */
               } else if( state == 4 ) {
                  if( *pt == '[' ) {
                     first = 1;
                     state = 5;
                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected opening square bracket "
                               "at '%.15s'.", status, pt );
                     break;
                  }

/* If we are looking for the first digit in an NPIX value, skip spaces until
   we find a digit. Record the digit. Note an empty list of NPIX values is
   acceptable so also check for a closing square bacrket so long as we
   have not yet found any NPIX values in this list. */
               } else if( state == 5 ) {
                  if( isdigit( *pt ) ){
                     state = 6;
                     npix = *pt - '0';

                  } else if( first && *pt == ']' ){
                     if( order > mxord ) mxord = order;
                     state = 8;

                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected digit at '%.15s'.",
                               status, pt );
                     break;
                  }

/* If we are looking for the terminator marking the end of an NPIX value,
   skip digits until we find a space, comma or closing square bracket.
   Update the NPIX value with any digits. */
               } else if( state == 6 ) {
                  first = 0;

                  if( isdigit( *pt ) ){
                     npix = ( *pt - '0' ) + 10*npix;
                  } else if( isspace( *pt ) ) {
                     state = 7;
                  } else if( *pt == ',' ) {
                     state = 5;
                  } else if( *pt == ']' ) {
                     state = 8;
                  } else {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected digit, comma or "
                               "closing square bracket at '%.15s'.", status, pt );
                     break;
                  }

/* When the NPIX value is complete append it to the current list of NPIX
   values for the current order. */
                  if( state != 6 ) {
                     nval = orders[ order ].nval;
                     if( nval == 0 && order > mxord ) mxord = order;
                     nbyte = ( nval + 1 )*sizeof( size_t );
                     values = astGrow( orders[ order ].values, 1, nbyte );
                     if( astOK ) {
                        values[ nval ] = npix;
                        orders[ order ].values = values;
                        orders[ order ].nval = nval + 1;
                     }
                  }

/* If we are looking for a comma or closing square bracket marking the
   end of an NPIX value, skip spaces until we find one. */
               } else if( state == 7 ) {
                  if( *pt == ',' ){
                     state = 5;
                  } else if( *pt == ']' ) {
                     state = 8;
                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected comma or closing "
                               "square bracket at '%.15s'.", status, pt );
                     break;
                  }

/* If we are looking for a comma or closing curly brace following a list
   of NPIX values, skip spaces until we find one. */
               } else if( state == 8 ) {
                  if( *pt == ',' ){
                     state = 1;
                  } else if( *pt == '}' ) {
                     state = 9;
                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected comma or closing "
                               "curly brace at '%.15s'.", status, pt );
                     break;
                  }

/* If we are looking for any non-space characters following the closing
   curly brace, report an error if any are found. */
               } else if( state == 9 ) {
                  if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Illegal non-blank characters "
                               "following final closing curly brace at '%.15s'.",
                               status, pt );
                     break;
                  }

/* No other state value should be encountered. */
               } else {
                  astError( AST__INMOC, "%s(%s): Invalid JSON 'state' value "
                            "(%d) (internal AST programming error).", status,
                            method, astGetClass( this ), state );
                  break;
               }

/* Now deal with string-serialisation. */
            } else {

/* If we are looking for the first digit of an order or npix value, skip
   spaces until we find a digit. We do not know yet whether it is an order
   or npix value, but we use "npix" to record the value anyway. */
               if( state == 1 ) {
                  if( isdigit( *pt ) ) {
                     npix = *pt - '0';
                     state = 2;
                  } else if( !isspace( *pt ) ) {
                     astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Expected digit at '%.15s'.",
                               status, pt );
                     break;
                  }

/* If we are looking for the second and subsequent digits in an order or
   npix value, record digits until we find a space, comma, slash or dash... */
               } else if( state == 2 ) {

/* digit - update the order or npix value recorded in "npix". */
                  if( isdigit( *pt ) ) {
                     npix = ( *pt - '0' ) + 10*npix;

/* space or comma - the value previously recorded is an npix value,
   either a single npix value or the end of a range of npix values.
   Record the npix values and then look for the start of the next
   numerical value. Report an error if the order has not yet been
   specified. */
                  } else if( isspace( *pt ) || *pt == ',') {
                     if( order < 0 ) {
                        astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
                                  status, method, astGetClass( this ), text );
                        astError( AST__INMOC, "No order value found at start "
                                  "of string.", status );
                        break;
                     }

                     if( !isrange ) {
                        npix0 = npix;
                        nadd = 1;
                     } else {
                        isrange = 0;
                        nadd = npix - npix0 + 1;
                     }

                     nval = orders[ order ].nval;
                     nbyte = ( nval + nadd )*sizeof( size_t );
                     values = astGrow( orders[ order ].values, 1, nbyte );
                     if( astOK ) {
                        for( ; npix0 <= npix; npix0++ ) {
                           values[ nval++ ] = npix0;
                        }

                        orders[ order ].values = values;
                        orders[ order ].nval = nval;
                     }

                     state = 1;

/* slash - the value previously recorded is an order value. Update the
   maximum order and then look for the start of the next numerical value. */
                  } else if( *pt == '/' ) {
                     order = npix;
                     if( order > mxord ) mxord = order;
                     state = 1;

/* dash - the value previously recorded is an npix value marking the
   start of a range. Remember the range start then look for the start
   of the next numerical value. */
                  } else if( *pt == '-' ) {
                     isrange = 1;
                     npix0 = npix;
                     state = 1;

                  } else {
                     astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
                               status, method, astGetClass( this ), text );
                     astError( AST__INMOC, "Illegal character at '%.15s'.",
                               status, pt );
                     break;
                  }

/* No other state value should be encountered. */
               } else {
                  astError( AST__INMOC, "%s(%s): Invalid string 'state' value "
                            "(%d) (internal AST programming error).", status,
                            method, astGetClass( this ), state );
                  break;
               }
            }

/* Advance to look at the next character in the text string. */
            pt++;
         }

/* Use the source function to get a pointer to a null-terminated string
   holding the next set of characters to read. NULL is returned if there
   are no more characters to read. */
         text = (*source)( data, &nc, status );
      }

/* Check JSON mocs are terminated properly. */
      if( *json ) {
         if( state != 9 && astOK ) {
            astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
                      status, method, astGetClass( this ), text );
            astError( AST__INMOC, "No closing curly brace found.", status );
         }

/* Incorporate any final numerical value in a string moc. The end of
   string is like a terminator (space or comma) in "state 2" for
   string mocs above. */
      } else if( state == 2 ) {
         if( order < 0 ) {
            astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
                      status, method, astGetClass( this ), text );
            astError( AST__INMOC, "No order value found at start of string.",
                      status );
         }

         if( !isrange ) {
            npix0 = npix;
            nadd = 1;
         } else {
            isrange = 0;
            nadd = npix - npix0 + 1;
         }

         nval = orders[ order ].nval;
         nbyte = ( nval + nadd )*sizeof( size_t );
         values = astGrow( orders[ order ].values, 1, nbyte );
         if( astOK ) {
            for( ; npix0 <= npix; npix0++ ) {
               values[ nval++ ] = npix0;
            }

            orders[ order ].values = values;
            orders[ order ].nval = nval;
         }
      }

/* If the MaxOrder attribute is set in the Moc, use it in preference to
   the value supplied for parameter "maxorder". */
      if( astTestMaxOrder( this ) ) {
         maxorder = astGetMaxOrder( this );

/* Otherwise, we use the supplied "maxorder" value unless "maxorder" was
   not supplied (i.e. is negative), in which case we use the value
   determind above from the supplied text. */
      } else {
         if( maxorder < 0 ) maxorder = mxord;
         astSetMaxOrder( this, maxorder );
      }

/* For each order and NPIX value found during the parsing of the text
   (except for any that have an order greater than 'maxorder', which are
   ignored), get the upper and lower bounds of the cells at maxorder
   contained within this cell, and append this as a new range to the Moc. */
      for( order = 0; order <= maxorder; order++ ) {
         nval = orders[ order ].nval;
         values = orders[ order ].values;
         shift = 2*( maxorder - order );

         for( ipix = 0; ipix < nval; ipix++,values++ ){

            ilow = ( *values << shift );
            ihigh = ( (*values + 1 ) << shift ) - 1;

            irange = this->nrange++;
            this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
            if( astOK ) {
               pr = this->range + 2*irange;
               pr[ 0 ] = ilow;
               pr[ 1 ] = ihigh;
            } else {
               break;
            }
         }

/* Free the list of NPIX values at each order. */
         orders[ order ].values = astFree( orders[ order ].values );
      }
   }
}

/*
*++
*  Name:
c     astAddPixelMask<X>
f     AST_ADDPIXELMASK<X>

*  Purpose:
*     Add a set of pixels to a Moc

*  Type:
*     Public function.

*  Synopsis:
c     #include "moc.h"
c     void astAddPixelMask<X>( AstMoc *this, int cmode, AstFrameSet *wcs,
c                              <Xtype> value, int oper, int flags,
c                              <Xtype> badval, const <Xtype> array[],
c                              const int dims[2] )
f     CALL AST_ADDPIXELMASK<X>( THIS, CMODE, WCS, VALUE, OPER, FLAGS,
f                               BADVAL, ARRAY, DIMS, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This is a set of functions that modifies a Moc by combining it
*     with a subset of the pixel positions contained within a supplied
*     2-dimensional array. A FrameSet must be supplied describing the World
*     Coordinate Systems associated with the array. The current Frame of
*     this FrameSet must be a SkyFrame or a CmpFrame containing a SkyFrame.
*
*     The subset of pixels to be combined with the Moc are selected using
c     the "value" and "oper"
f     the VALUE and OPER
*     parameters. The way in which the existing Moc and the selected pixels
*     are combined together is determined by the
c     "cmode" parameter.
f     CMODE parameter.
*
*     An adaptive alogorithm is used to find the HEALPix cells that are
*     inside the selected area in the pixel array. An initial grid,
*     corresponding to the HEALPix cells at the order given by the Moc's
*     "MinOrder" attribute, is placed over the pixel array. Each of these
*     cells is tested at 9 positions (corners, edge-centres and cell-centre).
*     If all 9 positions are inside the selected area of pixels, then the
*     whole cell is assumed to be inside. If no positions are inside the
*     selected area, then the whole cell is assumed to be outside. If there
*     is a mix of inside and outside positions, the cell is divided into
*     four sub-cells at HEALPix order "MinOrder+1", and the same test is
*     applied to each sub-cell in turn. When the HEALPix order reaches the
*     value of the Moc's "MaxOrder" attribute, each cell is tested only at
*     the cell centre, and is assumed to be inside the selected area if the
*     cell centre is inside the selected area.
*
*     This process means that contiguous "islands" or "holes" in the
*     supplied pixel mask may be missed if they are smaller than the cell
*     size associated with HEALPix order "MinOrder".
*
*     If no value has yet been set for the MaxOrder attribute, then this
*     function will automatically set it to the smallest value that results
*     in the cells in the Moc being no larger than half the size of the pixels
*     in the centre of the array. Note, if the value set for attribute
*     MinOrder is greater than or equal to MaxOrder, a value of
*     (MaxOrder-1) will be used in place of MinOrder.
*
*     You should use a function which matches the numerical type of the
*     data you are processing by replacing <X> in the generic function
*     name
c     astAddPixelMask<X>
f     AST_ADDPIXELMASK<X>
c     by an appropriate 1- or 2-character type code. For example, if you
*     are procesing data with type
c     "float", you should use the function astAddPixelMaskF
f     REAL, you should use the function AST_ADDPIXELMASKR
*     (see the "Data Type Codes" section below for the codes appropriate to
*     other numerical types).

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     cmode
f     CMODE = INTEGER (Given)
*        Indicates how the Moc and select pixels are to be combined. Any of the
*        following values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the selected pixels.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the selected pixels.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the selected pixels.
c     wcs
f     WCS = AstFrameSet * (Given)
*        Pointer to a FrameSet defining the World Coordinate Systems
*        associated with the image. The current Frame should be a SkyFrame
*        or a CmpFrame containing a SkyFrame. The base Frame should have
*        the same number of axes as the current Frame and should represent
*        "grid" coordinates within a pixel array (i.e. the first pixel is
*        centred at (1.0,1.0,...) and the distance between pixel centres
*        is 1.0 on both axes).  The array supplied for parameter
c        "array"
f        ARRAY
*        is assumed to be a 2-dimensional slice from this array, spanned
*        by the grid axes corresponding to the SkyFrame axes.
c     value
f     VALUE = <Xtype> (Given)
*        A data value that specifies the selected pixels. See parameter
c        "oper".
f        OPER.
c     oper
f     OPER = INTEGER (Given)
*        Indicates how the
c        "value"
f        VALUE
*        parameter is used to select the required pixels. It can
*        have any of the following values:
c        - AST__LT: select pixels with value less than "value".
c        - AST__LE: select pixels with value less than or equal to "value".
c        - AST__EQ: select pixels with value equal to "value".
c        - AST__NE: select pixels with value not equal to "value".
c        - AST__GE: select pixels with value greater than or equal to "value".
c        - AST__GT: select pixels with value greater than "value".
f        - AST__LT: select pixels with value less than VALUE.
f        - AST__LE: select pixels with value less than or equal to VALUE.
f        - AST__EQ: select pixels with value equal to VALUE.
f        - AST__NE: select pixels with value not equal to VALUE.
f        - AST__GE: select pixels with value greater than or equal to VALUE.
f        - AST__GT: select pixels with value greater than VALUE.
c     flags
f     FLAGS = INTEGER (Given)
c        The bitwise OR of a set of flag values which may be used to
f        The sum of a set of flag values which may be used to
*        provide additional control over the operation. See
*        the "Control Flags" section below for a description of the
*        options available.  If no flag values are to be set, a value
*        of zero should be given.
c     badval
f     BADVAL = <Xtype> (Given)
*        This parameter should have the same type as the elements of
*        the data array. It specifies the value used to flag missing
*        data (bad pixels). Such pixels are never included in the Moc.
*
c        If the AST__USEBAD flag is set via the "flags" parameter,
f        If the AST__USEBAD flag is set via the FLAGS parameter,
*        then this value is used to test for bad pixels in the
*        supplied data array.
c     array
f     ARRAY( * ) = <Xtype> (Given)
c        Pointer to the
f        The
*        2-dimensional data array. The numerical type of this array should
*        match the 1- or 2-character type code appended to the function name
*        (e.g. if you are using
c        astAddPixelMaskF, the type of each array element should be "float").
f        AST_ADDPIXELMASKR, the type of each array element should be REAL).
*
*        The storage order of data within this array should be such that the
*        index of the first grid dimension varies most rapidly (i.e.
c        Fortran array indexing is used).
f        normal Fortran array storage order).
c     dims
f     DIMS( 2 ) = INTEGER (Given)
c        Pointer to an array
f        An array
*        containing the length of each pixel axis, in pixels.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*  Control Flags:
c     The following flags are defined in the "ast.h" header file and
f     The following flags are defined in the AST_PAR include file and
*     may be used to provide additional control over the process.
*     Having selected a set of flags, you should supply the
c     bitwise OR of their values via the "flags" parameter:
f     sum of their values via the FLAGS parameter:
*
*     - AST__USEBAD: Indicates that there may be bad pixels in the
*     input array which must be recognised by comparing with the
c     value given for "badval".
f     value given for BADVAL.
*     If this flag is not set, all input values are treated literally.

*  Data Type Codes:
*     To select the appropriate masking function, you should
c     replace <X> in the generic function name astAddPixelMask<X> with a
f     replace <X> in the generic function name AST_ADDPIXELMASK<X> with a
*     1- or 2-character data type code, so as to match the numerical
*     type <Xtype> of the data you are processing, as follows:
c     - D: double
c     - F: float
c     - L: long int
c     - UL: unsigned long int
c     - I: int
c     - UI: unsigned int
c     - S: short int
c     - US: unsigned short int
c     - B: byte (signed char)
c     - UB: unsigned byte (unsigned char)
f     - D: DOUBLE PRECISION
f     - R: REAL
f     - I: INTEGER
f     - UI: INTEGER (treated as unsigned)
f     - S: INTEGER*2 (short integer)
f     - US: INTEGER*2 (short integer, treated as unsigned)
f     - B: BYTE (treated as signed)
f     - UB: BYTE (treated as unsigned)
*
c     For example, astAddPixelMaskD would be used to process "double"
c     data, while astAddPixelMaskS would be used to process "short int"
c     data, etc.
f     For example, AST_ADDPIXELMASKD would be used to process DOUBLE
f     PRECISION data, while AST_ADDPIXELMASKS would be used to process
f     short integer data (stored in an INTEGER*2 array), etc.
f
f     For compatibility with other Starlink facilities, the codes W
f     and UW are provided as synonyms for S and US respectively (but
f     only in the Fortran interface to AST).

*--
*/

/* Define a macro to implement the function for a specific data type. */
#define MAKE_ADDPIXELMASK(X,Xtype,XtypeId) \
static void AddPixelMask##X( AstMoc *this, int cmode, AstFrameSet *wcs, \
                             Xtype value, int oper, int flags, \
                             Xtype badval, const Xtype array[], \
                             const int dims[2], int *status ) { \
\
/* Local Variables: */ \
   AstCmpMap *array2proj;   /* Mapping for HEALPix grid coordinates */ \
   AstCmpMap *array2proj_min;/* Mapping for best HEALPix grid coordinates */ \
   AstCmpMap *tempmap;      /* Temporary Mapping pointer */ \
   AstFrameSet *array2hpx12;/* Array grid coords -> HPX12 grid coords */ \
   AstFrameSet *fs;         /* Connects grid and sky using HPX projection */ \
   AstFrameSet *fsconv;     /* Conversion FrameSet */ \
   AstFrameSet *picked;     /* WCS FrameSet with 2-dimensional current Frame */ \
   AstMapping *maps[ AST__MXORDHPX + 1 ]; /* hpx->grid mappings for all orders */ \
   AstMapping *tempmap2;    /* Temporary Mapping */ \
   AstWinMap *incmap;       /* Mapping from grid coords at order N+1 to order N */ \
   AstXphMap *xphmap;       /* "iproj" grid coords -> HPX12 grid coords */ \
   CellList clist;          /* Staging area for list of cells in the Moc */ \
   PixelMask pixelmask;     /* Strucure defining the pixel mask */ \
   double delta;            /* Width of safety margin */ \
   double glbnd[2];         /* Lower bounds of region in grid coords */ \
   double gubnd[2];         /* Upper bounds of region in grid coords */ \
   double ina[2];           /* Bottom left corner of input box */ \
   double inb[2];           /* Top right corner of input box */ \
   double lbnd_in[ 2 ];     /* Grid coord bounds of selected array pixels */ \
   double outa[2];          /* Bottom left corner of output box */ \
   double outb[2];          /* Top right corner of output box */ \
   double pixarea;          /* Area of array pixel in square arc-sec */ \
   double res;              /* Mean pixel resolution in arc-sec */ \
   double ubnd_in[ 2 ];     /* Grid coord bounds of selected array pixels */ \
   int glbnd_min[2];        /* Best lower bounds of region in grid coords */ \
   int gubnd_min[2];        /* Best upper bounds of region in grid coords */ \
   int hx;                  /* Upper bound on 1st grid axis of selection box */ \
   int hy;                  /* Upper bound on 2ns grid axis of selection box */ \
   int i;                   /* Loop count */ \
   int iax;                 /* Axis index */ \
   int iproj;               /* Identifier for type of HEALPix projection */ \
   int iproj_min;           /* Identifier for best type of HEALPix projection */ \
   int j;                   /* Loop count */ \
   int lx;                  /* Lower bound on 1st grid axis of selection box */ \
   int ly;                  /* Lower bound on 2nd grid axis of selection box */ \
   int maxorder;            /* MOC order for final grid */ \
   int minorder;            /* MOC order for initial grid */ \
   int ok;                  /* At least one selected pixel? */ \
   size_t npix;             /* Number of pixels in bounding box */ \
   size_t npix_min;         /* Number of pixels in smallest bounding box */ \
\
/* Check the global error status. */ \
   if ( !astOK ) return; \
\
/* Validate. */ \
   if( !astIsAFrameSet( wcs ) ) { \
      astError( AST__BDCLS, "astAddPixelMask"#X"(%s): A %s was supplied for " \
                "parameter 'wcs' but a FrameSet is required.", status,  \
                astGetClass(this), astGetClass(wcs) ); \
   } \
\
   if( dims[ 0 ] < 1 || dims[ 1 ] < 1 ) { \
      astError( AST__INVAR, "astAddPixelMask"#X"(%s): Invalid values " \
                "(%d,%d) supplied for parameter 'dims'.", status,  \
                astGetClass(this), dims[ 0 ], dims[ 1 ] ); \
   } \
\
/* The current Frame of the supplied FrameSet must contain a SkyFrame  \
   (possibly plus other Frames).  Identify the sky axes in the FrameSet, \
   and create a FrameSet by picking the associated pixel axes from the  \
   base Frame axes. */ \
   picked = (AstFrameSet *) FindSkyAxes( (AstFrame *) wcs, "astAddPixelMask"#X, \
                                         status ); \
\
/* We proceed only if a SkyFrame was found. */ \
   if( picked ) { \
\
/* If the MaxOrder attribute has not been set, set it now to the smallest \
   value that gives cells that are no larger than half the pixel size at  \
   centre of the array (i.e 0.25 of the pixel area). */ \
      if( !astTestMaxOrder( this ) ) { \
         pixarea = GetPixelArea( picked, dims, status ); \
         res = sqrt( 0.25*pixarea ); \
         maxorder = ResToOrder( res ); \
         res = OrderToRes( maxorder ); \
         if( res*res > 0.25*pixarea ) maxorder++; \
         astSetMaxOrder( this, maxorder ); \
      } else { \
         maxorder = astGetMaxOrder( this ); \
      } \
\
/* Get the minimum order that defines the spacing of the initial grid. \
   Bounded holes within selected areas that are smaller than a cell of \
   this grid may be missed (i.e. "filled in"). */ \
   minorder = astGetMinOrder( this ); \
\
/* Ensure we do not start at a higher order than we can handle. */ \
   if( minorder >= maxorder ) minorder = maxorder - 1; \
\
/* Invert the WCS FrameSet for the picked (i.e. sky) axes, so that the \
   current Frame represents grid coords in the array and the base Frame \
   represents sky coords. */ \
      astInvert( picked ); \
\
/* Get the integer grid coordinate bounds of the region within the array \
   that contains the selected pixels. */ \
      if( oper == AST__LT ) { \
         ok = GetSelectionBoundsLT##X( value, array, dims, &lx, &hx, \
                                       &ly, &hy, status ); \
      } else if( oper == AST__LE ) { \
         ok = GetSelectionBoundsLE##X( value, array, dims, &lx, &hx, \
                                       &ly, &hy, status ); \
      } else if( oper == AST__EQ ) { \
         ok = GetSelectionBoundsEQ##X( value, array, dims, &lx, &hx, \
                                       &ly, &hy, status ); \
      } else if( oper == AST__NE ) { \
         ok = GetSelectionBoundsNE##X( value, array, dims, &lx, &hx, \
                                       &ly, &hy, status ); \
      } else if( oper == AST__GE ) { \
         ok = GetSelectionBoundsGE##X( value, array, dims, &lx, &hx, \
                                       &ly, &hy, status ); \
      } else if( oper == AST__GT ) { \
         ok = GetSelectionBoundsGT##X( value, array, dims, &lx, &hx, \
                                       &ly, &hy, status ); \
      } else if( astOK ){ \
         astError( AST__OPRIN, "astAddPixelMask"#X"(%s): Invalid operation " \
                   "code (%d) supplied (programming error).", status, \
                   astGetClass( this ), oper ); \
      } \
\
/* If no pixels are selected, the resulting Moc will be unchanged unless \
   "cmode" is AST__AND, in which case the resulting Moc will be empty. */ \
      if( !ok ) { \
         if( cmode == AST__AND ) { \
            this->nrange = 0; \
            this->range = astFree( this->range ); \
            ClearCache( this, status ); \
         } \
\
/* Otherwise, get a FrameSet describing a map of the whole sky using an \
   HPX12 projection, with the maximum MOC order. Invert it so that the base \
   frame represents ICRS and the current Frame represents HEALPix grid \
   coordinates. Each pixel in the grid corresponds to a HEALPix cell on \
   the sky. */ \
      } else { \
         fs = GetHPX12FrameSet( this, maxorder, status ); \
         astInvert( fs ); \
\
/* Get the Mapping from the array's grid coords to HPX12 grid coords at \
   maxorder. */ \
         fsconv = astConvert( picked, fs, "" ); \
         array2hpx12 = astGetMapping( fsconv, AST__BASE, AST__CURRENT ); \
         fsconv = astAnnul( fsconv ); \
         fs = astAnnul( fs ); \
\
/* We need to map the array into a HEALPix projection, of which there \
   are four: HPX centred on RA=0 (HPX0), HPX centred on RA=12h (HPX12), \
   XPH centred on the north pole (XPHN), XPH centred on the south pole \
   (XPHS). Problems arise if the array spans any of discontinuities in \
   the projection, so loop round all four projections and find the one \
   that maps the array onto the smallest box in HEALPix grid coords. */ \
         npix_min = 0; \
         iproj_min = -1; \
         array2proj_min = NULL; \
         for( iproj = 0; iproj <= AST__MXPRJHPX; iproj++ ) { \
\
/* Extend the array2hpx12 Mapping to get a Mapping from the array's grid \
   coords to grid coords in the current variant HPX projection. The \
   "xphmap" Mapping goes from grid coords in the projection given by \
   "iproj" into grid coords within an HPX12 projection. */ \
            xphmap = astXphMap( maxorder, iproj, " ", status ); \
            astInvert( xphmap ); \
            array2proj = astCmpMap( array2hpx12, xphmap, 1, "", status ); \
            xphmap = astAnnul( xphmap ); \
\
/* Use this Mapping to determine the bounding box of the selected pixels \
   within HEALPix grid coords in the projection given by "iproj" at \
   "maxorder. */ \
            lbnd_in[ 0 ] = lx - HALF; \
            ubnd_in[ 0 ] = hx + HALF; \
            lbnd_in[ 1 ] = ly - HALF; \
            ubnd_in[ 1 ] = hy + HALF; \
\
            for( iax = 0; iax < 2; iax++ ){ \
               astMapBox( array2proj, lbnd_in, ubnd_in, 1, iax, glbnd + iax, \
                          gubnd + iax, NULL, NULL ); \
            } \
\
/* Get the number of HEALPix cells in the bounding box. */ \
            npix = (int)( ( gubnd[ 0 ] - glbnd[ 0 ] + 1 )* \
                          ( gubnd[ 1 ] - glbnd[ 1 ] + 1 ) ); \
\
/* If this is the smallest bounding box found so far, record its details, \
   increasing its size by 5% at each edge for safety. */ \
            if( iproj == 0 || npix < npix_min ) { \
               delta =  0.05*( gubnd[0] - glbnd[0] + 1 ); \
               glbnd_min[ 0 ] = (int) ( glbnd[ 0 ] - delta ); \
               gubnd_min[ 0 ] = (int) ( gubnd[ 0 ] + delta ); \
               delta =  0.05*( gubnd[1] - glbnd[1] + 1 ); \
               glbnd_min[ 1 ] = (int) ( glbnd[ 1 ] - delta ); \
               gubnd_min[ 1 ] = (int) ( gubnd[ 1 ] + delta ); \
               npix_min = npix; \
               iproj_min = iproj; \
               if( array2proj_min ) array2proj_min = astAnnul( array2proj_min ); \
               array2proj_min = astClone( array2proj ); \
            } \
\
/* Free resources. */ \
            array2proj = astAnnul( array2proj ); \
         } \
\
/* Report an error if the pixel mask could not be mapped into a \
   defined region using any of the available HPX-like projections. */ \
         if( !array2proj_min && astOK ) { \
            astError( AST__NODEF, "astAddPixelMask"#X"(%s): The supplied " \
                      "pixel array cannot be mapped to HEALPix coordinates.", \
                      status, astGetClass( this ) ); \
         } \
\
/* Store information defining the pixel mask in a structure so that it \
   can be passed easily to other functions. */ \
         pixelmask.data = array; \
         pixelmask.value = astStore( NULL, &value, sizeof(value) ); \
         if( flags & AST__USEBAD ) { \
            pixelmask.bad = astStore( NULL, &badval, sizeof(badval) ); \
         } else { \
            pixelmask.bad = NULL; \
         } \
         pixelmask.type = XtypeId; \
         pixelmask.nx = dims[ 0 ]; \
         pixelmask.ny = dims[ 1 ]; \
         pixelmask.oper = oper; \
\
         if( astOK ) { \
\
/* Generate Mappings for all required orders. Each of these Mappings goes \
   from grid coords in the HPX like projection at order N to grid coords \
   in the pixel array. The Mapping for order (N-1) is derived from the \
   Mapping for order N by dividing each grid cell at order (N-1) into four. \
   This is done by prepending a suitable WinMap to the start of the Mapping \
   for order N. The winmap maps the bottom left 2x2 box of pixels at order \
   N (0.5:2.5,0.5:2.5) onto the bottom left pixel at order (N-1) \
   (0.5:1.5,0.5:1.5). */ \
            ina[ 0 ] = 0.5; \
            ina[ 1 ] = 0.5; \
            inb[ 0 ] = 1.5; \
            inb[ 1 ] = 1.5; \
            outa[ 0 ] = 0.5; \
            outa[ 1 ] = 0.5; \
            outb[ 0 ] = 2.5; \
            outb[ 1 ] = 2.5; \
            incmap = astWinMap( 2, ina, inb, outa, outb, "", status ); \
            astInvert( array2proj_min ); \
            for( i = maxorder; i >= 0; i-- ) { \
               if( i < minorder ) { \
                  maps[ i ] = NULL; \
               } else { \
                  maps[ i ] = astSimplify( array2proj_min ); \
                  tempmap = astCmpMap( incmap, array2proj_min, \
                                               1, "", status ); \
                  (void) astAnnul( array2proj_min ); \
                  array2proj_min = tempmap; \
               } \
            } \
            incmap = astAnnul( incmap ); \
\
/* Get the Mapping from hpx-like grid coords at order maxorder to \
   hpx-like grid coords at order minorder. */ \
            astInvert( maps[minorder] ); \
            tempmap = astCmpMap( maps[maxorder], maps[minorder], 1, \
                                 " ", status ); \
            astInvert( maps[minorder] ); \
            tempmap2 = astSimplify( tempmap ); \
            tempmap = astAnnul( tempmap ); \
\
/* Use this Mapping to transform the bounding box from maxorder to \
   minorder. Add a one pixel safety margin. */ \
            ina[ 0 ] = glbnd_min[ 0 ]; \
            inb[ 0 ] = glbnd_min[ 1 ]; \
            ina[ 1 ] = gubnd_min[ 0 ]; \
            inb[ 1 ] = gubnd_min[ 1 ]; \
            astTran2( tempmap2, 2, ina, inb, 1, outa, outb ); \
            glbnd_min[ 0 ] = (int)( outa[ 0 ] + 0.5 ) - 1; \
            glbnd_min[ 1 ] = (int)( outb[ 0 ] + 0.5 ) - 1; \
            gubnd_min[ 0 ] = (int)( outa[ 1 ] + 0.5 ) + 1; \
            gubnd_min[ 1 ] = (int)( outb[ 1 ] + 0.5 ) + 1; \
            tempmap2 = astAnnul( tempmap2 ); \
\
/* Initialise a CellList structure holding the grid coords of the cells \
   to add into the Moc at each order. This also holds Mappings from \
   projection "iproj_min" to HPX12 at each order. */ \
            memset( &clist, 0, sizeof(clist) ); \
            clist.maxorder = maxorder; \
            for( i = minorder; i <= maxorder; i++ ) { \
               clist.maps[ i ] = astXphMap( i, iproj_min, " ", status ); \
            } \
\
/* Loop over all cells in the best bounding box. */ \
            for( j = glbnd_min[ 1 ]; j <= gubnd_min[ 1 ]; j++ ) { \
               for( i = glbnd_min[ 0 ]; i <= gubnd_min[ 0 ]; i++ ) { \
\
/* Add to the Moc any parts of the current cell that are within the \
   selected areas. */ \
                  PutCell( this, maps, i, j, minorder, &clist, 1, \
                           &pixelmask, cmode, "astAddPixelMask"#X, status ); \
               } \
            } \
         } \
\
/* Convert all the grid coords stored in "clist" into nested indices at \
   order "maxorder" and incorporate them into the current contents of the \
   Moc. */ \
         IncorporateCells( this, &clist, 0, cmode, "astAddPixelMask"#X, \
                           status ); \
\
/* Free resources. */ \
         for( i = minorder; i <= maxorder; i++ ) { \
            maps[ i ] = astAnnul( maps[ i ] ) ; \
            clist.ix[ i ] = astFree( clist.ix[ i ] ); \
            clist.iy[ i ] = astFree( clist.iy[ i ] ); \
            clist.maps[ i ] = astAnnul( clist.maps[ i ] ); \
         } \
         for( i = 0; i <= maxorder; i++ ) { \
            if( maps[ i ] ) maps[ i ] = astAnnul( maps[ i ] ) ; \
         } \
         pixelmask.value = astFree( pixelmask.value ); \
         pixelmask.bad = astFree( pixelmask.bad ); \
         array2hpx12 = astAnnul( array2hpx12 ); \
         array2proj_min = astAnnul( array2proj_min ); \
      } \
\
      picked = astAnnul( picked ); \
   } \
}

/* Expand the above macro to generate a function for each required
   data type. */
#if HAVE_LONG_DOUBLE     /* Not normally implemented */
MAKE_ADDPIXELMASK(LD,long double,LONG_DOUBLE)
#endif
MAKE_ADDPIXELMASK(D,double,DOUBLE)
MAKE_ADDPIXELMASK(L,long int,LONG_INT)
MAKE_ADDPIXELMASK(UL,unsigned long int,UNSIGNED_LONG_INT)
MAKE_ADDPIXELMASK(I,int,INT)
MAKE_ADDPIXELMASK(UI,unsigned int,UNSIGNED_INT)
MAKE_ADDPIXELMASK(S,short int,SHORT_INT)
MAKE_ADDPIXELMASK(US,unsigned short int,UNSIGNED_SHORT_INT)
MAKE_ADDPIXELMASK(B,signed char,SIGNED_CHAR)
MAKE_ADDPIXELMASK(UB,unsigned char,UNSIGNED_CHAR)
MAKE_ADDPIXELMASK(F,float,FLOAT)

/* Undefine the macro. */
#undef MAKE_ADDPIXELMASK



static void AddRegion( AstMoc *this, int cmode, AstRegion *region, int *status ){
/*
*++
*  Name:
c     astAddRegion
f     AST_ADDREGION

*  Purpose:
*     Add a Region into a Moc.

*  Type:
*     Public virtual function.


*  Synopsis:
c     #include "moc.h"
c     void astAddRegion( AstMoc *this, int cmode, AstRegion *region )
f     CALL AST_ADDREGION( THIS, CMODE, REGION, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function modifies a Moc by combining it with a supplied Region.
*     The Region must be defined within a SkyFrame, or within a CmpFrame that
*     contains a SkyFrame. The Region will be converted to ICRS before being
*     combined with the Moc. The way in which they are combined is determined
*     by the
c     "cmode" parameter.
f     CMODE parameter.
*
*     Note, since Moc is a subclass of Region this method can be used to add
*     a Moc into another Moc. In such cases, the data is transferred from
*     one Moc to another directly. For other classes of Region an adaptive
*     algorithm is used to find the HEALPix cells that are inside the Region.
*     An initial grid, corresponding to the HEALPix cells at the order given
*     by the Moc's "MinOrder" attribute, is placed over the bounding box of
*     the supplied Region. Each of these cells is tested at 9 positions
*     (corners, edge-centres and cell-centre). If all 9 positions are inside
*     the supplied Region, then the whole cell is assumed to be inside the
*     Region. If no positions are inside the supplied Region, then the whole
*     cell is assumed to be outside the Region. If there is a mix of inside
*     and outside positions, the cell is divided into four sub-cells at
*     HEALPix order "MinOrder+1", and the same test is applied to each
*     sub-cell in turn. When the HEALPix order reaches the value of the
*     Moc's "MaxOrder" attribute, each cell is tested only at the cell
*     centre, and is assumed to be inside the Region if the cell centre is
*     in the Region.
*
*     This process means that contiguous "islands" or "holes" in the
*     supplied region may be missed if they are smaller than the cell size
*     associated with HEALPix order "MinOrder".

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     cmode
f     CMODE = INTEGER (Given)
*        Indicates how the Moc and Region are to be combined. Any of the
*        following values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the Region.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the Region.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the Region.
c     region
f     REGION = INTEGER (Given)
*        Pointer to the Region to be combined with the Moc.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*  Notes:
*     - When combining the Region with the Moc, it is assumed that the Moc
*     has not been inverted (i.e. the current value of the Moc's 'Negated'
*     attribute is ignored).
*     - If no value has yet been set for attribute MaxOrder, then this
*     function will automatically set it to a value that depends on the
*     class of Region being added. If the Region being added is another
*     Moc, the MaxOrder attribute of the Moc is used. For other classes
*     of Region, the value used corresponds to the resolution closest to
*     0.1% of the linear size of the Region being added (determined using
*     method astGetRegionDisc).
*--
*/

/* Local Variables: */
   AstCmpMap *reg2proj;     /* Region coords -> "iproj" grid coords */
   AstCmpMap *tempmap;      /* Temporary Mapping pointer */
   AstFrame *gridframe;     /* Pointer to a Frame describing grid coords */
   AstFrame *picked_frame;  /* Frame in which picked Region is defined */
   AstFrameSet *fs;         /* Connects grid and sky using HPX projection */
   AstFrameSet *fsconv;     /* Conversion FrameSet */
   AstFrameSet *reg2hpx12;  /* Connects Region coords and HPX12 grid coords */
   AstMapping *hpx2region;  /* HEALPix grid coordinates -> mapped region */
   AstMapping *maps[ AST__MXORDHPX + 1 ]; /* hpx->grid mappings for all orders */
   AstMapping *tempmap2;    /* Temporary Mapping pointer */
   AstMoc *that;            /* The Moc being added to "this" */
   AstRegion *mapped;       /* Region Mapped into HEALPix grid coordinates */
   AstRegion *mapped_min;   /* Region Mapped into best HEALPix grid coordinates */
   AstRegion *picked;       /* Region spanning just sky axes */
   AstWinMap *incmap;       /* Mapping from grid coords at order N+1 to order N */
   AstXphMap *xphmap;       /* "iproj" grid coords -> HPX12 grid coords */
   CellList clist;          /* Staging area for list of cells in the Moc */
   double centre[2];        /* Centre of bounding disc */
   double delta;            /* Width of safety margin */
   double glbnd[2];         /* Lower bounds of region in grid coords */
   double gubnd[2];         /* Upper bounds of region in grid coords */
   double ina[2];           /* Bottom left corner of input box */
   double inb[2];           /* Top right corner of input box */
   double outa[2];          /* Bottom left corner of output box */
   double outb[2];          /* Top right corner of output box */
   double radius;           /* Radius of bounding disc */
   int glbnd_min[2];        /* Best lower bounds of region in grid coords */
   int gubnd_min[2];        /* Best upper bounds of region in grid coords */
   int i;                   /* Loop count */
   int iproj;               /* Identifier for type of HEALPix projection */
   int iproj_min;           /* Identifier for best type of HEALPix projection */
   int irange;              /* Index of range in "that" */
   int ix;                  /* X grid index */
   int iy;                  /* Y grid index */
   int maxorder;            /* Maximum HEALPix order */
   int minorder;            /* Minimum HEALPix order */
   int negated;             /* Is the Region negated? */
   int nold;                /* Number of ranges originally in "this" */
   int shift;               /* No. of bits to shift from that_order to maxorder */
   int that_order;          /* Order of Moc being added to "this" */
   int64_t *pr2;            /* Point to next range in "this" */
   int64_t *pr;             /* Point to next range in "that" */
   int64_t ihigh;           /* High bound of range at maxorder */
   int64_t ilow;            /* Low bound of range at maxorder */
   size_t npix;             /* Number of pixels in bounding box */
   size_t npix_min;         /* Number of pixels in smallest bounding box */

/* Check inherited status */
   if( !astOK ) return;

/* First handle cases where a Moc is being added. */
   if( astIsAMoc( region ) ) {

/* Get a pointer to the Moc being added. */
      that = (AstMoc *) region;

/* If the Moc being added ("that") is empty then the resulting Moc will be
   unchanged unless "cmode" is AST__AND, in which case the resulting Moc
   will be empty. */
      if( that->nrange == 0 ) {
         if( cmode == AST__AND ) {
            this->nrange = 0;
            this->range = astFree( this->range );
            ClearCache( this, status );
         }

/* If "that" is not empty, append its cell ranges to "this". */
      } else {

/* Get the order of the Moc being added. */
         that_order = astGetMaxOrder( that );

/* Get the HEALPix order of the Moc being modified. Set it to the MaxOrder
   value of the Moc being added if it has not already been set. */
         if( astTestMaxOrder( this ) ){
            maxorder = astGetMaxOrder( this );
         } else {
            maxorder = that_order;
            astSetMaxOrder( this, that_order );
         }

/* Record the original number of ranges in "this". */
         nold = this->nrange;

/* Append each cell range in "that" to the end of the array of cell
   ranges in "this", converting them to MaxOrder first. */
         shift = 2*( maxorder - that_order );
         pr = that->range;
         for( irange = 0; irange < that->nrange; irange++, pr += 2 ) {

/* Convert the bounds of the curent range from "that_order" to
   "maxorder". */
            if( shift > 0 ) {
               ilow = ( pr[ 0 ] << shift );
               ihigh = ( ( pr[ 1 ] + 1 ) << shift ) - 1;
            } else {
               ilow = ( pr[ 0 ] >> -shift );
               ihigh = ( pr[ 1 ] >> -shift );
            }

/* Append it to the end of the array of ranges in "this". */
            this->range = astGrow( this->range, this->nrange + 1, 2*sizeof(*(this->range)) );
            if( astOK ) {
               pr2 = this->range +2*(this->nrange++);
               pr2[ 0 ] = ilow;
               pr2[ 1 ] = ihigh;
            } else {
               break;
            }
         }

/* Normalise the Moc. */
         astMocNorm( this, astGetNegated( that ), cmode, nold, maxorder,
                     "astAddRegion" );
      }

/* Mow handle cases where the Region being added is not a Moc. */
   } else {

/* The supplied Region must contain a SkyFrame (possibly plus other
   Frames).  Identify the sky axes in the Region and attempt to create
   a new Region containing just the sky axes. */
      picked = (AstRegion *) FindSkyAxes( (AstFrame *) region, "astAddRegion",
                                          status );

/* We proceed only if a SkyFrame was found. */
      if( picked ) {

/* Record the Nagated flag for the Region then set it false. */
         negated = astGetNegated( picked );
         astClearNegated( picked );

/* Get the maximum HEALPix order. This defines the finest resolution of
   the resulting MOC. Set it to 0.1 % of the diameter of the Region if it
   has not already been set. */
         if( astTestMaxOrder( this ) ){
            maxorder = astGetMaxOrder( this );
         } else {
            astGetRegionDisc( picked, centre, &radius );
            maxorder = ResToOrder( 0.001*radius*AST__DR2D*3600.0 );
            astSetMaxOrder( this, maxorder );
         }

/* Get the minimum order that defines the spacing of the initial grid.
   Bounded holes within selected areas that are smaller than a cell of
   this grid may be missed (i.e. "filled in"). */
         minorder = astGetMinOrder( this );

/* Ensure we do not start at a higher order than we can handle. */
         if( minorder >= maxorder ) minorder = maxorder - 1;

/* Get a pointer to the Frame in which the Region is defined. We use this
   with astConvert below, rather than the original Region, so that the
   FrameSet returned by astConvert will not include the masking effects of
   the Region. */
         picked_frame = astRegFrame( picked );

/* Get a FrameSet describing a map of the whole sky in ICRS using an
   HPX12 projection, with the maximum MOC order. Invert it so that the
   base frame represents ICRS and the current Frame represents HPX12 grid
   coordinates. Each pixel in the grid corresponds to a HEALPix cell on
   the sky (order "maxorder"). We use an HPX12 projection rather than
   HPX0 because the grid indices in an HPX12 projection are more easily
   converted to a HEALPix nested index (one less shift on each axis). */
         fs = GetHPX12FrameSet( this, maxorder, status );
         gridframe = astGetFrame( fs, AST__BASE );
         astInvert( fs );

/* Get the Mapping from the Region's coords to HPX12 grid coords at
   "maxorder". */
         fsconv = astConvert( picked_frame, fs, "" );
         reg2hpx12 = astGetMapping( fsconv, AST__BASE, AST__CURRENT );
         fsconv = astAnnul( fsconv );
         fs = astAnnul( fs );

/* We need to map the region into a HEALPix projection, of which there
   are four variants: HPX centred on RA=0, HPX centred on RA=12h, XPH
   centred on the north pole, XPH centred on the south pole. Problems
   arise if the region spans any of discontinuities in the projection, so
   loop round all four projections and find the one that maps the region
   onto the smallest box in grid coords. */
         npix_min = 0;
         iproj_min = -1;
         mapped_min = NULL;
         for( iproj = 0; iproj <= AST__MXPRJHPX; iproj++ ) {

/* Extend the reg2hpx12 Mapping to get a Mapping from the region's
   coords to grid coords in the current variant HPX projection. The
   "xphmap" Mapping goes from grid coords in the projection given by
   "iproj" into grid coords within an HPX12 projection so invert it
   before using it. */
            xphmap = astXphMap( maxorder, iproj, " ", status );
            astInvert( xphmap );
            reg2proj = astCmpMap( reg2hpx12, xphmap, 1, "", status );
            xphmap = astAnnul( xphmap );

/* Use this FrameSet to map the picked region into grid coords within the
   projection specified by "iproj". If the Region is undefined in the
   current projection, annull the error so that we can skip to the
   next projection. */
            if( astOK ) {
               int rep = astReporting( 0 );
               mapped = astMapRegion( picked, reg2proj, gridframe );
               if( astStatus == AST__NODEF ) astClearStatus;
               astReporting( rep );
            }

/* Find the bounds of the Region in grid coords. Get the number of pixels
   in the bounding box. */
            if( mapped ) {
               astGetRegionBounds( mapped, glbnd, gubnd );
               npix = (int)( ( gubnd[0] - glbnd[0] + 1 )*( gubnd[1] - glbnd[1] + 1 ) );

/* If this is the smallest bounding box found so far, record its details,
   increasing its size by 5% at each edge for safety. */
               if( iproj == 0 || npix < npix_min ) {
                  delta =  0.05*( gubnd[0] - glbnd[0] + 1 );
                  glbnd_min[ 0 ] = (int) ( glbnd[ 0 ] + HALF - delta );
                  gubnd_min[ 0 ] = (int) ( gubnd[ 0 ] + HALF + delta );
                  delta =  0.05*( gubnd[1] - glbnd[1] + 1 );
                  glbnd_min[ 1 ] = (int) ( glbnd[ 1 ] + HALF - delta );
                  gubnd_min[ 1 ] = (int) ( gubnd[ 1 ] + HALF + delta );
                  npix_min = npix;
                  iproj_min = iproj;
                  if( mapped_min ) mapped_min = astAnnul( mapped_min );
                  mapped_min = astClone( mapped );
               }

/* Free resources. */
               mapped = astAnnul( mapped );
            }
            reg2proj = astAnnul( reg2proj );
         }

/* Report an error if the supplied Region could not be mapped into a
   defined region using any of the available HPX-like projections. */
         if( !mapped_min && astOK ) {
            astError( AST__NODEF, "astAddRegion(%s): The supplied %s cannot "
                      "be mapped to HEALPix coordinates.", status,
                      astGetClass( this ), astGetClass( region ) );
         }

/* Generate Mappings for all required orders. Each of these Mappings goes
   from grid coords in the HPX like projection at order N to grid coords
   in the pixel array. The Mapping for order (N-1) is derived from the
   Mapping for order N by dividing each grid cell at order (N-1) into four.
   This is done by prepending a suitable WinMap to the start of the Mapping
   for order N. The winmap maps the bottom left 2x2 box of pixels at order
   N (0.5:2.5,0.5:2.5) onto the bottom left pixel at order (N-1)
   (0.5:1.5,0.5:1.5). */
         ina[ 0 ] = 0.5;
         ina[ 1 ] = 0.5;
         inb[ 0 ] = 1.5;
         inb[ 1 ] = 1.5;
         outa[ 0 ] = 0.5;
         outa[ 1 ] = 0.5;
         outb[ 0 ] = 2.5;
         outb[ 1 ] = 2.5;
         incmap = astWinMap( 2, ina, inb, outa, outb, "", status );
         hpx2region = (AstMapping *) astUnitMap( 2, "", status );
         for( i = maxorder; i >= 0; i-- ) {
            if( i < minorder ) {
               maps[ i ] = NULL;
            } else {
               maps[ i ] = astSimplify( hpx2region );
               tempmap = astCmpMap( incmap, hpx2region, 1, "", status );
               (void) astAnnul( hpx2region );
               hpx2region = (AstMapping *) tempmap;
            }
         }
         incmap = astAnnul( incmap );
         hpx2region = astAnnul( hpx2region );

/* Get the Mapping from hpx-like grid coords at order maxorder to
   hpx-like grid coords at order minorder. */
         astInvert( maps[minorder] );
         tempmap = astCmpMap( maps[maxorder], maps[minorder], 1, " ", status );
         astInvert( maps[minorder] );
         tempmap2 = astSimplify( tempmap );
         tempmap = astAnnul( tempmap );

/* Use this Mapping to transform the bounding box from maxorder to
   minorder. Add a one pixel safety margin. */
         ina[ 0 ] = glbnd_min[ 0 ];
         inb[ 0 ] = glbnd_min[ 1 ];
         ina[ 1 ] = gubnd_min[ 0 ];
         inb[ 1 ] = gubnd_min[ 1 ];
         astTran2( tempmap2, 2, ina, inb, 1, outa, outb );
         glbnd_min[ 0 ] = (int)( outa[ 0 ] + 0.5 ) - 1;
         glbnd_min[ 1 ] = (int)( outb[ 0 ] + 0.5 ) - 1;
         gubnd_min[ 0 ] = (int)( outa[ 1 ] + 0.5 ) + 1;
         gubnd_min[ 1 ] = (int)( outb[ 1 ] + 0.5 ) + 1;
         tempmap2 = astAnnul( tempmap2 );

/* Initialise a CellList structure holding the grid coords of the cells
   to add into the Moc at each order. This also holds Mappings from
   projection "iproj_min" to HPX12 at each order. */
         memset( &clist, 0, sizeof(clist) );
         clist.maxorder = maxorder;
         for( i = minorder; i <= maxorder; i++ ) {
            clist.maps[ i ] = astXphMap( i, iproj_min, " ", status );
         }

/* Loop over all cells in the best bounding box at minorder. */
         for( iy = glbnd_min[ 1 ]; iy <= gubnd_min[ 1 ]; iy++ ) {
            for( ix = glbnd_min[ 0 ]; ix <= gubnd_min[ 0 ]; ix++ ) {

/* Add to the CellList any parts of the current cell that are within the
   selected areas. */
               PutCell( this, maps, ix, iy, minorder, &clist, 0, mapped_min,
                        cmode, "astAddRegion", status );
            }
         }

/* Convert all the grid coords stored in "clist" into nested indices at
   order "maxorder" and incorporate them into the current contents of the
   Moc. */
         IncorporateCells( this, &clist, negated, cmode, "astAddRegion", status );

/* Reinstate the original value of the Nagated flag for the Region. */
         astSetNegated( picked, negated );

/* Free resources. */
         for( i = minorder; i <= maxorder; i++ ) {
            maps[ i ] = astAnnul( maps[ i ] ) ;
            clist.ix[ i ] = astFree( clist.ix[ i ] );
            clist.iy[ i ] = astFree( clist.iy[ i ] );
            clist.maps[ i ] = astAnnul( clist.maps[ i ] );
         }
         picked = astAnnul( picked );
         mapped_min = astAnnul( mapped_min );
         picked_frame = astAnnul( picked_frame );
         reg2hpx12 = astAnnul( reg2hpx12 );
         gridframe = astAnnul( gridframe );
      }
   }
}

static void AppendChildren( AstMoc *this, Cell *cell, int order,
                            Cell **cell_foot, int *status ){
/*
*  Name:
*     AppendChildren

*  Purpose:
*     Create Cells for the direct children of a specified Cell.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void AppendChildren( AstMoc *this, Cell *cell, int order,
*                          Cell **cell_foot, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function creates Cell structures to describe the four direct
*     children of the supplied cell, and appends them to the chain of
*     linked Cells for order (order+1). If the parent cell is flagged as
*     an interior cell, then all the children are also flagged as interior.

*  Parameters:
*     this
*        The Moc.
*     cell
*        The Cell structure describing the cell to be subdivided.
*     order
*        The order of the cell to be subdivided.
*     cell_foot
*        Pointer to an array with (AST__MXORDHPX+1) elements, one for
*        each order. Ech element is a pointer to the last Cell structure
*        in a chain of linked structures holding information about the cells
*        at the corresponding order.
*     status
*        Pointer to inherited status.

*/

/* Local Variables: */
   Cell *new_cell;
   int gx0;
   int gy0;
   int ix;
   int iy;

/* Check inherited status */
   if( !astOK ) return;

/* Increment the order to get the order for the new child cells. */
   order++;

/* Get the grid coords in an HPX12 projection at the centre of the bottom
   left child cell at the new order. */
   gx0 = 2*cell->ix - 1;
   gy0 = 2*cell->iy - 1;

/* Loop over the four direct children of the supplied cell. */
   for( iy = 0; iy < 2; iy++ ) {
      for( ix = 0; ix < 2; ix++ ) {

/* Create the new Cell, appending it to the chain of Cells for the new
   order. */
         new_cell = MakeCell( gx0 + ix, gy0 + iy, order, cell_foot, status );

/* If the parent Cell is interior, the child cells are also interior. */
         new_cell->interior = cell->interior;
      }
   }
}

static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
*  Name:
*     ClearAttrib

*  Purpose:
*     Clear an attribute value for a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void ClearAttrib( AstObject *this, const char *attrib, int *status )

*  Class Membership:
*     Moc member function (over-rides the astClearAttrib protected
*     method inherited from the Region class).

*  Description:
*     This function clears the value of a specified attribute for a
*     Moc, so that the default value will subsequently be used.

*  Parameters:
*     this
*        Pointer to the Moc.
*     attrib
*        Pointer to a null terminated string specifying the attribute
*        name.  This should be in lower case with no surrounding white
*        space.
*     status
*        Pointer to the inherited status variable.
*/

/* Local Variables: */
   AstMoc *this;             /* Pointer to the Moc structure */

/* Check the global error status. */
   if ( !astOK ) return;

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) this_object;

/* Check the attribute name and clear the appropriate attribute. */

/* MaxOrder and MaxRes. */
/* -------------------- */
   if ( !strcmp( attrib, "maxorder" ) || !strcmp( attrib, "maxres" ) ) {
      if( astTestMaxOrder( this ) ) {
         astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
                   "value for a %s.", status, attrib, astGetClass( this ) );
         astError( AST__NOWRT, "The previously set value cannot be changed." ,
                   status);
      } else {
         astClearMaxOrder( this );
      }

/* MinOrder. */
/* --------- */
   } else if ( !strcmp( attrib, "minorder" ) ) {
      astClearMinOrder( this );

/* MinRes. */
/* ------- */
   } else if ( !strcmp( attrib, "minres" ) ) {
      astClearMinOrder( this );

/* If the name was not recognised, test if it matches any of the
   read-only attributes of this class. If it does, then report an
   error. */
   } else if ( !strcmp( attrib, "moctype" ) ||
               !strcmp( attrib, "moclength" ) ||
               !strcmp( attrib, "mocarea" ) ){
      astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
                "value for a %s.", status, attrib, astGetClass( this ) );
      astError( AST__NOWRT, "This is a read-only attribute." , status);

/* If the attribute is still not recognised, pass it on to the parent
   method for further interpretation. */
   } else {
      (*parent_clearattrib)( this_object, attrib, status );
   }
}

static void ClearCache( AstMoc *this, int *status ){
/*
*
*  Name:
*     ClearCache

*  Purpose:
*     Clear the cached values stored in a Moc structure.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void ClearCache( AstMoc *this, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     Clears the cached values stored in a Moc structure. It should be
*     called whenever anythiong modifies the region represented by the Moc.

*  Parameters:
*     this
*        The Moc.
*     status
*        Pointer to the inherited status variable.

*/

/* Check the global error status. */
   if ( !astOK ) return;

/* Delete the normalised form so that it is recalculated when it is
   next required. */
   this->inorm = astFree( this->inorm );
   this->knorm = astFree( this->knorm );

/* Delete the Moc's base mesh and the index of the base mesh that
   orders the mesh points around the perimeter. */
   if( this->basemesh ) this->basemesh = astAnnul( this->basemesh );
   this->meshdist = astFree( this->meshdist );

/* Indicate the bounding box needs to be recalculated. */
   this->lbnd[ 0 ] = AST__BAD;
   this->lbnd[ 1 ] = AST__BAD;
   this->ubnd[ 0 ] = AST__BAD;
   this->ubnd[ 1 ] = AST__BAD;

/* Indicate the Moc's area needs to be recalculated. */
   this->mocarea = AST__BAD;

/* The default uncertainty. */
   if( this->unc ) this->unc = astAnnul( this->unc );
}

static void ClearMaxOrder( AstMoc *this, int *status ){
/*
*
*  Name:
*     ClearMaxOrder

*  Purpose:
*     Clear the value for the MaxOrder attribute

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void ClearMaxOrder( AstMoc *this, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     Clears the value of the MaxPlot attribute.

*  Parameters:
*     this
*        The Moc.
*     status
*        Pointer to the inherited status variable.

*/

/* Check the global error status. */
   if ( !astOK ) return;

/* Clear the value */
   this->maxorder = -INT_MAX;

/* Clear cached items that depend on the max order value. */
   ClearCache( this, status );
}

static void CombineRanges( AstMoc *this, int cmode, const char *method,
                           int *status ){
/*
*  Name:
*     CombineRanges

*  Purpose:
*     Combine the ranges of nested indices currently stored in a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void CombineRanges( AstMoc *this, int cmode, const char *method,
*                         int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function combines the separate ranges of nested index stored
*     in a Moc, using the specified combination method.

*  Parameters:
*     this
*        Pointer to the Moc.
*     cmode
*        Indicates how the ranges are to be combined. Any of the following
*        values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the cell list.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the cell list.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the cell list.
*     method:
*        Name of calling method.
*     status
*        The inherited status.

*/

/* Local Variables: */
   int irange;
   int nnew;
   int64_t *newlist;
   int64_t *pcur;
   int64_t *pnew;
   int64_t *pr;
   int64_t *pw;
   int64_t cur_end;
   int64_t cur_start;

/* Check inherited status */
   if( !astOK ) return;

/* Nothing to do if there are fewer than 2 ranges in the Moc. */
   if( this->nrange > 1 ) {

/* Now sort all the ranges currently stored in the Moc into increasing order
   of lower bound. */
      qsort( this->range, this->nrange, 2*sizeof(*(this->range)), Comp_range );

/* Result is union of old and new */
/* ------------------------------ */
      if( cmode == AST__OR ){

/* Merge overlapping ranges. "pnew" points to each range in the Moc in
   turn. If it overlaps the previous range (the "current" range), then
   the previous range is extended to incorprate it. */
         nnew = 1;
         cur_start = this->range[0];
         cur_end = this->range[1];
         pnew = this->range + 2;
         pw = this->range;
         for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {

/* Gap between new range and current range. New range becomes current
   range. Increment the number of new ranges. */
            if( pnew[ 0 ] > cur_end + 1 ) {
               pw[ 0 ] = cur_start;
               pw[ 1 ] = cur_end;
               pw += 2;
               cur_start = pnew[ 0 ];
               cur_end = pnew[ 1 ];
               nnew++;

/* New range extends the current range. The upper bound of the current
   range is set to the upper bound of the new range. */
            } else if( pnew[ 1 ] > cur_end ){
               cur_end = pnew[ 1 ];

/* New range is contained within the current range. Do nothing. */
            }
         }

/* Add in the final range. */
         pw[ 0 ] = cur_start;
         pw[ 1 ] = cur_end;

/* Update the number of ranges in the Moc. */
         this->nrange =  nnew;

/* Result is intersection of old and new */
/* ------------------------------ */
      } else if( cmode == AST__AND ){

/* Look for overlaps of adjcent ranges. "pnew" points to each range in the Moc in
   turn. If it overlaps the "current" range, then append the overlap
   range to a new list of ranges. */
         nnew = 0;
         newlist = NULL;
         pcur = this->range;
         pnew = this->range + 2;
         for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {

/* Gap between new range and current range, so no intersection and
   nothing to store. New range becomes current range. */
            if( pnew[ 0 ] > pcur[ 1 ] ) {
               pcur = pnew;

/* New range overlaps the current range. Store the overlap in the list of
   new ranges. New range becomes the current range if it extends beyond
   the current range. */
            } else {
               newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
               if( astOK ) {
                  pr = newlist +2*(nnew++);
                  pr[ 0 ] = pnew[ 0 ];
                  if( pnew[ 1 ] > pcur[ 1 ] ) {
                     pr[ 1 ] = pcur[ 1 ];
                     pcur = pnew;
                  } else {
                     pr[ 1 ] = pnew[ 1 ];
                  }
               }
            }
         }

/* Update the ranges in the Moc. */
         (void) astFree( this->range );
         this->range = newlist;
         this->nrange =  nnew;

/* Result is exclusive disjunction of old and new */
/* ---------------------------------------------- */
      } else if( cmode == AST__XOR ){

/* Look for overlaps of adjcent ranges. "pnew" points to each range in the Moc in
   turn. If it overlaps the "current" range, then append the earlier non-overlap
   range to a new list of ranges. */
         nnew = 0;
         newlist = NULL;
         pcur = this->range;
         pnew = this->range + 2;
         for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {

/* Gap between new range and current range, so no intersection. Add the
   current range to the new range list. The new range then becomes the
   current range. */
            if( pnew[ 0 ] > pcur[ 1 ] ) {
               newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
               if( astOK ) {
                  pr = newlist +2*(nnew++);
                  pr[ 0 ] = pcur[ 0 ];
                  pr[ 1 ] = pcur[ 1 ];
               }
               pcur = pnew;

/* New range overlaps the current range. Store the earlier section of the
   current range (before the new range starts) as a new range. */
            } else {
               newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
               if( astOK ) {
                  pr = newlist +2*(nnew++);
                  pr[ 0 ] = pcur[ 0 ];
                  pr[ 1 ] = pnew[ 0 ] - 1;
               }

/* If the new range ends after the current range ends, modify the lower bound
   of the new range to be adjacent to the upper bound of the current
   range. Then use the new range as the current range. */
               if( pcur[ 1 ] < pnew[ 1 ] ) {
                  pnew[ 0 ] = pcur[ 1 ] + 1;
                  pcur = pnew;

/* If the new range ends before the current range ends, modify the lower bound
   of the current range to be adjacent to the upper bound of the new range.
   The current range pointer remains unchanged. */
               } else {
                  pcur[ 0 ] = pnew[ 1 ] + 1;
               }
            }
         }

/* Add in the final range. */
         newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
         if( astOK ) {
            pr = newlist +2*(nnew++);
            pr[ 0 ] = pcur[ 0 ];
            pr[ 1 ] = pcur[ 1 ];
         }

/* Update the ranges in the Moc. */
         (void) astFree( this->range );
         this->range = newlist;
         this->nrange =  nnew;

/* Unknown operation new */
/* --------------------- */
      } else if( astOK ){
         astError( AST__BDPAR, "%s(%s): Bad value (%d) suppied for "
                   "parameter 'cmode'.", status, method,
                   astGetClass(this), cmode );
      }

/* Check the new ranges do not overlap and are not reversed. */
      if( astOK && this->nrange > 0 ) {
         pcur = this->range;
         if( pcur[ 1 ] < pcur[ 0 ] ) {
            astError( AST__INTER, "CombineRanges(%s): Range 0 [%zu:%zu]"
                      " - upper bound lower than lower bound (internal "
                      "programming error).", status, astGetClass(this),
                      pcur[ 0 ], pcur[ 1 ] );
         } else {
            pnew = this->range + 2;
            for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {
               if( pnew[ 1 ] < pnew[ 0 ] ) {
                  astError( AST__INTER, "CombineRanges(%s): Range %d [%zu:%zu]"
                            " - upper bound lower than lower bound (internal "
                            "programming error).", status, astGetClass(this),
                            irange, pnew[ 0 ], pnew[ 1 ] );
                  break;
               } else if( pcur[ 1 ] >= pnew[ 0 ] ) {
                  astError( AST__INTER, "CombineRanges(%s): Range %d [%zu:%zu]"
                            " overlaps range %d [%zu:%zu] (internal programming "
                            "error).", status, astGetClass(this), irange - 1,
                            pcur[ 0 ], pcur[ 1 ], irange, pnew[ 0 ], pnew[ 1 ] );
                  break;
               }
               pcur = pnew;
            }
         }
      }
   }

/* Clear the cached information stored in the Moc structure so that it is
   re-calculated when next needed. */
   ClearCache( this, status );
}

static int Comp_corner( const void *a, const void *b ){
/*
*  Name:
*     Comp_corner

*  Purpose:
*     A comparison function for use with the qsort function.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int Comp_corner( const void *a, const void *b )

*  Class Membership:
*     Moc member function

*  Description:
*     This function compares two sky positions (held in two Corner
*     structures) and returns a value indicating which comes first.
*     Positions are sorted first by Dec and then by RA.
*
*     Values that are close to each other in Dec and RA are considered
*     to be co-incident. The tolerance in Dec is given by Comp_Corner_Tol.
*     The tolerance in RA depends on the value of Comp_Corner_Exact:
*        0 - The RA equivalent of the arc-distance given by Comp_Corner_Tol
*        1 - Zero tolerance (i.e. RA values must match exactly to be
*            considered co-incident).

*  Parameters:
*     a
*        Pointer to the address of the first Corner.
*     b
*        Pointer to the address of the second Corner.

*  Returned Value:
*     An integer less than, equal to, or greater than  zero if  the
*     first  parameter  is considered to be respectively less than,
*     equal to, or greater than the second.

*/
   astDECLARE_GLOBALS
   astGET_GLOBALS(NULL);
   double ratol;

   Corner *ac = *((Corner **) a);
   Corner *bc = *((Corner **) b);

   double ra1 = ac->ra;
   double dec1 = ac->dec;
   double ra2 = bc->ra;
   double dec2 = bc->dec;

/* If required, shift the RA values from [0,2.PI[ to [-PI,+PI[. */
   if( Comp_Corner_Loop != 0 ) {
      while( ra1 < -AST__DPI ) ra1 += 2*AST__DPI;
      while( ra2 < -AST__DPI ) ra2 += 2*AST__DPI;
      while( ra1 >= AST__DPI ) ra1 -= 2*AST__DPI;
      while( ra2 >= AST__DPI ) ra2 -= 2*AST__DPI;
   }

/* Get the tolerance to allow in RA. */
   if( Comp_Corner_Exact ) {
      ratol = 0.0;
   } else if( ac->cosdec > bc->cosdec ){
      ratol = Comp_Corner_Tol/ac->cosdec;
   } else {
      ratol = Comp_Corner_Tol/bc->cosdec;
   }

/* Compare Dec, then RA. Allow some tolerance on Dec so that the cells on
   a single HEALPix ring (constant Dec) get sorted by RA together. */
   if( fabs( dec1 - dec2 ) <= Comp_Corner_Tol ) {
      if( fabs( ra1 - ra2 ) <= ratol ) {
         return 0;
      } else if( ra1 < ra2 ) {
         return -1;
      } else {
         return 1;
      }
   } else if( dec1 < dec2 ) {
      return -1;
   } else {
      return 1;
   }
}

static int Comp_decra( const void *a, const void *b ){
/*
*  Name:
*     Comp_decra

*  Purpose:
*     A comparison function for use with the qsort function.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int Comp_decra( const void *a, const void *b )

*  Class Membership:
*     Moc member function

*  Description:
*     This function compares two sky positions (held in two double
*     arrays) and returns a value indicating which comes first.
*     Positions are sorted first by Dec and then by RA.

*  Parameters:
*     a
*        Pointer to the integer index of the first position within
*        the arrays pointed to by Comp_Decra_Ptr1/2. (Ptr1 holds RA
*        values, Ptr2 holds Dec values).
*     b
*        Pointer to the index of the second position.

*  Returned Value:
*     An integer less than, equal to, or greater than  zero if  the
*     first  parameter  is considered to be respectively less than,
*     equal to, or greater than the second.

*/
   astDECLARE_GLOBALS
   astGET_GLOBALS(NULL);

/* Get the integer indices of the two points */
   int ia = *((int *) a);
   int ib = *((int *) b);

/* Get the RA and Dec of the two points */
   double ra1 = Comp_Decra_Ptr1[ ia ];
   double dec1 = Comp_Decra_Ptr2[ ia ];
   double ra2 = Comp_Decra_Ptr1[ ib ];
   double dec2 = Comp_Decra_Ptr2[ ib ];

/* Compare Dec, then RA. */
   if( dec1 == dec2 ) {
      if( ra1 == ra2 ) {
         return 0;
      } else if( ra1 < ra2 ) {
         return -1;
      } else {
         return 1;
      }
   } else if( dec1 < dec2 ) {
      return -1;
   } else {
      return 1;
   }
}

static int Comp_range( const void *a, const void *b ){
/*
*  Name:
*     Comp_range

*  Purpose:
*     A comparison function for use with the qsort function.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int Comp_range( const void *a, const void *b )

*  Class Membership:
*     Moc member function

*  Description:
*     This function compares two Moc ranges and returns a
*     value indicating which has the larger lower bound. A
*     range is a pair of consecutive int64_t values, in the
*     order (lower bound, upper bound).

*  Parameters:
*     a
*        Pointer to the first range.
*     b
*        Pointer to the second range.

*  Returned Value:
*     An integer less than, equal to, or greater than  zero if  the
*     first  parameter  is considered to be respectively less than,
*     equal to, or greater than the second.

*/
   if( *(int64_t *) a < *(int64_t *) b ) {
      return -1;
   } else if( *(int64_t *) a > *(int64_t *) b ) {
      return 1;
   } else {
      return 0;
   }
}

static int Comp_int64( const void *a, const void *b ){
/*
*  Name:
*     Comp_int64

*  Purpose:
*     A comparison function for use with the qsort function.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int Comp_int64( const void *a, const void *b )

*  Class Membership:
*     Moc member function

*  Description:
*     This function compares two signed 64 bit integers and returns a
*     value indicating which is larger.

*  Parameters:
*     a
*        Pointer to the first uint_64 value.
*     b
*        Pointer to the second uint_64 value.

*  Returned Value:
*     An integer less than, equal to, or greater than  zero if  the
*     first  parameter  is considered to be respectively less than,
*     equal to, or greater than the second.

*/
   if( *(int64_t *) a < *(int64_t *) b ) {
      return -1;
   } else if( *(int64_t *) a > *(int64_t *) b ) {
      return 1;
   } else {
      return 0;
   }
}

static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
*  Name:
*     Equal

*  Purpose:
*     Test if two Objects are equivalent.

*  Type:
*     Private function.

*  Synopsis:
*     #include "object.h"
*     int Equal( AstObject *this_object, AstObject *that_object, int *status )

*  Class Membership:
*     Moc member function (over-rides the astEqual protected
*     method inherited from the Region class).

*  Description:
*     This function returns a boolean result (0 or 1) to indicate whether
*     two Mocs are equivalent.

*  Parameters:
*     this
*        Pointer to the first Moc.
*     that
*        Pointer to the second Moc.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     One if the Mocs are equivalent, zero otherwise.

*  Notes:
*     - A value of zero will be returned if this function is invoked
*     with the global status set, or if it should fail for any reason.
*/

/* Local Variables: */
   AstMoc *that;
   AstMoc *this;
   int irange;
   int result;
   int64_t *p1;
   int64_t *p2;

/* Initialise. */
   result = 0;

/* Check the global error status. */
   if ( !astOK ) return result;

/* Invoke the Equal method inherited from the parent Region class. This checks
   that the Objects are both of the same class, and have the same Negated
   and Closed flags (amongst other things). */
   if( (*parent_equal)( this_object, that_object, status ) ) {

/* Obtain pointers to the two Moc structures. */
      this = (AstMoc *) this_object;
      that = (AstMoc *) that_object;

/* Test their MaxOrder values are equal. */
      if( astGetMaxOrder(this) == astGetMaxOrder(that) ) {

/* Test the ranges of cells included in both are equal. */
         if( this->nrange == that->nrange ) {
            result = 1;
            p1 = this->range;
            p2 = that->range;
            for( irange = 0; irange < this->nrange; irange++ ) {
               if( p1[ 0 ] != p2[ 0 ] ||
                   p1[ 1 ] != p2[ 1 ] ) {
                  result = 0;
                  break;
               }
               p1 += 2;
               p2 += 2;
            }
         }
      }
   }

/* If an error occurred, clear the result value. */
   if ( !astOK ) result = 0;

/* Return the result, */
   return result;
}

static AstFrame *FindSkyAxes( AstFrame *frame, const char *method,
                              int *status ){
/*
*  Name:
*     FindSkyAxes

*  Purpose:
*     Extract the sky axes from a supplied Frame.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     AstFrame *FindSkyAxes( AstFrame *frame, const char *method,
*                            int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function searches the supplied Frame for a SkyFrame. If found,
*     it creates a new Frame containing just the sky axes and returns a
*     pointer to the new Frame. If the supplied Frame is itself a SkyFrame,
*     then the returned pointer is just a clone of the supplied pointer.
*
*     The supplied Frame must be either a Region or a FrameSet. If it is
*     a Region, an error will be reported if the returned Frame is not
*     also a Region. If it is a FrameSet, the returned Frame will be a
*     FrameSet in which the current Frame is a SkyFrame, and the base Frame
*     holds the two corresponding axes from the supplied baseFrame. An
*     error will be reported if the sky axes do not correspond to a
*     distinct pair of axes in the supplied FrameSet.

*  Parameters:
*     frame
*        Pointer to the Frame - a Region with any number of axes, or a
*        FrameSet with a 2D base Frame (the current frame can have any
*        number of axes).
*     method
*        The name of the calling method.
*     status
*        Inherited status value.

*  Returned Value:
*     Pointer to the new Frame.

*/

/* Local Variables: */
   AstFrame *bfrm;
   AstFrame *check_frame;
   AstFrame *frm;
   AstFrame *result = NULL;
   AstMapping *map2;
   AstMapping *map;
   AstPermMap *pm;
   int *perm;
   int iax;
   int nax;
   int pax;
   int skyaxes[ 2 ];

/* Check the global error status. */
   if( !astOK ) return result;

/* Get the Frame to check for sky axes. */
   if( astIsARegion( frame ) ){
      check_frame = astRegFrame( frame );
   } else if( astIsAFrameSet( frame ) ){
      check_frame = astGetFrame( frame, AST__CURRENT );
   } else {
      check_frame = astClone( frame );
   }

/* Get the indices of the sky axes in the supplied Frame. */
   skyaxes[ 0 ] = -1;
   skyaxes[ 1 ] = -1;
   nax = astGetNaxes( check_frame );
   for( iax = 0; iax < nax; iax++ ) {
      astPrimaryFrame( check_frame, iax, &frm, &pax );
      if( astIsASkyFrame( frm ) ){
         if( pax == 0 ){
            skyaxes[ 0 ] = iax;
         } else {
            skyaxes[ 1 ] = iax;
         }
      }
      frm = astAnnul( frm );
   }
   check_frame = astAnnul( check_frame );

/* Report an error if a pair of skyaxes was not found. */
   if( skyaxes[ 0 ] == -1 || skyaxes[ 1 ] == -1 ) {
      if( astOK ) {
         astError( AST__NOSKY, "%s(Moc): The supplied %s does not contain "
                   "a pair of celestial coordinate axes.", status, method,
                   astGetClass(frame) );
      }

/* We proceed only if a SkyFrame was found. First deal with Regions. */
   } else if( astIsARegion( frame ) ){

/* The Region may also contain other axes in addition to the sky axes. So
   attempt to pick the sky axes from the Region. Report an error if this is
   not possible (as indicated by the returned object not being a Region). */
      result = astPickAxes( frame, 2, skyaxes, NULL );
      if( !astIsARegion( result ) && astOK ){
         astError( AST__BDSKY, "%s(Moc): The region of sky described by the "
                   "supplied %s cannot be determined.", status, method,
                   astGetClass(frame) );
      }

/* Now deal with FrameSets. */
   } else if( astIsAFrameSet( frame ) ){

/* The astMapSplit has no "invert" parameter and so can only pick inputs, not
   outputs. But we want to pick the sky axis outputs, so we need to invert
   the FrameSet before calling astMapSplit. */
      astInvert( frame );

/* Split the mapping in parallel to obtain a Mapping from the output sky
   axes to the corresponding two inputs. This may or may not be possible
   depending on the nature of the Mapping. */
      perm = astMapSplit( (AstMapping *) frame, 2, skyaxes, &map );

/* Re-invert the FrameSet to bring it back top its original state. */
      astInvert( frame );

/* Check if the above map split was successful. */
      if( map && astGetNout( map ) == 2 ) {

/* Create the returned FrameSet, and initialise it to hold a copy of the
   required two axes from the base Frame in the supplied FrameSet. */
         bfrm = astGetFrame( frame, AST__BASE );
         frm = astPickAxes( bfrm, 2, skyaxes, NULL );
         result = (AstFrame *) astFrameSet( frm, "", status );
         frm = astAnnul( frm );
         bfrm = astAnnul( bfrm );

/* Invert the Mapping returned by astMapSplit so that it goes from grid
   coords to sky coords. */
         astInvert( map );

/* Create PermMap that permutes the grid coords from the order used in
   the supplied FrameSet to the order expected for the inputs of "map".
   No need to do this if there is no actual change of axes. */
         if( perm[ 0 ] != 0 ) {
            pm = astPermMap( 2, perm, 2, perm, NULL, "", status );
            map2 = (AstMapping *) astCmpMap( pm, map, 1, "", status );
            pm = astAnnul( pm );
            astAnnul( map );
            map = map2;
         }
         perm = astFree( perm );

/* Get a Frame containing the sky axes picked from the supplied current
   Frame. The Frame will be a SkyFrame. Add it into the returned
   FrameSet, using the above Mapping to connect it to the base Frame. It
   becomes the new current Frame. */
         frm = astPickAxes( frame, 2, skyaxes, NULL );
         astAddFrame( result, AST__BASE, map, frm );
         frm = astAnnul( frm );
         map = astAnnul( map );

/* Report an error if the sky axes are not independent of the other axes. */
      } else if( astOK ) {
         astError( AST__BDSKY, "%s(Moc): The sky axes in the supplied WCS "
                   "FrameSet cannot be split from the other axes.", status,
                   method );
      }

/* Report an error for any other class of Frame. */
   } else if( astOK ) {
      astError( AST__BDCLS, "FindSkyAxes(Moc): A %s was supplied but a "
                "Region or FrameSet is required.", status,
                astGetClass( frame ) );
   }

/* Return the resulting Frame. */
   return result;
}

static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
*  Name:
*     GetAttrib

*  Purpose:
*     Get the value of a specified attribute for a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     const char *GetAttrib( AstObject *this, const char *attrib, int *status )

*  Class Membership:
*     Moc member function (over-rides the protected astGetAttrib
*     method inherited from the Region class).

*  Description:
*     This function returns a pointer to the value of a specified
*     attribute for a Moc, formatted as a character string.

*  Parameters:
*     this
*        Pointer to the Moc.
*     attrib
*        Pointer to a null-terminated string containing the name of
*        the attribute whose value is required. This name should be in
*        lower case, with all white space removed.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     - Pointer to a null-terminated string containing the attribute
*     value.

*  Notes:
*     - The returned string pointer may point at memory allocated
*     within the Moc, or at static memory. The contents of the
*     string may be over-written or the pointer may become invalid
*     following a further invocation of the same function or any
*     modification of the Moc. A copy of the string should
*     therefore be made if necessary.
*     - A NULL pointer will be returned if this function is invoked
*     with the global error status set, or if it should fail for any
*     reason.
*/

/* Local Variables: */
   AstMoc *this;                 /* Pointer to the Moc structure */
   astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
   const char *result;           /* Pointer value to return */
   double dval;                  /* Floating point attribute value */
   int ival;                     /* Integer attribute value */

/* Initialise. */
   result = NULL;

/* Check the global error status. */
   if ( !astOK ) return result;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(this_object);

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) this_object;

/* Compare "attrib" with each recognised attribute name in turn,
   obtaining the value of the required attribute. If necessary, write
   the value into "getattrib_buff" as a null-terminated string in an appropriate
   format.  Set "result" to point at the result string. */

/* MaxOrder. */
/* --------- */
   if ( !strcmp( attrib, "maxorder" ) ) {
      ival = astGetMaxOrder( this );
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%d", ival );
         result = getattrib_buff;
      }

/* MaxRes. */
/* ------- */
   } else if ( !strcmp( attrib, "maxres" ) ) {
      if( !astTestMaxOrder( this ) ) {
         dval = 0.0;
      } else {
         ival = astGetMaxOrder( this );
         dval = OrderToRes( ival );
      }
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
         result = getattrib_buff;
      }

/* MinOrder. */
/* --------- */
   } else if ( !strcmp( attrib, "minorder" ) ) {
      ival = astGetMinOrder( this );
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%d", ival );
         result = getattrib_buff;
      }

/* MinRes. */
/* ------- */
   } else if ( !strcmp( attrib, "minres" ) ) {
      ival = astGetMinOrder( this );
      dval = OrderToRes( ival );
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
         result = getattrib_buff;
      }

/* MocArea. */
/* -------- */
   } else if ( !strcmp( attrib, "mocarea" ) ) {
      dval = astGetMocArea( this );
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
         result = getattrib_buff;
      }

/* MocType. */
/* -------- */
   } else if ( !strcmp( attrib, "moctype" ) ) {
      ival = astGetMocType( this );
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%d", ival );
         result = getattrib_buff;
      }

/* MocLength. */
/* ---------- */
   } else if ( !strcmp( attrib, "moclength" ) ) {
      ival = astGetMocLength( this );
      if ( astOK ) {
         (void) sprintf( getattrib_buff, "%d", ival );
         result = getattrib_buff;
      }

/* If the attribute name was not recognised, pass it on to the parent
   method for further interpretation. */
   } else {
      result = (*parent_getattrib)( this_object, attrib, status );
   }

/* Return the result. */
   return result;

}

static AstMapping *GetCachedMapping( AstMoc *this, int order,
                                     const char *method, int *status ){
/*
*  Name:
*     GetCachedMapping

*  Purpose:
*     Get a Mapping from ICRS to HPX12 grid coords.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     AstMapping *GetCachedMapping( AstMoc *this, int order,
*                                   const char *method, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns a Mapping that goes from ICRS to grid
*     coordinates within an all-sky HPX12 projection.

*  Parameters:
*     this
*        The Moc.
*     order
*        The required HEALPix order.
*     method
*        A string holding the name of the calling methid. Used in error
*        messages.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     The Mapping. This is a direct copy of the cached pointer, so it
*     should not be annulled by the calling function when no longer needed.

*/

/* Local Variables: */
   AstFrameSet *fs;

/* Check the global error status. */
   if ( !astOK ) return NULL;

/* If the cache does not contain a Mapping for the requested order,
   create one now. */
   if( !this->cached_maps[ order ] ) {

/* Get a FrameSet describing a map of the whole sky in ICRS using an
   HPX12 projection, with the maximum MOC order. The current frame
   represents ICRS and the base Frame represents HPX12 grid coordinates.
   Each pixel in the grid corresponds to a HEALPix cell on the sky. */
      fs = GetHPX12FrameSet( this, order, status );

/* Get the Mapping and store the cache. */
      this->cached_maps[ order ] = astGetMapping( fs, AST__CURRENT,
                                                  AST__BASE );
/* Free resources */
      fs = astAnnul( fs );
   }

/* Return the cached Mapping. */
   return this->cached_maps[ order ];
}

static void GetCell( AstMoc *this, int icell, int *order, int64_t *npix,
                     int *status ) {
/*
*++
*  Name:
c     astGetCell
f     AST_GETCELL

*  Purpose:
*     Identify the next cell in a normalised Moc.

*  Type:
*     Public virtual function.

*  Synopsis:
c     #include "moc.h"
c     void astGetCell( AstMoc *this, int icell, int *order, int64_t *npix )
f     CALL AST_GETCELL( THIS, ICELL, ORDER, NPIX, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function returns the order and "npix" value for the cell at a
*     specified index in the normalised Moc. See the MOC recommendation
*     for more information about "npix" values and MOC normalisation.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     icell
f     ICELL = INTEGER (Given)
*        The index of the cell for which information is required. The
*        first cell has index
c        zero.
f        one.
*        An error will be reported if the supplied value is greater than
c        or equal to
*        the value of the MocLength attribute.
c     order
f     ORDER = INTEGER (Returned)
*        Returned holding the HEALPix order of the cell at the requested
*        index.
c     npix
f     NPIX = INTEGER*8 (Returned)
*        Returned holding the "npix" value of the cell at the requested
*        index.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*--
*/

/* Local Variables: */
   int moclen;

/* Check the global error status. */
   if ( !astOK ) return;

/* Ensure we have the normalised form available in the Moc structure. */
   GetNorm( this, "astGetCell", status );

/* Validate. */
   moclen = astGetMocLength( this );
   if( icell < 0 ) {
      astError( AST__INVAR, "astGetCell(%s): Invalid value (%d "
                "zero-based) supplied for parameter 'icell' - "
                "must be greater than or equal to zero.", status,
                astGetClass( this ), icell );

   } else if( icell >= moclen ) {
      astError( AST__INVAR, "astGetCell(%s): Invalid value (%d "
                "zero-based) supplied for parameter 'icell' - "
                "must be less than %d.", status, astGetClass( this ),
                 icell, moclen );

/* Decode the nuniq value to get the order and npix, using a fast log2
   function. */
   } else {
      if( this->inorm ) {
         *order = log2_32( (this->inorm)[ icell ] / 4 ) / 2;
         *npix = (this->inorm)[ icell ] - ( 1 << (2 + 2*(*order)) );
      } else {
         *order = log2_64( (this->knorm)[ icell ] / 4 ) / 2;
         *npix = (this->knorm)[ icell ] - ( 1L << (2 + 2*(*order)) );
      }
   }
}

static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) {
/*
*  Name:
*     GetDefUnc

*  Purpose:
*     Obtain a pointer to the default uncertainty Region for a mOC.

*  Type:
*     Private function.

*  Synopsis:
*     #include "region.h"
*     AstRegion *GetDefUnc( AstRegion *this, int *status )

*  Class Membership:
*     Moc member function (over-rides the astGetDefUnc protected
*     method inherited from the Region class).

*  Description:
*     This function returns a pointer to a Region which represents the
*     default uncertainty associated with a position on the boundary of the
*     given Moc. The returned Region refers to the base Frame within the
*     FrameSet encapsulated by the Moc (always ICRS).

*  Parameters:
*     this
*        Pointer to the Moc.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     A pointer to the Region. This should be annulled (using astAnnul)
*     when no longer needed.

*  Notes:
*     - A NULL pointer will be returned if this function is invoked
*     with the global error status set, or if it should fail for any
*     reason.
*/

/* Local Variables: */
   AstFrame *bfrm;
   AstMoc *this;
   AstRegion *result;
   double centre[ 2 ];
   double res;

/* Initialise */
   result = NULL;

/* Check the global error status. */
   if ( !astOK ) return result;

/* Get a pointer to the Moc structure. */
   this = (AstMoc *) this_region;

/* If the default uncertainty Region is not available within the Moc
   structure, create it now. */
   if( !this->unc ) {

/* Get the resolution in rads. */
      res = AST__DD2R*OrderToRes( astGetMaxOrder( this ) )/3600.0;

/* Create a Circle centred at the origin of ICRS with a radius equal to
   the resolution. The base Frame of the FrameSet encapsulated by the
   parent Region structure will always be ICRS. */
      bfrm = astGetFrame( this_region->frameset, AST__BASE );
      centre[ 0 ] = 0.0;
      centre[ 1 ] = 0.0;
      this->unc = (AstRegion *) astCircle( bfrm, 1, centre, &res, NULL,
                                           " ", status );
      bfrm = astAnnul( bfrm );
   }

/* Return a pointer to the cached unertainty Region. */
   result = astClone( this->unc );

/* Return NULL if an error occurred. */
   if( !astOK ) result = astAnnul( result );

/* Return the required pointer. */
   return result;
}

static AstFrameSet *GetHPX12FrameSet( AstMoc *this, int order, int *status ){
/*
*  Name:
*     GetHPX12FrameSet

*  Purpose:
*     Get a WCS FrameSet for an all-sky HPX12 projection.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     AstFrameSet *GetHPX12FrameSet( AstMoc *this, int order, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns a FrameSet containing two Frames. The base
*     Frame represents grid coordinates in an all-sky HPX12 projection.
*     The current Frame represents ICRS.

*  Parameters:
*     this
*        The Moc.
*     order
*        The HEALPix order.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     The WCS FrameSet.

*/

/* Local Variables: */
   AstCmpMap *map1;
   AstCmpMap *map2;
   AstCmpMap *map3;
   AstFrame *gf;
   AstFrameSet *result;
   AstMatrixMap *mm;
   AstShiftMap *sm1;
   AstShiftMap *sm2;
   AstSkyFrame *sf;
   AstWcsMap *wm;
   double m;
   double matrix[ 4 ];
   double shift[ 2 ];
   int nppf;

/* Check the global error status. */
   if ( !astOK ) return NULL;

/* Get the number of pixels along one edge of a facet. */
   nppf = ( 1 << order );

/* A ShiftMap that puts the origin at the centre of the array. The array
   of values has dimensions (5*nppf,5*nppf). */
   shift[ 0 ] = -0.5*( 1.0 + 5*nppf );
   shift[ 1 ] = shift[ 0 ];
   sm1 = astShiftMap( 2, shift, " ", status );

/* A MatrixMap that converts grid coordinates into radians and rotates by
   45 degrees. */
   m = AST__DPI/( 4.0*nppf );
   matrix[ 0 ] = m;
   matrix[ 1 ] = m;
   matrix[ 2 ] = -m;
   matrix[ 3 ] = m;
   mm = astMatrixMap( 2, 2, 0, matrix, " ", status );

/* The HPX projection, with "LONPOLE" set to 180 degrees. This needs to
   be inverted so it goes from cartesian to spherical coords. Set the
   protected LonCheck attribute so that the projection is not clipped for
   longitude values outside the primary range. */
   wm = astWcsMap( 2, AST__HPX, 1, 2, "PV1_3=180", status );
   astSetLonCheck( wm, 0 );
   astInvert( wm );

/* A shiftmap to put the origin of native spherical coords at 12 hours. */
   shift[ 0 ] = AST__DPI;
   shift[ 1 ] = 0.0;
   sm2 = astShiftMap( 2, shift, " ", status );

/* Join them all together in series to get the mapping form grid coords
   to ICRS (lon,lat). */
   map1 = astCmpMap( sm1, mm, 1, " ", status );
   sm1 = astAnnul( sm1 );
   mm = astAnnul( mm );

   map2 = astCmpMap( map1, wm, 1, " ", status );
   map1 = astAnnul( map1 );
   wm = astAnnul( wm );

   map3 = astCmpMap( map2, sm2, 1, " ", status );
   map2 = astAnnul( map2 );
   sm2 = astAnnul( sm2 );

/* Create a basic 2D Frame to represent grid coords. */
   gf = astFrame( 2, "Domain=GRID", status );

/* Create a SkyFrame representing ICRS. */
   sf = astSkyFrame( "System=ICRS", status );

/* Create the required FrameSet, initially containing just the grid frame. */
   result = astFrameSet( gf, " ", status );
   gf = astAnnul( gf );

/* Add in the ICRS Frame. */
   astAddFrame( result, AST__BASE, map3, sf );
   map3 = astAnnul( map3 );
   sf = astAnnul( sf );

/* Return the FrameSet. */
   return result;
}

static void GetMocData( AstMoc *this, size_t mxsize, void *data, int *status ) {
/*
*++
*  Name:
c     astGetMocData
f     AST_GETMOCDATA

*  Purpose:
*     Get the FITS binary table data describing a Moc

*  Type:
*     Public function.

*  Synopsis:
c     #include "moc.h"
c     void astGetMocData( AstMoc *this, size_t mxsize, void *data )
f     CALL AST_GETMOCDATA( THIS, MXSIZE, DATA, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function retrieves the data values that form the FITS binary
*     table representation of the MOC and stores them in a supplied array.
*     Such a table contains a single scalar-valued column in which each
*     row holds a signed integer identifier for a single HEALPix cell,
*     following the scheme described in the MOC recommendation. Depending
*     on the order of the Moc, these integers may be 4 bytes or 8 bytes.
*
*     The number of rows in the table and the required integer data type
*     are available through the MocType and MocLength attributes of the
*     Moc class.
*
*     The FITS headers to store in the FITS binary table can be obtained
*     using function
c     astGetMocHeader.
f     AST_GETMOCHEADER.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     mxsize
f     MXSIZE = INTEGER (Given)
*        The length of the supplied array in bytes. An error will be reported
*        if this value is smaller than the number required to describe the
*        Moc (the product of the MocType and MocLength attributes).
c     data
f     DATA( * ) = BYTE (Returned)
c        Pointer to the
f        The
*        area of memory in which to return the signed integer cell
*        identifiers. This area is assumed to contain at least
c        "mxsize" bytes.
f        MXSIZE bytes.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*--
*/

/* Local Variables: */
   size_t nbyte;
   int moclen;
   int type;

/* Check the global error status. */
   if ( !astOK ) return;

/* Ensure we have the normalised form available in the Moc structure. */
   GetNorm( this, "astGetMocData", status );

/* Validate. */
   type = astGetMocType( this );
   moclen = astGetMocLength( this );
   nbyte = type*moclen;
   if( mxsize < nbyte && astOK ) {
      astError( AST__BADSIZ, "astGetMocData(%s): The supplied array "
                "has %zu bytes but %zu are required.", status,
                astGetClass(this), mxsize, nbyte );

/* Copy the data into the supplied array. */
   } else {
      if( this->inorm ) {
         memcpy( data, this->inorm, nbyte );
      } else {
         memcpy( data, this->knorm, nbyte );
      }
   }
}

static AstFitsChan *GetMocHeader( AstMoc *this, int *status ){
/*
*++
*  Name:
c     astGetMocHeader
f     AST_GETMOCHEADER

*  Purpose:
*     Get the FITS binary table headers describing a Moc

*  Type:
*     Public virtual function.

*  Synopsis:
c     #include "moc.h"
c     AstFitsCHan *astGetMocHeader( AstMoc *this )
f     RESULT = AST_GETMOCHEADER( THIS, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function returns a FitsChan holding the headers that should be
*     stored in a FITS binary table extension describing the supplied Moc.
*     The data values for the extension can be obtained using method
c     astGetMocData.
f     AST_GETMOCDATA.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*--
*/

/* Local Variables: */
   AstFitsChan *result;
   int maxorder;
   int type;
   int moclen;

/* Initialise. */
   result = NULL;

/* Check the global error status. */
   if ( !astOK ) return result;

/* Create an empty FitsChan. */
   result = astFitsChan( NULL, NULL, "", status );

/* Get the Moc data type, length and highest order. */
   type = astGetMocType( this );
   moclen = astGetMocLength( this );
   maxorder = astGetMaxOrder( this );

/* Store the required values in it. */
   astSetFitsS( result, "XTENSION", "BINTABLE", "HEALPix Multi Order"
                " Coverage map", 1 );
   astSetFitsI( result, "BITPIX", 8, NULL, 1 );
   astSetFitsI( result, "NAXIS", 2, NULL, 1 );
   astSetFitsI( result, "NAXIS1", type, "Bytes per row", 1 );
   astSetFitsI( result, "NAXIS2", moclen, "No. of rows", 1 );
   astSetFitsI( result, "PCOUNT", 0, NULL, 1 );
   astSetFitsI( result, "GCOUNT", 1, NULL, 1 );
   astSetFitsI( result, "TFIELDS", 1, NULL, 1 );
   astSetFitsS( result, "TFORM1", (type==4)?"1J":"1K", NULL, 1 );
   astSetFitsS( result, "TTYPE1", "UNIQ", "HEALPix UNIQ pixel number", 1 );
   astSetFitsS( result, "PIXTYPE", "HEALPIX", "HEALPix magic code", 1 );
   astSetFitsS( result, "ORDERING", "NUNIQ", "NUNIQ coding method", 1 );
   astSetFitsS( result, "COORDSYS", "C", "ICRS coordinates", 1 );
   astSetFitsI( result, "MOCORDER", maxorder, "MOC resolution (best order)", 1 );

/* If an error occurred, annul the returned FitsChan. */
   if ( !astOK ) result = astAnnul( result );

/* Return the result. */
   return result;
}

static double GetMocArea( AstMoc *this, int *status ){
/*
*  Name:
*     GetMocArea

*  Purpose:
*     Get the value of the MocArea attribute.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     double GetMocArea( AstMoc *this, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the value of the MocArea attribute.

*  Parameters:
*     this
*        Pointer to the Moc.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     The MocArea value.

*/

/* Local Variables; */
   double res;
   int irange;
   int64_t *pr;
   int64_t count;

/* Check the global error status. */
   if ( !astOK ) return 0.0;

/* If the area of the moc has not yet been calculated, calculate it now. */
   if( this->mocarea == AST__BAD ) {

/* All cells in a MOC have equal area, so first Count the number of cells
   in the Moc. These are all at HEALPix order given by the MaxOrder
   attribute. The "this->range" array contains the lower and upper bounds
   of a set of contiguous ranges in nested index at order MaxOrder. */
      count = 0;
      pr = this->range;
      for( irange = 0; irange < this->nrange; irange++, pr += 2 ) { \

/* Increment count by the length of the current range of nested index. */
         count += ( pr[ 1 ] - pr[ 0 ] + 1 );
      }

/* Get the mean resolution of the MOC, in arc-mins. This is the square root
   of the cell area. */
      res = OrderToRes( astGetMaxOrder( this ) )/60.0;

/* Get the total area. */
      this->mocarea = res*res*count;
   }

/* Return the MocArea value. */
   return this->mocarea;
}

static int GetMocLength( AstMoc *this, int *status ){
/*
*  Name:
*     GetMocLength

*  Purpose:
*     Get the value of the MocLength attribute.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int GetMocLength( AstMoc *this, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the value of the MocLength attribute.

*  Parameters:
*     this
*        Pointer to the Moc.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     The Moclength value.

*/


/* Check the global error status. */
   if ( !astOK ) return 0;

/* Ensure the the normalised form of the MOC is available. */
   GetNorm( this, "astGetMocLength", status );

/* Return the MocLength value. */
   return this->moclength;
}

static void GetMocString( AstMoc *this, int json, size_t mxsize, char *string,
                          size_t *size, int *status ){
/*
*++
*  Name:
c     astGetMocString
f     AST_GETMOCSTRING

*  Purpose:
*     Get the JSON or string-encoded representation of a Moc

*  Type:
*     Public function.

*  Synopsis:
c     #include "moc.h"
c     void astGetMocString( AstMoc *this, int json, size_t mxsize,
c                           char *string, size_t *size, int *status )
f     CALL AST_GETMOCSTRING( THIS, JSON, MXSIZE, STRING, SIZE, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function stores the JSON or string-encoded representation of
*     the supplied Moc in the supplied string buffer.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc.
c     json
f     JSON = LOGICAL (Given)
c        If non-zero,
f        If .TRUE.,
*        the Moc is encoded using JSON serialisation. Otherwise it is
*        encoded using string-serialisation.
c     mxsize
f     MXSIZE = INTEGER (Given)
*        The length of the supplied string buffer in bytes. An error will
*        be reported if this value is smaller than the number required to
*        describe the Moc. However, if zero is supplied, the buffer will
*        be ignored - no string will be returned but the required size of
*        the buffer will still be returned in
c        'size'.
f        SIZE.
c     string
f     STRING( * ) = BYTE (Returned)
c        Pointer to the
f        The
*        area of memory in which to return the JSON or string-encoded
*        representation of the Moc. This area is assumed to contain at least
c        'mxsize' bytes. Only used if 'mxsize' is greater than zero.
f        MXSIZE bytes. Only used if MXSIZE is greater than zero.
c        Note, the string is not null-terminated.
c     size
f     SIZE = INTEGER*8 (Returned)
*        Returned holding the number of bytes needed to store the complete
*        JSON or string-encoded representation of the Moc.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*--
*/

/* Local Variables: */
   SinkData data2;
   void *data;
   void (*sink)( void *, size_t, const char *, int * );
   size_t buflen;

/* Initialise */
   *size = 0;

/* Check the global error status. */
   if ( !astOK ) return;

/* If a buffer size of zero has been supplied, use a sink function that
   just accumulates the length of the strings. */
   if( mxsize == 0 ) {
      data = size;
      sink = Sink1;

/* Otherwise use a sink function that appends the strings to the supplied
   buffer. */
   } else {
      data2.string = string;
      data2.mxsize = mxsize;
      data = &data2;
      sink = Sink2;
   }

/* Do the work. The sink function is called to write out the buffer each
   time the buffer is filled. */
   buflen = ( mxsize > 0 ) ? mxsize : 80;
   astGetMocText( this, json, buflen, sink, data, "astGetMocString" );

/* Return the used size, if it is not already there. */
   if( mxsize > 0 ) *size = mxsize - data2.mxsize;
}

void astGetMocText_( AstMoc *this, int json, size_t buflen,
                     void (*sink)( void *, size_t, const char *, int * ),
                     void *data, const char *method, int *status ){
/*
*+
*  Name:
*     astGetMocText

*  Purpose:
*     Gets a JSON or string-encoded representation of the supplied Moc.

*  Type:
*     Protected function.

*  Synopsis:
*     #include "moc.h"
*     void astGetMocText( AstMoc *this, int json, size_t buflen,
*                         void (*sink)( void *, size_t, const char *, int * ),
*                         void *data, const char *method, int *status )

*  Class Membership:
*     Moc method.

*  Description:
*     This function converts the supplied Moc to a text string using the
*     JSON or string serialisation described in the MOC recommendation.

*  Parameters:
*     this
*        Pointer to the Moc.
*     json
*        If non-zero, the Moc is encoded using JSON serialisation. Otherwise
*        it is encoded using string-serialisation.
*     buflen
*        The maximum number of characters sent to the sink function in each
*        call.
*     sink
*        A function to call to write out each section of the MOC's JSON or
*        string representation. It should have the following synopsis:
*
*        void sink( void *data, size_t bufsize, const char *buf, int *status )
*
*        The sink function should write out the first "bufsize" bytes
*        from "buf" to the appropriate external destination. The "data"
*        pointer can be used for any purpose. The text supplied to the
*        sink function in the buffer will not be null terminated. A NULL
*        pointer may be supplied, in which case the text will be written
*        out to standard output.
*     data
*        A pointer to an arbitrary structure to be passed to the sink
*        function. Can be NULL.
*     method
*        Method name to include in error messages.
*-
*/

/* Macro to append a token to the buffer, flushing the buffer using the
   sink function if the buffer fills up. */
#define TOKEN_WRITE \
   ptok = token; \
   while( nc > nleft ) { \
      if( nleft ) memcpy( pwrite, ptok, nleft ); \
      if( sink ) { \
         (*sink)( data, buflen, buf, status ); \
      } else { \
         printf( "%.*s", (int) buflen, buf ); \
      } \
      ptok += nleft; \
      nc -= nleft; \
      nleft = buflen; \
      pwrite = buf; \
   } \
   if( nc > 0 ) { \
      memcpy( pwrite, ptok, nc ); \
      pwrite += nc; \
      nleft -= nc; \
   }

/* Local Variables: */
   char *buf;
   char *ptok;
   char *pwrite;
   char token[ 30 ];
   int first;
   int icell;
   int maxorder;
   int moclen;
   int neworder;
   int order;
   int64_t npix;
   int64_t npix_start;
   int64_t npix_prev;
   size_t nc;
   size_t nleft;

/* Check the global error status. */
   if ( !astOK ) return;

/* Ensure we have the normalised form available in the Moc structure. */
   GetNorm( this, method, status );

/* Allocate a suitable buffer */
   buf = astMalloc( buflen );

/* Initialise a pointer into the buffer at which to store the next
   character, and the number of elements currently left in the buffer. */
   pwrite = buf;
   nleft = buflen;

/* Loop over all cells in the Moc. */
   first = 1;
   order = -1;
   npix_start = 0;
   npix_prev = 0;
   moclen = astGetMocLength( this );
   for( icell = 0; icell < moclen; icell++ ){

/* Decode the nuniq value to get the order and npix, using a fast log2
   function. */
      if( this->inorm ) {
         neworder = log2_32( (this->inorm)[ icell ] / 4 ) / 2;
         npix = (this->inorm)[ icell ] - ( 1 << (2 + 2*(neworder)) );
      } else {
         neworder = log2_64( (this->knorm)[ icell ] / 4 ) / 2;
         npix = (this->knorm)[ icell ] - ( 1L << (2 + 2*(neworder)) );
      }

/* First do JSON serialisatioon... */
      if( json ) {

/* If the order has changed, complete any previous pixlist, and start a
   new one. */
         if( neworder != order){
            order = neworder;
            nc = sprintf( token, first?"{\"%d\":[%zu":"],\"%d\":[%zu",
                          order, npix );
            first = 0;
            TOKEN_WRITE;

/* If the order has not changed, just append the new npix value to the
   existing pixlist. */
         } else {
            nc = sprintf( token, ",%zu", npix );
            TOKEN_WRITE;
         }

/* Now do string serialisation... */
      } else {

/* If the order has changed, finish any previous pixlist, record the new
   order then append " <order>/<npix>" to the sink. */
         if( neworder != order){
            if( npix_start < npix_prev ) {
               nc = sprintf( token, "-%zu", npix_prev );
               TOKEN_WRITE;
            }
            order = neworder;
            npix_start = npix;
            nc = sprintf( token, first?"%d/%zu":" %d/%zu", order, npix );
            first = 0;
            TOKEN_WRITE;

/* If we have reached the first non-contiguous npix value, write out the
   end of the previous range. */
         } else if( npix > npix_prev + 1 ) {
            if( npix_start < npix_prev ) {
               nc = sprintf( token, "-%zu", npix_prev );
               TOKEN_WRITE;
            }

/* Then write the (potential) start of the new range. */
            nc = sprintf( token, ",%zu", npix );
            TOKEN_WRITE;
            npix_start = npix;
         }
         npix_prev = npix;
      }
   }

/* Terminate the last pixlist. */
   if( json ) {
      nc = sprintf( token, "]" );
      TOKEN_WRITE;
   } else if( npix_start < npix ) {
      nc = sprintf( token, "-%zu", npix );
      TOKEN_WRITE;
   }

/* If the Moc's maximum order has not yet been reached, append it
   explicity. */
   maxorder = astGetMaxOrder( this );
   if( order < maxorder ) {
      if( json ) {
         nc = sprintf( token, ",\"%d\":[]", maxorder );
      } else {
         nc = sprintf( token, " %d/", maxorder );
      }
      TOKEN_WRITE;
   }

/* Terminate the complete JSON string. */
   if( json ) {
      nc = sprintf( token, "}" );
      TOKEN_WRITE;
   }

/* Flush anything remaining in the buffer. */
   if( pwrite > buf ) {
      if( sink ) {
         (*sink)( data, pwrite - buf, buf, status );
      } else {
         printf( "%.*s",  (int)( pwrite - buf ), buf );
      }
   }

/* Free the buffer. */
   buf = astFree( buf );
}

#undef TOKEN_WRITE

static int GetMocType( AstMoc *this, int *status ){
/*
*  Name:
*     GetMocType

*  Purpose:
*     Get the value of the MocType attribute.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int GetMocType( AstMoc *this, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the value of the MocType attribute.

*  Parameters:
*     this
*        Pointer to the Moc.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     The MocType value - 4 or 8.

*/


/* Check the global error status. */
   if ( !astOK ) return 4;

/* Ensure the the normalised form of the MOC is available. */
   GetNorm( this, "astGetMocType", status );

/* Return 4 or 8, depending on whether "iorm" or "knorm" is used to store
   the normalised data within the moc structure. */
   return ( this->inorm == NULL ) ? 8 : 4;
}


static void GetNorm( AstMoc *this, const char *method, int *status ){
/*
*  Name:
*     GetNorm

*  Purpose:
*     Get the normalised form of a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void GetNorm( AstMoc *this, const char *method, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function ensures that a normalised form of the Moc is
*     available within the Moc structure. It uses the "unmap"
*     algorithm described in the MOC recommendation.

*  Parameters:
*     this
*        Pointer to the Moc.
*     method
*        The name of the calling method, for error messages.
*     status
*        Pointer to the inherited status variable.

*/

/* Local Variables: */
   int maxorder;
   int order;
   int irange;
   int moclen;
   size_t nnew;
   size_t nrange;
   size_t shift;
   int64_t *newranges;
   int64_t *pr;
   int64_t *ranges;
   int64_t m1;
   int64_t m2;
   int64_t max;
   int64_t min;
   int64_t npix;
   int64_t offset;

/* Check the global error status. */
   if ( !astOK ) return;

/* Do nothing if the normalised form is already available. */
   if( !this->inorm && !this->knorm ) {
      nrange = this->nrange;
      ranges = astStore( NULL, this->range, 2*nrange*sizeof(*ranges));
      moclen = 0;
      maxorder = astGetMaxOrder( this );
      if( astOK ) {

/* Define a macro to do the work for a generic data type. */
#define UNMAP \
         norm = NULL; \
         for( order = 0; order <= maxorder && nrange > 0; order++ ) { \
            nuniq_offset = ( 1L << 2*( order + 1 ) ); \
\
            shift = 2*( maxorder - order ); \
            offset = ( 1L << shift ) - 1; \
\
            newranges = NULL; \
            nnew = 0; \
            pr = ranges; \
            for( irange = 0; irange < nrange; irange++ ) { \
               min = *(pr++); \
               max = *(pr++); \
               m1 = ( ( min + offset ) >> shift ); \
               m2 = ( ( max + 1 ) >> shift ); \
               if( m2 > m1 ) { \
                  if( INT_MAX - moclen < m2 - m1 + 2 ) { \
                     astError( AST__BGMOC, "%s(%s): The normalised MOC " \
                               "has too many elements.", status, method, \
                               astGetClass( this ) ); \
                     break; \
                  } else { \
                     for( npix = m1; npix < m2; npix++ ) { \
                        norm = astGrow( norm, moclen+1, sizeof(*norm) ); \
                        if( astOK ) { \
                           norm[ moclen++ ] =  nuniq_offset + npix; \
                       } \
                     } \
                  } \
                  m1 = ( m1 << shift ); \
                  m2 = ( m2 << shift ) - 1; \
                  if( m1 <= min ) { \
                     if( m2 >= max ) { \
                        /* Nothing to do if whole range has been removed */ \
                     } else { \
                        newranges = astGrow( newranges, nnew+1, 2*sizeof(*newranges) ); \
                        if( astOK ) { \
                           newranges[ 2*nnew ] = m2 + 1; \
                           newranges[ 2*nnew + 1 ] = max; \
                           nnew++; \
                        } \
                     } \
                  } else { \
                     if( m2 >= max ) { \
                        newranges = astGrow( newranges, nnew+1, 2*sizeof(*newranges) ); \
                        if( astOK ) { \
                           newranges[ 2*nnew ] = min; \
                           newranges[ 2*nnew + 1 ] = m1 - 1; \
                           nnew++; \
                        } \
                     } else { \
                        newranges = astGrow( newranges, nnew+2, 2*sizeof(*newranges) ); \
                        if( astOK ) { \
                           newranges[ 2*nnew ] = min; \
                           newranges[ 2*nnew + 1 ] = m1 - 1; \
                           newranges[ 2*nnew + 2 ] = m2 + 1; \
                           newranges[ 2*nnew + 3 ] = max; \
                           nnew += 2; \
                        } \
                     } \
                  } \
               } else { \
                  newranges = astGrow( newranges, nnew+1, 2*sizeof(*newranges) ); \
                  if( astOK ) { \
                     newranges[ 2*nnew ] = min; \
                     newranges[ 2*nnew + 1 ] = max; \
                     nnew++; \
                  } \
               } \
            } \
\
            ranges = astFree( ranges ); \
            ranges = newranges; \
            nrange = nnew; \
         } \
\
         ranges = astFree( ranges ); \
         this->moclength = moclen;

/* Use the above macro to do the work for 4 byte identifiers... */
         if( maxorder < 14 ) {
            int *norm, nuniq_offset;
            UNMAP
            this->inorm = norm;
            this->knorm = astFree( this->knorm );

/* Use the above macro to do the work for 8 byte identifiers... */
         } else {
            int64_t *norm, nuniq_offset;
            UNMAP
            this->knorm = norm;
            this->inorm = astFree( this->inorm );
         }
      }
   }

#undef UNMAP

}

static double GetPixelArea( AstFrameSet *wcs, const int *dims, int *status ){
/*
*  Name:
*     GetPixelArea

*  Purpose:
*     Get the area of a pixel in the centre of a 2D array.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     double GetPixelArea( AstFrameSet *wcs, const int *dims, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the area of a pixel at the centre of the
*     supplied 2-dimensional array, in square arc-seconds.

*  Parameters:
*     wcs
*        Pointer to the WCS FrameSet describing the 2D array. The base
*        Frame is assumed to describe grid coordinates. The current Frame
*        should be a SkyFrame.
*     dims
*        Pointer to an array holding the dimensions of the array, in
*        pixels.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     The pixel area, in square arc-seconds.

*/

/* Local Variables: */
   double a[ 3 ];
   double b[ 3 ];
   double d1;
   double d2;
   double p1[ 2];
   double p2[ 2];
   double result;
   double x[ 3 ];
   double y[ 3 ];

/* Initialise. */
   result = AST__BAD;

/* Check the global error status. */
   if ( !astOK ) return result;

/* Transform the central grid coords, plus the grid coords at positions
   offset one pixel along each grid axis, into sky coords. */
   x[ 0 ] = 0.5*( dims[ 0 ] + 1.0 );
   x[ 1 ] = x[ 0 ] + 1.0;
   x[ 2 ] = x[ 0 ];
   y[ 0 ] = 0.5*( dims[ 1 ] + 1.0 );
   y[ 1 ] = y[ 0 ];
   y[ 2 ] = y[ 0 ] + 1.0;
   astTran2( wcs, 3, x, y, 1, a, b );

/* Find the arc-distances, in radians, from the centre to the other two
   positions. */
   p1[ 0 ] = a[ 0 ];
   p1[ 1 ] = b[ 0 ];
   p2[ 0 ] = a[ 1 ];
   p2[ 1 ] = b[ 1 ];
   d1 = astDistance( wcs, p1, p2 );
   p2[ 0 ] = a[ 2 ];
   p2[ 1 ] = b[ 2 ];
   d2 = astDistance( wcs, p1, p2 );

/* Calculate the area, converting to square arc-seconds. */
   if( d1 != AST__BAD && d2 != AST__BAD ) {
      d1 *= AST__DR2D*3600.0;
      d2 *= AST__DR2D*3600.0;
      result = d1*d2;
   }

/* Return the pixel size. */
   return result;
}

/*
*  Name:
*     GetSelectionBounds

*  Purpose:
*     Get the grid bounds of a box containing all selected pixels.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int GetSelectionBounds<Oper><X>( <Xtype> value, const <Xtype> array[],
*                                      const int dims[ 2 ], int *lx, int *hx,
*                                      int *ly, int *hy, int *status );

*  Class Membership:
*     Moc member function

*  Description:
*     The bounds of a box in grid coordinates that contain all selected
*     pixels is returned.

*  Parameters:
*     value
*        The data value defining selected pixels.
*     array
*        The data array.
*     dims
*        The number of pixels along each pixel axis.
*     lx
*        Returned holding the lowest index on the first grid axis
*        of the selected pixels.
*     hx
*        Returned holding the highest index on the first grid axis
*        of the selected pixels.
*     ly
*        Returned holding the lowest index on the second grid axis
*        of the selected pixels.
*     hy
*        Returned holding the highest index on the second grid axis
*        of the selected pixels.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     A flag indicating if one or more selected pixels were found. Zero
*     is returned if no selected pixels were found.

*  Notes:
*     - <Oper> must be one of LT, LE, EQ, NE, GE, GT.

*/

/* Define a macro to implement the function for a specific data
   type and operation. */
#define MAKE_GETSELECTIONBOUNDS(X,Xtype,Oper,OperI) \
static int GetSelectionBounds##Oper##X( Xtype value, const Xtype array[], \
                                        const int dims[ 2 ], int *lx, int *hx, \
                                        int *ly, int *hy, int *status ){ \
\
/* Local Variables: */ \
   const Xtype *pv;  /* Pointer to next data value to test */ \
   int ix;           /* Pixel column */ \
   int iy;           /* Pixel row */ \
\
/* Initialise the bounding box. */ \
   *lx = INT_MAX; \
   *hx = -INT_MAX; \
   *ly = INT_MAX; \
   *hy = -INT_MAX; \
\
/* Check the global error status. */ \
   if ( !astOK ) return 0; \
\
/* Loop round all pixels in the array. */ \
   pv = array;  \
   for( iy = 1; iy <= dims[ 1 ]; iy++ ) { \
      for( ix = 1; ix <= dims[ 0 ]; ix++,pv++ ) { \
\
/* Test the pixel value, extending the bounding box if the test is \
   passed. This relies on the compiler optimisation to remove the  \
   "if" statements for all but the operation appropriate to the  \
   function being defined. */  \
         if( OperI == AST__LT ) {  \
            if( *pv < value ) EXTEND_BOX; \
         } else if( OperI == AST__LE ) { \
            if( *pv <= value ) EXTEND_BOX; \
         } else if( OperI == AST__EQ ) { \
            if( *pv == value ) EXTEND_BOX; \
         } else if( OperI == AST__NE ) { \
            if( *pv != value ) EXTEND_BOX; \
         } else if( OperI == AST__GE ) { \
            if( *pv >= value ) EXTEND_BOX; \
         } else  if( OperI == AST__GT ) { \
            if( *pv > value ) EXTEND_BOX; \
         }  \
      } \
   } \
\
/* Return the appropriate flag value. */ \
   return ( *lx <= *hx ) && ( *ly <= *hy ); \
}




/* Define a macro to extend the bound box. */
#define EXTEND_BOX { \
   if( ix < *lx ) *lx = ix; \
   if( ix > *hx ) *hx = ix; \
   if( iy < *ly ) *ly = iy; \
   if( iy > *hy ) *hy = iy; \
}

/* Define a macro that uses the above macros to to create implementations
   of GetSelectionBounds for all operations. */
#define MAKEALL_GETSELECTIONBOUNDS(X,Xtype) \
MAKE_GETSELECTIONBOUNDS(X,Xtype,LT,AST__LT) \
MAKE_GETSELECTIONBOUNDS(X,Xtype,LE,AST__LE) \
MAKE_GETSELECTIONBOUNDS(X,Xtype,EQ,AST__EQ) \
MAKE_GETSELECTIONBOUNDS(X,Xtype,GE,AST__GE) \
MAKE_GETSELECTIONBOUNDS(X,Xtype,GT,AST__GT) \
MAKE_GETSELECTIONBOUNDS(X,Xtype,NE,AST__NE)

/* Expand the above macro to generate a function for each required
   data type and operation. */
#if HAVE_LONG_DOUBLE     /* Not normally implemented */
MAKEALL_GETSELECTIONBOUNDS(LD,long double)
#endif
MAKEALL_GETSELECTIONBOUNDS(D,double)
MAKEALL_GETSELECTIONBOUNDS(L,long int)
MAKEALL_GETSELECTIONBOUNDS(UL,unsigned long int)
MAKEALL_GETSELECTIONBOUNDS(I,int)
MAKEALL_GETSELECTIONBOUNDS(UI,unsigned int)
MAKEALL_GETSELECTIONBOUNDS(S,short int)
MAKEALL_GETSELECTIONBOUNDS(US,unsigned short int)
MAKEALL_GETSELECTIONBOUNDS(B,signed char)
MAKEALL_GETSELECTIONBOUNDS(UB,unsigned char)
MAKEALL_GETSELECTIONBOUNDS(F,float)

/* Undefine the macros. */
#undef MAKE_GETSELECTIONBOUNDS
#undef MAKEALL_GETSELECTIONBOUNDS
#undef EXTEND_BOX


static void IncorporateCells( AstMoc *this, CellList *clist, int negate,
                              int cmode, const char *method, int *status ){
/*
*  Name:
*     IncorporateCells

*  Purpose:
*     Incorporate a list of HEALPix cells into the given Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void IncorporateCells( AstMoc *this, CellList *clist, int negate,
*                            int cmode, const char *method, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function incorporates the supplied list of HEALPix cells into
*     the Moc.

*  Parameters:
*     this
*        Pointer to the Moc.
*     clist
*        Structure holding information about the cells to be incorporated
*        in the Moc.
*     negate
*        If zero, the list of HEALPix cells to incorporate into the
*        Moc are those included in "clist". If non-zero, the list of
*        HEALPix cells to incorporate into the Moc are those NOT included
*        in "clist".
*     cmode
*        Indicates how the Moc is to be combined with the incorporated
*        cells. Any of the following values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the cell list.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the cell list.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the cell list.
*     method:
*        Name of calling method.
*     status
*        The inherited status.

*/

/* Local Variables: */
   AstPointSet *ps1;
   AstPointSet *ps2;
   double **ptr2;
   double *ptr1[ 2 ];
   double *px;
   double *py;
   int i;
   int irange;
   int nold;
   int order;
   int shift;
   int64_t *nested;
   int64_t *pend;
   int64_t *pn;
   int64_t *pr;
   int64_t ihigh;
   int64_t ilow;

/* Check inherited status */
   if( !astOK ) return;

/* Record the original number of ranges of nested indices in the Moc. */
   nold = this->nrange;

/* Loop over each order present in the CellList structure. */
   for( order = 0; order <= clist->maxorder; order++ ) {
      if( clist->len[ order ] > 0 ) {
         nested = astMalloc( clist->len[ order ]*sizeof( *nested ) );

/* Transform the grid coords at the current order from their own
   HPX-like projection to the HPX12 projection. */
         ps1 = astPointSet( clist->len[ order ], 2, "", status );
         ptr1[ 0 ] = clist->ix[ order ];
         ptr1[ 1 ] = clist->iy[ order ];
         astSetPoints( ps1, ptr1 );
         ps2 = astTransform( clist->maps[ order ], ps1, 1, NULL );
         ptr2 = astGetPoints( ps2 );
         if( astOK ) {

/* Round each one to the nearest pixel centre and convert to a nested
   HEALPix index at the current order. */
            pn = nested;
            px = ptr2[ 0 ];
            py = ptr2[ 1 ];
            for( i = 0; i < clist->len[ order ]; i++ ) {
               if( *px != AST__BAD && *py != AST__BAD ) {
                  *(pn++) = XyToNested( order, (int)( *(px++) + 0.5 ),
                                               (int)( *(py++) + 0.5 ) );
               } else if( astOK ) {
                  astError( AST__INTER, "%s(%s): Bad HPX12 grid coord "
                            "element %d order %d (internal programming "
                            "error).", status, method, astGetClass(this ),
                             i, order );
                  break;
               }
            }
         }

/* Check no errors above. */
         if( astOK ) {

/* Sort them into ascending order. */
            qsort( nested, clist->len[ order ], sizeof(*nested),
                   Comp_int64 );

/* Look for ranges within the sorted list of indices. */
            shift = 2*( clist->maxorder - order );
            pn = nested;
            pend = nested + clist->len[ order ];
            while( pn < pend ) {
               ilow = *pn;
               ihigh = ilow;
               while( ++pn < pend && *pn == ihigh + 1 ) ihigh++;

/* Process index range ilow->ihigh. Get the equivalent range at order
   "maxorder", and append to the list of ranges in the Moc. */
               irange = this->nrange++;
               this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
               if( astOK ) {
                  pr = this->range + 2*irange;
                  pr[ 0 ] = ( ilow << shift );
                  pr[ 1 ] = ( ( ihigh + 1 ) << shift ) - 1;
               } else {
                  break;
               }
            }
         }

/* Free resources. */
         ps1 = astAnnul( ps1 );
         ps2 = astAnnul( ps2 );
         nested = astFree( nested );
      }
   }

/* Normalise the Moc. */
   astMocNorm( this, negate, cmode, nold, clist->maxorder, method );
}

void astInitMocVtab_(  AstMocVtab *vtab, const char *name, int *status ) {
/*
*+
*  Name:
*     astInitMocVtab

*  Purpose:
*     Initialise a virtual function table for a Moc.

*  Type:
*     Protected function.

*  Synopsis:
*     #include "moc.h"
*     void astInitMocVtab( AstMocVtab *vtab, const char *name )

*  Class Membership:
*     Moc vtab initialiser.

*  Description:
*     This function initialises the component of a virtual function
*     table which is used by the Moc class.

*  Parameters:
*     vtab
*        Pointer to the virtual function table. The components used by
*        all ancestral classes will be initialised if they have not already
*        been initialised.
*     name
*        Pointer to a constant null-terminated character string which contains
*        the name of the class to which the virtual function table belongs (it
*        is this pointer value that will subsequently be returned by the Object
*        astClass function).
*-
*/

/* Local Variables: */
   astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
   AstMappingVtab *mapping;      /* Pointer to Mapping component of Vtab */
   AstObjectVtab *object;        /* Pointer to Object component of Vtab */
   AstRegionVtab *region;        /* Pointer to Region component of Vtab */

/* Check the local error status. */
   if ( !astOK ) return;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(NULL);

/* Initialize the component of the virtual function table used by the
   parent class. */
   astInitRegionVtab( (AstRegionVtab *) vtab, name );

/* Store a unique "magic" value in the virtual function table. This
   will be used (by astIsAMoc) to determine if an object belongs
   to this class.  We can conveniently use the address of the (static)
   class_check variable to generate this unique value. */
   vtab->id.check = &class_check;
   vtab->id.parent = &(((AstRegionVtab *) vtab)->id);

/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
   virtual methods for this class. */
   vtab->AddRegion = AddRegion;
   vtab->AddMocData = AddMocData;
   vtab->AddMocString = AddMocString;
   vtab->GetMocString = GetMocString;
   vtab->AddCell = AddCell;
   vtab->GetCell = GetCell;
   vtab->TestCell = TestCell;

#if HAVE_LONG_DOUBLE     /* Not normally implemented */
   vtab->AddPixelMaskLD = AddPixelMaskLD;
#endif
   vtab->AddPixelMaskB  = AddPixelMaskB;
   vtab->AddPixelMaskD  = AddPixelMaskD;
   vtab->AddPixelMaskF  = AddPixelMaskF;
   vtab->AddPixelMaskI  = AddPixelMaskI;
   vtab->AddPixelMaskL  = AddPixelMaskL;
   vtab->AddPixelMaskS  = AddPixelMaskS;
   vtab->AddPixelMaskUB = AddPixelMaskUB;
   vtab->AddPixelMaskUI = AddPixelMaskUI;
   vtab->AddPixelMaskUL = AddPixelMaskUL;
   vtab->AddPixelMaskUS = AddPixelMaskUS;

   vtab->GetMocArea = GetMocArea;
   vtab->GetMocData = GetMocData;
   vtab->GetMocType = GetMocType;
   vtab->GetMocLength = GetMocLength;
   vtab->GetMocHeader = GetMocHeader;

   vtab->ClearMaxOrder = ClearMaxOrder;
   vtab->GetMaxOrder = GetMaxOrder;
   vtab->SetMaxOrder = SetMaxOrder;
   vtab->TestMaxOrder = TestMaxOrder;

   vtab->ClearMinOrder = ClearMinOrder;
   vtab->GetMinOrder = GetMinOrder;
   vtab->SetMinOrder = SetMinOrder;
   vtab->TestMinOrder = TestMinOrder;

/* Save the inherited pointers to methods that will be extended, and
   replace them with pointers to the new member functions. */
   object = (AstObjectVtab *) vtab;
   mapping = (AstMappingVtab *) vtab;
   region = (AstRegionVtab *) vtab;

   parent_clearattrib = object->ClearAttrib;
   object->ClearAttrib = ClearAttrib;
   parent_getattrib = object->GetAttrib;
   object->GetAttrib = GetAttrib;
   parent_setattrib = object->SetAttrib;
   object->SetAttrib = SetAttrib;
   parent_testattrib = object->TestAttrib;
   object->TestAttrib = TestAttrib;
   parent_equal = object->Equal;
   object->Equal = Equal;

   parent_transform = mapping->Transform;
   mapping->Transform = Transform;

   region->RegBaseBox = RegBaseBox;
   region->RegBaseMesh = RegBaseMesh;
   region->RegPins = RegPins;
   region->GetDefUnc = GetDefUnc;
   region->RegTrace = RegTrace;

/* Declare the copy constructor, destructor and class dump
   functions. */
   astSetDump( vtab, Dump, "Moc", "IVOA Multi-Order Coverage map" );
   astSetDelete( vtab, Delete );
   astSetCopy( vtab, Copy );

/* If we have just initialised the vtab for the current class, indicate
   that the vtab is now initialised, and store a pointer to the class
   identifier in the base "object" level of the vtab. */
   if( vtab == &class_vtab ) {
      class_init = 1;
      astSetVtabClassIdentifier( vtab, &(vtab->id) );
   }
}

static int log2_64( int64_t svalue ) {
/*
*  Name:
*     log2_64

*  Purpose:
*     Fast log2 function for signed 64 bit integers.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int log2_64( int64_t svalue )

*  Description:
*     This function returns the log base 2 of the supplied 64 bit integer
*     value. By Desmond Hume.

*/
    uint64_t value = svalue;
    value |= value >> 1;
    value |= value >> 2;
    value |= value >> 4;
    value |= value >> 8;
    value |= value >> 16;
    value |= value >> 32;
    return Tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
}

static int log2_32( int svalue ) {
/*
*  Name:
*     log2_32

*  Purpose:
*     Fast log2 forunction for 32 bit integers.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int log2_32( int svalue )

*  Description:
*     This function returns the log base 2 of the supplied 32 bit integer
*     value. By Desmond Hume.

*/
    unsigned int value = svalue;
    value |= value >> 1;
    value |= value >> 2;
    value |= value >> 4;
    value |= value >> 8;
    value |= value >> 16;
    return Tab32[(unsigned int)(value*0x07C4ACDD) >> 27];
}

static Cell *MakeCell( int ix, int iy, int order, Cell **foot, int *status ){
/*
*  Name:
*     MakeCell

*  Purpose:
*     Create a structure holding a descripion of a single HEALPix cell.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     Cell *MakeCell( int ix, int iy, int order, Cell **foot, int *status )

*  Description:
*     This function creates a Cell structure, initialising its contents
*     to null values, and then stores the supplied grid indices in the
*     Cell and adds it to the end of the linked list of Cell structures.

*  Parameters:
*     ix
*        The one-based integer grid X index for the HPX12 pixel to be
*        described by the Cell.
*     iy
*        The one-based integer grid Y index for the HPX12 pixel to be
*        described by the Cell.
*     order
*        The HEALPix order at which the pixel is defined.
*     foot
*        Address of the last existing Cell pointer currently on the
*        linked list of Cell pointers. It is updated on exit to point to
*        the new Cell.
*     status
*        Pointer to the inherited status.

*  Returned Value:
*     Pointer to the new Cell. It should be freed using astFree when no
*     longer needed.

*/

/* Local Variables: */
   Cell *new;

/* Check inherited status */
   if( !astOK ) return NULL;

/* Allocate new structure, initialising it to all zeros to nullify all
   fields. */
   new = astCalloc( 1, sizeof( *new ) );
   if( new ) {

/* Store supplied values in the new Cell. */
      new->ix = ix;
      new->iy = iy;

/* Append it to the end of the chain of Cells at the same order. */
      new->prev = foot[ order ];
      foot[ order ] = new;
   }

   return new;
}

static void MakeCorners( AstMoc *this, int order, Cell *cell_foot,
                         Corner **corner_foot, int sort, int *status ){
/*
*  Name:
*     MakeCorners

*  Purpose:
*     Incorporate cell information into a linked list of structures
*     holding information about corners.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void MakeCorners( AstMoc *this, int order, Cell *cell_foot,
*                       Corner **corner_foot, int sort, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function finds the four corner positions of each Cell
*     in the chain specified by "cell_foot", and for each one registers
*     the cell with the corresponding Corner structure. New Corner
*     structures are created for any corner positions that do not
*     already have a corresponding structure, and appended to the
*     end of the supplied linked list of Corner structures. Co-incident
*     Corner structures are then merged.

*  Parameters:
*     this
*        The Moc.
*     order
*        The HEALPix order of the supplied cells.
*     cell_foot
*        Pointer to the last Cell structure in a chain of linked structures
*        holding information about the cells for which corners are to be
*        created.
*     corner_foot
*        Pointer to a Corner pointer, in which to return a pointer to the
*        last Corner created by this function. This is the last Corner in
*        a chain of linked Corner structures holding information about
*        the corners at the corresponding order. All RA values are in the
*        range [0,2.PI[.
*     sort
*        If non-zero, the created chain of Corner structures is sorted,
*        first by Dec then by RA.
*     status
*        Pointer to the inherited status variable.

*/

/* Local Variables; */
   AstMapping *map;
   AstPointSet *ps1;
   AstPointSet *ps2;
   Cell **pcell;
   Cell *cell;
   Corner *root;
   Corner **index;
   Corner *foot;
   Corner *prev;
   Corner *next;
   Corner *corner;
   Corner **pi;
   astDECLARE_GLOBALS
   double **ptr2;
   double **ptr1;
   double *p1;
   double *p0;
   double dectol;
   double ratol;
   int cross;
   int icell;
   int icorner;
   int lim;
   int ncell;
   int ncorner;
   int nppf;
   int s1;
   int s2;
   int sxy;

/* Check inherited status */
   if( !astOK ) return;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(this);

/* Initialise */
   foot = NULL;

/* Count the number of Cells in the supplied chain of cells. Create four
   new empty Corner structures for each one, and append them to the end
   of the chain of Corners. */
   ncell = 0;
   cell = cell_foot;
   while( cell ) {
      ncell++;
      cell = cell->prev;
      for( icorner = 0; icorner < 4; icorner++ ) {
         corner = astCalloc( 1, sizeof( *corner ) );
         corner->prev = foot;
         foot = corner;
      }
   }

/* The number of new corners - four per cell. */
   if( ncell > INT_MAX/4 - 2 ) {
      astError( AST__BGMOC, "astRegBaseMesh(%s): The normalised MOC has "
                "too many elements.", status, astGetClass( this ) );
   } else {
      ncorner = 4*ncell;
   }

/* Get a pointset to hold the grid coords at the four corners of each
   cell. */
   ps1 = astPointSet( ncorner, 2, " ", status );
   ptr1 = astGetPoints( ps1 );

/* Get a pointset to hold the corresponding sky coords at the four
   corners of each cell. */
   ps2 = astPointSet( ncorner, 2, " ", status );
   ptr2 = astGetPoints( ps2 );

/* Check all pointers are safe to use. */
   if( astOK ) {

/* Get the sums of ix and iy that correspond to the RA=0h/24h line. */
      nppf = ( 1 << order );
      s1 = nppf + 1;
      s2 = 9*nppf + 1;
      lim = nppf;

/* Get the grid coords at the corners, within an HPX12 projection at the
   specified order. Note, to avoid problems at discontinuities, the corner
   positions are very slighly inside the cell rather than being exactly
   on the boundary. Also get a flag ("cross") which indicates if any cell
   falls on the RA=0h/RA=24h discontinuity. */
      cross = 0;
      p0 = ptr1[ 0 ];
      p1 = ptr1[ 1 ];
      cell = cell_foot;
      for( icell = 0; icell < ncell; icell++ ) {
         *(p0++) = cell->ix - HALF;
         *(p0++) = cell->ix - HALF;
         *(p0++) = cell->ix + HALF;
         *(p0++) = cell->ix + HALF;
         *(p1++) = cell->iy - HALF;
         *(p1++) = cell->iy + HALF;
         *(p1++) = cell->iy + HALF;
         *(p1++) = cell->iy - HALF;

         sxy = cell->ix + cell->iy;
         if( abs( sxy - s1 ) <= lim || abs( sxy - s2 ) <= lim ) cross = 1;

         cell = cell->prev;
      }

/* Get a Mapping that goes from ICRS to grid coordinates in an HPX12
   projection of the whole sky at the specified order. Do not annul
   the Mapping pointer since it has not been cloned in GetCachedMapping. */
      map = GetCachedMapping( this, order, "astRegBaseMesh", status );

/* Use the inverse transformation of this mapping to convert the grid
   coords at the cell corners into sky coords. */
      (void) astTransform( map, ps1, 0, ps2 );

/* Ensure all RA values are in range [0,2PI[ and store the (RA,Dec) values
   in the relevant Corner structures. Set up the links between the new
   Corners and the Cells to which they refer. */
      p0 = ptr2[ 0 ];
      p1 = ptr2[ 1 ];
      cell = cell_foot;
      corner = foot;
      for( icell = 0; icell < ncell && astOK; icell++ ){

         for( icorner = 0; icorner < 4; icorner++,p0++,p1++ ){
            if( *p0 == AST__BAD || *p1 == AST__BAD ) {
               astError( AST__INTER, "astRegBaseMesh(%s): Bad sky coords "
                         "at corner (internal programming error).", status,
                         astGetClass( this ) );
               break;
            }

            while( *p0 >= 2*AST__DPI ) *p0 -= 2*AST__DPI;
            while( *p0 < 0 ) *p0 += 2*AST__DPI;

            corner->ra = *p0;
            corner->dec = *p1;
            corner->cosdec = cos( *p1 );
            corner->ncell = 1;
            corner->cells[ 0 ] = cell;

            if( icorner == 0 ) {
               cell->bl = corner;
            } else if( icorner == 1 ) {
               cell->tl = corner;
            } else if( icorner == 2 ) {
               cell->tr = corner;
            } else {
               cell->br = corner;
            }
            corner = corner->prev;
         }

         cell = cell->prev;
      }
   }

/* Free resources. */
   ps1 = astAnnul( ps1 );
   ps2 = astAnnul( ps2 );

/* If any cells cross the RA=0/24 line, we need to sort and merge adjacent
   corners twice, once centred on RA=12 and once centred on RA=0. Otherwise
   adjacent corners on either side of the RA=0/24 line will not be merged. */
   for( Comp_Corner_Loop = 0; Comp_Corner_Loop <= cross; Comp_Corner_Loop++ ) {

/* We now sort the corners, using DEC as the primary key and Ra as the
   secondary key (Dec is primary because the HEALPix cells form a number
   of rings on the sky, each at constant Dec). First create an index of
   the Corners in the linked list. This index is simply an array of
   pointers to the Corners in their initial positions within the linked
   list. */
      index = astMalloc( ncorner*sizeof(*index) );
      if( astOK ) {
         pi = index;
         icorner = 0;
         corner = foot;
         while( corner ) {
            icorner++;
            *(pi++) = corner;
            corner = corner->prev;
         }

/* Sanity check. */
         if( icorner != ncorner && astOK ) {
            astError( AST__INTER, "astRegBaseMesh(%s): Expected %d corners "
                      "but found %d (internal programming error).", status,
                      astGetClass(this), ncorner, icorner );
         }

/* Sort the index. For Comp_Corner_Loop==0 they are sorted around RA=12h.
   For Comp_Corner_Loop==1 they are sorted around RA=0/24h. Sort them
   exactly in RA (i.e. zero tolerance in RA) */
         Comp_Corner_Exact = 1;
         if( astOK ) qsort( index, ncorner, sizeof(*index), Comp_corner );

/* Merge corners that are co-incident. To begin with, use the first
   corner as the reference (root) corner and compare each subsequent
   corner to it. Allow some tolerance in RA. */
         Comp_Corner_Exact = 0;
         root = index[ 0 ];
         pi = index + 1;
         for( icorner = 1; icorner < ncorner; icorner++,pi++ ) {

/* If the current Corner is co-incident with the root corner, transfer
   information from the current Corner to the root Corner, and then mark
   the current Corner as unused by setting its RA bad. */
            if( Comp_corner( &root, pi ) == 0 ) {

/* Check the merged Corner will not be used by more than four cells. */
               if( root->ncell + (*pi)->ncell > 4 ) {
                  astError( AST__INTER, "astRegBaseMesh(%s): Corner is used "
                            "more than 4 times (internal programming error).",
                            status, astGetClass( this ) );
                  break;

/* Merge the cells from the current Corner into the root Corner. Update
   the pointers within each cell to point to the root corner instead of
   the current corner. */
               } else {
                  pcell = (*pi)->cells;
                  for( icell = 0; icell < (*pi)->ncell; icell++,pcell++ ) {
                     root->cells[ (root->ncell)++ ] = *pcell;

                     if( (*pcell)->bl == *pi ) {
                        (*pcell)->bl = root;
                     } else if( (*pcell)->tl == *pi ) {
                        (*pcell)->tl = root;
                     } else if( (*pcell)->tr == *pi ) {
                        (*pcell)->tr = root;
                     } else if( (*pcell)->br == *pi ) {
                        (*pcell)->br = root;
                     } else {
                        astError( AST__INTER, "astRegBaseMesh(%s): Orphaned "
                               "Corner (internal programming error).", status,
                               astGetClass( this ) );
                        break;
                     }
                  }
                  (*pi)->ra = AST__BAD;
               }

/* If the current cell is not co-incident with the root cell, use the
   current cell as the root cell from now on.  */
            } else {
               root = *pi;
            }
         }

/* If required, re-order the Corner structures within the chain so that
   they follow the same order as the index (i.e. sorted into increasing
   Dec). */
         if( sort ) {
            pi = index + ncorner - 1;
            foot = *pi;
            foot->prev = NULL;
            for( icorner = 1; icorner < ncorner; icorner++ ) {
               corner = *(--pi);
               corner->prev = foot;
               foot = corner;
            }
         }

/* Remove corners that have been flagged with a bad RA value, and fix up
   the links between the corners on either side. */
         ncorner = 0;
         next = NULL;
         corner = foot;
         while( corner ) {
            prev = corner->prev;

            if( corner->ra == AST__BAD ) {
               if( next ) next->prev = prev;
               if( corner == foot ) foot = prev;
               corner = astFree( corner );
            } else {
               ncorner++;
               next = corner;
            }

            corner = prev;
         }
      }

/* Free resources. */
      index = astFree( index );
   }

/* Indicate if each remaining corner is an interior position (i.e. surrounded
   by cells on all sides OR is at the corner of at least one interior cell
   from an earlier order). */
   corner = foot;
   while( corner ) {

/* If the corner touches a cell that has been flagged as interior at a
   previous order, then it must be interior at this order too. */
      pcell = corner->cells;
      for( icell = 0; icell < corner->ncell; icell++,pcell++ ) {
         if( (*pcell)->interior ) {
            corner->interior = 1;
            break;
         }
      }

/* In general, each corner of a cell is coincident with three other
   corners from three neighbouring cells. So each corner position occurs
   four times at an interior position. However, there are 8 corner
   positions where only three cells meet. These are at the southern and
   northern corners of the equatorial facets. If the current corner is
   used three times, we need to see if this is a four-corner position
   that is missing a corner, or a three-corner position which is not
   missing a corner (i.e. an interior position). */
      if( !corner->interior ) {
         if( corner->ncell == 3 ) {
            dectol = Comp_Corner_Tol;
            ratol = dectol/cos(THETAX);
            if( fabs( corner->ra ) < ratol ||
                fabs( corner->ra - AST__DPIBY2 ) < ratol ||
                fabs( corner->ra - AST__DPI ) < ratol ||
                fabs( corner->ra - 3*AST__DPIBY2 ) < ratol ||
                fabs( corner->ra - 2*AST__DPI ) < ratol ) {
               if( fabs( corner->dec - THETAX ) < dectol ||
                   fabs( corner->dec + THETAX ) < dectol ) {
                  corner->interior = 1;
               }
            }

/* Other corners are interior if they are used 4 times. */
         } else if( corner->ncell == 4 ) {
            corner->interior = 1;
         }
      }


/* Initialise the distance of the corner around the perimeter to indicate
   no distance has yet been found. */
      corner->dist = INT_MAX;

/* Move on to the next corner in the chain. */
      corner = corner->prev;
   }

/* Return a pointer to the corner that's now at the foot of the chain. */
   *corner_foot = foot;

}

static void MergeRanges( AstMoc *this, int start, int *status ){
/*
*  Name:
*     MergeRanges

*  Purpose:
*     Merge contiguous cell ranges in a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void MergeRanges( AstMoc *this, int start, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function merges any conntiguous ranges of cells within a Moc,
*     starting at a specified range (earlier ranges are left unchanged).
*     The merged ranges are also ordered into increasing lower bound.

*  Parameters:
*     this
*        The Moc to use.
*     start
*        The index of the first range to check.
*     status
*        Pointer to the inherited status.

*/

/* Local Variables: */
   int irange;
   int nnew;
   int64_t *pr;
   int64_t *pw;

/* Check inherited status */
   if( !astOK ) return;

/* Nothing to do if the Moc is empty or has only one range, or if the
   first range to be merged is the last range. */
   if( this->nrange > 1 && start < this->nrange - 1 ) {

/* Sort the specified ranges into increasing order of lower bound. */
      qsort( this->range + 2*start, this->nrange - start,
             2*sizeof(*(this->range)), Comp_range );

/* "nnew" is the number of ranges in the Moc after the merge. Initialise
   it to the number of ranges not being merged, plus one (because the
   two or more ranges being merged must produce at least one merged
   range). */
      nnew = start + 1;

/* Merge contiguous or overlapping ranges. */
      pw = this->range + 2*start;
      pr = this->range + 2*start + 2;
      for( irange = nnew; irange < this->nrange; irange++, pr += 2 ) {

/* If the new range starts at or before the end of the current range, and
   the new range ends after the end of the current range, extend the
   current range to include the new range. */
         if( pr[ 0 ] <= pw[ 1 ] + 1 ) {
            if( pr[ 1 ] > pw[ 1 ] ) {
               pw[ 1 ] = pr[ 1 ];
            }

/* If the new range starts after the end of the current range, increment
   the current range and initialise it to the bounds of the current
   range. */
         } else {
            pw += 2;
            pw[ 0 ] = pr[ 0 ];
            pw[ 1 ] = pr[ 1 ];
            nnew++;
         }
      }

/* Store the new number of ranges in the Moc. This may be smaller than
   the original number, but will never be greater. Then realloc the
   ranges array to save space. */
      this->nrange = nnew;
      this->range = astRealloc( this->range, 2*nnew*sizeof(*(this->range)) );
   }

/* Clear the cached information stored in the Moc structure so that it is
   re-calculated when next needed. */
   ClearCache( this, status );
}

void astMocNorm_( AstMoc *this, int negate, int cmode, int nold,
                  int maxorder, const char *method, int *status ){
/*
*+
*  Name:
*     astMocNorm

*  Purpose:
*     Normalise the supplied Moc.

*  Type:
*     Protected function.

*  Synopsis:
*     #include "moc.h"
*     void astMocNorm( AstMoc *this, int negate, int cmode, int nold,
*                      int maxorder, const char *method )

*  Class Membership:
*     Moc member function

*  Description:
*     This function normalises the supplied Moc. It is assumed that the
*     ranges of HEALPix cell indices within Moc structure are in two
*     groups: 1) range zero to range 'nold-1' are assumed to be already
*     normalised, 2) ranges 'nold' to the end are assumed not be be
*     normalised.

*  Parameters:
*     this
*        Pointer to the Moc.
*     negate
*        If non-zero, the HEALPix cells in ranges "nold" to the end are
*        negated before being merged with the earlier ranges.
*     cmode
*        Indicates how ranges 'nold' to the end are to be combined with the
*        earlier ranges. Any of the following values may be supplied:
*        - AST__AND: The modified Moc is the intersection of the original
*        Moc and the cell list.
*        - AST__OR: The modified Moc is the union of the original Moc and
*        the cell list.
*        - AST__XOR: The modified Moc is the exclusive disjunction of the
*        original Moc and the cell list.
*     nold
*        The number of cell ranges that are already normalised.
*     maxorder
*        The maximum HEALPix order.
*     method:
*        Name of calling method to include in error messages.
*     status
*        The inherited status.

*/

/* Check inherited status */
   if( !astOK ) return;

/* Sort the new ranges into increasing order of lower bound. */
   MergeRanges( this, nold, status );

/* If the cell list is to be inverted, we replace the new ranges
   with the gaps between the new ranges. */
   if( negate ) NegateRanges( this, nold, maxorder, status );

/* Combine all the ranges using the specified combination method. */
   CombineRanges( this, cmode, method, status );
}

static void NegateRanges( AstMoc *this, int start, int order,
                          int *status ){
/*
*  Name:
*     NegateRanges

*  Purpose:
*     Negate the ranges of contiguous cells in a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void NegateRanges( AstMoc *this, int start, int order,
*                        int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function negates the specified ranges of cells within a Moc
*     by replacing the ranges with new ranges that describe the gaps
*     between the original ranges. Ranges before "start" are left
*     unchanged.
*
*     Note, it is assumed that the ranges being negated are sorted into
*     increasing order of lower bound and that they do not overlap or
*     touch. These conditions can be achieved by calling CombineRanges
*     before calling this function.

*  Parameters:
*     this
*        The Moc to use.
*     start
*        The index of the first range to negate.
*     order
*        The HEALPix order to which the ranges refer.
*     status
*        Pointer to the inherited status.

*/

/* Local Variables: */
   int irange;
   int64_t *pr;
   int64_t istart;
   int64_t max_nest;
   int64_t next_start;

/* Check inherited status */
   if( !astOK ) return;

/* Nothing to do if the Moc is empty. */
   if( this->nrange > 0 ) {

/* Get the maximum number of nested indices at the highest order in use. */
      max_nest = 12*( 1L << 2*order );

/* Does the first range start at zero? If so, there is no gap before it. */
      if( this->range[ 2*start ] == 0 ) {

/* Loop round all the new ranges stored in the Moc above. Replace each
   range with the gap between it and the following range. */
         pr = this->range + 2*start;
         for( irange = start; irange < this->nrange; irange++ ) {

/* The start of the next "gap" is the end of the current "range". */
            pr[ 0 ] = pr[ 1 ] + 1;

/* Check that the current range does not extend all the way to the last
   nested index value. If it does not, stoe the end of the current gap.
   If it does, break out of the range loop. */
            if( pr[ 0 ] < max_nest ) {
               pr[ 1 ] = pr[ 2 ] - 1;
            } else {
               break;
            }

/* Increment the pointer to the next range. */
            pr += 2;
         }

/* If the last range extended to the last nested index value, we will end
   up with one fewer range than we had to begin with. */
         this->nrange = irange;

/* Now handle cases where the first range does not start at zero. There
   will be a gap before it. */
      } else {

/* Loop round all the new ranges stored in the Moc above. Replace each
   range with the gap between it and the preceding range. */
         istart = 0;
         pr = this->range + 2*start;
         for( irange = start; irange < this->nrange; irange++ ) {

/* Record the index at which the next gap starts. */
            next_start = pr[ 1 ] + 1;

/* The end of the "current range" is changed to be the end of the gap
   between the previous range and the current range. */
            pr[ 1 ] = pr[ 0 ] - 1;

/* The start of the "current range" is changed to be the start of the gap
   between the previous range and the current range. */
            pr[ 0 ] = istart;

/* Update the start of the next gap. */
            istart = next_start;

/* Increment the pointer to the next range. */
            pr += 2;
         }

/* If the last range ended before the last nested index value, there
   is a gap after it and so we will end up with one more range than we
   had to begin with. Extend the ranges array and then add in the gap
   following the final range. */
         if( istart < max_nest ) {
            irange = this->nrange++;
            this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
            pr = this->range + 2*irange;
            if( astOK ) {
               pr[ 0 ] = istart;
               pr[ 1 ] = max_nest - 1;
            }
         }
      }
   }
}

static void NestedToXy( int64_t nested, int order, int *ix, int *iy ){
/*
*  Name:
*     NestedToXy

*  Purpose:
*     Get the HPX12 grid coords of the cell at given HEALPix nested index

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void NestedToXy( int64_t nested, int order, int *ix, int *iy )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the HPX12 grid coords of the cell at given
*     HEALPix nested index. This function is the inverse of XyToNested.

*  Parameters:
*     nested
*        The nested index of the required HEALPix cell.
*     order
*        The HEALPix order.
*     ix
*        Pointer to an int returned holding the one-based grid X index of
*        the cell within the HPX12 projection.
*     iy
*        Pointer to an int returned holding the one-based grid Y index of
*        the cell within the HPX12 projection.

*/

/* Local Variables: */
   int fi;
   int fx;
   int fy;
   int nppf;
   int64_t tx;
   int64_t ty;
   int64_t tj;

/* The least significant "2*order" bits hold the x index in the even bits
   and the y index in the odd bits (both relative to the south corner of
   the facet). The next more significant four bits holds the facet index.
   Get the zero-based facet index. */
   fi = ( nested >> (2*order) );

/* Facet indices must be in the range 0-11. */
   if( fi < 12 ) {

/* Get the lowest "2*order" bits. */
      tj = nested & ( ( 1L << (2*order) ) - 1 );

/* Reverse the process in XyToNested to extract the even and odd bits
   from "tj" into "tx" and "ty" - the zero-based offsets from the bottom
   left (i.e. south) corner of the facet to the requested cell. */
      tx = (tj >> 1 ) & 0x5555555555555555;
      ty = tj & 0x5555555555555555;

      tx = (tx | (tx >> 1))  & 0x3333333333333333;
      tx = (tx | (tx >> 2))  & 0x0F0F0F0F0F0F0F0F;
      tx = (tx | (tx >> 4))  & 0x00FF00FF00FF00FF;
      tx = (tx | (tx >> 8))  & 0x0000FFFF0000FFFF;
      tx = (tx | (tx >> 16)) & 0x00000000FFFFFFFF;

      ty = (ty | (ty >> 1))  & 0x3333333333333333;
      ty = (ty | (ty >> 2))  & 0x0F0F0F0F0F0F0F0F;
      ty = (ty | (ty >> 4))  & 0x00FF00FF00FF00FF;
      ty = (ty | (ty >> 8))  & 0x0000FFFF0000FFFF;
      ty = (ty | (ty >> 16)) & 0x00000000FFFFFFFF;

/* Get the number of cells along one edge of a facet.*/
      nppf = ( 1 << order );

/* Change tx so that it is measured in the opposite direction, starting
   at the bottom left corner of the facet. */
      tx = nppf - 1 - tx;

/* Get the offset in facet along X and Y from the bottom left corner of
   the HPX12 projection to the botton left corner of the facet. */
      if( fi < 4 ) {
         fx = fi;
         fy = fx + 1;
      } else if( fi < 8 ) {
         fx = fi - 4;
         fy = fx;
      } else {
         fx = fi - 7;
         fy = fx - 1;
      }

/* Convert these offsets from facets to cells and add them onto the
   offsets within the facet found above. Increment to get the required
   1-based X and Y indices. */
      *ix = tx + fx*nppf + 1;
      *iy = ty + fy*nppf + 1;

/* Return -1 if the facet index is out of bounds */
   } else {
      *ix = -1;
      *iy = -1;
   }

}

static double OrderToRes( int order ){
/*
*  Name:
*     OrderToRes

*  Purpose:
*     Get the mean resolution of a specified HEALPix order.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     double OrderToRes( int order )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the mean resolution, in arc-seconds, corresponding
*     to the supplied HEALPix order.

*  Parameters:
*     order
*        The HEALPix order.

*  Returned Value:
*     The mean resolution, in arc-seconds.
*/

/* Check the supplied value. */
   if ( order < 0 || order > AST__MXORDHPX ) return 0;

/* There are 12*4^(order) equal area pixels covering the whole sphere. So
   divide 4.pi steradians by 12*4^(order) to get the area of one pixel.
   Convert to square arc-seconds and then take the square root to get he
   mean pixel size. */
   return 211076.29/(double)( 1 << order );
}

static void PutCell( AstMoc *this, AstMapping **map, int ix, int iy, int order,
                     CellList *clist, int regtype, void *data, int cmode,
                     const char *method, int *status ){
/*
*  Name:
*     PutCell

*  Purpose:
*     Add a HEALPix cell into a CellList if it is an interior cell.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void PutCell( AstMoc *this, AstMapping **map, int ix, int iy, int order,
*                   CellList *clist, int regtype, void *data, int cmode,
*                   const char *method, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function examines a single specified HEALPix cell at a
*     specified order to see if it overlaps a specified region of
*     interest on the sky. If the cell is contained entirely within the
*     region of interest, the complete cell is added to the supplied
*     CellList. If the cell falls entirely outside the region of interest
*     it is ignored. If it falls on the edge of the region of interest, the
*     cell is subdivided into four child cells (i.e. the HEALPix order is
*     increased by one), and this function is called recursively to check
*     each child cell.

*  Parameters:
*     this
*        Pointer to the Moc.
*     map
*        Pointer to an array of Mapping pointers. Each Mapping transforms
*        from grid coordinates within an all-sky HPX-like projection
*        to  the coordinate system used for determining if a given position
*        is inside or outside the region of interest (as indicated by the
*        value of parameter "regtype"). The Mapping stored at index "i"
*        within the supplied array of pointers is for HEALPix order "i".
*        The supplied array should have "maxorder+1" elements. The
*        HPX-like projection used is indicated by the "iproj" field in the
*        "clist" structure, as is the maxorder value.
*     ix
*        The 1-based grid index of the cell to be added, on the first
*        (X) input axis of Mapping "map[order]".
*     iy
*        The 1-based grid index of the cell to be added, on the second
*        (Y) input axis of Mapping "map[order]".
*     order
*        The 0-based order to which "ix" and "iy" refer.
*     clist
*        Pointer to a structure holding a list of the grid coords, at
*        all orders, at the centre of the cells that are inside the region
*        of interest. The contents of this structure are updated on exit to
*        hold any parts of the supplied cell that are inside the region of
*        intertest.
*     regtype
*        Indicates how the region of interest is defined:
*        0: By an AST Region object. The outputs of "map" should be in
*        the Frame represented by the Region. The "data" pointer should
*        be a pointer to the Region.
*        1: By a 2D pixel array. The outputs of "map" should be in grid
*        coordinates within the pixel array. The "data" pointer should
*        be a pointer to a PixelMask structure.
*     data
*        Pointer to an object that defines the region of interest. See
*        "regtype".
*     method
*        Name of calling method - for error messages.
*     status
*        Inherited status value.

*  Notes:
*     - Speeding up this algorithm by passing down corner information
*     from the higher level sounds plausible, but in fact causes problems
*     for regions which intersect the discontinuities at RA=0/6/12/18/24
*     hours. At such points, the fact that a corner position is a valid
*     interior point at order N does not guarantee that it is a valid
*     interior point at order N+1, because the edges of the valid region
*     of the projection changes with the order.

*/

/* Local Variables: */
   AstPointSet *ps3;
   AstPointSet *ps2;
   AstPointSet *ps1;
   AstRegion *region;
   double **ptr3;
   double **ptr1;
   double *px;
   double *py;
   int i;
   int ii;
   int allin;
   int allout;
   int inside[9];
   int ixc;
   int iyc;
   int j;
   int npoint;

/* Check the global error status. */
   if( !astOK ) return;

/* If we have reached the maximum order, just test the centre of the
   specified cell. */
   if( order == clist->maxorder ) {
      npoint = 1;
      ps1 = astPointSet( npoint, 2, "", status );
      ptr1 = astGetPoints( ps1 );
      if( astOK ) {
         ptr1[ 0 ][ 0 ] = ix;
         ptr1[ 1 ][ 0 ] = iy;
      }

/* Otherwise, test a 3x3 grid of points covering the specified cell. */
   } else {
      npoint = 9;
      ps1 = astPointSet( npoint, 2, "", status );
      ptr1 = astGetPoints( ps1 );
      if( astOK ) {
         px = ptr1[ 0 ];
         py = ptr1[ 1 ];
         for( j = -1; j <= 1; j++ ) {
            for( i = -1; i <= 1; i++ ) {
               *(px++) = ix + HALF*i;
               *(py++) = iy + HALF*j;
            }
         }
      }
   }

/* Transform these positions into region of interest coordinates. */
   ps2 = astTransform( map[order], ps1, 1, NULL );

/* If all the positions are inside the region of interest, we assume the
   entire cell is inside the region of interest.  First do ROIs defined
   by a Region... */
   if( regtype == 0 ) {

/* If the ROI is defined by an AST Region object, use the Region to
   transform the points in ps2. The results will be bad if the point is
   outside the Region. Construct an array of 9 flags indicating which
   points on the 3x3 grid covering the cell are inside the ROI. The array
   of flags is in raster order, bottom left to top right. */
      region = (AstRegion *) data;
      ps3 = astTransform( region, ps2, 1, NULL );
      ptr3 = astGetPoints( ps3 );
      if( astOK ) {
         px = ptr3[ 0 ];

/* If only a single point is being tested, test it. */
         if( npoint == 1  ){
            inside[ 0 ] = ( *px != AST__BAD );

/* Otherwise a 3x3 grid of points is being tested. */
         } else {
            for( i = 0; i < npoint; i++ ) {
               inside[ i ] = ( *(px++) != AST__BAD );
            }
         }
      }
      ps3 = astAnnul( ps3 );

/* If the ROI is defined by a pixel array, see which of the positions
   correspond to selected pixels in the array and fill the "inside"
   array with appropriate values. */
   } else {
      TestPixels( (PixelMask *) data, &npoint, ps2, inside, status );
   }

/* Test to see if all positions are inside, or outside, the ROI. */
   allin = 1;
   allout = 1;
   for( i = 0; i < npoint; i++ ) {
      if( inside[ i ] ) {
         allout = 0;
      } else {
         allin = 0;
      }
   }

/* If the specified pixel appears to be inside the region of interest, add it
   to the supplied CellList. */
   if( allin ) {
      if( ix == AST__BAD || iy == AST__BAD ) {
         astError( AST__INTER, "%s(%s): Bad interior cell centre "
                   "(internal programming error).", status, method,
                   astGetClass( this ) );
      } else {
         if( clist->len[ order ] < INT_MAX - 2 ) {
            ii = clist->len[ order ]++;
            clist->ix[ order ] = astGrow( clist->ix[ order ], ii + 1,
                                          sizeof(*(clist->ix[ order ])));
            clist->iy[ order ] = astGrow( clist->iy[ order ], ii + 1,
                                          sizeof(*(clist->iy[ order ])));
         } else {
            astError( AST__BGMOC, "%s(%s): The normalised MOC has too "
                      "many elements.", status, method, astGetClass( this ) );
         }
         if( astOK ) {
            clist->ix[ order ][ ii ] = ix;
            clist->iy[ order ][ ii ] = iy;
         }
      }

/* Otherwise, if the cell appears to be partially inside the region of
   interest, divide up the current cell into 4 cells at order "order+1", and
   call this function recursively to add the bits of each one that are inside
   the region of interest. Do not do this if we are at the mximum order. */
   } else if( !allout && order < clist->maxorder ) {
      iyc = 2*iy - 1;
      for( j = 0; j < 2; j++,iyc++ ) {
         ixc = 2*ix - 1;
         for( i = 0; i < 2; i++,ixc++ ) {
            PutCell( this, map, ixc, iyc, order + 1, clist, regtype, data,
                     cmode, method, status );
         }
      }
   }

/* Free resources. */
   ps2 = astAnnul( ps2 );
   ps1 = astAnnul( ps1 );
}

static void RegBaseBox( AstRegion *this_region, double *lbnd,
                        double *ubnd, int *status ){
/*
*  Name:
*     RegBaseBox

*  Purpose:
*     Returns the bounding box of an un-negated Region in the base Frame of
*     the encapsulated FrameSet.

*  Type:
*     Private function.

*  Synopsis:
*     #include "circle.h"
*     void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd,
*                      int *status )

*  Class Membership:
*     Circle member function (over-rides the astRegBaseBox protected
*     method inherited from the Region class).

*  Description:
*     This function returns the upper and lower axis bounds of a Region in
*     the base Frame of the encapsulated FrameSet, assuming the Region
*     has not been negated. That is, the value of the Negated attribute
*     is ignored.

*  Parameters:
*     this
*        Pointer to the Region.
*     lbnd
*        Pointer to an array in which to return the lower axis bounds
*        covered by the Region in the base Frame of the encapsulated
*        FrameSet. It should have at least as many elements as there are
*        axes in the base Frame.
*     ubnd
*        Pointer to an array in which to return the upper axis bounds
*        covered by the Region in the base Frame of the encapsulated
*        FrameSet. It should have at least as many elements as there are
*        axes in the base Frame.
*     status
*        Pointer to the inherited status variable.

*/

/* Local Variables: */
   AstMoc *this;
   AstPointSet *ps;
   double **ptr;
   double *pdec;
   double *pra;
   double maxdec;
   double maxra0;
   double maxra12;
   double mindec;
   double minra0;
   double minra12;
   double ra0;
   double ra12;
   double wid0;
   double wid12;
   int ip;
   int np;

/* Check the global error status. */
   if ( !astOK ) return;

/* Get a pointer to the Moc structure */
   this = (AstMoc *) this_region;

/* The bounding box is cached in the Moc structure, in the form of RA and
   Dec values. If the bounds have not yet been calculated, calculate them
   now. */
   if( this->lbnd[ 0 ] == AST__BAD ) {

/* Get a mesh of points over the boundary of the MOC, in ICRS (the base
   Frame). */
     ps = astRegBaseMesh( this );
     ptr = astGetPoints( ps );
     np = astGetNpoint( ps );
     if( astOK ) {

/* Go though all the positions in the mesh, maintaining two bounding
   boxes - one centred on RA=0 and one centred on RA=12. */
        mindec = DBL_MAX;
        maxdec = -DBL_MAX;
        minra0 = DBL_MAX;
        maxra0 = -DBL_MAX;
        minra12 = DBL_MAX;
        maxra12 = -DBL_MAX;

        pra = ptr[ 0 ];
        pdec = ptr[ 1 ];
        for( ip = 0; ip < np; ip++,pdec++,pra++ ){
           if( *pdec < mindec ) mindec = *pdec;
           if( *pdec > maxdec ) maxdec = *pdec;

           ra12 = *pra;
           while( ra12 < 0.0 ) ra12 += 2*AST__DPI;
           while( ra12 > 2*AST__DPI ) ra12 -= 2*AST__DPI;
           if( ra12 < minra12 ) minra12 = ra12;
           if( ra12 > maxra12 ) maxra12 = ra12;

           ra0 = *pra;
           while( ra0 < -AST__DPI ) ra0 += 2*AST__DPI;
           while( ra0 > AST__DPI ) ra0 -= 2*AST__DPI;
           if( ra0 < minra0 ) minra0 = ra0;
           if( ra0 > maxra0 ) maxra0 = ra0;
         }

/* Get the width of the two bounding boxes, and choose the narrower. */
         wid0 = maxra0 - minra0;
         wid12 = maxra12 - minra12;
         if( wid0 < wid12 ) {
            this->lbnd[ 0 ] = minra0;
            this->ubnd[ 0 ] = maxra0;
         } else {
            this->lbnd[ 0 ] = minra12;
            this->ubnd[ 0 ] = maxra12;
         }

         this->lbnd[ 1 ] = mindec;
         this->ubnd[ 1 ] = maxdec;
      }

/* Free resources */
      ps = astAnnul( ps );
   }

/* Return the bounds. */
   lbnd[ 0 ] = this->lbnd[ 0 ];
   ubnd[ 0 ] = this->ubnd[ 0 ];
   lbnd[ 1 ] = this->lbnd[ 1 ];
   ubnd[ 1 ] = this->ubnd[ 1 ];
}

static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){
/*
*  Name:
*     RegBaseMesh

*  Purpose:
*     Return a PointSet containing points spread around the boundary of a
*     Region.

*  Type:
*     Private function.

*  Synopsis:
*     #include "region.h"
*     AstPointSet *RegBaseMesh( AstRegion *this )

*  Class Membership:
*     Region virtual function.

*  Description:
*     This function returns a PointSet containing a set of points on
*     the boundary of the Moc. The points refer to the base Frame of
*     the encapsulated FrameSet (which is always ICRS for a Moc). The
*     points in the returned PointSet are ordered so that Dec increased
*     from start to end. Points with equal Dec (allowing for rounding
*     error) are sorted into increasing RA. All RA values are in the
*     range [0,2PI[.

*  Parameters:
*     this
*        Pointer to the Region.

*  Returned Value:
*     Pointer to the PointSet.

*  Notes:
*    - A NULL pointer is returned if an error has already occurred, or if
*    this function should fail for any reason.
*    - The implementation for the Moc class ignores the MeshSize
*    attribute defined by the parent Region class. Instead, a mesh point is
*    placed at every normalised Moc cell corner that falls on the boundary.
*/

/* Local Variables: */
   AstMoc *this;
   AstPointSet *result;
   Cell **neb;
   Cell *cell0;
   Cell *cell1;
   Cell *cell2;
   Cell *cell;
   Cell *cell_foot[ AST__MXORDHPX + 1 ];
   Cell *prev_cell;
   Corner *corner;
   Corner *corner_foot;
   Corner *new_corner;
   Corner *old_corner;
   Corner *prev_corner;
   Corner *start;
   astDECLARE_GLOBALS
   double **ptr;
   double *pdec;
   double *pra;
   int *pni;
   int core;
   int dist;
   int icell;
   int icorner;
   int ix;
   int iy;
   int maxorder;
   int minorder;
   int npoint;
   int nused;
   int order;
   int64_t *pnk;
   int64_t npix;

/* Initialise */
   result = NULL;

/* Check the local error status. */
   if ( !astOK ) return result;

/* Get a pointer to the Moc structure. */
   this = (AstMoc *) this_region;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(this);

/* If the Moc structure contains a pointer to a PointSet holding
   a previously created mesh, return it. */
   if( this->basemesh ) {
      result = astClone( this->basemesh );

/* Otherwise, if the Moc is empty, or covers the whole sky, indicate that
   it has no boundary by returned a PointSet containing a single point with
   a value of AST__BAD on every axis. */
   } else if( this->nrange == 0 || ( this->range[0] == 0 &&
              this->range[1] == 12*( 1L << 2*astGetMaxOrder(this) ) - 1 )){

      this->basemesh = astPointSet( 1, 2, "", status );
      ptr = astGetPoints( this->basemesh );
      this->meshdist = astMalloc( sizeof( *(this->meshdist ) ) );
      if( ptr ) {
         ptr[ 0 ][ 0 ] = AST__BAD;
         ptr[ 1 ][ 0 ] = AST__BAD;
         this->meshdist[ 0 ] = 0.0;
      }
      result = astClone( this->basemesh );

/* Otherwise, create a new mesh. */
   } else {

/* Get the resolution of the Moc. Convert from arc-seconds to radians.
   Use a tenbgth of the MOC resolution at the current order. */
      Comp_Corner_Tol = 0.1*AST__DD2R*OrderToRes( astGetMaxOrder( this ) )/3600.0;

/* Initialise pointers to the end of the chain of Cell structures at each
   order. */
      for( order = 0; order <= AST__MXORDHPX; order++ ) {
         cell_foot[ order ] = NULL;
      }

/* Ensure the the normalised form of the MOC is available. */
      GetNorm( this, "astRegBaseMesh", status );

/* Set the pointer to the first nuniq value in the normalised moc. Use a
   local variable appropriate to the type of the normalised moc. */
      if( this->knorm ) {
         pnk = this->knorm;
         pni = NULL;
      } else {
         pni = this->inorm;
         pnk = NULL;
      }

/* Go through all the nuniq values in the normalised MOC. */
      minorder = -1;
      maxorder = -1;
      for( icell = 0; icell < this->moclength; icell++ ) {

/* Decode the nuniq value to get the order and npix, using a fast log2
   function. Increment the pointer to the next nuniq value. */
         if( pnk ) {
            order = log2_64( *pnk / 4 ) / 2;
            npix = *pnk - ( 1L << (2 + 2*order) );
            pnk++;
         } else {
            order = log2_32( *pni / 4 ) / 2;
            npix = *pni - ( 1 << (2 + 2*order) );
            pni++;
         }

/* Convert the nested index (npix) to grid coords in an HPX12 projection. */
         NestedToXy( npix, order, &ix, &iy );

/* Create a Cell structure to describe the current nuniq value and append it
   to the end of the chain of Cell structures at the current order. */
         (void) MakeCell( ix, iy, order, cell_foot, status );

/* Keep track of the highest and lowest order for which we have cells. */
         if( cell_foot[ order ] ) {
            if( minorder == -1 ) minorder = order;
            maxorder = order;
         }
      }

/* Find the edges of the cells at increasing orders. Each pass round this
   loop moves bundary cells from 'order' to the equivalent 4 child cells
   at 'order+1', and then deletes all remaining cells at 'order'. So after
   the final pass (which has order = maxorder-1), all cells will be at
   order 'maxorder'. */
      for( order = minorder; order < maxorder; order++ ) {

/* Skip this order if there are no cells at it. */
         if( cell_foot[ order ] && astOK ) {

/* Create chains of Corner structures for all cells at the current order.
   A Corner holds the (RA,Dec) of a point on the sky, plus pointers to
   up to four Cell structures that has that point at one of its corners.
   A Corner is an "interior position" if it corresponds to a corner of
   exactly four cells (there are a few special cases where interior
   points may only be used by 3 cells). Note, whether a corner is
   interior does not depend on whether the adjacent cells are flagged
   as interior, it just depends on how many cells touch the corner. */
            MakeCorners( this, order, cell_foot[ order ],
                         &corner_foot, 0, status );

/* Identify interior cells (cells for which all the corners are
   interior positions). Cells which are children of interior cells
   at the next lower order will already be flagged as interior. */
            cell = cell_foot[ order ];
            while( cell ) {
               if( cell->bl->interior && cell->tl->interior &&
                   cell->tr->interior && cell->br->interior ) {
                  cell->interior = 1;
               }
               cell = cell->prev;
            }

/* Find interior cells that are adjacent to at least one boundary cell
   (i.e. find interior cells that are not also "core" cells). Subdivide
   these, plus all boundary cells, into four child cells at the next
   order, and add these child cells to the chain of cells at the next
   order. We need to retain a layer of interior cells that are on the
   inside of the boundary layer to prevent the inside edge of the hole
   of unused cells in the middle of the region being mistaken for a
   boundary of the region. */
            cell = cell_foot[ order ];
            while( cell ) {

/* Assume this cell is a core cell (i.e. surrounded by interior cells on
   all sides). */
               core = 1;

/* If the bottom left corner touches a cell that is not interior, then
   the current cell is not a core cell. */
               neb = cell->bl->cells;
               for( icell = 0; icell < cell->bl->ncell; icell++ ) {
                  if( !((*(neb++))->interior) ) {
                     core = 0;
                     break;
                  }
               }

/* Otherwise, if the top left corner touches a cell that is not interior,
   then the current cell is not a core cell. */
               if( core ) {
                  neb = cell->tl->cells;
                  for( icell = 0; icell < cell->tl->ncell; icell++ ) {
                     if( !((*(neb++))->interior) ) {
                        core = 0;
                        break;
                     }
                  }
               }

/* Otherwise, if the top right corner touches a cell that is not interior,
   then the current cell is not a core cell. */
               if( core ) {
                  neb = cell->tr->cells;
                  for( icell = 0; icell < cell->tr->ncell; icell++ ) {
                     if( !((*(neb++))->interior) ) {
                        core = 0;
                        break;
                     }
                  }
               }

/* Otherwise, if the bottom right corner touches a cell that is not interior,
   then the current cell is not a core cell. */
               if( core ) {
                  neb = cell->br->cells;
                  for( icell = 0; icell < cell->br->ncell; icell++ ) {
                     if( !((*(neb++))->interior) ) {
                        core = 0;
                        break;
                     }
                  }
               }

/* If the current cell is core, check it is flagged as interior. */
               if( core ) {
                  if( !( cell->interior ) && astOK ) {
                     astError( AST__INTER, "astRegBaseMesh(%s): Core cell "
                               "not flagged as interior (internal programming "
                               "error).", status, astGetClass( this ) );
                     break;
                  }

/* If the current cell is not core, append its children to the end of the
   chain of cells at the next order. These child cells will be flagged as
   interior if the parent cell is interior. */
               } else {
                  AppendChildren( this, cell, order, cell_foot, status );
               }

/* Move on to the next cell. */
               cell = cell->prev;
            }

/* Delete all cells at the current order */
            cell = cell_foot[ order ];
            while( cell ) {
                prev_cell = cell->prev;
                (void) astFree( cell );
                cell = prev_cell;
            }

/* Delete all corners at the current order */
            corner = corner_foot;
            while( corner ) {
                prev_corner = corner->prev;
                (void) astFree( corner );
                corner = prev_corner;
            }

/* Ensure the Cell foot pointer is nullified for this order. */
            cell_foot[ order ] = NULL;
         }
      }

/* All cells are now at the maximum order. Create the chain of Corners
   for the cells at this order. Indicate that the chain should be sorted
   (primary key is Dec, secondary key is RA). */
      MakeCorners( this, maxorder, cell_foot[ maxorder ], &corner_foot,
                   1, status );

/* We now measure how far around the perimeter we need to go to reach
   each non-interior corner, starting from an arbitrary starting point.
   This information is needed by the astRegTrace method. The distance
   to each non-interior corner (in units of corners) is stored in the
   "dist" component of the Corner structure. These will have been
   initialised above to INT_MAX, indicating "no distance yet found".
   Initialise the distance from the start to zero and then loop
   until all non-interior corners have a distance. */
      dist = 0;
      while( astOK ) {

/* Find the first non-interior corner that has not yet been assigned a
   perimeter distance. */
         corner = corner_foot;
         while( corner && ( corner->interior || corner->dist != INT_MAX ) ) {
             corner = corner->prev;
         }

/* We are done if no such non-interior corner was found. */
         if( !corner ) break;

/* There could be several disjoint areas in the Moc, each having its own
   permimeter. The corner we have found above marks the start of such a
   perimeter. Loop until we arrive back at the starting corner. */
         old_corner = NULL;
         start = corner;
         while( astOK  ) {

/* Store the distance (as a number of corners) along the perimeter from the
   start to the current corner. If the corner already has a distance
   (i.e. because it is the touching point between two otherwise disjoint
   regions), leave the existing distance unchanged. */
            if( corner->dist == INT_MAX ) {
               corner->dist = dist++;

/* The Moc may contain several disjoint regions. We mark the start of each such
   region with a negative "dist" value. */
               if( !old_corner ) corner->dist = -corner->dist;
            }

/* Indicate we have not yet chosen the next corner on the path. */
            new_corner = NULL;

/* Since the current corner is not interior it can be used by at most 3
   cells. Save convenience pointers to them. These pointers are checked
   in the order "cell0", "cell1", "cell2" to see if an onward route can
   be found to a corner of the cell. If the corner is used by only one
   cell, it becomes "cell0". */
            if( corner->ncell == 1 ) {
               cell0 = corner->cells[ 0 ];
               cell1 = NULL;
               cell2 = NULL;

/* If the corner is used by two cells, the cell that contains the old
   corner as well as the current corner becomes "cell1", and the other
   cell becomes "cell0". This gives priority to onward routes that go to
   the new cell ("cell0") rather than going back to the old cell. */
            } else if( corner->ncell == 2 ) {
               cell1 = corner->cells[ 0 ];
               if( cell1->bl == old_corner ||
                   cell1->tl == old_corner ||
                   cell1->tr == old_corner ||
                   cell1->br == old_corner ) {
                  cell0 = corner->cells[ 1 ];
               } else {
                  cell0 = cell1;
                  cell1 = corner->cells[ 0 ];
               }
               cell2 = NULL;

/* If the corner is used by three cells, the cell that contains the old
   corner as well as the current corner becomes "cell2" (lowest priority),
   and the other two cells become "cell0" and "cell1". This gives priority
   to onward routes that do not go back to the old cell. */
            } else if( corner->ncell == 3 ) {
               for( icell = 0; icell < 3; icell++ ) {
                  cell2 = corner->cells[ icell ];
                  if( cell2->bl == old_corner ||
                      cell2->tl == old_corner ||
                      cell2->tr == old_corner ||
                      cell2->br == old_corner ) break;
               }
               if( icell == 4 && old_corner ) {
                  astError( AST__INTER, "astRegBaseMesh(%s): Old corner "
                            "not found (internal programming error).",
                            status, astGetClass( this ) );
                  break;
               }

/* Of the other two cells, prefer routes that continue to the cell that
   is diagonally opposite the cell containing the old corner. Assign the
   remaining two cells to "cell0" and "cell1", then swap them if cell1
   is diagonally opposite "cell2". */
               cell0 = corner->cells[ ( icell + 1 ) % 3 ];
               cell1 = corner->cells[ ( icell + 2 ) % 3 ];
               if( ( cell2->tr == corner && cell1->bl == corner ) ||
                   ( cell2->br == corner && cell1->tl == corner ) ||
                   ( cell2->bl == corner && cell1->tr == corner ) ||
                   ( cell2->tl == corner && cell1->br == corner ) ) {
                  cell = cell0;
                  cell0 = cell1;
                  cell1 = cell;
               }

/* The corner should never be used by more than 3 cells. */
            } else {
               astError( AST__INTER, "astRegBaseMesh(%s): Boundary corner "
                         "used by %d cells (internal programming error).",
                         status, astGetClass( this ), corner->ncell );
               break;
            }

/* Find the corner in cell0 and see if the neighbouring corner in
   clockwise direction within cell0 is a boundary point that has not yet
   been included in the path. If so, we use it as the next corner. */
            if( corner == cell0->bl && !cell0->tl->interior && cell0->tl->dist == INT_MAX ) {
               new_corner = cell0->tl;
            } else if( corner == cell0->tl && !cell0->tr->interior && cell0->tr->dist == INT_MAX ) {
               new_corner = cell0->tr;
            } else if( corner == cell0->tr && !cell0->br->interior && cell0->br->dist == INT_MAX ) {
               new_corner = cell0->br;
            } else if( corner == cell0->br && !cell0->bl->interior && cell0->bl->dist == INT_MAX ) {
               new_corner = cell0->bl;

/* If this failed to produce a new corner, then do the same using cell1
   (if it exists). */
            } else if( cell1 ) {
               if( corner == cell1->bl && !cell1->tl->interior && cell1->tl->dist == INT_MAX ) {
                  new_corner = cell1->tl;
               } else if( corner == cell1->tl && !cell1->tr->interior && cell1->tr->dist == INT_MAX ) {
                  new_corner = cell1->tr;
               } else if( corner == cell1->tr && !cell1->br->interior && cell1->br->dist == INT_MAX ) {
                  new_corner = cell1->br;
               } else if( corner == cell1->br && !cell1->bl->interior && cell1->bl->dist == INT_MAX ) {
                  new_corner = cell1->bl;

/* If this failed to produce a new corner, then do the same using cell3
   (if it exists). */
               } else if( cell2 ) {
                  if( corner == cell2->bl && !cell2->tl->interior && cell2->tl->dist == INT_MAX ) {
                     new_corner = cell2->tl;
                  } else if( corner == cell2->tl && !cell2->tr->interior && cell2->tr->dist == INT_MAX ) {
                     new_corner = cell2->tr;
                  } else if( corner == cell2->tr && !cell2->br->interior && cell2->br->dist == INT_MAX ) {
                     new_corner = cell2->br;
                  } else if( corner == cell2->br && !cell2->bl->interior && cell2->bl->dist == INT_MAX ) {
                     new_corner = cell2->bl;
                  }
               }
            }

/* The above may fail to find a suitable new corner if two disjoint
   regions touch at the new corner, since that corner will already
   have a distance assigned to it when it comes to be checked as part of
   tracing the second disjoint region. So try the whole thing again, but
   this time allowing the new corner to have a pre-assigned distance (i.e.
   to already have been used). This means we give priority to adjacent
   corners that have not already been used, but allow used corners to be
   re-used if necessary. */
            if( !new_corner ) {
               if( corner == cell0->bl && !cell0->tl->interior ) {
                  new_corner = cell0->tl;
               } else if( corner == cell0->tl && !cell0->tr->interior ) {
                  new_corner = cell0->tr;
               } else if( corner == cell0->tr && !cell0->br->interior ) {
                  new_corner = cell0->br;
               } else if( corner == cell0->br && !cell0->bl->interior ) {
                  new_corner = cell0->bl;

               } else if( cell1 ) {
                  if( corner == cell1->bl && !cell1->tl->interior ) {
                     new_corner = cell1->tl;
                  } else if( corner == cell1->tl && !cell1->tr->interior ) {
                     new_corner = cell1->tr;
                  } else if( corner == cell1->tr && !cell1->br->interior ) {
                     new_corner = cell1->br;
                  } else if( corner == cell1->br && !cell1->bl->interior ) {
                     new_corner = cell1->bl;

                  } else if( cell2 ) {
                     if( corner == cell2->bl && !cell2->tl->interior ) {
                        new_corner = cell2->tl;
                     } else if( corner == cell2->tl && !cell2->tr->interior ) {
                        new_corner = cell2->tr;
                     } else if( corner == cell2->tr && !cell2->br->interior ) {
                        new_corner = cell2->br;
                     } else if( corner == cell2->br && !cell2->bl->interior ) {
                        new_corner = cell2->bl;
                     }
                  }
               }

/* Count the number of consecutive corners that have already been used.
   Abort if the last five corners were all re-used, since we have
   probably got into a loop. */
               if( new_corner && ++nused == 5 && astOK ) {
                  astError( AST__INTER, "astRegBaseMesh(%s): Re-drawing "
                            "previously drawn corners (internal "
                            "programming error).", status, astGetClass( this ) );
               }
            } else {
               nused = 0;
            }

/* If we have arrived back at the first corner, break out of the loop. */
            if( new_corner == start ) break;

/* Sanity check. Check the next corner selected above is OK. */
            if( ( !new_corner || new_corner->interior ) && astOK ) {
               if( !new_corner ) {
                  astError( AST__INTER, "astRegBaseMesh(%s): Next perimeter "
                            "corner is undefined (internal programming error).",
                            status, astGetClass( this ) );
               } else {
                  astError( AST__INTER, "astRegBaseMesh(%s): Next perimeter "
                            "corner is interior (internal programming error).",
                            status, astGetClass( this ) );
               }
            }

/* Record the previous corner, and move on to the next corner. */
            old_corner = corner;
            corner = new_corner;
         }
      }

/* Create the returned PointSet and put the (ra,dec) values into it
   from each non-interior corner. First count the number of non-interior
   corners. */
      npoint = 0;
      corner = corner_foot;
      while( corner ) {
          if( !(corner->interior) ) npoint++;
          corner = corner->prev;
      }

/* Allocate an array to hold the indices within the returned PointSet in
   order of increasing perimeter distance. */
      this->meshdist = astMalloc( npoint*sizeof( *(this->meshdist ) ) );

/* Create the PointSet, and get pointers to its data arrays. */
      result = astPointSet( npoint, 2, " ", status );
      ptr = astGetPoints( result );
      if( astOK ) {
         pra = ptr[ 0 ];
         pdec = ptr[ 1 ];

/* Store the RA and Dec values at all non-interior corners, then delete
   the corners. Also invert the "distance" values stored in the Corner
   structures to get an array that indexes the mesh in order of distance
   around the perimeter. Negative values in this array indicate breaks
   in the perimeter between separate disjoint regions. */
         icorner = 0;
         corner = corner_foot;
         while( corner ) {
             if( !corner->interior ) {
                *(pra++) = corner->ra;
                *(pdec++) = corner->dec;

                if( corner->dist >= 0 ) {
                   (this->meshdist)[ corner->dist ] = icorner++;
                } else {
                   (this->meshdist)[ -corner->dist ] = -(icorner++);
                }

             }
             prev_corner = corner->prev;
             (void) astFree( corner );
             corner = prev_corner;
         }

/* Store it in the parent Region structure for future use. */
         this->basemesh = astClone( result );
      }

/* Free the remaining cells. */
      cell = cell_foot[ maxorder ];
      while( cell ) {
          prev_cell = cell->prev;
          (void) astFree( cell );
          cell = prev_cell;
      }
   }

/* Annul the result if an error has occurred. */
   if( !astOK ) result = astAnnul( result );

/* Return a pointer to the output PointSet. */
   return result;
}

static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc,
                    int **mask, int *status ){
/*
*  Name:
*     RegPins

*  Purpose:
*     Check if a set of points fall on the boundary of a given Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc,
*                  int **mask, int *status ){

*  Class Membership:
*     Moc member function (over-rides the astRegPins protected
*     method inherited from the Region class).

*  Description:
*     This function returns a flag indicating if the supplied set of
*     points all fall on the boundary of the given Moc.
*
*     Some tolerance is allowed, as specified by the resolution of the Moc,
*     and the supplied uncertainty Region "unc" which describes the
*     uncertainty of the supplied points.

*  Parameters:
*     this
*        Pointer to the Moc.
*     pset
*        Pointer to the PointSet. The points are assumed to refer to the
*        base Frame of the FrameSet encapsulated by "this" (always ICRS
*        for a Moc)
*     unc
*        Pointer to a Region representing the uncertainties in the points
*        given by "pset". The Region is assumed to represent the base Frame
*        of the FrameSet encapsulated by "this". Zero uncertainity is assumed
*        if NULL is supplied.
*     mask
*        Pointer to location at which to return a pointer to a newly
*        allocated dynamic array of ints. The number of elements in this
*        array is equal to the value of the Npoint attribute of "pset".
*        Each element in the returned array is set to 1 if the
*        corresponding position in "pset" is on the boundary of the Region
*        and is set to zero otherwise. A NULL value may be supplied
*        in which case no array is created. If created, the array should
*        be freed using astFree when no longer needed.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     Non-zero if the points all fall on the boundary of the given
*     Region, to within the tolerance specified. Zero otherwise.

*/

/* Local variables: */
   AstMoc *this;
   AstPointSet *mesh;
   astDECLARE_GLOBALS
   double **ptr_mesh;
   double **ptr_pins;
   double *pdec_mesh2;
   double *pdec_mesh;
   double *pra_mesh;
   double *pra_mesh2;
   double *safe;
   double cosdec;
   double dec_high;
   double dec_low;
   double dec_pin;
   double dra;
   double drad;
   double l1;
   double l2;
   double lbnd_unc[ 2 ];
   double ra_high;
   double ra_low;
   double ra_pin;
   double ubnd_unc[ 2 ];
   int *index;
   int *pi;
   int *pm;
   int imesh2;
   int imesh;
   int ipin;
   int len_mesh;
   int len_pins;
   int on;
   int result;

/* Initialise */
   result = 0;
   if( mask ) *mask = NULL;

/* Check the inherited status. */
   if( !astOK ) return result;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(this_region);

/* Get a pointer to the Moc structure. */
   this = (AstMoc *) this_region;

/* Check the supplied PointSet has the 2 axis values per point. */
   if( astGetNcoord( pset ) != 2 && astOK ) {
      astError( AST__INTER, "astRegPins(%s): Illegal number of axis "
                "values per point (%d) in the supplied PointSet - should be "
                "2 (internal AST programming error).", status, astGetClass( this ),
                astGetNcoord( pset ) );
   }

/* Get the number of axes in the uncertainty Region and check it is the
   same as above. */
   if( unc && astGetNaxes( unc ) != 2 && astOK ) {
      astError( AST__INTER, "astRegPins(%s): Illegal number of axes "
                "(%d) in the supplied uncertainty Region - should be 2 "
                "(internal AST programming error).", status,
                astGetClass( this ), astGetNaxes( unc ) );
   }

/* Get the centre of the region in the base Frame. We use this as a "safe"
   interior point within the region. */
   safe = astRegCentre( this, NULL, NULL, 0, AST__BASE );

/* We now find the maximum distance on each axis that a point can be from the
   boundary of the Moc for it still to be considered to be on the boundary.
   First get the resolution of the Moc, in radians. */
   l1 = AST__DD2R*OrderToRes( astGetMaxOrder( this ) )/3600.0;

/* Get the Region which defines the uncertainty of the supplied points and
   get its bounding box. First re-centre the uncertainty at the interior
   position to avoid problems from uncertainties that straddle a discontinuity. */
   if( unc ) {
      if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT );
      astGetRegionBounds( unc, lbnd_unc, ubnd_unc );

/* Find the geodesic length of the diagonal of this bounding box. */
      l2 = astDistance( unc, lbnd_unc, ubnd_unc );

/* Use a zero sized box "unc" if no box was supplied. */
   } else {
      l2 = 0.0;
   }

/* The required border width is the total diagonal of the two bounding
   boxes. */
   drad = l1 + l2;

/* Get a mesh of points on the boundary of the Moc. The mesh is sorted into
   increasing Dec (values with equal Dec are sorted into increasing RA).
   All RA values are in the range [0,2PI[. */
   mesh = astRegBaseMesh( this );
   ptr_mesh = astGetPoints( mesh );
   len_mesh = astGetNpoint( mesh );

/* Get pointers to the (ra,dec) values at all pis, and the number of
   pins. */
   ptr_pins = astGetPoints( pset );
   len_pins = astGetNpoint( pset );

/* If a mask is required, allocate it now, initialising it to all zeros. */
   if( mask ) *mask = astCalloc( len_pins, sizeof(**mask) );

/* Sort the supplied PointSet in the same way. Use an index to avoid
   modifying the supplied PointSet. */
   index = astMalloc( len_pins*sizeof( *index ) );
   if( astOK ) {
      Comp_Decra_Ptr1 = ptr_pins[ 0 ];
      Comp_Decra_Ptr2 = ptr_pins[ 1 ];
      pi = index;
      for( ipin = 0; ipin < len_pins; ipin++ ) *(pi++) = ipin;
      qsort( index, len_pins, sizeof(*index), Comp_decra );

/* Initialise pointers to the first point in the mesh, and its index. */
      pra_mesh = ptr_mesh[ 0 ];
      pdec_mesh = ptr_mesh[ 1 ];
      imesh = 0;

/* For the moment, assume all pins are on the boundary. */
      result = 1;

/* Now loop through the sorted pins. */
      pm = mask ? *mask : NULL;
      pi = index;
      for( ipin = 0; ipin < len_pins; ipin++,pi++,pm++ ) {
         ra_pin = ptr_pins[ 0 ][ *pi ];
         dec_pin = ptr_pins[ 1 ][ *pi ];

/* Get the bounds of the box in which a mesh point must fall for the pin
   to be on the boundary. */
         cosdec= cos( dec_pin );
         if( cosdec > 0.0 ) {
            dra = drad/cos( dec_pin );
            ra_low = ra_pin - dra;
            ra_high = ra_pin + dra;
         } else {
            ra_low = -DBL_MAX;
            ra_high = DBL_MAX;
         }
         dec_low = dec_pin - drad;
         dec_high = dec_pin + drad;

/* Move forward through the mesh until a mesh point is found that has a
   Dec value larger than or equal to the lower Dec limit for the current
   pin. */
         for( ; imesh < len_mesh; imesh++,pra_mesh++,pdec_mesh++ ) {
            if( *pdec_mesh >= dec_low ) break;
         }

/* If no such mesh point is found, the current pin and all subsequent
   pins in the sorted list do not fall on the boundary. So we can break
   out of the pin loop. */
         if( imesh == len_mesh ) {
            result = 0;
            break;
         }

/* Move forward through the mesh until a mesh point is found that has a
   RA value within the RA limits for the current pin, or the upper Dec
   limit is exceeded. Modify each mesh RA value so that it is within
   [-PI,+PI[ of the pin's RA before checking if it is within the limits.
   If such a mesh point is found, the pin is on the boundary. */
         pra_mesh2 = pra_mesh;
         pdec_mesh2 = pdec_mesh;
         imesh2 = imesh;
         on = 0;
         for( ; imesh2 < len_mesh; imesh2++,pra_mesh2++,pdec_mesh2++ ) {
            if( *pdec_mesh2 > dec_high ) break;

            dra = *pra_mesh2 - ra_pin;
            while( dra >= AST__DPI ) dra -= 2*AST__DPI;
            while( dra < -AST__DPI ) dra += 2*AST__DPI;
            dra += ra_pin;

            if( dra >= ra_low && dra <= ra_high ) {
               on = 1;
               break;
            }
         }

/* If a suitable mesh point was found and a mask is being created, flag
   that the pin is on the boundary and continue to check further pins. */
         if( on ) {
            if( mask ) *pm = 1;

/* If no suitable mesh point was found, the current pin does not fall on
   the boundary but later ones may do. We can break out of the pin loop
   unless a mask is being created, in which case we need to continue to
   check later pins. */
         } else {
            result = 0;
            if( !mask ) break;
         }
      }
   }

/* Free resources */
   mesh = astAnnul( mesh );
   index = astFree( index );
   safe = astFree( safe );

/* If an error has occurred, return zero. */
   if( !astOK ) {
      result = 0;
      if( mask ) *mask = astFree( *mask );
   }

/* Return the result. */
   return result;
}

static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr,
                     int *status ){
/*
*  Name:
*     RegTrace

*  Purpose:
*     Return requested positions on the boundary of a 2D Region.

*  Type:
*     Private function.

*  Synopsis:
*     #include "region.h"
*     int astRegTrace( AstRegion *this, int n, double *dist, double **ptr );

*  Class Membership:
*     Moc member function (overrides the astRegTrace method inherited from
*     the parent Region class).

*  Description:
*     This function returns positions on the boundary of the supplied
*     Region, if possible. The required positions are indicated by a
*     supplied list of scalar parameter values in the range zero to one.
*     Zero corresponds to some arbitrary starting point on the boundary,
*     and one corresponds to the end (which for a closed region will be
*     the same place as the start).

*  Parameters:
*     this
*        Pointer to the Region.
*     n
*        The number of positions to return. If this is zero, the function
*        returns without action (but the returned function value still
*        indicates if the method is supported or not).
*     dist
*        Pointer to an array of "n" scalar parameter values in the range
*        0 to 1.0.
*     ptr
*        A pointer to an array of pointers. The number of elements in
*        this array should equal tthe number of axes in the Frame spanned
*        by the Region. Each element of the array should be a pointer to
*        an array of "n" doubles, in which to return the "n" values for
*        the corresponding axis. The contents of the arrays are unchanged
*        if the supplied Region belongs to a class that does not
*        implement this method.

*  Returned Value:
*     Non-zero if the astRegTrace method is implemented by the class
*     of Region supplied, and zero if not.

*/

/* Local Variables; */
   AstMoc *this;
   AstPointSet *mesh;
   double **ptr_mesh;
   double whi;
   double wlo;
   double x;
   int i;
   int imesh;
   int imesh_lo;
   int imesh_hi;
   int jhi;
   int jlo;
   int len_mesh;

/* Check inherited status. */
   if( ! astOK ) return 0;

/* Get a pointer to the Moc structure. */
   this = (AstMoc *) this_region;

/* Check we have some points to find. */
   if( n > 0 ) {

/* Get a mesh of points on the boundary of the Moc. The mesh is sorted into
   increasing Dec (values with equal Dec are sorted into increasing RA).
   All RA values are in the range [0,2PI[. */
      mesh = astRegBaseMesh( this );
      ptr_mesh = astGetPoints( mesh );
      len_mesh = astGetNpoint( mesh );
      if( astOK ) {

/* Another array is created at the same time as the above mesh, which
   holds indices into the mesh array. These index values are sorted
   so that the corresponding mesh positions move monotonically around
   the perimeter of the MOC. We use this array to find the required
   positions. Do each required distance in turn. */
         for( i = 0; i < n; i++ ) {

/* Scale the supplied distance (in the range 0 -> 1) into the range 0
   -> len_mesh-1. Do linear interpolation between adjacent points. A Moc
   may contain several disjoint regions. If so, the first point in the
   second and each subsequent region is indicated by a negative value in
   the "meshdist" array. Return bad values for positions between the
   start of each such regioon and the end of the previous. This tells the
   curve plotting algorithm in the Plot class to introduce a break, rather
   than connecting the two regions with a straight line. The
   "this->meshdist" converts distance values into indices into the mesh. */
            x = dist[ i ]*( len_mesh - 1 );
            jlo = (int)( x );
            jhi = jlo + 1;
            whi = x - jlo;
            wlo = 1.0 - whi;

            if( jlo < 0 ) {
               imesh =  (this->meshdist)[ 0 ];
               ptr[ 0 ][ i ] = ptr_mesh[ 0 ][ imesh ];
               ptr[ 1 ][ i ] = ptr_mesh[ 1 ][ imesh ];

            } else if( jhi >= len_mesh ) {
               imesh =  (this->meshdist)[ len_mesh - 1 ];
               ptr[ 0 ][ i ] = ptr_mesh[ 0 ][ imesh ];
               ptr[ 1 ][ i ] = ptr_mesh[ 1 ][ imesh ];

            } else {
               imesh_hi = (this->meshdist)[ jhi ];
               if( imesh_hi < 0 ) {
                  ptr[ 0 ][ i ] = AST__BAD;
                  ptr[ 1 ][ i ] = AST__BAD;
               } else {
                  imesh_lo = (this->meshdist)[ jlo ];
                  if( imesh_lo < 0 ) imesh_lo = -imesh_lo;

                  ptr[ 0 ][ i ] = wlo*ptr_mesh[ 0 ][ imesh_lo ] +
                                  whi*ptr_mesh[ 0 ][ imesh_hi ];
                  ptr[ 1 ][ i ] = wlo*ptr_mesh[ 1 ][ imesh_lo ] +
                                  whi*ptr_mesh[ 1 ][ imesh_hi ];
               }
            }
         }
      }

/* Free resources. */
      mesh = astAnnul( mesh );
   }

/* Return the result. */
   return 1;
}

static int ResToOrder( double res ){
/*
*  Name:
*     ResToOrder

*  Purpose:
*     Get the HEALPix order for a given resolution.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int ResToOrder( double res )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the HEALPix order that gives a resolution
*     closest to the supplied resolution.

*  Parameters:
*     res
*        The resolution, in arc-seconds.

*  Returned Value:
*     The HEALPix order.
*/

/* Check the supplied value. */
   if ( res <= 0.0 ) return 0;

/* The inverse of OrderToRes. */
   return (int)( 0.5 + log( 211076.29/res )/0.6931472 );
}


static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
/*
*  Name:
*     SetAttrib

*  Purpose:
*     Set an attribute value for a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void SetAttrib( AstObject *this, const char *setting, int *status )

*  Class Membership:
*     Moc member function (extends the astSetAttrib method inherited from
*     the Region class).

*  Description:
*     This function assigns an attribute value for a Moc, the attribute
*     and its value being specified by means of a string of the form:
*
*        "attribute= value "
*
*     Here, "attribute" specifies the attribute name and should be in lower
*     case with no white space present. The value to the right of the "="
*     should be a suitable textual representation of the value to be assigned
*     and this will be interpreted according to the attribute's data type.
*     White space surrounding the value is only significant for string
*     attributes.

*  Parameters:
*     this
*        Pointer to the Moc.
*     setting
*        Pointer to a null terminated string specifying the new attribute
*        value.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     void
*/

/* Local Vaiables: */
   AstMoc *this;                 /* Pointer to the Moc structure */
   double dval;                  /* Floating point attribute value */
   int ival;                     /* Integer attribute value */
   int len;                      /* Length of setting string */
   int nc;                       /* Number of characters read by astSscanf */

/* Check the global error status. */
   if ( !astOK ) return;

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) this_object;

/* Obtain the length of the setting string. */
   len = strlen( setting );

/* Test for each recognised attribute in turn, using "astSscanf" to parse the
   setting string and extract the attribute value (or an offset to it in the
   case of string values). In each case, use the value set in "nc" to check
   that the entire string was matched. Once a value has been obtained, use the
   appropriate method to set it. */

/* MaxOrder. */
/* --------- */
   if ( nc = 0,
               ( 1 == astSscanf( setting, "maxorder= %d %n", &ival, &nc ) )
               && ( nc >= len ) ) {
      if( astTestMaxOrder( this ) ) {
         astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.",
                   status, setting, astGetClass( this ) );
         astError( AST__NOWRT, "The previously set \"MaxOrder\" value cannot "
                   "be changed." , status );
      } else {
         astSetMaxOrder( this, ival );
      }

/* MaxRes. */
/* ------- */
   } else if ( nc = 0,
               ( 1 == astSscanf( setting, "maxres= %lg %n", &dval, &nc ) )
               && ( nc >= len ) ) {
      if( astTestMaxOrder( this ) ) {
         astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.",
                   status, setting, astGetClass( this ) );
         astError( AST__NOWRT, "The previously set \"MaxRes\" value cannot "
                   "be changed." , status );
      } else {
         ival = ResToOrder( dval );
         astSetMaxOrder( this, ival );
      }

/* MinOrder. */
/* --------- */
   } else if ( nc = 0,
               ( 1 == astSscanf( setting, "minorder= %d %n", &ival, &nc ) )
               && ( nc >= len ) ) {
      astSetMinOrder( this, ival );

/* MinRes. */
/* ------- */
   } else if ( nc = 0,
               ( 1 == astSscanf( setting, "minres= %lg %n", &dval, &nc ) )
               && ( nc >= len ) ) {
      ival = ResToOrder( dval );
      astSetMinOrder( this, ival );

/* Define a macro to see if the setting string matches any of the
   read-only attributes of this class. */
#define MATCH(attrib) \
        ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \
                  ( nc >= len ) )

/* If the attribute was not recognised, use this macro to report an error
   if a read-only attribute has been specified. */
   } else if ( MATCH( "moctype" ) ||
               MATCH( "moclength" ) ||
               MATCH( "mocarea" ) ){
      astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status,
                setting, astGetClass( this ) );
      astError( AST__NOWRT, "This is a read-only attribute." , status);

/* If the attribute is still not recognised, pass it on to the parent
   method for further interpretation. */
   } else {
      (*parent_setattrib)( this_object, setting, status );
   }

/* Undefine macros local to this function. */
#undef MATCH
}

static void SetMaxOrder( AstMoc *this, int value, int *status ){
/*
*
*  Name:
*     SetMaxOrder

*  Purpose:
*     Sety the value for the MaxOrder attribute

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void SetMaxOrder( AstMoc *this, int value, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     Sets the value of the MaxPlot attribute.

*  Parameters:
*     this
*        The Moc.
*     value
*        The new attribute value.
*     status
*        Pointer to the inherited status variable.

*/

/* Check the global error status. */
   if ( !astOK ) return;

/* Store the supplied value. */
   this->maxorder = astMIN(astMAX(value,0),AST__MXORDHPX);

/* Clear the cached information stored in the Moc structure so that it is
   re-calculated when next needed. */
   ClearCache( this, status );
}

static void Sink1( void *data, size_t nc, const char *buf, int *status ){
/*
*  Name:
*     Sink1

*  Purpose:
*     A sink function for use with astGetMocText

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void Sink1( void *data, size_t nc, const char *buf, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function accumulates the length of the strings written out by
*     astGetMocText, but does not actually write the strings out anywhere.

*  Parameters:
*     data
*        Pointer to an arbitrary structure used to communicate with the
*        code that is calling astGetMocText.
*     nc
*        The number of character to be written out from "buf".
*     buf
*        A buffer holding the characters to be written out.
*     status
*        Pointer to the inherited status variable.

*/
   if( data ) *((size_t *) data) += nc;
}

static void Sink2( void *data, size_t nc, const char *buf, int *status ){
/*
*  Name:
*     Sink2

*  Purpose:
*     A sink function for use with astGetMocText

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void Sink2( void *data, size_t nc, const char *buf, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function appends the supplied strings to the string pointed to
*     by the supplied data structure, reporting an error if the string is
*     too small.

*  Parameters:
*     data
*        Pointer to an arbitrary structure used to communicate with the
*        code that is calling astGetMocText.
*     nc
*        The number of character to be written out from "buf".
*     buf
*        A buffer holding the characters to be written out.
*     status
*        Pointer to the inherited status variable.

*/
   if( !astOK ) return;

   SinkData *data2 = (SinkData *) data;

   if( nc > data2->mxsize ) {
      astError( AST__SMBUF, "astGetMocString(Moc): The supplied string "
                "buffer is too small.", status );
   } else {
      memcpy( data2->string, buf, nc );
      data2->string += nc;
      data2->mxsize -= nc;
   }
}

static const char *Source1( void *data, size_t *nc, int *status ){
/*
*  Name:
*     Source1

*  Purpose:
*     A source function for use with astAddMocText

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     const char *Source1( void *data, size_t *nc, int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns a pointer to the next set of characters to be
*     added into the Moc by astAddMocText, together with the number of
*     characters in the set.

*  Parameters:
*     data
*        Pointer to an arbitrary structure used to communicate with the
*        code that is calling astAddMocText.
*     nc
*        Returned holding the number of character to be added into the Moc.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     A pointer to the characters to be added into the Moc, or NULL if no
*     more characters remain to be added or an error has occurred.

*/

   SourceData *data2 = (SourceData *) data;
   const char *result = NULL;
   *nc = 0;

   if( !astOK || ! data ) return result;

   result = data2->string;
   *nc = data2->mxsize;

   data2->string = NULL;
   data2->mxsize = 0;

   return result;
}

static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
*  Name:
*     TestAttrib

*  Purpose:
*     Test if a specified attribute value is set for a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int TestAttrib( AstObject *this, const char *attrib, int *status )

*  Class Membership:
*     Moc member function (over-rides the astTestAttrib protected
*     method inherited from the Region class).

*  Description:
*     This function returns a boolean result (0 or 1) to indicate whether
*     a value has been set for one of a Moc's attributes.

*  Parameters:
*     this
*        Pointer to the Moc.
*     attrib
*        Pointer to a null terminated string specifying the attribute
*        name.  This should be in lower case with no surrounding white
*        space.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     One if a value has been set, otherwise zero.

*  Notes:
*     - A value of zero will be returned if this function is invoked
*     with the global status set, or if it should fail for any reason.
*/

/* Local Variables: */
   AstMoc *this;             /* Pointer to the Moc structure */
   int result;                   /* Result value to return */

/* Initialise. */
   result = 0;

/* Check the global error status. */
   if ( !astOK ) return result;

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) this_object;

/* Check the attribute name and test the appropriate attribute. */

/* MaxOrder. */
/* --------- */
   if ( !strcmp( attrib, "maxorder" ) ) {
      result = astTestMaxOrder( this );

/* MaxRes. */
/* ------- */
   } else if ( !strcmp( attrib, "maxres" ) ) {
      result = astTestMaxOrder( this );

/* MinOrder. */
/* --------- */
   } else if ( !strcmp( attrib, "minorder" ) ) {
      result = astTestMinOrder( this );

/* MinRes. */
/* ------- */
   } else if ( !strcmp( attrib, "minres" ) ) {
      result = astTestMinOrder( this );

/* If the name is not recognised, test if it matches any of the
   read-only attributes of this class. If it does, then return
   zero. */
   } else if ( !strcmp( attrib, "moctype" ) ||
               !strcmp( attrib, "moclength" ) ||
               !strcmp( attrib, "mocarea" ) ) {
      result = 0;

/* If the attribute is still not recognised, pass it on to the parent
   method for further interpretation. */
   } else {
      result = (*parent_testattrib)( this_object, attrib, status );
   }

/* Return the result, */
   return result;
}

static int TestCell( AstMoc *this, int order, int64_t npix, int parent,
                     int *status ) {
/*
*++
*  Name:
c     astTestCell
f     AST_TESTCELL

*  Purpose:
*     Tests if a single HEALPix cell is included in a Moc.

*  Type:
*     Public virtual function.

*  Synopsis:
c     #include "moc.h"
c     void astTestCell( AstMoc *this, int order, int64_t npix, int parent )
f     RESULT = AST_TESTCELL( THIS, ORDER, NPIX, PARENT, STATUS )

*  Class Membership:
*     Moc method.

*  Description:
*     This function returns
c     a non-zero value
f     .TRUE.
*     if the Moc includes the specified cell.

*  Parameters:
c     this
f     THIS = INTEGER (Given)
*        Pointer to the Moc to be modified.
c     order
f     ORDER = INTEGER (Given)
*        The HEALPix order of the cell to test.
c        Zero
f        .FALSE.
*        is returned if this is higher than the maximum order allowed in
*        the Moc (as given by its MaxOrder attribute).
c     npix
f     NPIX = INTEGER*8 (Given)
*        The "npix" value identifying the cell to test (see the MOC
*        recommendation for more details).
c     parent
f     PARENT = LOGICAL (Given)
*        Indicates the value to return if the tested cell is not included
*        at the specified order, but a parent cell (at a lower order) is
*        included.
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*  Returned Value:
c     astTestCell()
c        One if the specified cell is included in the Moc at the
c        specified order, of (if "parent" is non-zero) a parent cell is
c        included in the Moc. Zero otherwise.
f     AST_TESTCELL = LOGICAL
f        .TRUE. if the specified cell is included in the Moc at the
f        specified order, of (if PARENT is non-zero) a parent cell is
f        included in the Moc. .FALSE. otherwise.

*--
*/

/* Local Variables: */
   int irange;
   int maxorder;
   int shift;
   int64_t *pr;
   int64_t ihigh;
   int64_t ilow;
   int result;

/* Initialise */
   result = 0;

/* Check the global error status. */
   if ( !astOK ) return 0;

/* Validate */
   if( order < 0 || order > AST__MXORDHPX ) {
      astError( AST__INVAR, "astTestCell(%s): Invalid value (%d) "
                "supplied for parameter 'order' - must be no greater "
                "than %d.", status, astGetClass( this ), order,
                AST__MXORDHPX );

   } else if( npix < 0 || npix > MaxNpix( order ) ) {
      astError( AST__INVAR, "astTestCell(%s): Invalid value (%zu) "
                "supplied for parameter 'npix' - must be greater "
                "than 0 and less than %zu.", status, astGetClass( this ),
                npix, MaxNpix( order ) + 1 );
   }

/* If the specified order is greater than the MaxOrder attribute, return
   zero. */
   maxorder = astGetMaxOrder( this );
   if( order <= maxorder && astOK ) {

/* Get the upper and lower bounds of the range of cells at MaxOrder
   that corresponds to the specified cell. */
      shift = 2*( maxorder - order );
      ilow = ( npix << shift );
      ihigh = ( (npix + 1 ) << shift ) - 1;

/* See if this range of cells is included in the Moc. */
      pr = this->range;
      for( irange = 0; irange < this->nrange; irange++, pr += 2 ) {
         if( pr[ 0 ] <= ilow && pr[ 1 ] >= ihigh ) {
            result = 1;
            break;
         }
      }

/* If it is included, it may be that the entire parent cell is included.
   We should return non-zero for this only if "parent" is non-zero. So
   if parent is zero, check that the entire parent is NOT included in
   the MOC. */
      if( result && !parent ) {

/* Get the npix value for the immediate parent cell at order (order-1). */
         npix = ( npix >> 2 );

/* Get the upper and lower bounds of the range of cells at MaxOrder
   that corresponds to the immediate parent cell. */
         shift += 2;
         ilow = ( npix << shift );
         ihigh = ( (npix + 1 ) << shift ) - 1;

/* See if this range of cells is included in the Moc. If so return zero. */
         pr = this->range;
         for( irange = 0; irange < this->nrange; irange++, pr += 2 ) {
            if( pr[ 0 ] <= ilow && pr[ 1 ] >= ihigh ) {
               result = 0;
               break;
            }
         }
      }
   }

/* Return the result. */
   return result;
}



#define MAKE_TESTOP(test) \
\
/* First handle cases where a bad value is defined. */ \
   if( bad ) { \
\
/* Loop round all positions to be tested. */ \
      for( ipos = 0; ipos < *npos; ipos++,px++,py++) { \
         inside[ ipos ] = 0; \
\
/* Get the 1-based grid indices of the pixel containing the position. */ \
         if( *px != AST__BAD && *py != AST__BAD ) { \
            ix = (int)( *px + 0.5 ); \
            iy = (int)( *py + 0.5 ); \
\
/* Check that the position is within the array. */ \
            if( ix > 0 && ix <= nx && \
                iy > 0 && iy <= ny ){ \
\
/* Get the vector index of the required array element. */ \
               ii = ( ix - 1 ) + ( iy - 1 )*nx; \
\
/* If it passes the selection test, set the returned flag to zero. */ \
               inside[ ipos ] = ( p[ii] != *bad && ( test) ); \
            } \
         } \
      } \
\
/* Now handle cases where no bad value is defined. */ \
   } else { \
\
/* Loop round all positions to be tested. */ \
      for( ipos = 0; ipos < *npos; ipos++,px++,py++ ) { \
         inside[ ipos ] = 0; \
\
/* Get the 1-based grid indices of the pixel containing the position. */ \
         if( *px != AST__BAD && *py != AST__BAD ) { \
            ix = (int)( *px + 0.5 ); \
            iy = (int)( *py + 0.5 ); \
\
/* Check that the position is within the array. */ \
            if( ix > 0 && ix <= nx && \
                iy > 0 && iy <= ny ){ \
\
/* Get the vector index of the required array element. */ \
               ii = ( ix - 1 ) + ( iy - 1 )*nx; \
\
/* If it passes the selection test, set the returned flag to zero. */ \
               inside[ ipos ] = ( test ); \
            } \
         } \
      } \
   }

#define MAKE_TESTTYPE(Xtype) \
\
   const Xtype *p = (Xtype *) pixelmask->data; \
   Xtype *v = (Xtype *) pixelmask->value; \
   Xtype *bad = (Xtype *) pixelmask->bad; \
 \
/* Do each type of test. */ \
   if( pixelmask->oper == AST__LT ) {  \
      MAKE_TESTOP( p[ii] < *v ); \
   } else if( pixelmask->oper == AST__LE ) { \
      MAKE_TESTOP( p[ii] <= *v ); \
   } else if( pixelmask->oper == AST__EQ ) { \
      MAKE_TESTOP( p[ii] == *v ); \
   } else if( pixelmask->oper == AST__NE ) { \
      MAKE_TESTOP( p[ii] != *v ); \
   } else if( pixelmask->oper == AST__GE ) { \
      MAKE_TESTOP( p[ii] >= *v ); \
   } else  if( pixelmask->oper == AST__GT ) { \
      MAKE_TESTOP( p[ii] > *v ); \
   }


static void TestPixels( PixelMask *pixelmask, int *npos, AstPointSet *ps,
                        int inside[ 9 ], int *status ){
/*
*  Name:
*     TestPixels

*  Purpose:
*     Test if a set of positions are inside a pixel array.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     void TestPixels( PixelMask *pixelmask, int *npos, AstPointSet *ps,
*                      int inside[ 9 ], int *status )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns flags indicating which of the supplied positions
*     correspond to selected pixels in the supplied pixel array.

*  Parameters:
*     pixelmask
*        Structure defining the pixel mask.
*     npos
*        Supplied holding the number of positions in the supplied PointSet.
*        Returned holding the number of usable values in the "inside"
*        array. The numer of positions, together with their order, is
*        defined by the "PutCell" function.
*     ps
*        PointSet holding the 2D positions to be tested, in grid coords.
*     inside
*        Pointer to an array of 9 ints that are returned non-zero if
*        the corresponding position is inside the region of interest.
*        They are stored in raster order from bottom left to top right.
*     status
*        Pointer to the inherited status variable.

*/

/* Local Variables: */
   double **ptr;
   double *px;
   double *py;
   int ipos;
   int ix;
   int iy;
   int nx;
   int ny;
   size_t ii;

/* Check the global error status. */
   if ( !astOK ) return;

/* Get pointers to the arrays of X and Y grid coordinate values. */
   ptr = astGetPoints( ps );
   if( astOK ) {
      px = ptr[ 0 ];
      py = ptr[ 1 ];

/* Store convenience values */
      nx = pixelmask->nx;
      ny = pixelmask->ny;

/* Do each data type in turn. This stores "*npos" flags, corresponding to
   the "*npos" positions in "ps", at the start of the "inside" array. */
      if( pixelmask->type == DOUBLE ){
         MAKE_TESTTYPE(double)
      } else if( pixelmask->type == LONG_INT ){
         MAKE_TESTTYPE(long int)
      } else if( pixelmask->type == UNSIGNED_LONG_INT ){
         MAKE_TESTTYPE(unsigned long int)
      } else if( pixelmask->type == INT ){
         MAKE_TESTTYPE(int)
      } else if( pixelmask->type == UNSIGNED_INT ){
         MAKE_TESTTYPE(unsigned int)
      } else if( pixelmask->type == SHORT_INT ){
         MAKE_TESTTYPE(short int)
      } else if( pixelmask->type == UNSIGNED_SHORT_INT ){
         MAKE_TESTTYPE(unsigned short int)
      } else if( pixelmask->type == SIGNED_CHAR ){
         MAKE_TESTTYPE(signed char)
      } else if( pixelmask->type == UNSIGNED_CHAR ){
         MAKE_TESTTYPE(unsigned char)
      } else if( pixelmask->type == FLOAT ){
         MAKE_TESTTYPE(float)

#if HAVE_LONG_DOUBLE     /* Not normally implemented */
      } else if( pixelmask->type == LONG_DOUBLE ){
         MAKE_TESTTYPE(long double)
#endif

      } else {
        astError( AST__INTER, "TestPixel(Moc): Unsupported data type %d "
                  "(internal programming error).", status, pixelmask->type );
      }

   }
}

#undef MAKE_TESTOP
#undef MAKE_TESTTYPE

static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in,
                               int forward, AstPointSet *out, int *status ) {
/*
*  Name:
*     Transform

*  Purpose:
*     Apply a Moc to transform a set of points.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     AstPointSet *Transform( AstMapping *this, AstPointSet *in,
*                             int forward, AstPointSet *out, int *status )

*  Class Membership:
*     Moc member function (over-rides the astTransform protected
*     method inherited from the Region class).

*  Description:
*     This function takes a Moc and a set of points encapsulated in a
*     PointSet and transforms the points by setting axis values to
*     AST__BAD for all points which are outside the Moc. Points inside
*     the Moc are copied unchanged from input to output.

*  Parameters:
*     this
*        Pointer to the Moc.
*     in
*        Pointer to the PointSet holding the input coordinate data.
*     forward
*        A non-zero value indicates that the forward coordinate transformation
*        should be applied, while a zero value requests the inverse
*        transformation.
*     out
*        Pointer to a PointSet which will hold the transformed (output)
*        coordinate values. A NULL value may also be given, in which case a
*        new PointSet will be created by this function.
*     status
*        Pointer to the inherited status variable.

*  Returned Value:
*     Pointer to the output (possibly new) PointSet.

*  Notes:
*     -  The forward and inverse transformations are identical for a
*     Region.
*     -  A null pointer will be returned if this function is invoked with the
*     global error status set, or if it should fail for any reason.
*     -  The number of coordinate values per point in the input PointSet must
*     match the number of axes in the Frame represented by the Moc.
*     -  If an output PointSet is supplied, it must have space for sufficient
*     number of points and coordinate values per point to accommodate the
*     result. Any excess space will be ignored.
*/

/* Local Variables: */
   AstMapping *map1;        /* Sky->HPX12 grid coords Mapping */
   AstMoc *this;            /* Pointer to Moc */
   AstPointSet *ps1;        /* PointSet holding HPX12 grid coords */
   AstPointSet *pset_tmp;   /* Pointer to PointSet holding ICRS positions */
   AstPointSet *result;     /* Pointer to output PointSet */
   double **ptr;            /* Pointer to grid (x,y) coordinate data */
   double **ptr_out;        /* Pointer to output current Frame coordinate data */
   double *px;              /* Pointer to grid X values */
   double *px_out;          /* Pointer to output X values */
   double *py;              /* Pointer to grid Y values */
   double *py_out;          /* Pointer to output Y values */
   int inside;              /* Point inside Moc? */
   int ipoint;              /* Index of input point */
   int irange;              /* Index of current range */
   int neg;                 /* Has the Region been negated? */
   int npoint;              /* No. of input points */
   int order;               /* HEALPix order used by the moc */
   int64_t *pn;            /* Pointer to range of nested index */
   int64_t inest;          /* Nested index of cell holding current position */

/* Check the global error status. */
   if ( !astOK ) return NULL;

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) this_mapping;

/* Apply the parent mapping using the stored pointer to the Transform member
   function inherited from the parent Region class. This function validates
   all parameters and generates an output PointSet if necessary,
   containing a copy of the input PointSet. */
   result = (*parent_transform)( this_mapping, in, forward, out, status );

/* First use the encapsulated FrameSet to transform the supplied positions
   from the current Frame in the encapsulated FrameSet (the Frame
   represented by the Region), to the base Frame (the Frame in which the
   Moc is defined - i.e. ICRS). Note, the returned pointer may be a clone
   of the "in" pointer, and so we must be carefull not to modify the
   contents of the returned PointSet. */
   pset_tmp = astRegTransform( this, in, 0, NULL, NULL );

/* Get the number of points to be transformed. */
   npoint = astGetNpoint( pset_tmp );

/* Get a pointer to the output axis values. */
   ptr_out = astGetPoints( result );

/* See if the Region has been negated. */
   neg = astGetNegated( this );

/* We will now extend the parent astTransform method by performing the
   calculations needed to generate the output coordinate values. */

/* Perform coordinate arithmetic. */
/* ------------------------------ */
   if ( astOK ) {

/* Get the HEALPix order used by the Moc. */
      order = astGetMaxOrder( this );

/* Get a Mapping that goes from ICRS to grid coordinates in an HPX12
   projection of the whole sky with the Moc's order. */
      map1 = GetCachedMapping( this, order, "astTransform", status );

/* Use this Mapping to convert all the ICRS positions to HPX12 grid
   coords. Note the inverse and forward transformations of a Moc are
   identical so we do not need to consider the value of the Invert
   attribute. */
      ps1 = astTransform( map1, pset_tmp, 1, NULL );

/* Get pointers to the first X and Y grid coordinate values. */
      ptr = astGetPoints( ps1 );
      px = ptr[ 0 ];
      py = ptr[ 1 ];

/* Get pointers to the first output sky coordinate values. */
      ptr_out = astGetPoints( result );
      px_out = ptr_out[ 0 ];
      py_out = ptr_out[ 1 ];

/* Check all the positions. */
      for( ipoint = 0; ipoint < npoint; ipoint++ ) {

/* Convert from grid (x,y) to nested index. */
         inest = XyToNested( order, (int)( *(px++) + 0.5 ),
                             (int)( *(py++) + 0.5 ) );

/* Test if this nested index is contained in the Moc. Each pair of
   adjacent values in the "this->range" array are the upper and lower
   bounds of a range of nested index contained in the Moc. The ranges are
   stored in ascending order. Loop until a range is found that ends
   after or at the value of "inest". */
         inside = 0;
         pn = this->range;
         for( irange = 0; irange < this->nrange; irange++ ) {
            if( pn[ 1 ] >= inest ) {

/* The current range ends at or after "inest". The current position is
   inside the Moc if the current range starts at or before "inest". */
               if( pn[ 0 ] <= inest ) inside = 1;

/* No need to check any more ranges. */
               break;
            }
            pn += 2;
         }

/* Negate the inside flag if the Region has been negated. */
         if( neg ) inside = !inside;

/* Set the output values bad if the current position is no inside the Moc. */
         if( !inside ) {
            *px_out = AST__BAD;
            *py_out = AST__BAD;
         }

/* Move on to the next output position. */
         px_out++;
         py_out++;
      }

/* Free resources */
      ps1 = astAnnul( ps1 );
   }
   pset_tmp = astAnnul( pset_tmp );

/* Annul the result if an error has occurred. */
   if( !astOK ) result = astAnnul( result );

/* Return a pointer to the output PointSet. */
   return result;
}

static int64_t XyToNested( int order, int ix, int iy ){
/*
*  Name:
*     XyToNested

*  Purpose:
*     Get the HEALPix nested index of the cell at given HPX12 grid coords.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     int64_t XyToNested( int order, int ix, int iy )

*  Class Membership:
*     Moc member function

*  Description:
*     This function returns the HEALPix nested index of a cell with given
*     grid coords in the HPX12 projection.

*  Parameters:
*     order
*        The HEALPix order.
*     ix
*        The one-based grid X index of the cell within the HPX12
*        projection.
*     iy
*        The one-based grid Y index of the cell within the HPX12
*        projection.

*  Notes:
*     The (ix,iy) values give the grid coords within the all-sky projection
*     and so can be very large. For this reason, the AstMoc class
*     restricts the maximum order to 27, compared to the value of 29
*     included in the MOC recommendation. Using a value of 27 means that the
*     largest value of ix and iy will fit into a signed 4 byte integer.

*  Returned Value:
*     The HEALPix nested index, or int64_MAX if the supplied grid
*     indices do not correspond to a HEALPix cell.
*/

/* Local Variables: */
   int fi;
   int fx;
   int fy;
   int nppf;
   int64_t tx;
   int64_t ty;
   int64_t result;

/* Initialise the returned index to a "bad" value. */
   result = INT64_MAX;

/* Get the number of cells along one edge of a facet.*/
   nppf = ( 1 << order );

/* Get the zero based facet (x,y) indices - offsets from the bottom left
   facet in Calabretta & Roukema 2005 Fig 3. Convert ix and iy from
   one-based to zero-based before doing the calculations. */
   fx = (--ix)/nppf;
   fy = (--iy)/nppf;

/* Get the (x,y) offsets of the cell into the facet, measured from the
   bottom left corner of the facet as shown in Calabretta & Roukema 2005
   Fig 3. Do this now before moving the top right facet to the bottom
   left. */
   tx = ix - nppf*fx;
   ty = iy - nppf*fy;

/* The top right facet is the same as the bottom left facet. */
   if( fx == 4 && fy == 4 ) {
      fx = 0;
      fy = 0;
   }

/* Check that the facet indices are legal. */
   if( fx >= 0 && fx < 5 && fy >= 0 && fy < 5 &&
       fx >= fy - 1 && fx <= fy + 1 ) {

/* Get the facet index in the range 0 - 11. The facet numbering scheme is
   shown in Fig 5 of the HEALPix paper (Gorski et al 2005). For instance
   (fx,fy)=(0,0) (bottom left facet in Calabretta & Roukema 2005 Fig 3) has
   fi=4, and (fx,fy)=(2,2) (centre facet) has fi=6. Facet 4 is duplicated,
   occupying at both the bottom left and the top right corners. */
      if( fx != 4 || fy != 4 ) {
         fi = ( fx + fy )/2 + ( fx - fy + 1 )*4;
      } else {
         fi = 4;
      }

/* Change tx so that it is measured in the opposite direction, starting
   at the bottom right corner of the facet. */
      tx = nppf - 1 - tx;

/* Get the scalar index of the tile within the facet. Interleave the bits
   of tx and ty (tx gives the even bits) and add this index onto
   the index of the first tile in the facet, to get the index of the
   required tile. Interleaving is performed using the "Binary
   Magic Numbers" method with code based on the public domain
   example (collection (C) 1997-2005 Sean Eron Anderson) available at:
   http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN */
      tx = (tx | (tx << 16)) & 0x0000FFFF0000FFFF;
      tx = (tx | (tx <<  8)) & 0x00FF00FF00FF00FF;
      tx = (tx | (tx <<  4)) & 0x0F0F0F0F0F0F0F0F;
      tx = (tx | (tx <<  2)) & 0x3333333333333333;
      tx = (tx | (tx <<  1)) & 0x5555555555555555;

      ty = (ty | (ty << 16)) & 0x0000FFFF0000FFFF;
      ty = (ty | (ty <<  8)) & 0x00FF00FF00FF00FF;
      ty = (ty | (ty <<  4)) & 0x0F0F0F0F0F0F0F0F;
      ty = (ty | (ty <<  2)) & 0x3333333333333333;
      ty = (ty | (ty <<  1)) & 0x5555555555555555;

/* Combine them all together. to form the final index. */
      result = ( (int64_t) fi << ( 2*order ) ) | ty | (tx << 1);
   }

/* Return the result */
   return result;
}


/* Functions which access class attributes. */
/* ---------------------------------------- */
/* Implement member functions to access the attributes associated with
   this class using the macros defined for this purpose in the
   "object.h" file. For a description of each attribute, see the class
   interface (in the associated .h file). */

/*
*att++
*  Name:
*     MaxOrder

*  Purpose:
*     The highest HEALPix order used in the MOC.

*  Type:
*     Public attribute.

*  Synopsis:
*     Integer.

*  Description:
*     This attribute gives the best resolution of the MOC expressed as a
*     HEALPix order in the range zero to 27 (this class does not support
*     orders greater than 27). It's value can only be set once (for
*     instance as an option when the Moc constructor is invoked). An
*     error will be reported if a subsequent attempt to set or clear the
*     attribute is made. If no value is supplied for MaxOrder before the
*     first area of sky is added to the empty Moc, then a default value
*     will be selected and set, depending on the method used to add the
*     first area to the Moc:

c     - astAddCell: the order of the specified cell.
f     - AST_ADDCELL: the order of the specified cell.
*
c     - astAddRegion: if the added Region is a Moc, then the Moc's MaxOrder
f     - AST_ADDREGION: if the added Region is a Moc, then the Moc's MaxOrder
*                     value is used, Otherwise the value used corresponds to
*                     the resolution closest to 0.1% of the linear size of
*                     the Region being added (determined using method
c                     astGetRegionDisc).
f                     AST_GETREGIONDISC).
*
c     - astAddPixelMask<X>: the smallest order that results in the cells in
f     - AST_ADDPIXELMASK<X>: the smallest order that results in the cells in
*     the Moc being no larger than the size of the pixels in the centre of
*     the pixel mask.
*
c     - astAddMocData: the largest order present in the supplied normalised
f     - AST_ADDMOCDATA: the largest order present in the supplied normalised
*      MOC data array.
*
c     - astAddMocString: the largest order present in the supplied MOC.
f     - AST_ADDMOCString: the largest order present in the supplied MOC.
*
*     A default value of -1 will be returned for the MaxOrder attribute
*     prior to its value being set.
*
*     The MaxRes attribute is equivalent to MaxOrder but expresses the
*     resolution as a number of arc-seconds rather than as a HEALPix order.
*
*     Increasing the HEALPix order by one roughly halves the resolution of the
*     Moc. For instance, a value of 18 corresponds to a resolution of about
*     0.8 arc-second, and 19 corresponds to about 0.4 arc-seconds.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/
astMAKE_GET(Moc,MaxOrder,int,0,( ( this->maxorder != -INT_MAX ) ?
                                   this->maxorder : -1 ))
astMAKE_TEST(Moc,MaxOrder,( this->maxorder != -INT_MAX ))


/*
*att++
*  Name:
*     MinOrder

*  Purpose:
*     The lowest HEALPix order used in the MOC.

*  Type:
*     Public attribute.

*  Synopsis:
*     Integer.

*  Description:
*     This attribute controls the size of the largest hole or island that
*     could be missed when adding Regions or pixel masks into a Moc using
c     methods astAddRegion or astAddPixelMask.
f     methods AST_ADDREGION or AST__ADDPIXELMASK.
*     It gives the resolution of the initial grid used to identify areas
*     that are inside or outside the Region or pixel mask, expressed as a
*     HEALPix order in the range zero to 27 (this class does not support
*     orders greater than 27). Unselected areas (i.e. bounded "holes" or
*     or "islands"in the selection) that are smaller than one cell of this
*     initial grid may be missed (i.e. such holes may be "filled in" and
*     islands omitted in the resulting Moc).
*
*     The default value is (MaxOrder-4), with a lower limit of zero. For
*     instance, if MaxOrder is 16 (a resolution of 3.2 arc-seconds), then
*     MinOrder will be 12, meaning that bounded holes within selected areas
*     may be filled in if the hole is smaller than 51 arc-seconds. Increase
*     the value of this attribute to ensures that only holes smaller than
*     this value can be missed. Note, doing so will increase the time spent
*     creating the Moc.
*
*     To ensure no pixels are missed, set MinOrder to some very large
*     value (larger than 27). If MinOrder is set greater than MaxOrder, the
*     value of MaxOrder will be used whenever MinOrder is required.
*
*     The MinRes attribute is equivalent to MinOrder but expresses the
*     resolution as a number of arc-seconds rather than as a HEALPix order.
*     Any change made to MinOrder will cause a corresponding change in the
*     MinRes attribute.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/
astMAKE_CLEAR(Moc,MinOrder,minorder,-INT_MAX)
astMAKE_GET(Moc,MinOrder,int,0,( ( this->minorder != -INT_MAX ) ?
                                this->minorder:astMAX(0,astGetMaxOrder(this)-5)))
astMAKE_SET(Moc,MinOrder,int,minorder, astMIN(astMAX(value,0),AST__MXORDHPX))
astMAKE_TEST(Moc,MinOrder,( this->minorder != -INT_MAX ))


/*
*att++
*  Name:
*     MaxRes

*  Purpose:
*     The best resolution of the MOC.

*  Type:
*     Public attribute.

*  Synopsis:
*     Floating point, read-only.

*  Description:
*     This attribute is an alternative to the MaxOrder attribute and gives
*     the best resolution of the MOC expressed as a number of arc-seconds.
*     It can be set only when the Moc is constructed - an error is
*     reported if any subsequent attempt is made to set or clear the value
*     of MaxRes. See attribute MaxOrder for more details.
*
*     A default value of zero will be returned for the MaxRes attribute
*     prior to its value (or the value of MaxOrder) being set.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/

/*
*att++
*  Name:
*     MinRes

*  Purpose:
*     The worst resolution of the MOC.

*  Type:
*     Public attribute.

*  Synopsis:
*     Floating point.

*  Description:
*     This attribute gives the poorest resolution of the MOC expressed as
*     a number of arc-seconds. When a new value is set for MinRes, the
*     MinOrder attribute will be set to the order that gives a resolution
*     closest to the requested resolution. When the current value of
*     MinRes is requested, the resolution corresponding to the current
*     value of MinOrder will be returned. When MinRes is tested or
*     cleared, the MinOrder attribute will be tested or cleared.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/


/*
*att++
*  Name:
*     MocType

*  Purpose:
*     The data type used to describe a Moc in FITS

*  Type:
*     Public attribute.

*  Synopsis:
*     Integer, read-only.

*  Description:
*     This read-only attribute gives the data type to be used when
*     writing out the Moc to a FITS binary table. The attribute takes the
*     value 4 or 8. The binary table should contain a single column of
*     signed integer values in which each integer has 4 or 8 bytes, as
*     indicated by the value of this attribute.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/

/*
*att++
*  Name:
*     MocArea

*  Purpose:
*     The area covered by the Moc, in square arc-minutes.

*  Type:
*     Public attribute.

*  Synopsis:
*     Floating point, read-only.

*  Description:
*     This read-only attribute gives the area covered by the Moc, in
*     square arc-minutes.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/

/*
*att++
*  Name:
*     MocLength

*  Purpose:
*     The table length used to describe a Moc in FITS

*  Type:
*     Public attribute.

*  Synopsis:
*     Integer, read-only.

*  Description:
*     This read-only attribute gives the length of the number of rows
*     needed to describe the Moc in a FITS binary table. This is the
*     number of cells in the normalised Moc.

*  Applicability:
*     Moc
*        All Mocs have this attribute.

*att--
*/




/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
*  Name:
*     Copy

*  Purpose:
*     Copy constructor for Moc objects.

*  Type:
*     Private function.

*  Synopsis:
*     void Copy( const AstObject *objin, AstObject *objout, int *status )

*  Description:
*     This function implements the copy constructor for Moc objects.

*  Parameters:
*     objin
*        Pointer to the object to be copied.
*     objout
*        Pointer to the object being constructed.
*     status
*        Pointer to the inherited status variable.

*  Notes:
*     -  This constructor makes a deep copy.
*/

/* Local Variables: */
   AstMoc *out;
   AstMoc *in;
   int order;

/* Check the global error status. */
   if ( !astOK ) return;

/* Obtain pointers to the input and output Mocs. */
   in = (AstMoc *) objin;
   out = (AstMoc *) objout;

/* Ensure safe values. */
   out->range = NULL;
   out->knorm = NULL;
   out->inorm = NULL;
   out->basemesh = NULL;
   out->meshdist = NULL;
   out->unc = NULL;

/* Copy dynamic arrays */
   out->range = astStore( NULL, in->range, astSizeOf( in->range ) );
   if( in->knorm ) out->knorm = astStore( NULL, in->knorm,
                                          astSizeOf( in->knorm ));
   if( in->inorm ) out->inorm = astStore( NULL, in->inorm,
                                          astSizeOf( in->inorm ));
   if( in->meshdist ) out->meshdist = astStore( NULL, in->meshdist,
                                          astSizeOf( in->meshdist ));
   if( in->unc ) out->unc = astCopy( in->unc );
   if( in->basemesh ) out->basemesh = astCopy( in->basemesh );

/* Ensure the output has no cached Mappings. These will be re-generated
   when required. */
   for( order = 0; order <= AST__MXORDHPX; order++ ) {
      out->cached_maps[ order ] = NULL;
   }
}


/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
*  Name:
*     Delete

*  Purpose:
*     Destructor for Moc objects.

*  Type:
*     Private function.

*  Synopsis:
*     void Delete( AstObject *obj, int *status )

*  Description:
*     This function implements the destructor for Moc objects.

*  Parameters:
*     obj
*        Pointer to the object to be deleted.
*     status
*        Pointer to the inherited status variable.

*  Notes:
*     This function attempts to execute even if the global error status is
*     set.
*/

/* Local Variables: */
   AstMoc *this;
   int order;

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) obj;

/* Free all resources. */
   this->range = astFree( this->range );
   this->knorm = astFree( this->knorm );
   this->inorm = astFree( this->inorm );
   this->meshdist = astFree( this->meshdist );

   for( order = 0; order <= AST__MXORDHPX; order++ ) {
      if( this->cached_maps[ order ] ) this->cached_maps[ order ] = astAnnul( this->cached_maps[ order ] );
   }

   if( this->unc ) this->unc = astAnnul( this->unc );
   if( this->basemesh ) this->basemesh = astAnnul( this->basemesh );
}

/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
*  Name:
*     Dump

*  Purpose:
*     Dump function for Moc objects.

*  Type:
*     Private function.

*  Synopsis:
*     void Dump( AstObject *this, AstChannel *channel, int *status )

*  Description:
*     This function implements the Dump function which writes out data
*     for the Moc class to an output Channel.

*  Parameters:
*     this
*        Pointer to the Moc whose data are being written.
*     channel
*        Pointer to the Channel to which the data are being written.
*     status
*        Pointer to the inherited status variable.
*/

/* Local Variables: */
   AstMoc *this;                 /* Pointer to the Moc structure */
   char name[50];                /* Name for output item */
   int irange;                   /* Index of current range */
   int ival;                     /* Integer attribute value */
   int set;                      /* Attribute value set? */
   int64_t *pr;                 /* Pointer to next range's bound value */

/* A union used to convert an signed 8 byte integer into two 4 byte
   integers. */
   union {
      int a[2];
      int64_t b;
   } un;

/* Check the global error status. */
   if ( !astOK ) return;

/* Obtain a pointer to the Moc structure. */
   this = (AstMoc *) this_object;

/* Write out values representing the instance variables for the
   Moc class.  Accompany these with appropriate comment strings,
   possibly depending on the values being written.*/

/* In the case of attributes, we first use the appropriate (private)
   Test...  member function to see if they are set. If so, we then use
   the (private) Get... function to obtain the value to be written
   out.

   For attributes which are not set, we use the astGet... method to
   obtain the value instead. This will supply a default value
   (possibly provided by a derived class which over-rides this method)
   which is more useful to a human reader as it corresponds to the
   actual default attribute value.  Since "set" will be zero, these
   values are for information only and will not be read back. */

/* MaxOrder. */
/* --------- */
/* Write out the MaxOrder attribute. */
   set = TestMaxOrder( this, status );
   ival = set ? GetMaxOrder( this, status ) : astGetMaxOrder( this );
   astWriteInt( channel, "MaxOrder", set, 0, ival, "Maximum HEALPix order" );

/* MinOrder. */
/* --------- */
/* Write out the MinOrder attribute. */
   set = TestMinOrder( this, status );
   ival = set ? GetMinOrder( this, status ) : astGetMinOrder( this );
   astWriteInt( channel, "MinOrder", set, 0, ival, "Minimum HEALPix order" );

/* Number of ranges. */
/* ----------------- */
   astWriteInt( channel, "NRange", 1, 0, this->nrange, "Number of ranges" );

/* The ranges of nested indices in the MOC. */
/* ---------------------------------------- */
/* The Channel class does not have support for 64 bit ints, so each 64 bit
   int is written out as two 4 byte ints. Each is only written out if it
   is non-zero. Only write out the upper bound if it is not the same as
   the lower bound. */
   pr = this->range;
   for( irange = 0; irange < this->nrange; irange++, pr += 2 ) {
      un.b = pr[0];
      if( un.a[0] ) {
         sprintf( name, "Lba%d", irange );
         astWriteInt( channel, name, 1, 0, un.a[0], "" );
      }
      if( un.a[1] ) {
         sprintf( name, "Lbb%d", irange );
         astWriteInt( channel, name, 1, 0, un.a[1], "" );
      }

      if( pr[1] != pr[0] ) {
         un.b = pr[1];
         if( un.a[0] ) {
            sprintf( name, "Uba%d", irange );
            astWriteInt( channel, name, 1, 0, un.a[0], "" );
         }
         if( un.a[1] ) {
            sprintf( name, "Ubb%d", irange );
            astWriteInt( channel, name, 1, 0, un.a[1], "" );
         }
      }
   }

}

/* Standard class functions. */
/* ========================= */
/* Implement the astIsAMoc and astCheckMoc functions using the macros
   defined for this purpose in the "object.h" header file. */
astMAKE_ISA(Moc,Region)
astMAKE_CHECK(Moc)

AstMoc *astMoc_( const char *options, int *status, ...) {
/*
*++
*  Name:
c     astMoc
f     AST_MOC

*  Purpose:
*     Create a Moc.

*  Type:
*     Public function.

*  Synopsis:
c     #include "moc.h"
c     AstMoc *astMoc( const char *options, ... )
f     RESULT = AST_MOC( OPTIONS, STATUS )

*  Class Membership:
*     Moc constructor.

*  Description:
*     This function creates a new Moc object and optionally initialises
*     its attributes.
*
*     The Moc class uses the IVOA MOC (Multi-Order Coverage) recommendation
*     to describe a region on the sky. The region is made up of an
*     arbitrary collection of cells from the HEALPix sky tessellation,
*     and thus may represent any area on the sky, subject to the
*     constraint that the edges of the area correspond to edges of the
*     HEALPix cells. See the MOC recommendation for further information
*     (http://www.ivoa.net/documents/MOC/).
*
*     As a description of a region on the sky, the Moc class can be seen
*     as an alternative to the Region class. Note, Mocs and Regions
*     are not interchangable (that is, a Moc is not a subclass of Region
*     and therefore Region methods cannot be applied to Mocs). The Moc
*     class is intended to describe an arbitrary collection of cells on
*     the sky, whereas the Region classes describe exact geometric shapes.
*     The Moc class has a method that allow a Region to be converted into
*     an approximating Moc, but Mocs cannot be converted into Regions.
*
*     The MOC recommendation requires that a MOC always describes a sky
*     area using the ICRS coordinate system. However, the Moc class
*     allows its attributes to be changed so that it represents any
*     celestial coordinate system that can be mapped to ICRS. Note,
*     changing the System attribute will not change the area on the
*     sky covered by the Moc - it will just change the way that area is
*     described. For instance, if a Moc is created that covers a particular
*     galaxy in ICRS, and the System attriubute is then changed to Galactic,
*     the Moc will still cover the same galaxy, but it will now be described
*     in Galactic coordinates rather than ICRS. When a Moc is written out
*     through a FitsChan, FITS headers describing the Moc will be stored
*     in the FitsChan. The binary data for the single column of the
*     coresponding FITS binary table can be retrieved from the Moc using
c     method astGetMocData.
f     method AST_GETMOCDATA.
*
*     In practice, to use this class an empty Moc object (i.e. a Moc
*     describing a null area of the sky) should first be created using the
c     astMoc
f     AST_MOC
*     constructor. Areas of the sky should then be added into the empty
*     Moc using one or more of the class methods.

*  Parameters:
c     maxorder
f     MAXORDER = INTEGER (Given)
*
c     options
f     OPTIONS = CHARACTER * ( * ) (Given)
c        Pointer to a null-terminated string containing an optional
c        comma-separated list of attribute assignments to be used for
c        initialising the new Moc. The syntax used is identical to
c        that for the astSet function and may include "printf" format
c        specifiers identified by "%" symbols in the normal way.
f        A character string containing an optional comma-separated
f        list of attribute assignments to be used for initialising the
f        new Moc. The syntax used is identical to that for the
f        AST_SET routine.
c     ...
c        If the "options" string contains "%" format specifiers, then
c        an optional list of additional parameters may follow it in
c        order to supply values to be substituted for these
c        specifiers. The rules for supplying these are identical to
c        those for the astSet function (and for the C "printf"
c        function).
f     STATUS = INTEGER (Given and Returned)
f        The global status.

*  Returned Value:
c     astMoc()
f     AST_MOC = INTEGER
*        A pointer to the new Moc.

*  Notes:
*     - A null Object pointer (AST__NULL) will be returned if this
c     function is invoked with the AST error status set, or if it
f     function is invoked with STATUS set to an error value, or if it
*     should fail for any reason.

*  Status Handling:
*     The protected interface to this function includes an extra
*     parameter at the end of the parameter list descirbed above. This
*     parameter is a pointer to the integer inherited status
*     variable: "int *status".

*--
*/

/* Local Variables: */
   astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
   AstMoc *new;                  /* Pointer to new Moc */
   va_list args;                 /* Variable parameter list */

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(NULL);

/* Check the global status. */
   if ( !astOK ) return NULL;

/* Initialise the Moc, allocating memory and initialising the
   virtual function table as well if necessary. */
   new = astInitMoc( NULL, sizeof( AstMoc ), !class_init,
                     &class_vtab, "Moc" );

/* If successful, note that the virtual function table has been
   initialised. */
   if ( astOK ) {
      class_init = 1;

/* Obtain the variable parameter list and pass it along with the options string
   to the astVSet method to initialise the new Moc's attributes. */
      va_start( args, status );
      astVSet( new, options, NULL, args );
      va_end( args );

/* If an error occurred, clean up by deleting the new object. */
      if ( !astOK ) new = astDelete( new );
   }


/* Return a pointer to the new Moc. */
   return new;
}

AstMoc *astMocId_( const char *options, ... ) {
/*
*  Name:
*     astMocId_

*  Purpose:
*     Create a Moc.

*  Type:
*     Private function.

*  Synopsis:
*     #include "moc.h"
*     AstMoc *astMocId_( const char *options, ... )

*  Class Membership:
*     Moc constructor.

*  Description:
*     This function implements the external (public) interface to the
*     astMoc constructor function. It returns an ID value (instead
*     of a true C pointer) to external users, and must be provided
*     because astMoc_ has a variable parameter list which cannot be
*     encapsulated in a macro (where this conversion would otherwise
*     occur).
*
*     The variable parameter list also prevents this function from
*     invoking astMoc_ directly, so it must be a re-implementation
*     of it in all respects, except for the final conversion of the
*     result to an ID value.

*  Parameters:
*     As for astMoc_.

*  Returned Value:
*     The ID value associated with the new Moc.
*/

/* Local Variables: */
   astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
   AstMoc *new;                  /* Pointer to new Moc */
   va_list args;                 /* Variable parameter list */
   int *status;                  /* Pointer to inherited status value */

/* Get a pointer to the inherited status value. */
   status = astGetStatusPtr;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(NULL);

/* Check the global status. */
   if ( !astOK ) return NULL;

/* Initialise the Moc, allocating memory and initialising the
   virtual function table as well if necessary. */
   new = astInitMoc( NULL, sizeof( AstMoc ), !class_init,
                     &class_vtab, "Moc" );

/* If successful, note that the virtual function table has been
   initialised. */
   if ( astOK ) {
      class_init = 1;

/* Obtain the variable parameter list and pass it along with the options string
   to the astVSet method to initialise the new Moc's attributes. */
      va_start( args, options );
      astVSet( new, options, NULL, args );
      va_end( args );

/* If an error occurred, clean up by deleting the new object. */
      if ( !astOK ) new = astDelete( new );
   }

/* Return an ID value for the new Moc. */
   return astMakeId( new );
}

AstMoc *astInitMoc_( void *mem, size_t size, int init, AstMocVtab *vtab,
                     const char *name, int *status ) {
/*
*+
*  Name:
*     astInitMoc

*  Purpose:
*     Initialise a Moc.

*  Type:
*     Protected function.

*  Synopsis:
*     #include "moc.h"
*     AstMoc *astInitMoc( void *mem, size_t size, int init, AstMocVtab *vtab,
*                         const char *name )

*  Class Membership:
*     Moc initialiser.

*  Description:
*     This function is provided for use by class implementations to initialise
*     a new Moc object. It allocates memory (if necessary) to accommodate
*     the Moc plus any additional data associated with the derived class.
*     It then initialises a Moc structure at the start of this memory. If
*     the "init" flag is set, it also initialises the contents of a virtual
*     function table for a Moc at the start of the memory passed via the
*     "vtab" parameter.

*  Parameters:
*     mem
*        A pointer to the memory in which the Moc is to be initialised.
*        This must be of sufficient size to accommodate the Moc data
*        (sizeof(Moc)) plus any data used by the derived class. If a value
*        of NULL is given, this function will allocate the memory itself using
*        the "size" parameter to determine its size.
*     size
*        The amount of memory used by the Moc (plus derived class data).
*        This will be used to allocate memory if a value of NULL is given for
*        the "mem" parameter. This value is also stored in the Moc
*        structure, so a valid value must be supplied even if not required for
*        allocating memory.
*     init
*        A logical flag indicating if the Moc's virtual function table is
*        to be initialised. If this value is non-zero, the virtual function
*        table will be initialised by this function.
*     vtab
*        Pointer to the start of the virtual function table to be associated
*        with the new Moc.
*     name
*        Pointer to a constant null-terminated character string which contains
*        the name of the class to which the new object belongs (it is this
*        pointer value that will subsequently be returned by the astGetClass
*        method).

*  Returned Value:
*     A pointer to the new Moc.

*  Notes:
*     -  A null pointer will be returned if this function is invoked with the
*     global error status set, or if it should fail for any reason.
*-
*/

/* Local Variables: */
   AstMoc *new;
   AstSkyFrame *sf;
   int order;

/* Check the global status. */
   if ( !astOK ) return NULL;

/* If necessary, initialise the virtual function table. */
   if ( init ) astInitMocVtab( vtab, name );

/* Initialise. */
   new = NULL;

/* Create an ICRS SkyFrame to be the Frame in which the Region is
   defined. */
   sf = astSkyFrame( "System=ICRS", status );

/* Initialise a Region structure (the parent class) as the first component
   within the Moc structure, allocating memory if necessary. */
   new = (AstMoc *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab,
                                   name, sf, NULL, NULL );
   if ( astOK ) {

/* Initialise the Moc data. */
/* ------------------------------ */
      new->unc = NULL;
      new->basemesh = NULL;
      new->knorm = NULL;
      new->inorm = NULL;
      new->moclength = 0;
      new->mocarea = AST__BAD;
      new->nrange = 0;
      new->meshdist = NULL;
      new->maxorder = -INT_MAX;
      new->minorder = -INT_MAX;
      new->lbnd[ 0 ] = AST__BAD;
      new->lbnd[ 1 ] = AST__BAD;
      new->ubnd[ 0 ] = AST__BAD;
      new->ubnd[ 1 ] = AST__BAD;

/* An array of cached Mappings, one for each HEALPix order. Each
   Mapping goes from ICRS to 1-based grid indices in an HPX12
   projection at the corresponding order. These Mappings are
   created as they are needed. */
      for( order = 0; order <= AST__MXORDHPX; order++ ) {
         new->cached_maps[ order ] = NULL;
      }

/* If an error occurred, clean up by deleting the new Moc. */
      if ( !astOK ) new = astDelete( new );
   }

/* Free the SkyFrame */
   sf = astAnnul( sf );

/* Return a pointer to the new Moc. */
   return new;
}

AstMoc *astLoadMoc_( void *mem, size_t size, AstMocVtab *vtab,
                     const char *name, AstChannel *channel, int *status ) {
/*
*+
*  Name:
*     astLoadMoc

*  Purpose:
*     Load a Moc.

*  Type:
*     Protected function.

*  Synopsis:
*     #include "moc.h"
*     AstMoc *astLoadMoc( void *mem, size_t size, AstMocVtab *vtab,
*                         const char *name, AstChannel *channel )

*  Class Membership:
*     Moc loader.

*  Description:
*     This function is provided to load a new Moc using data read
*     from a Channel. It first loads the data used by the parent class
*     (which allocates memory if necessary) and then initialises a
*     Moc structure in this memory, using data read from the input
*     Channel.
*
*     If the "init" flag is set, it also initialises the contents of a
*     virtual function table for a Moc at the start of the memory
*     passed via the "vtab" parameter.

*  Parameters:
*     mem
*        A pointer to the memory into which the Moc is to be
*        loaded.  This must be of sufficient size to accommodate the
*        Moc data (sizeof(Moc)) plus any data used by derived
*        classes. If a value of NULL is given, this function will
*        allocate the memory itself using the "size" parameter to
*        determine its size.
*     size
*        The amount of memory used by the Moc (plus derived class
*        data).  This will be used to allocate memory if a value of
*        NULL is given for the "mem" parameter. This value is also
*        stored in the Moc structure, so a valid value must be
*        supplied even if not required for allocating memory.
*
*        If the "vtab" parameter is NULL, the "size" value is ignored
*        and sizeof(AstMoc) is used instead.
*     vtab
*        Pointer to the start of the virtual function table to be
*        associated with the new Moc. If this is NULL, a pointer
*        to the (static) virtual function table for the Moc class
*        is used instead.
*     name
*        Pointer to a constant null-terminated character string which
*        contains the name of the class to which the new object
*        belongs (it is this pointer value that will subsequently be
*        returned by the astGetClass method).
*
*        If the "vtab" parameter is NULL, the "name" value is ignored
*        and a pointer to the string "Moc" is used instead.

*  Returned Value:
*     A pointer to the new Moc.

*  Notes:
*     - A null pointer will be returned if this function is invoked
*     with the global error status set, or if it should fail for any
*     reason.
*-
*/

/* Local Variables: */
   AstMoc *new;                  /* Pointer to the new Moc */
   astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
   char buff[50];                /* Buffer for item name */
   int irange;                   /* The index of the current range */
   int order;                    /* HEALPix order */
   int64_t *pr;                 /* Pointer to next range */
   int64_t llast;               /* Lower bounds of last range */
   int64_t ulast;               /* Upper bounds of last range */
   int64_t max_nest;            /* No of cells in whole sky */

/* A union used to convert an signed 8 byte integer into two 4 byte
   integers. */
   union {
      int a[2];
      int64_t b;
   } un;

/* Initialise. */
   new = NULL;

/* Check the global error status. */
   if ( !astOK ) return new;

/* Get a pointer to the thread specific global data structure. */
   astGET_GLOBALS(channel);

/* If a NULL virtual function table has been supplied, then this is
   the first loader to be invoked for this Moc. In this case the
   Moc belongs to this class, so supply appropriate values to be
   passed to the parent class loader (and its parent, etc.). */
   if ( !vtab ) {
      size = sizeof( AstMoc );
      vtab = &class_vtab;
      name = "Moc";

/* If required, initialise the virtual function table for this class. */
      if ( !class_init ) {
         astInitMocVtab( vtab, name );
         class_init = 1;
      }
   }

/* Invoke the parent class loader to load data for all the ancestral
   classes of the current one, returning a pointer to the resulting
   partly-built Moc. */
   new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name,
                        channel );
   if ( astOK ) {

/* Read input data. */
/* ================ */
/* Request the input Channel to read all the input data appropriate to
   this class into the internal "values list". */
      astReadClassData( channel, "Moc" );

/* Now read each individual data item from this list and use it to
   initialise the appropriate instance variable(s) for this class. */

/* In the case of attributes, we first read the "raw" input value,
   supplying the "unset" value as the default. If a "set" value is
   obtained, we then use the appropriate (private) Set... member
   function to validate and set the value properly. */
      new->maxorder = astReadInt( channel, "maxorder", -INT_MAX );
      if ( TestMaxOrder( new, status ) ) SetMaxOrder( new, new->maxorder, status );

      new->minorder = astReadInt( channel, "minorder", -INT_MAX );
      if ( TestMinOrder( new, status ) ) SetMinOrder( new, new->minorder, status );

/* Get the maximum number of nested indices at the order in use. */
      max_nest = 12*( 1L << (2*new->maxorder) );

/* Initialise other class properties. */
      new->nrange = astReadInt( channel, "nrange", 0 );
      new->range = astMalloc( 2*new->nrange*sizeof(*(new->range)) );
      if( astOK ) {
         llast = 0;
         ulast = 0;

         pr = new->range;
         for( irange = 0; irange < new->nrange; irange++,pr += 2 ) {
            (void) sprintf( buff, "lba%d", irange );
            un.a[0] = astReadInt( channel, buff, 0 );
            (void) sprintf( buff, "lbb%d", irange );
            un.a[1] = astReadInt( channel, buff, 0 );
            pr[ 0 ] = un.b;

            (void) sprintf( buff, "uba%d", irange );
            un.a[0] = astReadInt( channel, buff, 0 );
            (void) sprintf( buff, "ubb%d", irange );
            un.a[1] = astReadInt( channel, buff, 0 );
            pr[ 1 ] = ( un.b > 0 ) ? un.b : pr[ 0 ];

            if( pr[ 1 ] >= max_nest ) {
               astError( AST__LDERR, "astLoadMoc(Moc): Ill-formed Moc.",
                         status );
               astError( AST__LDERR, "Upper bound (%zu) is too high for "
                         "order %d.", status, pr[ 1 ], new->maxorder );
               break;

            } else if( pr[ 0 ] > pr[ 1 ] ) {
               astError( AST__LDERR, "astLoadMoc(Moc): Ill-formed Moc.",
                         status );
               astError( AST__LDERR, "Upper bound (%zu) lower than lower "
                         "bound (%zu).", status, pr[ 1 ], pr[ 0 ] );
               break;

            } else if( pr[ 0 ] <= ulast && irange != 0 ) {
               astError( AST__LDERR, "astLoadMoc(Moc): Ill-formed Moc.",
                         status );
               astError( AST__LDERR, "Range [%zu:%zu] overlaps range "
                         "[%zu:%zu].", status, pr[ 0 ], pr[ 1 ], llast,
                         ulast );
               break;
            }

            llast = pr[ 0 ];
            ulast = pr[ 1 ];
         }
      }

/* Initialise other values */
      new->unc = NULL;
      new->basemesh = NULL;
      new->inorm = NULL;
      new->knorm = NULL;
      new->moclength = 0;
      new->mocarea = AST__BAD;
      new->meshdist = NULL;
      new->lbnd[ 0 ] = AST__BAD;
      new->lbnd[ 1 ] = AST__BAD;
      new->ubnd[ 0 ] = AST__BAD;
      new->ubnd[ 1 ] = AST__BAD;

/* An array of cached Mappings, one for each HEALPix order. Each
   Mapping goes from ICRS to 1-based grid indices in an HPX12
   projection at the corresponding order. These Mappings are
   created as they are needed. */
      for( order = 0; order <= AST__MXORDHPX; order++ ) {
         new->cached_maps[ order ] = NULL;
      }

/* If an error occurred, clean up by deleting the new Moc. */
      if ( !astOK ) new = astDelete( new );
   }

/* Return the new Moc pointer. */
   return new;
}

/* Virtual function interfaces. */
/* ============================ */
/* These provide the external interface to the virtual functions defined by
   this class. Each simply checks the global error status and then locates and
   executes the appropriate member function, using the function pointer stored
   in the object's virtual function table (this pointer is located using the
   astMEMBER macro defined in "object.h").

   Note that the member function may not be the one defined here, as it may
   have been over-ridden by a derived class. However, it should still have the
   same interface. */

void astAddRegion_( AstMoc *this, int cmode, AstRegion *region, int *status ){
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,AddRegion))( this, cmode, region, status );
}

int astGetMocType_( AstMoc *this, int *status ){
   if ( !astOK ) return 4;
   return (**astMEMBER(this,Moc,GetMocType))( this, status );
}

int astGetMocLength_( AstMoc *this, int *status ){
   if ( !astOK ) return 0;
   return (**astMEMBER(this,Moc,GetMocLength))( this, status );
}

double astGetMocArea_( AstMoc *this, int *status ) {
   if ( !astOK ) return 0.0;
   return (**astMEMBER(this,Moc,GetMocArea))( this, status );
}

AstFitsChan *astGetMocHeader_( AstMoc *this, int *status ) {
   if ( !astOK ) return NULL;
   return (**astMEMBER(this,Moc,GetMocHeader))( this, status );
}

void astGetMocData_( AstMoc *this, size_t mxsize, void *array, int *status ) {
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,GetMocData))( this, mxsize, array, status );
}

void astGetMocString_( AstMoc *this, int json, size_t mxsize, char *string,
                       size_t *size, int *status ){
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,GetMocString))( this, json, mxsize, string, size,
                                         status );
}

void astSetMaxOrder_( AstMoc *this, int value, int *status ){
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,SetMaxOrder))( this, value, status );
}

void astClearMaxOrder_( AstMoc *this, int *status ){
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,ClearMaxOrder))( this, status );
}

void astAddMocData_( AstMoc *this, int cmode, int negate, int maxorder,
                     int len, int nbyte, const void *data, int *status ) {
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,AddMocData))( this, cmode, negate, maxorder, len,
                                       nbyte, data, status );
}

void astAddMocString_( AstMoc *this, int cmode, int negate, int maxorder,
                     size_t len, const char *string, int *json, int *status ) {
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,AddMocString))( this, cmode, negate, maxorder, len,
                                         string, json, status );
}

void astAddCell_( AstMoc *this, int cmode, int order, int64_t npix, int *status ) {
   if ( !astOK ) return;
   (**astMEMBER(this,Moc,AddCell))( this, cmode, order, npix, status );
}

int astTestCell_( AstMoc *this, int order, int64_t npix, int parents, int *status ) {
   if ( !astOK ) return 0;
   return (**astMEMBER(this,Moc,TestCell))( this, order, npix, parents, status );
}

void astGetCell_( AstMoc *this, int icell, int *order, int64_t *npix,
                  int *status ) {
   if ( !astOK ) return;
   return (**astMEMBER(this,Moc,GetCell))( this, icell, order, npix, status );
}



#define MAKE_ADDPIXELMASK_(X,Xtype) \
void astAddPixelMask##X##_( AstMoc *this, int cmode, AstFrameSet *wcs, Xtype value, \
                            int oper, int flags, Xtype badval, const Xtype array[], \
                            const int dims[2], int *status ) { \
   if ( !astOK ) return; \
   (**astMEMBER(this,Moc,AddPixelMask##X))( this, cmode, wcs, value, oper, \
                                            flags, badval, array, dims, status ); \
}
#if HAVE_LONG_DOUBLE     /* Not normally implemented */
MAKE_ADDPIXELMASK_(LD,long double)
#endif
MAKE_ADDPIXELMASK_(D,double)
MAKE_ADDPIXELMASK_(F,float)
MAKE_ADDPIXELMASK_(L,long int)
MAKE_ADDPIXELMASK_(UL,unsigned long int)
MAKE_ADDPIXELMASK_(I,int)
MAKE_ADDPIXELMASK_(UI,unsigned int)
MAKE_ADDPIXELMASK_(S,short int)
MAKE_ADDPIXELMASK_(US,unsigned short int)
MAKE_ADDPIXELMASK_(B,signed char)
MAKE_ADDPIXELMASK_(UB,unsigned char)
#undef MAKE_ADDPIXELMASK_





/* For debugging of astRegBaseMesh and astRegTrace......

static void dump_corner( Corner *this, int order ) {
   static FILE *fd = NULL;
   int i;
   double alpha, beta;

   if( !fd ) {
      fd = fopen( "corners.asc", "w" );
      fprintf( fd, "# this ra dec alpha beta order int dist prev cell0 cell1 cell2 cell3\n");

   }

   alpha = cos( this->dec )*cos( this->ra );
   beta = cos( this->dec )*sin( this->ra );

   fprintf( fd, "%p %g %g %g %g %d %d %d %p ", this, this->ra, this->dec,
            alpha, beta, order, this->interior, this->dist, this->prev );
   for( i = 0; i < this->ncell; i++ ) fprintf(fd, "%p ", this->cells[i] );
   for( ; i < 4; i++ ) fprintf( fd, "null " );
   fprintf(fd, "\n");

}

static void dump_cell( AstMoc *this, Cell *cell, int order ) {
   int status_value;
   int *old_status;
   int *status;
   double x, y, ra, dec, alpha, beta;
   static FILE *fd = NULL;

   status_value = 0;
   status = &status_value;
   old_status = astWatch( status );

   if( !fd ) {
      fd = fopen( "cells.asc", "w" );
      fprintf( fd, "# this x18 y18 ix iy ra dec alpha beta order int prev bl tl tr br\n");

   }

   AstMapping *map = GetCachedMapping( this, order, "dump_cell",
                                       status );

   x = cell->ix;
   y = cell->iy;
   astTran2( map, 1, &x, &y, 0, &ra, &dec );
   alpha = cos( dec )*cos( ra );
   beta = cos( dec )*sin( ra );


   double x18 = ( 1L << (18 - order) )*( cell->ix - 0.5 ) + 0.5;
   double y18 = ( 1L << (18 - order) )*( cell->iy - 0.5 ) + 0.5;

   fprintf( fd, "%p %g %g %d %d %g %g %g %g %d %d %p ", cell, x18, y18, cell->ix, cell->iy,
            ra, dec, alpha, beta, order, cell->interior, cell->prev );

   if( cell->bl ) {
      fprintf(fd, "%p ", cell->bl );
   } else {
      fprintf(fd, "null " );
   }

   if( cell->tl ) {
      fprintf(fd, "%p ", cell->tl );
   } else {
      fprintf(fd, "null " );
   }

   if( cell->tr ) {
      fprintf(fd, "%p ", cell->tr );
   } else {
      fprintf(fd, "null " );
   }

   if( cell->br ) {
      fprintf(fd, "%p ", cell->br );
   } else {
      fprintf(fd, "null " );
   }

   fprintf(fd, "\n");

   astWatch( old_status );

}


static void dump_moc( AstMoc *this, const char *fname, int *status ) {
   double *px, *py, *pa, *pb;
   FILE *fd;
   int64_t npix, *pr;
   int npoint, maxorder, irange, ix, iy;
   AstMapping *map;
   AstPointSet *ps, *ps2;
   double **ptr, **ptr2, alpha, beta;

   if( !astOK ) return;

   fd = fopen( fname, "w" );
   fprintf( fd, "# ix iy ra dec alpha beta\n");

   maxorder = astGetMaxOrder( this );
   map = GetCachedMapping( this, maxorder, "dump_moc", status );

   pr = this->range;
   for( irange = 0; irange < this->nrange; irange++, pr += 2 ){

      npoint = pr[ 1 ] - pr[ 0 ]  + 1;
      ps = astPointSet( npoint, 2, " ", status );
      ptr = astGetPoints( ps );
      if( astOK ) {
        px = ptr[ 0 ];
        py = ptr[ 1 ];
        for( npix = pr[ 0 ]; npix <= pr[1 ]; npix++ ) {
           NestedToXy( npix, maxorder, &ix, &iy );
           *(px++) = ix;
           *(py++) = iy;
        }
      }

      ps2 = astTransform( map, ps, 0, NULL );
      ptr2 = astGetPoints( ps2 );
      if( astOK ) {
        px = ptr[ 0 ];
        py = ptr[ 1 ];
        pa = ptr2[ 0 ];
        pb = ptr2[ 1 ];
        for( npix = pr[ 0 ]; npix <= pr[1 ]; npix++ ) {
           alpha = cos(*pb)*cos(*pa);
           beta = cos(*pb)*sin(*pa);
           fprintf( fd, "%g %g %g %g %g %g\n", *(px++), *(py++), *(pa++),
                    *(pb++), alpha, beta  );
        }
      }

      ps = astAnnul( ps );
      ps2 = astAnnul( ps2 );

   }

   fclose( fd );
}

*/



