/******************************************************************
 * DPUSER - The Next Generation
 *
 * File:     libfits/fits.h
 * Purpose:  Fits class definition
 * Author:   Thomas Ott
 *
 * Revision history
 * ================
 * 07.03.1999: file created
 ******************************************************************/
 
#ifndef FITS_H
#define FITS_H

#ifdef WIN
#pragma warning (disable: 4244) // disable warning for conversion from double to float, possible loss of data
#endif

#ifdef DPQT_ZLIB
#include <QtZlib/zlib.h>
#else
#include <zlib.h>
#endif /* DPQT_ZLIB */

#ifdef WIN
#define _TCHAR_DEFINED
#endif /* WIN */

#include "fitsio.h"

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "defines.h"
#include "dpuser_utils.h"

#include "dpComplex.h"
#include "math_utils.h"

//#include <fftw.h>

#define NDEAD 100000
#define MAXNAXIS 3

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

#define FITS_ERROR_OK 0
#define FITS_ERROR_MEMORY 1
#define FITS_ERROR_BITPIX 2
#define FITS_ERROR_SIZE 3
#define FITS_ERROR_UNKNOWN 4

typedef unsigned char UCHAR;
typedef unsigned short USHORT;

// C16: complex
// R8:  double
// R4:  float
// NA:  not assigned
// I1:  unsigned char
// I2:  short
// I4:  int
// I8:  long long
typedef enum { C16 = -128, R8 = -64, R4 = -32, NA = 0, I1 = 8, I2 = 16, I4 = 32, I8 = 64 } FitsBitpix;
typedef enum { UNKNOWN, EMPTY, IMAGE, TABLE, BINTABLE, BINTABLEIMAGE } FitsExtensionType;
//typedef enum { Unknown = 0, MemoryAlloc = 1, ArrayMatch = 3, FileRead = 4, FileWrite = 5 } FitsError;

/* struct to hold WCS information */
struct wcsinfo {
	double xrval;     /* O - X reference value           */
	double yrval;     /* O - Y reference value           */
	double xrpix;     /* O - X reference pixel           */
	double yrpix;     /* O - Y reference pixel           */
	double xinc;      /* O - X increment per pixel       */
	double yinc;      /* O - Y increment per pixel       */
	double rot;       /* O - rotation angle (degrees)    */
	char type[10];    /* O - type of projection ('-tan') */
};

int
worldpos (double xpix, double ypix, 
double xref, double yref,
double xrefpix, double yrefpix, double xinc, double yinc, double rot,
double cd1_1, double cd1_2, double cd2_1, double cd2_2,
char *type,
double *xpos, double *ypos);

int pixpos(double xpos, double ypos, double xref, double yref, 
      double xrefpix, double yrefpix, double xinc, double yinc, double rot,
      char *type, double *xpix, double *ypix, int *status);

int
worldpix (double xpos, double ypos,
          double xref, double yref,
          double xrefpix, double yrefpix, double xinc, double yinc, double rot,
          double cd1_1, double cd1_2, double cd2_1, double cd2_2,
          char *type,
          double *xpix, double *ypix);

double pixwave(double pix, double crpix, double crval, double cdelt, bool logwave);
int wavepix(double pix, double crpix, double crval, double cdelt, bool logwave);

/*
 * Global variables
 */

extern int FitsVerbose;
extern float FitsInfinity;
extern long numberOfFits;
extern int FitsInterrupt;
extern bool FitsInfo;
extern long MEDIAN_MEM;

//extern char *dpFitsExceptionReasons[];
//!The exception class for class Fits
//class dpFitsException {
//public:
//	dpFitsException(FitsError why = Unknown) { cause = why; }
//	char *reason() { return dpFitsExceptionReasons[cause]; }
//private:
//	FitsError cause;
//};

// Utility class to define a 1, 2, or 3-dimensional index
class INDEX {
public:
	INDEX() { N = 0; }
    INDEX(const dpint64 &x1, const dpint64 &x2) { X = x1; Y = x2; Z = 0; N = 2; }
    INDEX(const dpint64 &x1, const dpint64 &x2, const dpint64 &x3) { X = x1; Y = x2; Z = x3; N = 3; }
	INDEX(const INDEX &I) { X = I.x(); Y = I.y(); Z = I.z(); N = I.n(); }
    dpint64 x() const { return X; }
    dpint64 y() const { return Y; }
    dpint64 z() const { return Z; }
	int n() const { return N; }
private:
    dpint64 X, Y, Z;
	int N;
};

class dpRANGE {
public:
	dpRANGE() { N = 0; }
    dpRANGE(const dpint64 &x1, const dpint64 &x2) { X1 = x1; X2 = x2; Y1 = 0; Y2 = 0; Z1 = 0; Z2 = 0; N = 1; }
    dpRANGE(const dpint64 &x1, const dpint64 &x2, const dpint64 &y1, const dpint64 &y2) { X1 = x1; X2 = x2; Y2 = y1; Y2 = y2; Z1 = 0; Z2 = 0; N = 2; }
    dpRANGE(const dpint64 &x1, const dpint64 &x2, const dpint64 &y1, const dpint64 &y2, const dpint64 &z1, const dpint64 &z2) { X1 = x1; X2 = x2; Y2 = y1; Y2 = y2; Z1 = z1; Z2 = z2; N = 3; }
    dpint64 x1() const { return X1; }
    dpint64 y1() const { return Y1; }
    dpint64 z1() const { return Z1; }
    dpint64 x2() const { return X2; }
    dpint64 y2() const { return Y2; }
    dpint64 z2() const { return Z2; }
	int n() const { return N; }
private:
    dpint64 X1, X2, Y1, Y2, Z1, Z2;
	int N;
};

