//# fits.h:
//# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001,2003,2004
//# Associated Universities, Inc. Washington DC, USA.
//# 
//# This library is free software; you can redistribute it and/or modify it
//# under the terms of the GNU Library General Public License as published by
//# the Free Software Foundation; either version 2 of the License, or (at your
//# option) any later version.
//# 
//# This library 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 Library General Public
//# License for more details.
//# 
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//# 
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: casa-feedback@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA

# if !defined(AIPS_FITS)
# define AIPS_FITS

//# Note that aips.h has to come first for the correct definition of off_t.
# include <casacore/casa/aips.h>
# include <cfloat>
# include <climits>
# include <cstdlib>
# include <cctype>
# include <casacore/casa/iostream.h>
# include <casacore/casa/BasicSL/Complex.h>
# include <casacore/casa/BasicSL/IComplex.h>
# include <casacore/fits/FITS/FITSError.h>

namespace casacore { //# NAMESPACE CASACORE - BEGIN

//# All FITS code seems to assume longs are 4 bytes. Currently
//# this corresponds to an "int" on all useful platforms.
    typedef Int FitsLong;
//# recovered by GYL

//# Forward declarations
class ReservedFitsKeywordCollection;
class FitsNameResult;
class FitsValueResult;
class FitsKeyword;
class FitsParse;

//<summary> FITS templated helper class </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// NoConvert is a template class that is not intended for
// general use, it is used internally.
//</synopsis>

template <class TYPE>
class NoConvert {
    public:
	NoConvert() { }
	void operator = (int) {; }
};

//<summary> FITS helper class </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// FitsLogical is a helper class that is not intended for
// general use. 
//</synopsis>
//<example>
// Here is an example of the FitsLogical class. 
//<srcblock>
//	FitsLogical x;
//	FitsLogical y(True);
//	FitsLogical z = x;
//	...
//	x = y; y = False; x.undefine();
//	Bool b;
//	if (x.isdefined())
//		b = x;
//	b = y;  If y is undefined, b will be false.
//</srcblock>
//</example>
class FitsLogical {
	friend ostream & operator << (ostream &o, const FitsLogical &);
    public:
	FitsLogical() : v('\0') { }
	FitsLogical(Bool x) : v(x == True ? 'T' : 'F') { }
	FitsLogical & operator = (Bool x) { 
		v = (x == True ? 'T' : 'F'); return *this; }
  ///ARO 2021-02-20:
  ///Removed the following function, because it seems incorrectly implemented and isn't used
	///Bool isdefined() const { return v == '\0' ? True : False; }
	void undefine() { v = '\0'; }
	operator Bool() const { return v == 'T'; }
    protected:
	char v;
};

//<summary> helper class for FITS Binary Tables </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// This class is not intended for general use.  It only has meaning
// in the context of FITS Binary tables.  There its use is incorporated
// into the concept of a FitsField, where FitsBit is given a specialized
// interpretation.
//</synopsis>

class FitsBit {
    public:
	FitsBit() : bit_array(0) { }
	FitsBit(unsigned char x) : bit_array(x) { }
	FitsBit & operator = (unsigned char x) { bit_array = x; return *this; }
	operator unsigned char() const { return bit_array; }
    protected:
	unsigned char bit_array;
};

//<summary>  Variable Length Array Descriptor </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>

class FitsVADesc { 
	friend ostream & operator << (ostream &o, const FitsVADesc &);
    public:
	FitsVADesc() : no_elements(0), rel_offset(0) { }
	FitsVADesc(const FitsVADesc &x) :
		no_elements(x.no_elements), rel_offset(x.rel_offset) { }
	FitsVADesc & operator = (const FitsVADesc &x) {
		no_elements= x.no_elements;
		rel_offset = x.rel_offset; return *this; }
	FitsVADesc(int n, int o) : no_elements(n), rel_offset(o) { }
	void set(int n, int o) { no_elements = n; rel_offset = o; }
	int num() const 	{ return no_elements; }
	int offset() const 	{ return rel_offset; }
    protected:
	int no_elements;
	int rel_offset;
};

//<summary> static functions and enumerations </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// Many of the static functions are utility functions used internally in the
// implementation of the member functions of the FITS classes. They are placed
// in a single class to encapsulate them and to avoid adding many names to the 
// global name space. More important, from the user's perspective, are the
// enumerations. They form the basic vocabulary of a FITS application. For example,
// instead of referring to the FITS <src>NAXIS</src> keyword, 
// <src>FITS::NAXIS</src> should be used
//</synopsis>

class FITS {
    public:

    // FITS I/O Error message types

	// Basic FITS Data Types for keywords and data
	enum ValueType {
	    NOVALUE = 0, LOGICAL = 1, BIT = 2, CHAR = 3, BYTE = 4, 
	    SHORT = 5, LONG = 6, FLOAT = 7, DOUBLE = 8, COMPLEX = 9, 
	    ICOMPLEX = 10, DCOMPLEX = 11, VADESC = 12,
	    STRING, FSTRING, REAL
	}; // REAL means either FLOAT or DOUBLE
	   // STRING and FSTRING are used internally in parsing keywords

	static  FITS::ValueType getfitstype(NoConvert<FitsLogical> x) {
		x=0; return FITS::LOGICAL; }
	static  FITS::ValueType getfitstype(NoConvert<FitsBit> x) {
		x=0; return FITS::BIT; }
	static  FITS::ValueType getfitstype(NoConvert<char> x) {
		x=0; return FITS::CHAR; }
	static  FITS::ValueType getfitstype(NoConvert<unsigned char> x) {
		x=0; return FITS::BYTE; }
	static  FITS::ValueType getfitstype(NoConvert<short> x) {
		x=0; return FITS::SHORT; }
	static  FITS::ValueType getfitstype(NoConvert<Int> x) {
		x=0; return FITS::LONG; }
	static  FITS::ValueType getfitstype(NoConvert<long> x) {
		x=0; return FITS::LONG; }
	static  FITS::ValueType getfitstype(NoConvert<float> x) {
		x=0; return FITS::FLOAT; }
	static  FITS::ValueType getfitstype(NoConvert<double> x) {
		x=0; return FITS::DOUBLE; }
	static  FITS::ValueType getfitstype(NoConvert<Complex> x) {
		x=0; return FITS::COMPLEX; }
	static  FITS::ValueType getfitstype(NoConvert<IComplex> x) {
		x=0; return FITS::ICOMPLEX; }	
	static  FITS::ValueType getfitstype(NoConvert<DComplex> x) {
		x=0; return FITS::DCOMPLEX; }
	static  FITS::ValueType getfitstype(NoConvert<FitsVADesc> x) {
		x=0; return FITS::VADESC; }

	static  int fitssize(FITS::ValueType t);
	static  int localsize(FITS::ValueType t);