//!A class to handle up to 3-dimensional marices.
/*!
The FITS class provides all the standard operations on matrices. Those include
addition, subtraction, multiplication, division, but also taking sine, cosine, ...
of a complete matrix.

Filter creation functions are provided for circular, elliptical, ... filters.
*/

class Fits;

class FitsIndexRef {
public:
	FitsIndexRef(const Fits *source, const INDEX &I) { parent = (Fits *)source; index = I; }

	void operator=(const double &d);
	operator double();
private:
	Fits *parent;
	INDEX index;
};

class FitsRangeRef {
public:
	FitsRangeRef(const Fits *source, const dpRANGE &R) { parent = (Fits *)source; range = R; }

	void operator=(Fits &f);
	void operator=(const double &d);
	operator Fits();
private:
	Fits *parent;
	dpRANGE range;
};

class Fits
{
public:

// Constructor and destructor
	Fits();
	Fits(const Fits &);
	~Fits();

// Creation
    bool create(dpint64 x, dpint64 y, FitsBitpix t = R4);
    bool create(dpint64 x, dpint64 y, dpint64 z, FitsBitpix t = R4);
    bool recreate(dpint64 x, dpint64 y);
    bool recreate(dpint64 x, dpint64 y, dpint64 z);
	bool copy(const Fits &);
    void copyIndex(dpint64, dpint64);
    void swapIndex(dpint64, dpint64);
/*!Copy operator. \see copy*/
	inline void operator=(const Fits & a) { copy(a); }
//	inline void operator=(const FitsRangeRef &r) { r.parent->extractRange(*this, r.range.x1()+1, r.range.x2()+1, r.range.y1()+1, r.range.y2()+1, r.range.z1()+1, r.range.z2()+1); }

// FITS file handling
/*
 * 	bool ReadFits(char *fname);
 * 	bool ReadFits(char *fname, long image);
 * 	bool WriteFits(char *fname);
 * 	long FitsKey(char *fname, char *key);
 */
    int dpseek(long long, int);
    long long dptell();
    dpint64 dpread(void *ptr, dpint64 nmemb);
	bool OpenFITS(const char *fname, bool = true);
	bool CreateFITS(const char *fname);
	void CloseFITS();
    bool ReadFITSData(dpint64=0, dpint64=0, dpint64=0, dpint64=0, dpint64=0, dpint64=0);
	bool ReadFITSCubeImage(int which);
	bool WriteFITSData(void);
    bool ReadFITS(const char *fname, dpint64=0, dpint64=0, dpint64=0, dpint64=0, dpint64=0, dpint64=0);
    bool ReadFITSExtension(const char *fname, int ext, dpint64=0, dpint64=0, dpint64=0, dpint64=0, dpint64=0, dpint64=0);
	int  CountExtensions(const char *fname);
    int  FindExtensionByName(const char *fname, const char *extname);
    int  FindColumnByName(const char *colname);
	dpStringList ListFitsExtensions(const char *fname);
	dpStringList ListTableColumns(const char *fname, const int extension);
    int  CountTableColumns(const char *fname, const int extension);
    bool GetBintableColumn(int column, Fits &result);
    bool GetBintableColumn(int column, dpStringList &result);
    bool GetAsciitableColumn(int column, Fits &result);
    bool GetAsciitableColumn(int column, dpStringList &result);
    nodeEnum GetBintableType(int column);
	bool WriteFITS(const char *fname);
    bool WriteFITSExtension(const char *fname);
    bool updateHeader();
	bool getHeaderInformation();
	bool isSHARPIdata();
	void convertSHARPIdata();
    char* getFileName() const            { return FileName; }
    void  setFileName(const char *fname) { strncpy(FileName, fname, 255); }
    int   getExtensionNumber() const     { return extensionNumber; }
    void  setExtensionNumber(int ext)    { extensionNumber = ext; }
    int   getColumnNumber() const        { return columnNumber; }
    void  setColumnNumber(int col)       { columnNumber = col; }
    char* getColumnName() const          { return columnName; }

	bool WriteBMP(const char *fname);
	static bool WriteBMP(const char *fname, Fits &red, Fits &green, Fits &blue);

// FITS header handling
	void InitHeader(void);
	bool SetIntKey(const char *, int, const char * = NULL);
	bool SetIntKeys(const char *, int *, int);
	bool SetFloatKey(const char *, double, const char * = NULL);
	bool SetFloatKeys(const char *, double *, int);
	bool SetStringKey(const char *, const char *, const char * = NULL);
	bool SetStringKeys(const char *, char **, int);
	bool GetIntKey(const char *, int *);
	bool GetIntKeys(const char *, int *, int);
	bool GetFloatKey(const char *, double *) const;
	bool GetFloatKeys(const char *, double *, int);
    bool GetStringKey(const char *, char *) const;
	bool GetStringKeys(const char *, char **, int);
	bool tfieldWidth(int column, int *bytes, int *repeat, char *type);
	bool DeleteKey(const char *);
    int KeyPosition(const char *);
    bool MoveKey(const char *key, int position);
    bool InsertLine(const char *, bool atend = false);
    int ReadFitsHeader();
    int ReadFitsExtensionHeader(int extendNumber, bool trial = FALSE, bool raw = FALSE);
	bool WriteFitsHeader(FILE *fd);
	void CopyHeader(const Fits &);
	char *GetHeader() const;

// memory allocation
	void initializeMemory();
	bool allocateMemory(dpint64 = 0);
	void freeMemory();
	void setCasts();
//	bool setBitpix(int);
	bool setType(FitsBitpix, double = 1.0, double = 0.0);
	FitsBitpix getType();

// array size information
/*!Compare to another Fits structure*/
	inline bool matches(const Fits & a) {
		return (naxis[1] == a.naxis[1]) &&
		       (naxis[2] == a.naxis[2]) &&
					 (naxis[3] == a.naxis[3]); }
/*!Number of elements*/
    inline dpint64 Nelements(void) const { return n_elements; }
/*!Length of specified axes*/
    inline dpint64 Naxis(const UCHAR which) const {
		if (this == NULL) return 0;
		switch(which) {
			case 0: return naxis[0]; break;
			case 1: return naxis[1]; break;
			case 2: return naxis[2]; break;
			case 3: return naxis[3]; break;
			default: return 0; break;
		}
	}
/*!Set length of specified axes*/
    inline void setNaxis(const UCHAR which, const dpint64 what) {
		switch(which) {
			case 0: naxis[0] = what; break;
			case 1: naxis[1] = what; break;
			case 2: naxis[2] = what; break;
			case 3: naxis[3] = what; break;
			default: break;
		}
	}

/*!Set value of bzero*/
    inline void setBzero(const double &newbzero) { bzero = newbzero; }

	bool hasRefPix(void) const;

/*!Return value at specified position*/
    inline double ValueAt(const dpint64 index) const {
        if ((n_elements < 1) || (index >= n_elements) || (index < 0)) return 0.0;
		switch (membits) {
			case I1: return (double)i1data[index] * bscale + bzero; break;
			case I2: return (double)i2data[index] * bscale + bzero; break;
			case I4: return (double)i4data[index] * bscale + bzero; break;
            case I8: return (double)i8data[index] * bscale + bzero; break;
            case R4: return (double)r4data[index]; break;
			case R8: return r8data[index]; break;
            case C16: return Cabs(cdata[index]); break;
			default: break;
		}
		return 0.0;
	}

/*!Return value at specified sub-pixel position (2D-Case, Fortran-Notation).
 * Uses linear interpolation.*/
    inline double SubPixel(const double x1, const double x2) {
        int j = (int)x1;
        int k = (int)x2;
/*
        if (j < 1) j = 1;
        else if (j >= Naxis(1)) j = Naxis(1) - 1;
        if (k < 1) k = 1;
        else if (k >= Naxis(2)) k = Naxis(2) - 1;
*/
        double fy0 = 0., fy1 = 0., fy2 = 0., fy3 = 0.;
        if (j > 0 && j <= Naxis(1) && k > 0 && k <= Naxis(2)) fy0 = ValueAt(F_I(j, k));
        if (j >= 0 && j < Naxis(1) && k > 0 && k <= Naxis(2)) fy1 = ValueAt(F_I(j+1, k));
        if (j >= 0 && j < Naxis(1) && k >= 0 && k < Naxis(2)) fy2 = ValueAt(F_I(j+1, k+1));
        if (j > 0 && j <= Naxis(1) && k >= 0 && k < Naxis(2)) fy3 = ValueAt(F_I(j, k+1));
        double t = (x1 - j);
        double u = (x2 - k);
        return (1.0 - t) * (1.0 - u) * fy0 + t * (1.0 - u) * fy1 + t * u * fy2 + (1.0 - t) * u * fy3;
    }

/*!error message reporting*/
    inline bool fits_error(const char *msg) { dp_output("%s\n", msg); return FALSE; }
    inline bool reportError(dpint64 status) {
		switch (status) {
			case FITS_ERROR_SIZE: dp_output("The arrays do not match in size\n"); break;
			case FITS_ERROR_MEMORY: dp_output("Memory problem\n"); break;
			case FITS_ERROR_BITPIX: dp_output("BITPIX problem\n"); break;
			case FITS_ERROR_UNKNOWN: dp_output("Unknown error type\n"); break;
			default: break;
		}
			return status > 0 ? FALSE : TRUE; }

/*!progress information*/
	inline void Progress(int percent) { dp_output("progress: %i%% done\n", percent); }

/*!Return linear index of array element x, y, z (fortran-notation)*/
    inline dpint64 at(const dpint64 x, const dpint64 y, const dpint64 z = 1) const {
        return F_I(x, y, z);
	}
	bool setValue(double v, int x, int y, int z = 1);