	// data conversion routines: FITS - local
	static void f2l(FitsLogical *,void *,int);
	static void l2f(void *,FitsLogical *,int);
	static void f2l(FitsBit *,void *,int);
	static void l2f(void *,FitsBit *,int);
	static void f2l(char *,void *,int);
	static void l2f(void *,char *,int);
	static void f2l(unsigned char *,void *,int);
	static void l2f(void *,unsigned char *,int);
	static void f2l(short *,void *,int);
	static void l2f(void *,short *,int);
	static void f2l(Int *,void *,int);
	static void l2f(void *,Int *,int);
	static void f2l(long *,void *,int);
	static void l2f(void *,long *,int);
	static void f2l(float *,void *,int);
	static void l2f(void *,float *,int);
	static void f2l(double *,void *,int);
	static void l2f(void *,double *,int);
	static void f2l(Complex *,void *,int);
	static void l2f(void *,Complex *,int);
	static void f2l(IComplex *,void *,int);
	static void l2f(void *,IComplex *,int);
	static void f2l(DComplex *,void *,int);
	static void l2f(void *,DComplex *,int);
	static void f2l(FitsVADesc *,void *,int);
	static void l2f(void *,FitsVADesc *,int);
        static void swap2(void *, void *, int);
        static void swap4(void *, void *, int);
        static void swap8(void *, void *, int);

	// FITS Reserved Names. PZERO is named strangely because it can conflict with
        // a standard #define in sys/param.h.
	enum ReservedName {
	    USER_DEF, AUTHOR,   BITPIX,   BLANK,    BLOCKED,  BSCALE,
	    BUNIT,    BZERO,    CDELT,    COMMENT,  CROTA,    CRPIX,
	    CRVAL,    CTYPE,    DATAMAX,  DATAMIN,  DATE,     DATE_OBS,
	    END,      EPOCH,    EQUINOX,  EXTEND,   EXTLEVEL, EXTNAME,
	    EXTVER,   GCOUNT,   GROUPS,   HISTORY,  INSTRUME, NAXIS,
	    OBJECT,   OBSERVER, ORIGIN,   PCOUNT,   PSCAL,    PTYPE,
	    PZERO_FITS,    REFERENC, SIMPLE,   SPACES,   TBCOL,    TDIM,
	    TDISP,    TELESCOP, TFIELDS,  TFORM,    THEAP,    TNULL,
	    TSCAL,    TTYPE,    TUNIT,    TZERO,    XTENSION, ERRWORD,
            ALTRPIX, DATE_MAP
	};

	// Types of FITS Records
	enum FitsRecType { 
	    InitialState, BadBeginningRecord, HDURecord,
	    UnrecognizableRecord, SpecialRecord, EndOfFile 
	};

	// Supported FITS Physical Devices
	enum FitsDevice { 
	    Disk, Std, Tape9
	};

	// Types of FITS Header-Data Units
	enum HDUType {
	    NotAHDU, PrimaryArrayHDU, PrimaryGroupHDU, AsciiTableHDU,
	    BinaryTableHDU, ImageExtensionHDU, UnknownExtensionHDU,
            PrimaryTableHDU
	};

	// Options on FITS array manipulations
	enum FitsArrayOption { NoOpt = 0, CtoF = 1, FtoC = 2};

	static ReservedFitsKeywordCollection &ResWord;
	static void valstr(ostream &o, const ValueType &ty, const void *val);
	static Bool isa_digit(char c);
	static int digit2bin(char c);
	static Bool isa_text(char c);
	static Bool isa_letter(char);
	static int letter2bin(char);
	static void fstr2str(char *, const char *, int);
	static int str2fstr(char *, const char *, int);
	static void get_name(const char *s, int len, FitsNameResult &result);
	static int get_value_id(const char *s, int l, int &pos);
	static void get_value(const char *s, int len, FitsValueResult &result);
	static int trim_comment(const char *s, int len);
	static int chk_comment(const char *s, int len);
	static int get_comment(const char *s, int len, int &begpos);
	static void get_numeric(const char *s, int len, FitsValueResult &result);
    // utility function to parse the binary table variable array
    // column (i.e. uses the heap) of the form nPt(dddd) where n
    // is either 0 or 1, t is one of the standard FITS binary table
    // column types and dddd is the maximum number of elements used
    // by this column.  If there is a format error in the input
    // string (*s), then valType will have the value NOVALUE and
    // maxelem will be -1.
        static void parse_vatform(const char *s, FITS::ValueType &valType,
				  int &maxelem);
	static constexpr Int minInt = INT_MIN;
	static constexpr Int maxInt = INT_MAX;
	static constexpr float minfloat = FLT_MIN;
	static constexpr float maxfloat = FLT_MAX;
	static constexpr double mindouble = DBL_MIN;
	static constexpr double maxdouble = DBL_MAX;

    private:
	FITS();
	static constexpr double tenpowerD[309] = { 1.0,
	  1.0E1,   1.0E2,   1.0E3,   1.0E4,   1.0E5,   1.0E6,   1.0E7,   1.0E8,
	  1.0E9,  1.0E10,  1.0E11,  1.0E12,  1.0E13,  1.0E14,  1.0E15,  1.0E16,
	 1.0E17,  1.0E18,  1.0E19,  1.0E20,  1.0E21,  1.0E22,  1.0E23,  1.0E24,
	 1.0E25,  1.0E26,  1.0E27,  1.0E28,  1.0E29,  1.0E30,  1.0E31,  1.0E32,
	 1.0E33,  1.0E34,  1.0E35,  1.0E36,  1.0E37,  1.0E38,  1.0E39,  1.0E40,
	 1.0E41,  1.0E42,  1.0E43,  1.0E44,  1.0E45,  1.0E46,  1.0E47,  1.0E48,
	 1.0E49,  1.0E50,  1.0E51,  1.0E52,  1.0E53,  1.0E54,  1.0E55,  1.0E56,
	 1.0E57,  1.0E58,  1.0E59,  1.0E60,  1.0E61,  1.0E62,  1.0E63,  1.0E64,
	 1.0E65,  1.0E66,  1.0E67,  1.0E68,  1.0E69,  1.0E70,  1.0E71,  1.0E72,
	 1.0E73,  1.0E74,  1.0E75,  1.0E76,  1.0E77,  1.0E78,  1.0E79,  1.0E80,
	 1.0E81,  1.0E82,  1.0E83,  1.0E84,  1.0E85,  1.0E86,  1.0E87,  1.0E88,
	 1.0E89,  1.0E90,  1.0E91,  1.0E92,  1.0E93,  1.0E94,  1.0E95,  1.0E96,
	 1.0E97,  1.0E98,  1.0E99, 1.0E100, 1.0E101, 1.0E102, 1.0E103, 1.0E104,
	1.0E105, 1.0E106, 1.0E107, 1.0E108, 1.0E109, 1.0E110, 1.0E111, 1.0E112,
	1.0E113, 1.0E114, 1.0E115, 1.0E116, 1.0E117, 1.0E118, 1.0E119, 1.0E120,
	1.0E121, 1.0E122, 1.0E123, 1.0E124, 1.0E125, 1.0E126, 1.0E127, 1.0E128,
	1.0E129, 1.0E130, 1.0E131, 1.0E132, 1.0E133, 1.0E134, 1.0E135, 1.0E136,
	1.0E137, 1.0E138, 1.0E139, 1.0E140, 1.0E141, 1.0E142, 1.0E143, 1.0E144,
	1.0E145, 1.0E146, 1.0E147, 1.0E148, 1.0E149, 1.0E150, 1.0E151, 1.0E152,
	1.0E153, 1.0E154, 1.0E155, 1.0E156, 1.0E157, 1.0E158, 1.0E159, 1.0E160,
	1.0E161, 1.0E162, 1.0E163, 1.0E164, 1.0E165, 1.0E166, 1.0E167, 1.0E168,
	1.0E169, 1.0E170, 1.0E171, 1.0E172, 1.0E173, 1.0E174, 1.0E175, 1.0E176,
	1.0E177, 1.0E178, 1.0E179, 1.0E180, 1.0E181, 1.0E182, 1.0E183, 1.0E184,
	1.0E185, 1.0E186, 1.0E187, 1.0E188, 1.0E189, 1.0E190, 1.0E191, 1.0E192,
	1.0E193, 1.0E194, 1.0E195, 1.0E196, 1.0E197, 1.0E198, 1.0E199, 1.0E200,
	1.0E201, 1.0E202, 1.0E203, 1.0E204, 1.0E205, 1.0E206, 1.0E207, 1.0E208,
	1.0E209, 1.0E210, 1.0E211, 1.0E212, 1.0E213, 1.0E214, 1.0E215, 1.0E216,
	1.0E217, 1.0E218, 1.0E219, 1.0E220, 1.0E221, 1.0E222, 1.0E223, 1.0E224,
	1.0E225, 1.0E226, 1.0E227, 1.0E228, 1.0E229, 1.0E230, 1.0E231, 1.0E232,
	1.0E233, 1.0E234, 1.0E235, 1.0E236, 1.0E237, 1.0E238, 1.0E239, 1.0E240,
	1.0E241, 1.0E242, 1.0E243, 1.0E244, 1.0E245, 1.0E246, 1.0E247, 1.0E248,
	1.0E249, 1.0E250, 1.0E251, 1.0E252, 1.0E253, 1.0E254, 1.0E255, 1.0E256,
	1.0E257, 1.0E258, 1.0E259, 1.0E260, 1.0E261, 1.0E262, 1.0E263, 1.0E264,
	1.0E265, 1.0E266, 1.0E267, 1.0E268, 1.0E269, 1.0E270, 1.0E271, 1.0E272,
	1.0E273, 1.0E274, 1.0E275, 1.0E276, 1.0E277, 1.0E278, 1.0E279, 1.0E280,
	1.0E281, 1.0E282, 1.0E283, 1.0E284, 1.0E285, 1.0E286, 1.0E287, 1.0E288,
	1.0E289, 1.0E290, 1.0E291, 1.0E292, 1.0E293, 1.0E294, 1.0E295, 1.0E296,
	1.0E297, 1.0E298, 1.0E299, 1.0E300, 1.0E301, 1.0E302, 1.0E303, 1.0E304,
	1.0E305, 1.0E306, 1.0E307, 1.0E308 };
	static constexpr float tenpowerF[39] = { 1.0F,
	  1.0E1F,  1.0E2F,  1.0E3F,  1.0E4F,  1.0E5F,  1.0E6F,  1.0E7F,  1.0E8F,
	  1.0E9F, 1.0E10F, 1.0E11F, 1.0E12F, 1.0E13F, 1.0E14F, 1.0E15F, 1.0E16F,
	 1.0E17F, 1.0E18F, 1.0E19F, 1.0E20F, 1.0E21F, 1.0E22F, 1.0E23F, 1.0E24F,
	 1.0E25F, 1.0E26F, 1.0E27F, 1.0E28F, 1.0E29F, 1.0E30F, 1.0E31F, 1.0E32F,
	 1.0E33F, 1.0E34F, 1.0E35F, 1.0E36F, 1.0E37F, 1.0E38F };
	static constexpr int minfltexp = -38;
	static constexpr int maxfltexp = 38;
	static constexpr int mindblexp = -308;
	static constexpr int maxdblexp = 308;
	static constexpr int maxsigdigits = 17;
	static constexpr int maxdigl = 9; // max digits in a long
	static constexpr int maxexpdig = 3; // max digits in an exponent
	static double tenD(Int, int);
	static float tenF(Int, int);
	static int ckaccum(double &, Int, int);
	static int ckaccum(float &, Int, int);
};

inline FITS::FITS() { } // just a dummy function to prevent instantiation
inline Bool FITS::isa_digit(char c) { return isdigit(c) ? True : False; }
inline int FITS::digit2bin(char c) { return c - '0'; }
inline Bool FITS::isa_text(char c) { return isprint(c) ? True : False; }
inline Bool FITS::isa_letter(char c) { return isupper(c) ? True : False; }
inline int FITS::letter2bin(char c) { return c - 'A'; }

ostream & operator << (ostream &, const FITS::ValueType &);

inline double FITS::tenD(Int numb, int pow) {
	return (pow > 0) ? (((double)numb) * tenpowerD[pow]) :
	    ((pow < 0) ? (((double)numb) / tenpowerD[-pow]) : ((double)numb));
}
inline float FITS::tenF(Int numb, int pow) {
	return (pow > 0) ? (((float)numb) * tenpowerF[pow]) :
	    ((pow < 0) ? (((float)numb) / tenpowerF[-pow]) : ((float)numb));
}

//<summary> reserved FITS keyword </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>

class ReservedFitsKeyword {
    public:
	const char *aname() const;
	FITS::ReservedName name() const;
	int namesize() const;
	FITS::ValueType type() const;
	Bool isindexed() const;
	Bool isessential() const;
# if defined(TURBOCPP)
	// It is best for the following to be private, but 
	// C-Front won't allow an initializer list if they are private.
	// This issue isn't that crucial since functions in 
	// ReservedFitsKeywordCollection always return const items.
    private:
# endif
	FITS::ReservedName name_;
	const char *aname_;
	int namesize_;
	FITS::ValueType type_;
	Bool isindexed_; // 0 = NOT INDEXED, 1 = INDEXED
	Bool isessential_; // 0 = NO, 1 = YES
};

inline const char *ReservedFitsKeyword::aname() const { return aname_; }
inline int ReservedFitsKeyword::namesize() const { return namesize_; }
inline FITS::ValueType ReservedFitsKeyword::type() const { return type_; }
inline Bool ReservedFitsKeyword::isindexed() const { return isindexed_; }
inline Bool ReservedFitsKeyword::isessential() const { 
	return isessential_; }

//<summary> collection of reserved FITS keywords </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>

class ReservedFitsKeywordCollection {
    public:
	const ReservedFitsKeyword & operator [] (int i) const;
	int no() const;
	const ReservedFitsKeyword &get(FITS::ReservedName, Bool, FITS::ValueType,
		const void *, int, const char *&) const;
	const ReservedFitsKeyword &get(const char *, int, Bool, FITS::ValueType,
		const void *, int, const char *&) const;
	const char *aname(FITS::ReservedName) const;
	int essential_name(const char *, int) const;
	const ReservedFitsKeyword &get_essential(int, Bool, FITS::ValueType,
		const void *, int, const char *&) const;
	int isreserved(const char *, int) const;
	Bool isunique(int) const;
	Bool requires_value(int) const;
	const ReservedFitsKeyword &userdef_item() const;
	const ReservedFitsKeyword &err_item() const;
	const ReservedFitsKeyword &end_item() const;
	const ReservedFitsKeyword &spaces() const;
	const ReservedFitsKeyword &comment() const;
	const ReservedFitsKeyword &history() const;
	int rules(const ReservedFitsKeyword &, const char *, int, Bool,
		FITS::ValueType, const void *, int, const char *&) const;
    private:
	static constexpr int no_items = 56; // number of entries in the table
  //	Discussion of Reserved FitsKeyword Table
  //
  //	1. The reserved name itself (name_) is not unique;  there may
  //	   be more than one entry with the same reserved name.
  //	2. The combination of reserved name, the data type, and whether
  //	   it is indexed or not (name_, type_, isindexed_) is unique
  //	   within the table.
  //	3. The table is sorted by reserved name + type + isindexed.
	static constexpr ReservedFitsKeyword resword[56] = {
//        key            aname      namesize         isindexed           Section
//         |               |           |  type           | isessential   in|NOST
//        \|/             \|/         \|/\|/            \|/     \|/      \|/
//         ------          ------      -  -------        -----  -----    ------------
/*  0 */ { FITS::USER_DEF, "",         0, FITS::NOVALUE, False, False },
/*  1 */ { FITS::AUTHOR,   "AUTHOR",   6, FITS::STRING,  False, False }, // 5.2.2.3
/*  2 */ { FITS::BITPIX,   "BITPIX",   6, FITS::LONG,    False, True },  // 5.2.1.1
/*  3 */ { FITS::BLANK,    "BLANK",    5, FITS::LONG,    False, False }, // 5.2.2.5
/*  4 */ { FITS::BLOCKED,  "BLOCKED",  7, FITS::LOGICAL, False, False }, // 5.2.2.1
/*  5 */ { FITS::BSCALE,   "BSCALE",   6, FITS::REAL,    False, False }, // 5.2.2.5
/*  6 */ { FITS::BUNIT,    "BUNIT",    5, FITS::STRING,  False, False }, // 5.2.2.5
/*  7 */ { FITS::BZERO,    "BZERO",    5, FITS::REAL,    False, False }, // 5.2.2.5
/*  8 */ { FITS::CDELT,    "CDELT",    5, FITS::REAL,    True, False },  // 5.2.2.5
/*  9 */ { FITS::COMMENT,  "COMMENT",  7, FITS::NOVALUE, False, False }, // 5.2.2.4
/* 10 */ { FITS::CROTA,    "CROTA",    5, FITS::REAL,    True, False },  // 5.2.2.5
/* 11 */ { FITS::CRPIX,    "CRPIX",    5, FITS::REAL,    True, False },  // 5.2.2.5
/* 12 */ { FITS::CRVAL,    "CRVAL",    5, FITS::REAL,    True, False },  // 5.2.2.5
/* 13 */ { FITS::CTYPE,    "CTYPE",    5, FITS::STRING,  True, False },  // 5.2.2.5
/* 14 */ { FITS::DATAMAX,  "DATAMAX",  7, FITS::REAL,    False, False }, // 5.2.2.5
/* 15 */ { FITS::DATAMIN,  "DATAMIN",  7, FITS::REAL,    False, False }, // 5.2.2.5
/* 16 */ { FITS::DATE,     "DATE",     4, FITS::STRING,  False, False }, // 5.2.2.1
/* 17 */ { FITS::DATE_OBS, "DATE-OBS", 8, FITS::STRING,  False, False }, // 5.2.2.2
/* 18 */ { FITS::END,      "END",      3, FITS::NOVALUE, False, True },  // 5.2.1.1
/* 19 */ { FITS::EPOCH,    "EPOCH",    5, FITS::REAL,    False, False }, // 5.2.2.2
/* 20 */ { FITS::EQUINOX,  "EQUINOX",  7, FITS::REAL,    False, False }, // 5.2.2.2
/* 21 */ { FITS::EXTEND,   "EXTEND",   6, FITS::LOGICAL, False, True },  // 5.2.1.2
/* 22 */ { FITS::EXTLEVEL, "EXTLEVEL", 8, FITS::LONG,    False, False }, // 5.2.2.6
/* 23 */ { FITS::EXTNAME,  "EXTNAME",  7, FITS::STRING,  False, False }, // 5.2.2.6
/* 24 */ { FITS::EXTVER,   "EXTVER",   6, FITS::LONG,    False, False }, // 5.2.2.6
/* 25 */ { FITS::GCOUNT,   "GCOUNT",   6, FITS::LONG,    False, True },  // 5.2.1.2
/* 26 */ { FITS::GROUPS,   "GROUPS",   6, FITS::LOGICAL, False, True },  // 7.1.1.6
/* 27 */ { FITS::HISTORY,  "HISTORY",  7, FITS::NOVALUE, False, False }, // 5.2.2.4
/* 28 */ { FITS::INSTRUME, "INSTRUME", 8, FITS::STRING,  False, False }, // 5.2.2.2
/* 29 */ { FITS::NAXIS,    "NAXIS",    5, FITS::LONG,    False, True },  // 5.2.1.1
/* 30 */ { FITS::NAXIS,    "NAXIS",    5, FITS::LONG,    True, True },   // 5.2.1.1
/* 31 */ { FITS::OBJECT,   "OBJECT",   6, FITS::STRING,  False, False }, // 5.2.2.2
/* 32 */ { FITS::OBSERVER, "OBSERVER", 8, FITS::STRING,  False, False }, // 5.2.2.2
/* 33 */ { FITS::ORIGIN,   "ORIGIN",   6, FITS::STRING,  False, False }, // 5.2.2.1
/* 34 */ { FITS::PCOUNT,   "PCOUNT",   6, FITS::LONG,    False, True },  // 5.2.1.2
/* 35 */ { FITS::PSCAL,    "PSCAL",    5, FITS::REAL,    True, False },  // 7.1.2.2
/* 36 */ { FITS::PTYPE,    "PTYPE",    5, FITS::STRING,  True, False },  // 7.1.2.1
/* 37 */ { FITS::PZERO_FITS,    "PZERO",    5, FITS::REAL,    True, False },  // 7.1.2.3
/* 38 */ { FITS::REFERENC, "REFERENC", 8, FITS::STRING,  False, False }, // 5.2.2.3
/* 39 */ { FITS::SIMPLE,   "SIMPLE",   6, FITS::LOGICAL, False, True },  // 5.2.1.1
/* 40 */ { FITS::SPACES,   "        ", 8, FITS::NOVALUE, False, False }, // 5.2.2.4
/* 41 */ { FITS::TBCOL,    "TBCOL",    5, FITS::LONG,    True, False },  // 8.1.1
/* 42 */ { FITS::TDIM,     "TDIM",     4, FITS::STRING,  True, False },  // A.4, A.9.1
/* 43 */ { FITS::TDISP,    "TDISP",    5, FITS::STRING,  True, False },  // A.4
/* 44 */ { FITS::TELESCOP, "TELESCOP", 8, FITS::STRING,  False, False }, // 5.2.2.2
/* 45 */ { FITS::TFIELDS,  "TFIELDS",  7, FITS::LONG,    False, False }, // 8.1.1
/* 46 */ { FITS::TFORM,    "TFORM",    5, FITS::STRING,  True, False },  // 8.1.1
/* 47 */ { FITS::THEAP,    "THEAP",    5, FITS::LONG,    False, False }, // A.4, A.9.2
/* 48 */ { FITS::TNULL,    "TNULL",    5, FITS::STRING,  True, False },  // 8.1.2
/* 49 */ { FITS::TNULL,    "TNULL",    5, FITS::LONG  ,  True, False },  // A.4
/* 50 */ { FITS::TSCAL,    "TSCAL",    5, FITS::REAL,    True, False },  // 8.1.2
/* 51 */ { FITS::TTYPE,    "TTYPE",    5, FITS::STRING,  True, False },  // 8.1.2
/* 52 */ { FITS::TUNIT,    "TUNIT",    5, FITS::STRING,  True, False },  // 8.1.2
/* 53 */ { FITS::TZERO,    "TZERO",    5, FITS::REAL,    True, False },  // 8.1.2
/* 54 */ { FITS::XTENSION, "XTENSION", 8, FITS::STRING,  False, True },  // 5.2.1.2
/* 55 */ { FITS::ERRWORD,  "",         0, FITS::NOVALUE, False, False }  // last
};
	static constexpr const ReservedFitsKeyword &user_def_item = resword[0]; // user-defined keyword
	static constexpr const ReservedFitsKeyword &error_item = resword[55]; // error in keyword
	static constexpr const ReservedFitsKeyword &end__item = resword[18];
	static constexpr const ReservedFitsKeyword &spaces_item = resword[40];
	static constexpr const ReservedFitsKeyword &comment_item = resword[9];
	static constexpr const ReservedFitsKeyword &history_item = resword[27];
  // alphabetic index to table
	static constexpr int resalpha[26] = {
     // A  B  C   D   E  F   G   H   I  J  K  L  M   N   O   P  Q   R
	1, 2, 8, 14, 18, 0, 25, 27, 28, 0, 0, 0, 0, 29, 31, 34, 0, 38,
     //  S   T  U  V  W   X  Y  Z
	39, 41, 0, 0, 0, 54, 0, 0
};
	const ReservedFitsKeyword &match(int, const char *, int, Bool,
		FITS::ValueType, const void *, int, const char *&) const;

};

inline const ReservedFitsKeyword & ReservedFitsKeywordCollection::
	operator [] (int i) const { return resword[i]; }
inline int ReservedFitsKeywordCollection::no() const { return no_items; }
inline Bool ReservedFitsKeywordCollection::isunique(int i) const {
	return (Bool)(resword[i + 1].name() != resword[i].name()); }
inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::userdef_item()
	const { return user_def_item; }
inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::err_item() 
	const { return error_item; }
inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::end_item()
	const { return end__item; }
inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::spaces()
	const { return spaces_item; }
inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::comment()
	const { return comment_item; }
inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::history()
	const { return history_item; }

//<summary> analyse the name of a header card </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// Analyse the name of a header card
//</synopsis>

class FitsNameResult {
    public:
	Bool isaname;	// 1 if there is a name present, otherwise 0
	int begpos;	// beginning position of name
	int endpos;	// ending position of name
	Bool isaindex;	// whether an index is present or not
	int index;	// index if present
	int len;	// length of name without index
	enum ErrMsg { OK = 0, NO_0_NDX };
	ErrMsg err;
};

//<summary> analyse the value of a header card </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// Analyse the value of a header card
//</synopsis>

class FitsValueResult {
    public:
    	FITS::ValueType type;
	union {
    	    Bool b;
	    int s[2];	// for strings, s[0] is offset, s[1] length
    	    Int l;
    	    float f;
    	    double d;
	};
    	Complex c;
    	IComplex lc;
    	DComplex dc;
    	int begpos;		// beginning position of value
	int endpos;		// ending position of value
	Bool isa_point;		// 1 if a point, otherwise 0
	int pointpos;		// position of point, if any
    	int no_sig;		// number of significant digits
    	const char *errmsg;	// error message, if any
};

//<summary> parse a header card </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis> 
//  parse a header card 
//</synopsis>

class FitsParse {
	friend class FitsKeywordList;
    public:
	FitsKeyword &parse(const char *, int); // Parsing one string
	int no_errs() const;
	const char *err(int) const;
    private:
	FitsParse(int = 10);
	~FitsParse();
	int no_errs_;
	const int max_errs;
	const char **err_;
	int seterr(const char *);
	FitsKeyword &mkerr(const char *s, int len);
};

inline FitsParse::~FitsParse() { delete [] err_; }
inline int FitsParse::no_errs() const { return no_errs_; }
inline const char *FitsParse::err(int i) const { return err_[i]; }
inline int FitsParse::seterr(const char *s) {
	return no_errs_ < max_errs ? ( err_[no_errs_++] = s, 0) : -1; }

//<summary> FITS keyword </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// A FITS keyword contains a name, a value and a comment.
//</synopsis>
class FitsKeyword {
	friend class FitsKeywordList;
	friend class FitsParse;
	// A word about friends:  FitsKeywordList accesses the next and prev
	// pointers and the FitsKeyword constructors. 
	// FitsParse only accesses the FitsKeyword constructors.