	bool add(const double & a);
	bool sub(const double & a);
	bool mul(const double & a);
	bool div(const double & a);
	bool power(const double & a);
	bool ipower(const double & a);
	inline bool add(const float & a) { return add((double)a); }
	inline bool sub(const float & a) { return sub((double)a); }
	inline bool mul(const float & a) { return mul((double)a); }
	inline bool div(const float & a) { return div((double)a); }
    bool add(const dpComplex & a);
    bool sub(const dpComplex & a);
    bool mul(const dpComplex & a);
    bool div(const dpComplex & a);
// 	bool power(const dpComplex & a);
	bool add(const Fits & a);
	bool sub(const Fits & a);
	bool mul(const Fits & a);
	bool div(const Fits & a);
//	bool power(const Fits & a);
	bool convolve(const Fits & a);
	bool convol(const Fits & a);
	bool matrix_mul(const Fits & a);
	
	bool add1(const Fits &);
	bool add2(const Fits &);
	bool add3(const Fits &);
	bool sub1(const Fits &);
	bool sub2(const Fits &);
	bool sub3(const Fits &);
	bool mul1(const Fits &);
	bool mul2(const Fits &);
	bool mul3(const Fits &);
	bool div1(const Fits &);
	bool div2(const Fits &);
	bool div3(const Fits &);
	
/*
 * 	inline Fits operator+(const Fits & a) { Fits c(*this); c.add(a); return c; }
 * 	friend inline Fits operator+(const float &a, const Fits &b) { Fits c(b); return c.add(a); }
 * 	inline Fits operator-(const Fits & a) { Fits c(*this); c.sub(a); return c; }
 * 	friend inline Fits operator-(const float &a, const Fits &b)
 * 		{ Fits c(b); c *= -1.0; return(c.add(-a)); }
 * 	inline Fits operator*(const Fits & a) { Fits c(*this); c.mul(a); return c; }
 * 	friend inline Fits operator*(const float &a, const Fits &b) { Fits c(b); return c.mul(a); }
 * 	inline Fits operator/(const Fits & a) { Fits c(*this); c.div(a); return c; }
 * 	friend inline Fits operator/(const float & a, const Fits &b)
 * 		{ Fits c(b);
 * 			for (ULONG i = 0; i < c.n_elements*c.bitpix/R4; i++) {
 * 				if (fabs(c.data[i]) > EPS) c.data[i] = a / c.data[i];
 * 				else c.data[i] = 0.0;
 * 			}
 * 			return(c);
 * 		}
 */
/*!Return value at linear index position*/
    inline double operator[](const dpint64 & a) { return ValueAt(a); }
	FitsIndexRef operator[] (const INDEX &I) { return FitsIndexRef(this, I); }
	FitsRangeRef operator[] (const dpRANGE &R) { return FitsRangeRef(this, R); }

//	operator double() { double rv = -999.; if (setIndex) rv = ValueAt(C_I(whichIndex.x(), whichIndex.y(), whichIndex.z())); setIndex = FALSE; return rv; }
//	const double operator[] (const INDEX &I) const { return ValueAt(C_I(I.x(), I.y(), I.z())); }

/*!Return value at linear index position*/
    inline double operator()(const dpint64 & a) { return ValueAt(a); }
/*!Return value of array element at (a, b) (fortran-notation)*/
    inline double operator()(const dpint64 & a, const dpint64 & b) {
		return(ValueAt(F_I(a, b)));
	}
/*!Return value of array element at (a, b, c) (fortran-notation)*/
    inline double operator()(const dpint64 & a, const dpint64 & b, const dpint64 & c) {
		return(ValueAt(F_I(a, b, c)));
	}
/*
 * 	inline Fits operator()(const int &x1, const int &x2, const int &x3, const int &x4) {
 * 		int i, j, k, l;
 * 		Fits c(x3 - x1 + 1, x4 - x2 + 1, R4);
 * 		
 * 		for (i = x1, k = 0; i <= x3; i++, k++) {
 * 			for (j = x2, l = 0; j <= x4; j++, l++) {
 * 				c(k, l) = data[F_I(i, j)];
 * 			}
 * 		}
 * 		return(c);
 * 	}
 */
/*!Set all elements to value of a*/
	inline void operator=(const double & a) {
        dpint64 i;

		switch (membits) {
			case I1:
				bscale = 1.0;
				bzero = a;
				for (i = 0; i < n_elements; i++) i1data[i] = 0;
				break;
			case I2:
				bscale = 1.0;
				bzero = a;
				for (i = 0; i < n_elements; i++) i2data[i] = 0;
				break;
			case I4:
				bscale = 1.0;
				bzero = a;
				for (i = 0; i < n_elements; i++) i4data[i] = 0;
				break;
            case I8:
                bscale = 1.0;
                bzero = a;
                for (i = 0; i < n_elements; i++) i8data[i] = 0;
                break;
            case R4:
				bscale = 1.0;
				bzero = 0.0;
				for (i = 0; i < n_elements; i++) r4data[i] = (float)a;
				break;
			case R8:
				bscale = 1.0;
				bzero = 0.0;
				for (i = 0; i < n_elements; i++) r8data[i] = a;
				break;
			case C16:
				setType(R8);
				bscale = 1.0;
				bzero = 0.0;
				for (i = 0; i < n_elements; i++) r8data[i] = a;
				break;
			default: break;
		}
	}