    public:

	FitsKeyword(const FitsKeyword &);
	FitsKeyword & operator = (const FitsKeyword &);
	~FitsKeyword();

	//<group>
	// get info about the name
	const char *name() const;
	int namelen() const;
	Bool isreserved() const;
	Bool isindexed() const;
	const ReservedFitsKeyword &kw() const;
	int index() const;
	//</group>

	//<group>
	// access the keyword comment
	const char *comm() const;
	int commlen() const;
	//</group>

	// access the error status
	int err() const;

	// the datatype of the keyword
	FITS::ValueType type() const;

	// access the value of the keyword
	//<group>
	Bool asBool() const;
        const char *asString() const;
	int valStrlen() const;
	Int asInt() const;
	float asFloat() const;
	double asDouble() const;
	IComplex asIComplex() const;
	Complex asComplex() const;
	DComplex asDComplex() const;
	const void *value() const;
	//</group>

	// change the value of the keyword
	//<group>
	FitsKeyword & operator = (Bool);
	FitsKeyword & operator = (const char *);
	FitsKeyword & operator = (Int);
	FitsKeyword & operator = (float);
	FitsKeyword & operator = (double);
	FitsKeyword & operator = (IComplex);
	FitsKeyword & operator = (Complex);
	FitsKeyword & operator = (DComplex);
	//</group>