	inline Fits &operator+=(const Fits & a) { add(a); return *this; }
	inline Fits &operator-=(const Fits & a) { sub(a); return *this; }
	inline Fits &operator*=(const Fits & a) { mul(a); return *this; }
	inline Fits &operator/=(const Fits & a) { div(a); return *this; }
	inline Fits &operator+=(const float & a) { add(a); return *this; }
	inline Fits &operator-=(const float & a) { sub(a); return *this; }
	inline Fits &operator*=(const float & a) { mul(a); return *this; }
	inline Fits &operator/=(const float & a) { div(a); return *this; }
	inline Fits &operator+=(const double & a) { add(a); return *this; }
	inline Fits &operator-=(const double & a) { sub(a); return *this; }
	inline Fits &operator*=(const double & a) { mul(a); return *this; }
	inline Fits &operator/=(const double & a) { div(a); return *this; }
    inline Fits &operator+=(const dpComplex & a) { add(a); return *this; }
    inline Fits &operator-=(const dpComplex & a) { sub(a); return *this; }
    inline Fits &operator*=(const dpComplex & a) { mul(a); return *this; }
    inline Fits &operator/=(const dpComplex & a) { div(a); return *this; }
	
	Fits operator&(const Fits & a);
	Fits &operator^=(const float & r);
    Fits &operator^=(const dpComplex & r);
	Fits &operator&=(const Fits & a);

/* functions */
	bool Sin(bool = TRUE);
	bool Cos(bool = TRUE);
	bool Tan(bool = TRUE);
	bool Sinh(bool = TRUE);
	bool Cosh(bool = TRUE);
	bool Tanh(bool = TRUE);
	bool Asin(bool = TRUE);
	bool Acos(bool = TRUE);
	bool Atan(bool = TRUE);
	bool Atan2(Fits &arg2, bool = TRUE);
	bool Asinh(bool = TRUE);
	bool Acosh(bool = TRUE);
	bool Atanh(bool = TRUE);
	bool Exp();
	bool Log(const double & base = 10.0);
	bool Ln();
	bool Sqrt();
	bool Abs();