	// change the comment of the keyword
	void comm(const char *);

	// change the name of the keyword
        void name(const char *);

    private:
	FitsKeyword *next_;
	FitsKeyword *prev_;

	//<group>
	// the keyword name
	// if name_ is 0, keyword is not a user defined name
	// if ndx is 0, there is no index
	char *name_;
	const ReservedFitsKeyword *kw_;
	int ndx;
	short namelen_;
	//</group>

	//<group>
	// the keyword comment
	// if comm_ is 0, there is no comment
	char *comm_;
	short commlen_;
	//</group>


	//<group>
	// the keyword value
	FITS::ValueType type_;
	union {
	    Bool bval;
	    Int ival;
	    float fval;
	    double dval;
	};
	void *val; // pointer to allocated value, if any
	short vallen; // only used for string data
	void del_val(); // does an appropriate delete based on type
	//</group>

	void init(const FitsKeyword &);
	void setval(const FITS::ValueType &, const void *, int);
        void setcomm(const char *, int);
	static void err(const char *, const FITS::ValueType &, const void *,
		 const char *);
	static void memchk(void *);

	//<group>
	// private constructors for use by friends

	// constructs user-defined keywords
        // parms: name, namelen, type, val, vallen, comm, commlen
	FitsKeyword(const char *, int , 
		FITS::ValueType, const void *, int, const char *, int);
	// constructs reserved keywords
        // parms: resword, index, val, vallen, comm, commlen
	FitsKeyword(const ReservedFitsKeyword *, int,
		FITS::ValueType, const void *, int, const char *, int);
	//</group>


};

ostream & operator << (ostream &, const FitsKeyword &);

inline FitsKeyword::FitsKeyword(const FitsKeyword &k) : next_(0), prev_(0),
	name_(0), kw_(0), comm_(0), val(0)  { init(k); }
inline FitsKeyword & FitsKeyword::operator = (const FitsKeyword &k) { 
    	delete [] name_; delete [] comm_; del_val(); init(k); return *this; }
inline FitsKeyword::~FitsKeyword() { 
	delete [] name_; 
	delete [] comm_; 
        del_val(); 
}

inline const ReservedFitsKeyword &FitsKeyword::kw() const { return *kw_; }
inline Bool FitsKeyword::isreserved() const { return 
	(kw().name() != FITS::ERRWORD && kw().name() != FITS::USER_DEF)
		 ? True : False; }
inline const char *FitsKeyword::name() const {
    	return isreserved() ? kw().aname() : (namelen_ ? name_ : ""); }
inline int FitsKeyword::namelen() const { return namelen_; }
inline Bool FitsKeyword::isindexed() const {return ndx > 0 ? True : False;}
inline int FitsKeyword::index() const { return ndx; }

inline const char *FitsKeyword::comm() const {
    return comm_ ? comm_  : ""; }
inline int FitsKeyword::commlen() const { return commlen_; }
inline int FitsKeyword::err() const { return (kw().name() == FITS::ERRWORD); }
inline FITS::ValueType FitsKeyword::type() const { return type_; }

inline Bool FitsKeyword::asBool() const { return bval; }
inline const char *FitsKeyword::asString() const {
	return vallen ? (const char *)val : ""; }
inline int FitsKeyword::valStrlen() const { return vallen; }
inline Int FitsKeyword::asInt() const { 
	if( type() != FITS::LONG ) {
		cerr << "Unexpected keyword type in FitsKeyword::asInt()\n";
		exit(1);
	}
	return ival;
}
inline float FitsKeyword::asFloat() const { 
	switch( type() ) { 
		case FITS::BYTE:
		case FITS::SHORT:
		case FITS::LONG: return (float)ival;
		case FITS::FLOAT: return fval;
		case FITS::DOUBLE: return (float)dval;
		default:
			cerr << "Unexpected keyword type in asFloat()\n";
			exit(1);
	}
	return 0.0;
}
inline double FitsKeyword::asDouble() const { 
	switch( type() ) { 
		case FITS::BYTE:
		case FITS::SHORT:
		case FITS::LONG: return (double)ival;
		case FITS::FLOAT: return (double)fval;
		case FITS::DOUBLE: return dval;
		default:
			cerr << "Unexpected keyword type in asDouble()\n";
			exit(1);
	}
	return 0.0;
}
inline IComplex FitsKeyword::asIComplex() const {
	return *((IComplex *)val); }
inline Complex FitsKeyword::asComplex() const {
	return *((Complex *)val); }
inline DComplex FitsKeyword::asDComplex() const {
	return *((DComplex *)val); }

inline FitsKeyword & FitsKeyword::operator = (Bool x) {
	bval = x; type_ = FITS::LOGICAL; return *this; }
inline FitsKeyword & FitsKeyword::operator = (Int x) {
	ival = x; type_ = FITS::LONG; return *this; }
inline FitsKeyword & FitsKeyword::operator = (float x) {
	fval = x; type_ = FITS::FLOAT; return *this; }
inline FitsKeyword & FitsKeyword::operator = (double x) {
	dval = x; type_ = FITS::DOUBLE; return *this; }
inline FitsKeyword & FitsKeyword::operator = (IComplex x) {
	*((IComplex *)val) = x; type_ = FITS::ICOMPLEX; return *this; }
inline FitsKeyword & FitsKeyword::operator = (Complex x) {
	*((Complex *)val) = x; type_ = FITS::COMPLEX; return *this; }
inline FitsKeyword & FitsKeyword::operator = (DComplex x) {
	*((DComplex *)val) = x; type_ = FITS::DCOMPLEX; return *this; }

class ConstFitsKeywordList; // forward declaration

//<summary> linked list of FITS keywords </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis>
// A linked list of FITS keywords.
//</synopsis>

class FitsKeywordList {
    public:
	FitsKeywordList();
	~FitsKeywordList();
	FitsKeywordList(const FitsKeywordList &);
	FitsKeywordList(ConstFitsKeywordList &);
	FitsKeywordList & operator = (const FitsKeywordList &);

        // Convert the list to a string containing the 80-byte FITS headers.
        std::string toString() const;

        // delete the current keyword (the thing returned by curr()) from the list
	void del();

	// Add (make) a reserved keyword with the given value and optional comment
        // The comment will be truncated if necessary to fit the available space.
        // String values must be less than 69 characters.  String values longer than
        // that will result in an ERROR keyword instead of the desired keyword.
	// <group>
	void mk(FITS::ReservedName k, Bool v, const char *c = 0);
	void mk(FITS::ReservedName k, const char *v = 0, const char *c = 0);
	void mk(FITS::ReservedName k, Int v, const char *c = 0);
	void mk(FITS::ReservedName k, long v, const char *c = 0);
	void mk(FITS::ReservedName k, double v, const char *c = 0);
        // </group>

        // Add (make) an indexed reserved keyword with the given value and optional comment
        // The comment will be truncated if necessary to fit the available space.
        // String values must be less than 69 characters.  String values longer than
        // that will result in an ERROR keyword instead of the desired keyword.
        // <group>
	void mk(int n, FITS::ReservedName k, Bool v, const char *c = 0);
	void mk(int n, FITS::ReservedName k, const char *v, const char *c = 0);
	void mk(int n, FITS::ReservedName k, Int v, const char *c = 0);
	void mk(int n, FITS::ReservedName k, long v, const char *c = 0);
	void mk(int n, FITS::ReservedName k, double v, const char *c = 0);
        // </group>