	bool ginvert();
/*!Replace all values by 1/value*/
	inline Fits &invert() {
        dpint64 i;

		if (membits > R4) setType(R4);
		switch (membits) {
			case R4:
				for (i = 0; i < n_elements; i++) {
					if (fabs(r4data[i]) < EPS) r4data[i] = FitsInfinity;
					else r4data[i] = 1.0f / r4data[i];
				}
				break;
			case R8:
				for (i = 0; i < n_elements; i++) {
					if (fabs(r8data[i]) < EPS) r8data[i] = FitsInfinity;
					else r8data[i] = 1.0 / r8data[i];
				}
				break;
			case C16: {
				dpCOMPLEX c;

				c.r = 1.0;
				c.i = 0.0;
				for (i = 0; i < n_elements; i++) {
					if ((fabs(cdata[i].r) < EPS) || (fabs(cdata[i].i) < EPS)) {
						cdata[i].r = FitsInfinity;
						cdata[i].i = FitsInfinity;
					} else cdata[i] = Cdiv(c, cdata[i]);
				}
			}
			default: break;
				break;
		}
		return *this;
	}

/* Range manipulation */
	void extractLinearRange(Fits &source, Fits &indices);
    bool checkRange(int *, int *, int *, int *, int *, int *);
	bool checkRange(int n, long *x);
	bool correctRange(int n, long *x);
    bool setRange(double value, int x1, int x2, int y1, int y2, int z1 = 1, int z2 = -1);
    bool setRawRange(double value, dpint64 x1, dpint64 x2);
	bool setRawRange(double value, const Fits &where);
    bool setRawRange(const Fits &value, const Fits &where);
    bool addRawRange(double value, const Fits &where);
	bool subRawRange(double value, const Fits &where);
	bool mulRawRange(double value, const Fits &where);
	bool divRawRange(double value, const Fits &where);
	int setRange(float value, char *fname);
	bool setRange(Fits & value, int x1, int x2, int y1, int y2, int z1 = 1, int z2 = -1);
	bool extractRange(Fits &result, int x1, int x2, int y1, int y2, int z1, int z2);
	bool extractRange(Fits &result, Fits &indices);
	bool incRange(int n, long *x);
	bool decRange(int n, long *x);
	bool addRange(double v, int n, long *x);
	bool subRange(double v, int n, long *x);
	bool mulRange(double v, int n, long *x);
	bool divRange(double v, int n, long *x);
	bool addRange(const Fits & f, int n, long *x);
	bool subRange(const Fits & f, int n, long *x);
	bool mulRange(const Fits & f, int n, long *x);
	bool divRange(const Fits & f, int n, long *x);
	bool reindex(const Fits &indices);
    dpint64 mask2index();
	void deflate(void);
	bool norm( void );
	bool normub(Fits &result, double min, double max, int method = 0);
	bool norf(int nfact );
	bool clip(double lo, double hi);
	bool inclip(double lo, double hi, double value);
	void limits(long *, long *, long * = NULL, long * = NULL, long * = NULL, long * = NULL);
	void max_pixel(int *x, int *y, int r);
	double max_pos(int *x, int *y, int *z = NULL);
	void max_pos_in_mask(int *x, int *y, const Fits &mask);
	void min_pos(int *x, int *y, int *z = NULL);
	void centroid(double *x, double *y, double *z = NULL);
	bool centroids(Fits &result, Fits &positions, int radius);
    bool gausspos(Fits &result, Fits &positions, int radius);
    bool maxima(Fits &positions, int radius, int flag = 0, float fraction = 1.0);
	double get_max( void );
	double get_min( void );
	unsigned long maxLinearIndex(double *);
	unsigned long minLinearIndex(double *);
	void extractLinearIndex(Fits &, int, int);
	void get_minmax(double *min, double *max);
	void get_minmax(double *min, double *max, double ignore);
    double get_flux(bool check = FALSE, dpint64 *nv = NULL);
    dpint64 get_iflux();
	double get_avg( void );
	double get_avg(double);
	float get_median( void );
	float get_median(float);
        float get_nth(double fraction);
	double get_variance( void );
	double get_stddev( void );
	double get_variance( double );
	double get_stddev( double );
	double get_meandev( void );
	double get_meandev( double );
        Fits &collapse(Fits &source, int axis);
        Fits &collapse(Fits &source, int axis, double ignore);
        Fits &collapse_average(Fits &, int);
        Fits &collapse_average(Fits &, int, double);
        Fits &collapse_variance(Fits &, int);
        Fits &collapse_variance(Fits &, int, double);
        Fits &collapse_stddev(Fits &, int);
        Fits &collapse_stddev(Fits &, int, double);
        Fits &collapse_min(Fits &, int);
        Fits &collapse_min(Fits &, int, double);
        Fits &collapse_max(Fits &, int);
        Fits &collapse_max(Fits &, int, double);
        Fits &collapse_median(Fits &, int);
        Fits &collapse_median(Fits &, int, double);
        Fits &collapse_meddev(Fits &, int);
        float get_meddev( void );
	bool histogram(Fits &data, double min, double max, double binsize = 0.0);
	bool histogram_indices(Fits &data, double min, double max, double binsize = 0.0);
	bool histeq(Fits &data, double min, double max);
	bool createFitsIndex(const Fits &array, bool reverse = FALSE);
	double get_fwhm(int xcenter, int ycenter, int radius = 5);
	bool radial_avg(Fits &result, int x, int y, int radius, int center = 0);
        bool radial_profile(Fits &result, int xcen, int ycen);
        bool elliptical_profile(Fits &result, int xcen, int ycen, double angle, double ratio, int width);
        void makeStat(double *min, double *max, double *flux, double *avg);
	double ap_phot(int x, int y, int radius, int find_max = 0, int search_box = 2);
	void multiple_phot(int *x, int *y, int radius, int n, double *pdata, int find_max = 0, int search_box = 2);
	bool multiple_phot(Fits &positions, int radius, Fits *result, int find_max = 0, int search_box = 2);
	bool velmap(Fits &cube, double center, double fwhm, double threshold = 0.0, int method = 0);
        bool smooth1d(float fwhm, int axis);
	bool smooth(double fwhm, bool toR8 = FALSE);
	bool wien(const Fits & dbeam, float max = 1.0);
	bool quick_wien(const Fits & dbeam, float max = 1.0);
	bool lucy(const Fits & dbeam, int niter, double thresh = 0.000003);
	bool quick_lucy(const Fits & psf, int niter, double thresh = 0.000003);
	void clean(Fits &dbeam, int niter, float gain, Fits *s = NULL);
	bool maxEntropy(const Fits & dbeam, int niter, float thresh = 1e-9);

/* Filter creation */
	bool qgauss(int n1, int n2, float xcen, float ycen, float fwhm, int speed);
    bool qgauss(int n1, int n2, float xcen, float ycen, float fwhm1, float fwhm2, float angle, int speed);
    bool gauss(int n1, int n2, float xcen, float ycen, float fwhm1, float fwhm2 = -1.0, float angle = 0.0);
	bool dgauss(int n1, int n2, double xcen, double ycen, double fwhm1, double fwhm2 = -1.0, double angle = 0.0);
	bool ellipse(int n1, int n2, float xcen, float ycen, float fwhm1, float fwhm2 = -1.0, float angle = 0.0);
	bool circle(int n1, int n2, float xcen, float ycen, float radius);
    bool airy(int n1, int n2, float dia, float scl, float wav, float obscuration);
	bool lftf(int n1, int n2, float x1);
	bool hftf(int n1, int n2, float x1);
	bool cosbell(int n1, int n2, float xcen, float ycen, float radius1, float radius2);
	bool chinese_hat(int n1, int n2, float xcen, float ycen, float r);
	bool apodizer(int n1, int n2, float xcen, float radius1, float radius2);
	bool rect(int n1, int n2, int xcen, int ycen, int width, int height);
	bool delta(int n1, int n2, int x, int y);
	bool moffat(int n1, int n2, float xcen, float ycen, float power, float fwhm1, float fwhm2 = -1.0, float angle = 0.0);
	bool sersic(int n1, int n2, double Re, double x0, double y0, double angle, double q, double n);

	bool rr_ri(const Fits & a, const Fits & b);
	bool rr_aa(const Fits & a, const Fits & b);
	void ri_r( void );
	void ri_i( void );
	void ri_amp( void );
	void ri_pow( void );
	void ri_arg( void );
	void conj( void );
	bool rot90(int angle);
	bool rotate(float angle, float xcen = -1.0, float ycen = -1.0, bool smart = TRUE);
	void rotateWCS(double angle, double xc, double yc);
	bool flip(int axes);
	bool reass(void);
	bool old_reass(void);
	bool fft(void);
	bool correl(const Fits & a);
	bool correl_real(const Fits & a);
	bool mosaic(const char *fname);
	bool shift(int ssx, int ssy, int flag);
	bool ishift(int ssx, int ssy, int ssz);
	bool fshift(float ssx, float ssy, int method);
	double interpolate_pixel(double x, double y, const int kernel_size);
	bool lineshift(int line, float s, int axis);
    bool wrap(int ssx, int ssy, int ssz, bool updateWCS = TRUE);
        bool swapaxes(int newx, int newy, int newz);
    bool polyshift(double ssx, double ssy, const int kernel_size);
	