        // Add (make) a user defined keyword with the given name, value and optional comment.
        // The comment will be truncated if necessary to fit the available space.
        // The name must be no longer than 8 characters.  Names longer than that will 
        // result in an ERROR keyword instead of the desired keyword.
        // String values must no longer than 69 characters.  String values longer than
        // that will result in an ERROR keyword instead of the desired keyword.
        // <group>
	void mk(const char *n, Bool v, const char *c = 0);
	void mk(const char *n, const char *v = 0, const char *c = 0);
	void mk(const char *n, Int v, const char *c = 0);
	void mk(const char *n, long v, const char *c = 0);
	void mk(const char *n, float v, const char *c = 0);
	void mk(const char *n, double v, const char *c = 0);
	void mk(const char *n, Int r, Int i, const char *c = 0);
	void mk(const char *n, float r, float i, const char *c = 0);
	void mk(const char *n, double r, double i, const char *c = 0);
        // </group>

        // add a spaces line
	void spaces(const char *n = 0, const char *c = 0);

        // add a comment card
	void comment(const char *n = 0, const char *c = 0);

        // add a history card
	void history(const char *c = 0);

        // add the end card.  This must be at the end of the list.
	void end();
	
	// Retrieve specific keywords -- these also set the current mark
	//<group>
	// return the i-th keyword -- keyword numbering starts with 0
	FitsKeyword * operator () (int);
	// return first and next non-indexed reserved keyword
	FitsKeyword * operator () (const FITS::ReservedName &);
	FitsKeyword * next(const FITS::ReservedName &);
	// return first and next indexed reserved keyword
	FitsKeyword * operator () (const FITS::ReservedName &, int);
	FitsKeyword * next(const FITS::ReservedName &, int);
	// return first and next user-defined keyword
	FitsKeyword * operator () (const char *);
	FitsKeyword * next(const char *);
	//</group>

	//<group>
	Bool isempty() const;
	void     first();
	void     last();
	FitsKeyword *next();
	FitsKeyword *prev();
	FitsKeyword *curr();
	//</group>

	//<group>
	void delete_all(); 
	int rules(FitsKeyword &, 
		  FITSErrorHandler errhandler = FITSError::defaultHandler);
	int rules(FITSErrorHandler errhandler = FITSError::defaultHandler);
	Bool basic_rules();
	//</group>

	//<group>
	// For parsing a single string
	void parse(const char *, int); 
	int no_parse_errs() const;
	const char *parse_err(int) const;
	//</group>