	bool CubeMedian(const Fits & cube);
	bool CubeMedian(const Fits & cube, float reject);
	bool CubeMedian(const char *, int = 1, int = 0);
	bool CubeMedian(const char *, double reject);
        bool CubeQuantile(const char *fname, double quantile);
        bool CubeQuantile(const Fits &cube, double quantile);
	bool BezierInterpolate(Fits &result, int boxsize);
	
	bool enlarge(int scale, int flag = 0);
	void extract( void );
	void shrink(int fact = 2);
	void shrink(int fact, int axis);
	bool resize(int new1 = -1, int new2 = -1, int new3 = -1);
    bool rebin(int new1, int new2, int new3 = -1);
	bool rebin1d(double xstart, double xend, double xinc);
	bool rebin1d(const Fits &xvalues, double xstart, double xend, double xinc);
	bool rebin1d(const Fits & xvalues, const Fits &newxvalues);
	void cblank(double v = 0.0);
	void czero(double v = 0.0);
	
        int dpixCreate(float threshold, int boxsize = 3, int npass = 3);
        void dpl_apply(char *dname);
//	void dpl_create(float fact, int speed, char *dname);
//	void dpl_map(int xsize, int ysize, char *dname);

	
	bool CubeMinimum(const Fits &);
	bool CubeMaximum(const Fits &);
	bool CubeSum(const Fits &, int first = 1, int last = 0);
	bool ravg(char *templ, int first, int last);
	int wsamaxfind(Fits &result, Fits &mask, int xcen, int ycen, int spnum, float hthresh, int smooth);
	bool wsa(const char *fname, int x, int y, float thresh, int spnum, int smooth, Fits *sky, Fits *flat, Fits *dpl, Fits *mask, Fits *bigmask = NULL, Fits *flag = NULL);
	bool wsastat(const char *fname, int x, int y, float thresh, int spnum, int smooth, Fits *sky, Fits *flat, Fits *dpl, Fits *mask, Fits *bigmask = NULL, Fits *flag = NULL);
	bool ssaaddframe(Fits &frame, int sx, int sy, double fact = 1.0);
	void ssaaddmask(int xf, int yf, int sx, int sy);
	bool ssa(Fits & cube, int x, int y, int method = 0, Fits *sky = NULL, Fits *flat = NULL, Fits *dpl = NULL, Fits *mask = NULL);
	void rssa(char *prefix, int first, int last, int length, char *suffix, int x, int y, char *maskname, char *skyname, char *flatname, char *dplname);
	bool qssa(const char *fname, int x, int y, int method = 0, Fits *sky = NULL, Fits *flat = NULL, Fits *dpl = NULL, Fits *mask = NULL);
	void qssa(Fits & cube, int x, int y, int method = 0, Fits *sky = NULL, Fits *flat = NULL, Fits *dpl = NULL, Fits *mask = NULL);
	bool ShiftAddIntoBigger(Fits &what, int xshift, int yshift);
	void rssa(char *fname, int x, int y, int method = 0, Fits *sky = NULL, Fits *flat = NULL, Fits *dpl = NULL, Fits *mask = NULL);
	bool ssastat(const char *fname, int x, int y, int method, Fits *sky, Fits *flat, Fits *dpl, Fits *mask);
	bool sssa(const char *fname, Fits stats, int method, Fits *sky, Fits *flat, Fits *dpl, Fits *mask);
	void rmedian(char *prefix, int first, int last, int length, char *suffix);
	void minimum(char *prefix, int first, int last, int length, char *suffix);
	void makeflat(char *skyname, char *darkname);
	bool cube2single(char *cfname, char *prefix, int begin, char *suffix);
	
        bool spec3d(Fits &cube, int x, int y, int r1, int r2, int method = 0);
//        bool spec3d(Fits &cube, int x, int y, int r1, int r2, double reject, int method = 0);
        // Return the linear index of the array element at [x, y, z] (fortran-notation)
        // for performance reasons no overflow-check here, do this in higher-level functions!
        inline dpint64 F_I(dpint64 x, dpint64 y=1, dpint64 z=1) const {
            if ((x < 1) || (y < 1) || (z < 1)) return 0;
            if ((x > naxis[1]) || (y > naxis[2]) || (z > naxis[3])) return 0;
            return(x-1+(y-1)*naxis[1]+(z-1)*naxis[1]*naxis[2]);
        }
        // Return the linear index of the array element at [x, y, z] (C-notation)
        // for performance reasons no overflow-check here, do this in higher-level functions!
    inline dpint64 C_I(dpint64 x, dpint64 y=0, dpint64 z=0) const {
        if ((x < 0) || (y < 0) || (z < 0)) return 0;
        if ((x >= naxis[1]) || (y >= naxis[2]) || (z >= naxis[3])) return 0;
        return(x+y*naxis[1]+z*naxis[1]*naxis[2]);
    }