	void insert(FitsKeyword &);
    private:
	FitsKeyword *beg_;
	FitsKeyword *end_;
	FitsKeyword *pos;
	int total;
	int cursor;
	FitsKeyword &make(const char *nm,
		FITS::ValueType t, const void *v, const char *c);
	FitsKeyword &make(FITS::ReservedName nm,
		FITS::ValueType t, const void *v, const char *c);
	FitsKeyword &make(int ind, FITS::ReservedName nm,
		FITS::ValueType t, const void *v, const char *c);
        // construct an error keyword - this happens when a name is invalid (NULL
        // or more than 8 characters) or a string value is too long (more than
        // 69 characters).  It is the responsibility of the caller to the 
        // several mk functions to ensure that that doesn't happen.  By the time
        // it gets here, it is assumed that such problems are true errors.
        // This is used by the private make functions.
        FitsKeyword &makeErrKeyword(const char *name, FITS::ValueType type, 
				    const void *val, const char *errmsg);
	FitsParse card;
};

ostream & operator << (ostream &o, FitsKeywordList &); // print the entire list

inline FitsKeywordList::FitsKeywordList() : beg_(0), end_(0), pos(0), 
	total(0), cursor(0) { }
inline FitsKeywordList::~FitsKeywordList() { delete_all(); }
inline Bool FitsKeywordList::isempty() const { return total == 0 ? True : False; }
inline void FitsKeywordList::first() { cursor = 0; pos = beg_; }
inline void FitsKeywordList::last() { cursor = total; pos = end_; }
inline FitsKeyword *FitsKeywordList::curr() { return pos; }
inline FitsKeyword *FitsKeywordList::operator () (const FITS::ReservedName &n) {
	first(); return next(n); }
inline FitsKeyword *FitsKeywordList::operator () (const FITS::ReservedName &n,
	int ndx) { first(); return next(n,ndx); }
inline FitsKeyword *FitsKeywordList::operator () (const char *w) {
	first(); return next(w); }
inline void FitsKeywordList::parse(const char *s, int l) {
	insert(card.parse(s,l)); }
inline int FitsKeywordList::no_parse_errs() const { return card.no_errs();}
inline const char *FitsKeywordList::parse_err(int n) const { 
	return card.err(n); }

// FitsKeyword constructors for non-indexed Reserved keywords
inline void FitsKeywordList::mk(FITS::ReservedName k, Bool v, const char *c) {
	insert(make(k,FITS::LOGICAL,&v,c)); }
inline void FitsKeywordList::mk(FITS::ReservedName k, const char *v, 
	const char *c) { insert(make(k,FITS::STRING,v,c)); }
inline void FitsKeywordList::mk(FITS::ReservedName k, Int v, const char *c) {
	insert(make(k,FITS::LONG,&v,c)); }
inline void FitsKeywordList::mk(FITS::ReservedName k, long v, const char *c) {
	insert(make(k,FITS::LONG,&v,c)); }
inline void FitsKeywordList::mk(FITS::ReservedName k, double v, const char *c) {
	insert(make(k,FITS::DOUBLE,&v,c)); }
// FitsKeyword constructors for indexed Reserved keywords
inline void FitsKeywordList::mk(int n, FITS::ReservedName k, Bool v, 
	const char *c) { 
	Bool tmp; tmp = v; insert(make(n,k,FITS::LOGICAL,&tmp,c)); }
inline void FitsKeywordList::mk(int n, FITS::ReservedName k, const char *v, 
	const char *c) { insert(make(n,k,FITS::STRING,v,c)); }
inline void FitsKeywordList::mk(int n, FITS::ReservedName k, Int v, 
	const char *c) { insert(make(n,k,FITS::LONG,&v,c)); }
inline void FitsKeywordList::mk(int n, FITS::ReservedName k, long v, 
	const char *c) { insert(make(n,k,FITS::LONG,&v,c)); }
inline void FitsKeywordList::mk(int n, FITS::ReservedName k, double v, 
	const char *c) { insert(make(n,k,FITS::DOUBLE,&v,c)); }
// FitsKeyword constructors for User-Defined keywords
inline void FitsKeywordList::mk(const char *n, Bool v, const char *c) {
	Bool tmp; tmp = v; insert(make(n,FITS::LOGICAL,&tmp,c)); }
inline void FitsKeywordList::mk(const char *n, const char *v, const char *c) {
	insert(make(n,FITS::STRING,v,c)); }
inline void FitsKeywordList::mk(const char *n, Int v, const char *c) {
	insert(make(n,FITS::LONG,&v,c)); }
inline void FitsKeywordList::mk(const char *n, long v, const char *c) {
	insert(make(n,FITS::LONG,&v,c)); }
inline void FitsKeywordList::mk(const char *n, float v, const char *c) {
	insert(make(n,FITS::FLOAT,&v,c)); }
inline void FitsKeywordList::mk(const char *n, double v, const char *c) {
	insert(make(n,FITS::DOUBLE,&v,c)); }
inline void FitsKeywordList::mk(const char *n, Int r, Int i, const char *c) {
	IComplex v(r,i);
	insert(make(n,FITS::ICOMPLEX,&v,c)); }
inline void FitsKeywordList::mk(const char *n, float r, float i, const char *c)
	{ Complex v(r,i); insert(make(n,FITS::COMPLEX,&v,c)); }
inline void FitsKeywordList::mk(const char *n, double r, double i, 
	const char *c) { DComplex v(r,i);
	insert(make(n,FITS::DCOMPLEX,&v,c)); }
// Additional keyword constructors for commentary, etc.
inline void FitsKeywordList::spaces(const char *n, const char *c) {
	insert((n == 0 ? make(FITS::SPACES,FITS::NOVALUE,0,c) :
	       (c == 0 ? make(FITS::SPACES,FITS::NOVALUE,0,n) : 
			  make(n,FITS::NOVALUE,0,c)))); }
inline void FitsKeywordList::comment(const char *n, const char *c) {
	insert((n == 0 ? make(FITS::COMMENT,FITS::NOVALUE,0,c) :
	       (c == 0 ? make(FITS::COMMENT,FITS::NOVALUE,0,n) :
			  make(n,FITS::NOVALUE,0,c)))); }
inline void FitsKeywordList::history(const char *c) {
	insert(make(FITS::HISTORY,FITS::NOVALUE,0,c)); }
inline void FitsKeywordList::end() {
	insert(make(FITS::END,FITS::NOVALUE,0,0)); }

//<summary> list of read-only FITS keywords </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>

class ConstFitsKeywordList {
    public:
	ConstFitsKeywordList(FitsKeywordList &x) : kw(x) { }

	const FitsKeyword * operator () (int n) { return kw(n); }
	const FitsKeyword * operator () (const FITS::ReservedName &x) {
        	return kw(x); }
	const FitsKeyword * next(const FITS::ReservedName &x) {
		return kw.next(x); }
	const FitsKeyword * operator () (const FITS::ReservedName &x, int n) {
        	return kw(x,n); }
	const FitsKeyword * next(const FITS::ReservedName &x, int n) {
        	return kw.next(x,n); }
	const FitsKeyword * operator () (const char *x) { return kw(x); }
	const FitsKeyword * next(const char *x) { return kw.next(x); }

	Bool isempty() const 		{ return kw.isempty(); }
	void     first() 		{ kw.first(); }
	void     last() 		{ kw.last(); }
	const FitsKeyword *next() 	{ return kw.next(); }
	const FitsKeyword *prev() 	{ return kw.prev(); }
	const FitsKeyword *curr() 	{ return kw.curr(); }

    private:
	FitsKeywordList &kw;
};

//<summary> translator between Keyword lists and fixed FITS cars </summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
//<synopsis> 
// also contains the parser ???
//</synopsis> 

class FitsKeyCardTranslator {
    public:
	FitsKeyCardTranslator(int = 100);
	~FitsKeyCardTranslator();
	FitsKeywordList & parse(const char *, 
		FitsKeywordList &, int, FITSErrorHandler, Bool);
	int build(char *, FitsKeywordList &);
	int no_errs() const;
	const char *err(int) const;
	int err_cardno(int) const;
        static void fmtcard(char *, const FitsKeyword &);
    private:
	int cardno;		// the current card number within record
	static constexpr int FitsCardSize = 80;
	static constexpr int FitsMaxCard = 36;
	static constexpr int FitsRecSize = 2880;
	int max_errs;
	int no_errs_;
	const char **err_;
	int *err_cardno_;
	char *blanks;
};

inline FitsKeyCardTranslator::~FitsKeyCardTranslator() {
	delete [] err_; delete [] err_cardno_; delete [] blanks; }
inline int FitsKeyCardTranslator::no_errs() const { return no_errs_; }
inline const char *FitsKeyCardTranslator::err(int i) const { return err_[i]; }
inline int FitsKeyCardTranslator::err_cardno(int i) const {
	return err_cardno_[i]; }

// <summary>Utility functions for floating point values</summary>
// <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
// </reviewed>
class FitsFPUtil
{
public:
    // These functions are useful to tell if some type is a floating point type.
    // This is useful in a templated function, where the processing can vary
    // depending on whether the type is FP or not (e.g. blank handling).
    // <group>
    static Bool isFP(const float *);
    static Bool isFP(const double *);
    static Bool isFP(const void *);
    // </group>

    // For blanking purposes, we need to be able to get a NaN. The NaN we set
    // is all bits on.
    // <group>
    static void setNaN(double &val);
    static void setNaN(float &val);
    // </group>
};


} //# NAMESPACE CASACORE - END

# endif