	double getCRPIX(int which) const;
	double getCRVAL(int which) const;
    double getCDELT(int which) const;
    bool isAxisLog(int which);
    double getBSCALE() { return bscale; }
    double getBZERO() { return bzero; }

// comparison functions
    bool dpGT(Fits &a, Fits &b);
    bool dpGE(Fits &a, Fits &b);
    bool dpLT(Fits &a, Fits &b);
    bool dpLE(Fits &a, Fits &b);
    bool dpNE(Fits &a, Fits &b);
    bool dpEQ(Fits &a, Fits &b);
    bool dpGT(Fits &a, double b);
    bool dpGE(Fits &a, double b);
    bool dpLT(Fits &a, double b);
    bool dpLE(Fits &a, double b);
    bool dpNE(Fits &a, double b);
    bool dpEQ(Fits &a, double b);
    bool dpAND(Fits &a, Fits &b);
    bool dpOR(Fits &a, Fits &b);
    bool dpNOT();

    dpint64 where(Fits &a);
	unsigned long where(Fits &source, const char *comparison, double argument);
	bool whereAND(const Fits &arg);
	bool whereOR(const Fits &arg);
	
// Various output functions

	void *dataptr;				// generic datapointer
//	float *data;				// real numbers
	float *r4data;
	double *r8data;
	unsigned char *i1data;
    short *i2data;
	int *i4data;
    long long *i8data;
	dpCOMPLEX *cdata;   // complex numbers
//	int IsValid;					// Do we have valid data?
	unsigned long bytesAllocated; // Amount of memory allocated;
	
// data members connected to FITS files
	long fnaxis[MAXNAXIS+1];   // length of individual axis
	FILE *fd;
    gzFile gfd;
    fitsfile *fitsio_fd;
    int fitsio_status;
    int hdr;
	FitsBitpix membits; // Number of bits per pixel of object in memory
	FitsExtensionType extensionType; // Type of FITS extension

//	double crpix[MAXNAXIS], crval[MAXNAXIS], cdelt[MAXNAXIS], 
//	double crot;
	char crtype[10];
	char crunit[MAXNAXIS][10];
//	bool refpix;
private:
    dpint64 n_elements;        // number of elements in the array
    dpint64 naxis[MAXNAXIS+1];   // length of individual axis
	USHORT bytesPerPixel;    // How many bytes are there to a pixel
	FitsBitpix filebits; // Number of bits per pixel in the Fits file
	double bscale, bzero;
	int status;
	bool ro;
    bool gz;
    bool nocfitsio;
/*!The storage place of the FITS header (HeaderLength + 1 for the trailing 0) */
	char *header;
/*!Length (in bytes) of the FITS header, not including the trailing zero. */
	int HeaderLength;
/* if read in from a file, this is the original file name */
	char *FileName;
    int extensionNumber,
        columnNumber;

    /* if reading a BINTABLE memorize the column name */
public:
    char *columnName;

    bool isCompressed() { return gz; }
};

/*
 * 3D data reduction
 */
	
int megacal3d(Fits & a, const char *obs_run, const char *obs_band);
int calibrate3d(Fits & a, const Fits & calset);
//int makeCube3d(Fits & a, char *fname);
//int makeCube3d(Fits & a, int year, char band, float scale);
int expandCal3d(Fits & a);
bool norm3d(Fits & a, const char *fname);

/*
 * Utility Functions
 */

int DeadpixApply(Fits & a, Fits & dpl, int method = 0, int boxsize = 1);
float Median(Fits & a);
int Boxcar(Fits & a, int size, int method = 0, int method2 = 0);
int get_naxis3(char *fname);
void getFitsFile(char *, Fits &);
float *floatdata(const Fits &);
double *doubledata(const Fits &);

/*
 * Fits cube handling
 */

bool cube_avg(const char *fname, Fits & result, int first = 1, int last = 0);
bool cube_avg(const char *fname, Fits & result, double reject);
bool cube_avg(Fits & cube, Fits & result, int first = 1, int last = 0);
bool cube_avg(Fits & cube, Fits & result, double reject);
bool cube_avg(Fits & cube, Fits & result, int first, int last, double reject);
bool cube_median(char *fname, Fits & result, int first = 1, int last = 0);
bool spiffishift(Fits &in, Fits &result, int band);
bool spifficube(Fits &in, Fits &result, int band);
bool spiffiuncube(Fits &in, Fits &result, int band);
int cube_tile(Fits & cube, int columns);

void swapBytes(void *dataptr, int b, dpint64 n_elements);

/*
 * Statistics etc.
 */

double dp_max(Fits &v, bool check = FALSE);
double dp_max_i(Fits &v, double ignore, bool check = FALSE);
double dp_min(Fits &v, bool check = FALSE);
double dp_min_i(Fits &v, double ignore, bool check = FALSE);
double dp_total(Fits &v, bool check = FALSE);
double dp_total_i(Fits &v, double ignore, bool check = FALSE);
double dp_avg(Fits &v, bool check = FALSE);
double dp_avg_i(Fits &v, double ignore, bool check = FALSE);
void dp_stat(Fits &v, double *min, double *max, double *flux, double *avg, bool check = FALSE);

/*
 * WCS information
 */
wcsinfo readWCSinfo(const Fits &);

void voronoi(Fits &, Fits &, Fits &, double, Fits &, int);

#endif /* FITS_H */
