/******************************************************************
 * DPUSER - The Next Generation
 *
 * File:     libfits/fits_procs.cpp
 * Purpose:  Fits class methods to modify images
 * Author:   Thomas Ott
 *
 * Revision history
 * ================
 * 13.08.1999: file created
 ******************************************************************/
 
#include <algorithm>
#include <gsl/gsl_math.h>
//@#include <fftw.h>
#include <fftw3.h>
#include "fits.h"
#include "math_utils.h"
#include "platform.h"
#include "fitting.h"

void Fits::rotateWCS(double angle, double xc, double yc) {
	if (hasRefPix()) {
		double xr, yr;
		double cos_a, sin_a;
		double cd1_1, cd1_2, cd2_1, cd2_2;
		double rot;

		double x1, x2;
		bool hascd12;

		cos_a = cos((double)angle * M_PI / 180.);
		sin_a = sin((double)angle * M_PI / 180.);
		xr = getCRPIX(1);
		yr = getCRPIX(2);
		cd1_2 = cd2_1 = 0.0;
		cd1_1 = getCDELT(1);
		cd2_2 = getCDELT(2);
		hascd12 = GetFloatKey("CD1_2", &cd1_2);
		GetFloatKey("CD2_1", &cd2_1);
		rot = 0.0;
		if (!hascd12) {
			if (GetFloatKey("CDELT1", &cd1_1) && GetFloatKey("CDELT2", &cd2_2) && GetFloatKey("CROTA2", &rot)) {
				rot = -rot * M_PI / 180.;
				cd2_1 = -sin(rot) * cd2_2;
				cd1_2 = sin(rot) * cd1_1;
				cd1_1 *= cos(rot);
				cd2_2 *= cos(rot);
			}
		}

		SetFloatKey("CRPIX1", xc + cos_a * (xr - xc) - sin_a * (yr - yc));
		SetFloatKey("CRPIX2", yc + sin_a * (xr - xc) + cos_a * (yr - yc));

		dp_debug("cd11 %f cd12 %f cd21 %f cd22 %f angle %f\n", cd1_1, cd1_2, cd2_1, cd2_2, angle);

		SetFloatKey("CD1_1", cos_a * cd1_1 - sin_a * cd1_2);
		SetFloatKey("CD1_2", sin_a * cd1_1 + cos_a * cd1_2);
		SetFloatKey("CD2_1", cos_a * cd2_1 - sin_a * cd2_2);
		SetFloatKey("CD2_2", sin_a * cd2_1 + cos_a * cd2_2);

		DeleteKey("CDELT1");
		DeleteKey("CDELT2");
		DeleteKey("CROTA2");
	}
}

/*!
flip about specified axis
*/

bool Fits::flip(int axes) {
	if ((axes < 1) || (axes > naxis[0])) {
		dp_output("Wrong axes specifier %i for flip.\n", axes);
		return FALSE;
	}
	long x, y, z;
	unsigned long index1, index2;
	double delta;

	if (axes == 1) {
		switch (membits) {
			case I1: {
				unsigned char _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(0, y, z);
						index2 = index1 + Naxis(1) - 1;
						for (x = 0; x < Naxis(1) / 2; x++) {
							_tmp = i1data[index1];
							i1data[index1] = i1data[index2];
							i1data[index2] = _tmp;
							index1++;
							index2--;
						}
					}
				}
			}
			break;
			case I2: {
				short _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(0, y, z);
						index2 = index1 + Naxis(1) - 1;
						for (x = 0; x < Naxis(1) / 2; x++) {
							_tmp = i2data[index1];
							i2data[index1] = i2data[index2];
							i2data[index2] = _tmp;
							index1++;
							index2--;
						}
					}
				}
			}
			break;
			case I4: {
				long _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(0, y, z);
						index2 = index1 + Naxis(1) - 1;
						for (x = 0; x < Naxis(1) / 2; x++) {
							_tmp = i4data[index1];
							i4data[index1] = i4data[index2];
							i4data[index2] = _tmp;
							index1++;
							index2--;
						}
					}
				}
			}
			break;
            case I8: {
                long long _tmp;

                for (z = 0; z < Naxis(3); z++) {
                    for (y = 0; y < Naxis(2); y++) {
                        index1 = C_I(0, y, z);
                        index2 = index1 + Naxis(1) - 1;
                        for (x = 0; x < Naxis(1) / 2; x++) {
                            _tmp = i8data[index1];
                            i8data[index1] = i8data[index2];
                            i8data[index2] = _tmp;
                            index1++;
                            index2--;
                        }
                    }
                }
            }
            break;
            case R4: {
				float _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(0, y, z);
						index2 = index1 + Naxis(1) - 1;
						for (x = 0; x < Naxis(1) / 2; x++) {
							_tmp = r4data[index1];
							r4data[index1] = r4data[index2];
							r4data[index2] = _tmp;
							index1++;
							index2--;
						}
					}
				}
			}
			break;
			case R8: {
				double _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(0, y, z);
						index2 = index1 + Naxis(1) - 1;
						for (x = 0; x < Naxis(1) / 2; x++) {
							_tmp = r8data[index1];
							r8data[index1] = r8data[index2];
							r8data[index2] = _tmp;
							index1++;
							index2--;
						}
					}
				}
			}
			break;
			case C16: {
				double _tmpr, _tmpi;

				for (z = 0; z < Naxis(3); z++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(0, y, z);
						index2 = index1 + Naxis(1) - 1;
						for (x = 0; x < Naxis(1) / 2; x++) {
							_tmpr = cdata[index1].r;
							_tmpi = cdata[index1].i;
							cdata[index1].r = cdata[index2].r;
							cdata[index1].i = cdata[index2].i;
							cdata[index2].r = _tmpr;
							cdata[index2].i = _tmpi;
							index1++;
							index2--;
						}
					}
				}
			}
			break;
			default: break;
		}
		if (GetFloatKey("CRPIX1", &delta)) SetFloatKey("CRPIX1", Naxis(1) - delta + 1);
		if (GetFloatKey("CDELT1", &delta)) SetFloatKey("CDELT1", -delta);
		if (GetFloatKey("CD1_1", &delta)) SetFloatKey("CD1_1", -delta);
		if (GetFloatKey("CD1_2", &delta)) SetFloatKey("CD1_2", -delta);
	} else if (axes == 2) {
		switch (membits) {
			case I1: {
				unsigned char _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (x = 0; x < Naxis(1); x++) {
						index1 = C_I(x, 0, z);
						index2 = C_I(x, Naxis(2) - 1, z);
						for (y = 0; y < Naxis(2) / 2; y++) {
							_tmp = i1data[index1];
							i1data[index1] = i1data[index2];
							i1data[index2] = _tmp;
							index1 += Naxis(1);
							index2 -= Naxis(1);
						}
					}
				}
			}
			break;
			case I2: {
				short _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (x = 0; x < Naxis(1); x++) {
						index1 = C_I(x, 0, z);
						index2 = C_I(x, Naxis(2) - 1, z);
						for (y = 0; y < Naxis(2) / 2; y++) {
							_tmp = i2data[index1];
							i2data[index1] = i2data[index2];
							i2data[index2] = _tmp;
							index1 += Naxis(1);
							index2 -= Naxis(1);
						}
					}
				}
			}
			break;
			case I4: {
				long _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (x = 0; x < Naxis(1); x++) {
						index1 = C_I(x, 0, z);
						index2 = C_I(x, Naxis(2) - 1, z);
						for (y = 0; y < Naxis(2) / 2; y++) {
							_tmp = i4data[index1];
							i4data[index1] = i4data[index2];
							i4data[index2] = _tmp;
							index1 += Naxis(1);
							index2 -= Naxis(1);
						}
					}
				}
			}
			break;
            case I8: {
                long long _tmp;

                for (z = 0; z < Naxis(3); z++) {
                    for (x = 0; x < Naxis(1); x++) {
                        index1 = C_I(x, 0, z);
                        index2 = C_I(x, Naxis(2) - 1, z);
                        for (y = 0; y < Naxis(2) / 2; y++) {
                            _tmp = i8data[index1];
                            i8data[index1] = i8data[index2];
                            i8data[index2] = _tmp;
                            index1 += Naxis(1);
                            index2 -= Naxis(1);
                        }
                    }
                }
            }
            break;
            case R4: {
				float _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (x = 0; x < Naxis(1); x++) {
						index1 = C_I(x, 0, z);
						index2 = C_I(x, Naxis(2) - 1, z);
						for (y = 0; y < Naxis(2) / 2; y++) {
							_tmp = r4data[index1];
							r4data[index1] = r4data[index2];
							r4data[index2] = _tmp;
							index1 += Naxis(1);
							index2 -= Naxis(1);
						}
					}
				}
			}
			break;
			case R8: {
				double _tmp;

				for (z = 0; z < Naxis(3); z++) {
					for (x = 0; x < Naxis(1); x++) {
						index1 = C_I(x, 0, z);
						index2 = C_I(x, Naxis(2) - 1, z);
						for (y = 0; y < Naxis(2) / 2; y++) {
							_tmp = r8data[index1];
							r8data[index1] = r8data[index2];
							r8data[index2] = _tmp;
							index1 += Naxis(1);
							index2 -= Naxis(1);
						}
					}
				}
			}
			break;
			case C16: {
				double _tmpr, _tmpi;

				for (z = 0; z < Naxis(3); z++) {
					for (x = 0; x < Naxis(1); x++) {
						index1 = C_I(x, 0, z);
						index2 = C_I(x, Naxis(2) - 1, z);
						for (y = 0; y < Naxis(2) / 2; y++) {
							_tmpr = cdata[index1].r;
							_tmpi = cdata[index1].i;
							cdata[index1].r = cdata[index2].r;
							cdata[index1].i = cdata[index2].i;
							cdata[index2].r = _tmpr;
							cdata[index2].i = _tmpi;
							index1 += Naxis(1);
							index2 -= Naxis(1);
						}
					}
				}
			}
			break;
			default: break;
		}
		if (GetFloatKey("CRPIX2", &delta)) SetFloatKey("CRPIX2", Naxis(2) - delta + 1);
		if (GetFloatKey("CDELT2", &delta)) SetFloatKey("CDELT2", -delta);
		if (GetFloatKey("CD2_2", &delta)) SetFloatKey("CD2_2", -delta);
		if (GetFloatKey("CD2_1", &delta)) SetFloatKey("CD2_1", -delta);
	} else if (axes == 3) {
		switch (membits) {
			case I1: {
				unsigned char _tmp;
				long inc = Naxis(1) * Naxis(2);

				for (x = 0; x < Naxis(1); x++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(x, y, 0);
						index2 = C_I(x, y, Naxis(3) - 1);
						for (z = 0; z < Naxis(3) / 2; z++) {
							_tmp = i1data[index1];
							i1data[index1] = i1data[index2];
							i1data[index2] = _tmp;
							index1 += inc;
							index2 -= inc;
						}
					}
				}
			}
			break;
			case I2: {
				short _tmp;
				long inc = Naxis(1) * Naxis(2);

				for (x = 0; x < Naxis(1); x++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(x, y, 0);
						index2 = C_I(x, y, Naxis(3) - 1);
						for (z = 0; z < Naxis(3) / 2; z++) {
							_tmp = i2data[index1];
							i2data[index1] = i2data[index2];
							i2data[index2] = _tmp;
							index1 += inc;
							index2 -= inc;
						}
					}
				}
			}
			break;
			case I4: {
				long _tmp;
				long inc = Naxis(1) * Naxis(2);

				for (x = 0; x < Naxis(1); x++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(x, y, 0);
						index2 = C_I(x, y, Naxis(3) - 1);
						for (z = 0; z < Naxis(3) / 2; z++) {
							_tmp = i4data[index1];
							i4data[index1] = i4data[index2];
							i4data[index2] = _tmp;
							index1 += inc;
							index2 -= inc;
						}
					}
				}
			}
			break;
            case I8: {
                long long _tmp;
                long inc = Naxis(1) * Naxis(2);

                for (x = 0; x < Naxis(1); x++) {
                    for (y = 0; y < Naxis(2); y++) {
                        index1 = C_I(x, y, 0);
                        index2 = C_I(x, y, Naxis(3) - 1);
                        for (z = 0; z < Naxis(3) / 2; z++) {
                            _tmp = i8data[index1];
                            i8data[index1] = i8data[index2];
                            i8data[index2] = _tmp;
                            index1 += inc;
                            index2 -= inc;
                        }
                    }
                }
            }
            break;
            case R4: {
				float _tmp;
				long inc = Naxis(1) * Naxis(2);

				for (x = 0; x < Naxis(1); x++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(x, y, 0);
						index2 = C_I(x, y, Naxis(3) - 1);
						for (z = 0; z < Naxis(3) / 2; z++) {
							_tmp = r4data[index1];
							r4data[index1] = r4data[index2];
							r4data[index2] = _tmp;
							index1 += inc;
							index2 -= inc;
						}
					}
				}
			}
			break;
			case R8: {
				double _tmp;
				long inc = Naxis(1) * Naxis(2);

				for (x = 0; x < Naxis(1); x++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(x, y, 0);
						index2 = C_I(x, y, Naxis(3) - 1);
						for (z = 0; z < Naxis(3) / 2; z++) {
							_tmp = r8data[index1];
							r8data[index1] = r8data[index2];
							r8data[index2] = _tmp;
							index1 += inc;
							index2 -= inc;
						}
					}
				}
			}
			break;
			case C16: {
				double _tmpr, _tmpi;
				long inc = Naxis(1) * Naxis(2);

				for (x = 0; x < Naxis(1); x++) {
					for (y = 0; y < Naxis(2); y++) {
						index1 = C_I(x, y, 0);
						index2 = C_I(x, y, Naxis(3) - 1);
						for (z = 0; z < Naxis(3) / 2; z++) {
							_tmpr = cdata[index1].r;
							_tmpi = cdata[index1].i;
							cdata[index1].r = cdata[index2].r;
							cdata[index1].i = cdata[index2].i;
							cdata[index2].r = _tmpr;
							cdata[index2].i = _tmpi;
							index1 += inc;
							index2 -= inc;
						}
					}
				}
			}
			break;
			default: break;
		}
		if (GetFloatKey("CRPIX3", &delta)) SetFloatKey("CRPIX3", Naxis(3) - delta + 1);
		if (GetFloatKey("CDELT3", &delta)) SetFloatKey("CDELT3", -delta);
		if (GetFloatKey("CD3_3", &delta)) SetFloatKey("CD3_3", -delta);
	}
	return TRUE;
}

/*
 * swap axes of array
 */
bool Fits::swapaxes(int newx, int newy, int newz) {
// sanity checks
    if (newx < 1 || newy < 1 || newz < 1 || newx > 3 || newy > 3 || newz > 3) return FALSE;
    if (newx == newy || newx == newz || newy == newz) return FALSE;

    Fits old;
    old.copy(*this);
    setNaxis(1, old.Naxis(newx));
    setNaxis(2, old.Naxis(newy));
    setNaxis(3, old.Naxis(newz));

    int xx[4] = { 0, 0, 0, 0 };
    long c = 0;

// swap data
    switch (membits) {
        case I1:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        i1data[C_I(xx[newx], xx[newy], xx[newz])] = old.i1data[c];
                        c++;
                    }
                }
            }
        break;
        case I2:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        i2data[C_I(xx[newx], xx[newy], xx[newz])] = old.i2data[c];
                        c++;
                    }
                }
            }
        break;
        case I4:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        i4data[C_I(xx[newx], xx[newy], xx[newz])] = old.i4data[c];
                        c++;
                    }
                }
            }
        break;
        case I8:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        i8data[C_I(xx[newx], xx[newy], xx[newz])] = old.i8data[c];
                        c++;
                    }
                }
            }
        break;
        case R4:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        r4data[C_I(xx[newx], xx[newy], xx[newz])] = old.r4data[c];
                        c++;
                    }
                }
            }
        break;
        case R8:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        r8data[C_I(xx[newx], xx[newy], xx[newz])] = old.r8data[c];
                        c++;
                    }
                }
            }
        break;
        case C16:
            for (xx[3] = 0; xx[3] < old.Naxis(3); xx[3]++) {
                for (xx[2] = 0; xx[2] < old.Naxis(2); xx[2]++) {
                    for (xx[1] = 0; xx[1] < old.Naxis(1); xx[1]++) {
                        cdata[C_I(xx[newx], xx[newy], xx[newz])].r = old.cdata[c].r;
                        cdata[C_I(xx[newx], xx[newy], xx[newz])].i = old.cdata[c].i;
                        c++;
                    }
                }
            }
        break;
        default:
            break;
    }

// swap FITS keys
    char key[81], val[81];
    double value;

    if (newx != 1) {
        sprintf(key, "CRPIX%i", newx);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CRPIX1", value);
        else DeleteKey("CRPIX1");
        sprintf(key, "CRVAL%i", newx);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CRVAL1", value);
        else DeleteKey("CRVAL1");
        sprintf(key, "CDELT%i", newx);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CDELT1", value);
        else DeleteKey("CDELT1");
        sprintf(key, "CTYPE%i", newx);
        if (old.GetStringKey(key, val)) SetStringKey("CTYPE1", val);
        else DeleteKey("CTYPE1");
        sprintf(key, "CUNIT%i", newx);
        if (old.GetStringKey(key, val)) SetStringKey("CUNIT1", val);
        else DeleteKey("CUNIT1");
        sprintf(key, "CD%i_%i", newx, newx);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CD1_1", value);
        else DeleteKey("CD1_1");
    }
    if (newy != 2) {
        sprintf(key, "CRPIX%i", newy);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CRPIX2", value);
        else DeleteKey("CRPIX2");
        sprintf(key, "CRVAL%i", newy);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CRVAL2", value);
        else DeleteKey("CRVAL2");
        sprintf(key, "CDELT%i", newy);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CDELT2", value);
        else DeleteKey("CDELT2");
        sprintf(key, "CTYPE%i", newy);
        if (old.GetStringKey(key, val)) SetStringKey("CTYPE2", val);
        else DeleteKey("CTYPE2");
        sprintf(key, "CUNIT%i", newy);
        if (old.GetStringKey(key, val)) SetStringKey("CUNIT2", val);
        else DeleteKey("CUNIT2");
        sprintf(key, "CD%i_%i", newy, newy);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CD2_2", value);
        else DeleteKey("CD2_2");
    }
    if (newz != 3) {
        sprintf(key, "CRPIX%i", newz);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CRPIX3", value);
        else DeleteKey("CRPIX3");
        sprintf(key, "CRVAL%i", newz);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CRVAL3", value);
        else DeleteKey("CRVAL3");
        sprintf(key, "CDELT%i", newz);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CDELT3", value);
        else DeleteKey("CDELT3");
        sprintf(key, "CTYPE%i", newz);
        if (old.GetStringKey(key, val)) SetStringKey("CTYPE3", val);
        else DeleteKey("CTYPE3");
        sprintf(key, "CUNIT%i", newz);
        if (old.GetStringKey(key, val)) SetStringKey("CUNIT3", val);
        else DeleteKey("CUNIT3");
        sprintf(key, "CD%i_%i", newz, newz);
        if (old.GetFloatKey(key, &value)) SetFloatKey("CD3_3", value);
        else DeleteKey("CD3_3");
    }
    sprintf(key, "CD%i_%i", newx, newy);
    if (old.GetFloatKey(key, &value)) SetFloatKey("CD1_2", value);
    else DeleteKey("CD1_2");
    sprintf(key, "CD%i_%i", newx, newz);
    if (old.GetFloatKey(key, &value)) SetFloatKey("CD1_3", value);
    else DeleteKey("CD1_3");
    sprintf(key, "CD%i_%i", newy, newx);
    if (old.GetFloatKey(key, &value)) SetFloatKey("CD2_1", value);
    else DeleteKey("CD2_1");
    sprintf(key, "CD%i_%i", newy, newz);
    if (old.GetFloatKey(key, &value)) SetFloatKey("CD2_3", value);
    else DeleteKey("CD2_3");
    sprintf(key, "CD%i_%i", newz, newx);
    if (old.GetFloatKey(key, &value)) SetFloatKey("CD3_1", value);
    else DeleteKey("CD3_1");
    sprintf(key, "CD%i_%i", newz, newy);
    if (old.GetFloatKey(key, &value)) SetFloatKey("CD3_2", value);
    else DeleteKey("CD3_2");

    return TRUE;
}

/*
bool Fits::flip(int axes) {
	long i, j, ii, jj, n;
	short b;
	unsigned char v, *flipdata;

	if ((axes < 1) || (axes > naxis[0])) {
		dp_output("Wrong axes specifier %i for flip.\n", axes);
		return FALSE;
	}
	b = abs(membits) / 8;
	flipdata = i1data;
	switch (axes) {
		case 1:
			for (j = 1; j <= naxis[2]; j++) {
				for (i = 1; i <= naxis[1] / 2; i++) {
					ii = F_I(i, j) * b;
					jj = F_I(naxis[1] - i + 1, j) * b;
					for (n = 0; n < b; n++) {
						v = flipdata[ii + n];
						flipdata[ii + n] = flipdata[jj + n];
						flipdata[jj + n] = v;
					}
				}
			}
			break;
		case 2:
			for (i = 1; i <= naxis[1]; i++) {
				for (j = 1; j <= naxis[2] / 2; j++) {
					ii = F_I(i, j) * b;
					jj = F_I(i, naxis[2] - j + 1) * b;
					for (n = 0; n < b; n++) {
						v = flipdata[ii + n];
						flipdata[ii + n] = flipdata[jj + n];
						flipdata[jj + n] = v;
					}
				}
			}
			break;
		default: return FALSE; break;
	}
	return TRUE;
}
*/
/*!
Rotate by specified amount of degrees, which is an integer multiple of 90 degrees. This is reversible.
*/

bool Fits::rot90(int angle) {
	int x, y, xx;
	Fits a;
	
	angle -= (angle / 360) * 360;
	if ((angle != 0) && (angle != 90) && (angle != 180) && (angle != 270)) return fits_error("rot90: Invalid angle");
	switch (angle) {
		case 0: break;
		case 270: if (!a.create(naxis[2], naxis[1], membits)) return FALSE;
			a.CopyHeader(*this);
			if (membits > R4) {
				a.bscale = bscale;
				a.bzero = bzero;
			}
			xx = 0;
			switch (membits) {
				case I1:
					for (x = 1; x <= a.naxis[1]; x++) {
						for (y = a.naxis[2]; y > 0; y--, xx++) {
							a.i1data[a.F_I(x, y)] = i1data[xx];
						}
					}
					break;
				case I2:
					for (x = 1; x <= a.naxis[1]; x++) {
						for (y = a.naxis[2]; y > 0; y--, xx++) {
							a.i2data[a.F_I(x, y)] = i2data[xx];
						}
					}
					break;
				case I4:
					for (x = 1; x <= a.naxis[1]; x++) {
						for (y = a.naxis[2]; y > 0; y--, xx++) {
							a.i4data[a.F_I(x, y)] = i4data[xx];
						}
					}
					break;
                case I8:
                    for (x = 1; x <= a.naxis[1]; x++) {
                        for (y = a.naxis[2]; y > 0; y--, xx++) {
                            a.i8data[a.F_I(x, y)] = i8data[xx];
                        }
                    }
                    break;
                case R4:
					for (x = 1; x <= a.naxis[1]; x++) {
						for (y = a.naxis[2]; y > 0; y--, xx++) {
							a.r4data[a.F_I(x, y)] = r4data[xx];
						}
					}
					break;
				case R8:
					for (x = 1; x <= a.naxis[1]; x++) {
						for (y = a.naxis[2]; y > 0; y--, xx++) {
							a.r8data[a.F_I(x, y)] = r8data[xx];
						}
					}
					break;
				case C16:
					for (x = 1; x <= a.naxis[1]; x++) {
						for (y = a.naxis[2]; y > 0; y--, xx++) {
							a.cdata[a.F_I(x, y)].r = cdata[xx].r;
							a.cdata[a.F_I(x, y)].i = cdata[xx].i;
						}
					}
					break;
				default: return FALSE; break;
			}
			copy(a);
			break;
		case 180: switch (membits) {
			case I1: {
				unsigned char tempr;

				for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
					tempr = i1data[x];
					i1data[x] = i1data[y];
					i1data[y] = tempr;
				}
			}
				break;
			case I2: {
                short tempr;

				for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
					tempr = i2data[x];
					i2data[x] = i2data[y];
					i2data[y] = tempr;
				}
			}
				break;
			case I4: {
				long tempr;

				for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
					tempr = i4data[x];
					i4data[x] = i4data[y];
					i4data[y] = tempr;
				}
			}
				break;
            case I8: {
                long long tempr;

                for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
                    tempr = i8data[x];
                    i8data[x] = i8data[y];
                    i8data[y] = tempr;
                }
            }
                break;
            case R4: {
				float tempr;

				for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
					tempr = r4data[x];
					r4data[x] = r4data[y];
					r4data[y] = tempr;
				}
			}
				break;
			case R8: {
				double tempr;

				for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
					tempr = r8data[x];
					r8data[x] = r8data[y];
					r8data[y] = tempr;
				}
			}
				break;
			case C16: {
				double re, im;

				for (x = 0, y = (int)Nelements() - 1; x < (int)Nelements() / 2; x++, y--) {
					re = cdata[x].r;
					im = cdata[x].i;
					cdata[x].r = cdata[y].r;
					cdata[x].i = cdata[y].i;
					cdata[y].r = re;
					cdata[y].i = im;
				}
			}
				break;
			default: return FALSE; break;
		}
			break;
		case 90: if (!a.create(naxis[2], naxis[1], membits)) return FALSE;
			a.CopyHeader(*this);
			if (membits > R4) {
				a.bscale = bscale;
				a.bzero = bzero;
			}
			xx = 0;
			switch (membits) {
				case I1:
					for (x = a.naxis[1]; x > 0; x--) {
						for (y = 1; y <= a.naxis[2]; y++, xx++) {
							a.i1data[a.F_I(x, y)] = i1data[xx];
						}
					}
					break;
				case I2:
					for (x = a.naxis[1]; x > 0; x--) {
						for (y = 1; y <= a.naxis[2]; y++, xx++) {
							a.i2data[a.F_I(x, y)] = i2data[xx];
						}
					}
					break;
				case I4:
					for (x = a.naxis[1]; x > 0; x--) {
						for (y = 1; y <= a.naxis[2]; y++, xx++) {
							a.i4data[a.F_I(x, y)] = i4data[xx];
						}
					}
					break;
                case I8:
                    for (x = a.naxis[1]; x > 0; x--) {
                        for (y = 1; y <= a.naxis[2]; y++, xx++) {
                            a.i8data[a.F_I(x, y)] = i8data[xx];
                        }
                    }
                    break;
                case R4:
					for (x = a.naxis[1]; x > 0; x--) {
						for (y = 1; y <= a.naxis[2]; y++, xx++) {
							a.r4data[a.F_I(x, y)] = r4data[xx];
						}
					}
					break;
				case R8:
					for (x = a.naxis[1]; x > 0; x--) {
						for (y = 1; y <= a.naxis[2]; y++, xx++) {
							a.r8data[a.F_I(x, y)] = r8data[xx];
						}
					}
					break;
				case C16:
					for (x = a.naxis[1]; x > 0; x--) {
						for (y = 1; y <= a.naxis[2]; y++, xx++) {
							a.cdata[a.F_I(x, y)].r = cdata[xx].r;
							a.cdata[a.F_I(x, y)].i = cdata[xx].i;
						}
					}
					break;
				default: return FALSE; break;
			}
			copy(a);
			break;
		default: break;
	}

	rotateWCS((double)angle, Naxis(1) / 2. + 0.5, Naxis(2) / 2. + 0.5);
	
	return TRUE;
}

bool Fits::rotate(float angle, float xcen, float ycen, bool smart)
{
	float xc, xs, ang;
	int i, j, nj, ni, in, jn;
	Fits z, w;
	double rotxcen, rotycen;

	if (smart) {
		if (angle == 0.0) return rot90(0);
		if (angle == 90.0) return rot90(90);
		if (angle == 180.0) return rot90(180);
		if (angle == 270.0) return rot90(270);
	}
//	if (membits != R4) dp_output("Fits::rotate: Bitpix is being changed to R4 - check for R8 and C16.\n");
	if (!setType(R4)) return FALSE;
	if (!z.copy(*this)) return FALSE;
	if (!w.create(naxis[1], naxis[2])) return FALSE;

	ang = -angle * acos(0.0)/90.0;
	xc = cos(ang);
	xs = sin(ang);
	
/* expanding array and initializing */

	w = 0.0;
	if (!z.enlarge(2, 2)) return FALSE;
	
/* rotation into array of original size */

	if ((xcen == -1.0) && (ycen == -1.0)) {
		xcen = naxis[1] / 2.0 - 1.0;
		ycen = naxis[2] / 2.0 - 1.0;
		rotxcen = Naxis(1) / 2. + 0.5;
		rotycen = Naxis(2) / 2. + 0.5;
	} else {
		rotxcen = xcen - 0.5;
		rotycen = ycen - 0.5;
	}
	ni = (int)(2.0*xcen-1.0+0.5);
	nj = (int)(2.0*ycen-1.0+0.5);
	*this = 0.0;
	for (i = 1; i <= z.naxis[1]; i++) {
		for (j = 1; j <= z.naxis[2]; j++) {
			in = (int)(0.5 * ((i-ni) * xc + (j-nj) * xs + ni) + 0.5);
			jn = (int)(0.5 * ((j-nj) * xc - (i-ni) * xs + nj) + 0.5);
			if ((in >= 1) && (in <= naxis[1]) && (jn >= 1) && (jn <= naxis[2])) {
				r4data[F_I(in, jn)] += z.r4data[z.F_I(i, j)];
				w.r4data[w.F_I(in, jn)] += .25;
			}
		}
	}

/* dividing by weights */

	div(w);
	cblank();

	rotateWCS(angle, rotxcen, rotycen);

	return TRUE;
}

bool Fits::shift(int ssx, int ssy, int flag)
{
	int k, l;
	long i, j;
	
	l = naxis[3];
	if (l < 1) l = 1;

	if (flag == 0) {
	}
	if (flag == 1) return wrap(ssx, ssy, 0);
	if (flag == 2) {
		if ((labs(ssx) > naxis[1]) || (labs(ssy) > naxis[2])) {
			*this = 0.0;
		} else {
			int shift;
			
// shift the first axis by an integer amount
			shift = (int)ssx;
			
			if (shift < 0) {
				for (i = -shift; i < naxis[1]; i++) {
					for (j = 0; j < naxis[2]; j++) {
						for (k = 0; k < l; k++) {
							copyIndex(C_I(i + shift, j, k), C_I(i, j, k));
						}
					}
				}
				for (i = naxis[1] + shift; i < naxis[1]; i++) {
					for (j = 0; j < naxis[2]; j++) {
						for (k = 0; k < l; k++) {
							setValue(0.0, i+1, j+1, k+1);
						}
					}
				}
			} else {
				for (i = naxis[1] - 1 - shift; i >= 0; i--) {
					for (j = 0; j < naxis[2]; j++) {
						for (k = 0; k < l; k++) {
							copyIndex(C_I(i + shift, j, k), C_I(i, j, k));
						}
					}
				}
				for (i = 0; i < shift; i++) {
					for (j = 0; j < naxis[2]; j++) {
						for (k = 0; k < l; k++) {
							setValue(0.0, i+1, j+1, k+1);
						}
					}
				}
			}
			
// shift the second axis by an integer amount
			shift = (int)ssy;
			
			if (shift < 0) {
				for (i = 0; i < naxis[1]; i++) {
					for (j = -shift; j < naxis[2]; j++) {
						for (k = 0; k < l; k++) {
							copyIndex(C_I(i, j + shift, k), C_I(i, j, k));
						}
					}
				}
				for (i = 0; i < naxis[1]; i++) {
					for (j = naxis[2] + shift; j < naxis[2]; j++) {
						for (k = 0; k < l; k++) {
							setValue(0.0, i+1, j+1, k+1);
						}
					}
				}
			} else {
				for (i = 0; i < naxis[1]; i++) {
					for (j = naxis[2] - 1 - shift; j >= 0; j--) {
						for (k = 0; k < l; k++) {
							copyIndex(C_I(i, j + shift, k), C_I(i, j, k));
						}
					}
				}
				if (shift > naxis[2]) shift = naxis[2];
				for (i = 0; i < naxis[1]; i++) {
					for (j = 0; j < shift; j++) {
						for (k = 0; k < l; k++) {
							setValue(0.0, i+1, j+1, k+1);
						}
					}
				}
			}
		}
	}

	if (hasRefPix()) {
		SetFloatKey("CRPIX1", getCRPIX(1) + (double)ssx);
		SetFloatKey("CRPIX2", getCRPIX(2) + (double)ssy);
	}

	return TRUE;
}

/*!
Shift the array by an integer amount
*/
bool Fits::ishift(int ssx, int ssy, int ssz) {
	int sx, sy, sz, n;
	long x, y, z, xx;

	if ((labs(ssx) >= naxis[1]) || (labs(ssy) >= naxis[2]) || (labs(ssz) >= naxis[3])) {
		*this = 0.0;
		return TRUE;
	}
	n = naxis[1];
	if (naxis[2] > n) n = naxis[2];
	if (naxis[3] > n) n = naxis[3];
	n *= abs(membits) / 8;
	if (membits == C16) n /= 2;

	sx = -ssx;
	sy = -ssy;
	sz = -ssz;
		
	switch (membits) {
		case I1: {
			unsigned char *tmp;
			tmp = (unsigned char *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::ishift: Could not allocate memory\n");
				return FALSE;
			}
// Shift first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = i1data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if ((xx >= 0) && (xx < naxis[1])) i1data[C_I(x, y, z)] = tmp[xx];
					else i1data[C_I(x, y, z)] = 0;
				}
			}
// Shift second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = i1data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if ((xx >= 0) && (xx < naxis[2])) i1data[C_I(x, y, z)] = tmp[xx];
					else i1data[C_I(x, y, z)] = 0;
				}
			}
// Shift third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = i1data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if ((xx >= 0) && (xx < naxis[3])) i1data[C_I(x, y, z)] = tmp[xx];
					else i1data[C_I(x, y, z)] = 0;
				}
			}
			free(tmp);
		}
			break;
		case I2: {
			short *tmp;
			tmp = (short *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::ishift: Could not allocate memory\n");
				return FALSE;
			}
// Shift first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = i2data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if ((xx >= 0) && (xx < naxis[1])) i2data[C_I(x, y, z)] = tmp[xx];
					else i2data[C_I(x, y, z)] = 0;
				}
			}
// Shift second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = i2data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if ((xx >= 0) && (xx < naxis[2])) i2data[C_I(x, y, z)] = tmp[xx];
					else i2data[C_I(x, y, z)] = 0;
				}
			}
// Shift third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = i2data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if ((xx >= 0) && (xx < naxis[3])) i2data[C_I(x, y, z)] = tmp[xx];
					else i2data[C_I(x, y, z)] = 0;
				}
			}
			free(tmp);
		}
			break;
		case I4: {
			int *tmp;
			tmp = (int *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::ishift: Could not allocate memory\n");
				return FALSE;
			}
// Shift first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = i4data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if ((xx >= 0) && (xx < naxis[1])) i4data[C_I(x, y, z)] = tmp[xx];
					else i4data[C_I(x, y, z)] = 0;
				}
			}
// Shift second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = i4data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if ((xx >= 0) && (xx < naxis[2])) i4data[C_I(x, y, z)] = tmp[xx];
					else i4data[C_I(x, y, z)] = 0;
				}
			}
// Shift third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = i4data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if ((xx >= 0) && (xx < naxis[3])) i4data[C_I(x, y, z)] = tmp[xx];
					else i4data[C_I(x, y, z)] = 0;
				}
			}
			free(tmp);
		}
			break;
        case I8: {
            long long *tmp;
            tmp = (long long *)malloc(n);
            if (tmp == NULL) {
                dp_output("Fits::ishift: Could not allocate memory\n");
                return FALSE;
            }
    // Shift first axis
            if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
                for (x = 0; x < naxis[1]; x++) tmp[x] = i8data[C_I(x, y, z)];
                for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
                    if ((xx >= 0) && (xx < naxis[1])) i8data[C_I(x, y, z)] = tmp[xx];
                    else i8data[C_I(x, y, z)] = 0;
                }
            }
    // Shift second axis
            if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
                for (y = 0; y < naxis[2]; y++) tmp[y] = i8data[C_I(x, y, z)];
                for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
                    if ((xx >= 0) && (xx < naxis[2])) i8data[C_I(x, y, z)] = tmp[xx];
                    else i8data[C_I(x, y, z)] = 0;
                }
            }
    // Shift third axis
            if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
                for (z = 0; z < naxis[3]; z++) tmp[z] = i8data[C_I(x, y, z)];
                for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
                    if ((xx >= 0) && (xx < naxis[3])) i8data[C_I(x, y, z)] = tmp[xx];
                    else i8data[C_I(x, y, z)] = 0;
                }
            }
            free(tmp);
        }
            break;
        case R4: {
			float *tmp;
			tmp = (float *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::ishift: Could not allocate memory\n");
				return FALSE;
			}
// Shift first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = r4data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if ((xx >= 0) && (xx < naxis[1])) r4data[C_I(x, y, z)] = tmp[xx];
					else r4data[C_I(x, y, z)] = 0.0f;
				}
			}
// Shift second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = r4data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if ((xx >= 0) && (xx < naxis[2])) r4data[C_I(x, y, z)] = tmp[xx];
					else r4data[C_I(x, y, z)] = 0.0f;
				}
			}
// Shift third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = r4data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if ((xx >= 0) && (xx < naxis[3])) r4data[C_I(x, y, z)] = tmp[xx];
					else r4data[C_I(x, y, z)] = 0.0f;
				}
			}
			free(tmp);
		}
			break;
		case R8: {
			double *tmp;
			tmp = (double *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::ishift: Could not allocate memory\n");
				return FALSE;
			}
// Shift first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = r8data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if ((xx >= 0) && (xx < naxis[1])) r8data[C_I(x, y, z)] = tmp[xx];
					else r8data[C_I(x, y, z)] = 0.0;
				}
			}
// Shift second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = r8data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if ((xx >= 0) && (xx < naxis[2])) r8data[C_I(x, y, z)] = tmp[xx];
					else r8data[C_I(x, y, z)] = 0.0;
				}
			}
// Shift third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = r8data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if ((xx >= 0) && (xx < naxis[3])) r8data[C_I(x, y, z)] = tmp[xx];
					else r8data[C_I(x, y, z)] = 0.0;
				}
			}
			free(tmp);
		}
			break;
		case C16: {
			double *tmpr, *tmpi;
			tmpr = (double *)malloc(n);
			tmpi = (double *)malloc(n);
			if ((tmpr == NULL) || (tmpi == NULL)) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				if (tmpr != NULL) free(tmpr);
				if (tmpi != NULL) free(tmpi);
				return FALSE;
			}
// Shift first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) {
					tmpr[x] = cdata[C_I(x, y, z)].r;
					tmpi[x] = cdata[C_I(x, y, z)].i;
				}
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if ((xx >= 0) && (xx < naxis[1])) {
						cdata[C_I(x, y, z)].r = tmpr[xx];
						cdata[C_I(x, y, z)].i = tmpi[xx];
					} else {
						cdata[C_I(x, y, z)].r = 0.0;
						cdata[C_I(x, y, z)].i = 0.0;
					}
				}
			}
// Shift second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) {
					tmpr[y] = cdata[C_I(x, y, z)].r;
					tmpi[y] = cdata[C_I(x, y, z)].i;
				}
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if ((xx >= 0) && (xx < naxis[2])) {
						cdata[C_I(x, y, z)].r = tmpr[xx];
						cdata[C_I(x, y, z)].i = tmpi[xx];
					} else {
						cdata[C_I(x, y, z)].r = 0.0;
						cdata[C_I(x, y, z)].i = 0.0;
					}
				}
			}
// Shift third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) {
					tmpr[z] = cdata[C_I(x, y, z)].r;
					tmpi[z] = cdata[C_I(x, y, z)].i;
				}
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if ((xx >= 0) && (xx < naxis[3])) {
						cdata[C_I(x, y, z)].r = tmpr[xx];
						cdata[C_I(x, y, z)].i = tmpi[xx];
					} else {
						cdata[C_I(x, y, z)].r = 0.0;
						cdata[C_I(x, y, z)].i = 0.0;
					}
				}
			}
			free(tmpr);
			free(tmpi);
		}
			break;
		default:
			break;
	}
	if (hasRefPix()) {
		SetFloatKey("CRPIX1", getCRPIX(1) + (double)ssx);
		SetFloatKey("CRPIX2", getCRPIX(2) + (double)ssy);
		SetFloatKey("CRPIX3", getCRPIX(3) + (double)ssz);
	}

	return TRUE;
}

/*!
Shift and wrap the array by an integer amount
*/
bool Fits::wrap(int ssx, int ssy, int ssz, bool updateWCS) {
	int sx, sy, sz, n;
	long x, y, z, xx;

	n = naxis[1];
	if (naxis[2] > n) n = naxis[2];
	if (naxis[3] > n) n = naxis[3];
	n *= abs(membits) / 8;
	if (membits == C16) n /= 2;

	sx = labs(ssx) % naxis[1];
	sy = labs(ssy) % naxis[2];
	sz = labs(ssz) % naxis[3];
	if (ssx > 0) sx *= -1;
	if (ssy > 0) sy *= -1;
	if (ssz > 0) sz *= -1;
//	if (ssx > 0) if (naxis[1] % 2 != 0) sx--;
//	if (ssy > 0) if (naxis[2] % 2 != 0) sy--;
//	if (ssz > 0) if (naxis[3] % 2 != 0) sz--;
	if (sx < 0) sx += naxis[1];
	if (sy < 0) sy += naxis[2];
	if (sz < 0) sz += naxis[3];
		
	switch (membits) {
		case I1: {
			unsigned char *tmp;
			tmp = (unsigned char *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				return FALSE;
			}
// Shift and wrap first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = i1data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if (xx < 0) xx += naxis[1];
					if (xx >= naxis[1]) xx -= naxis[1];
					i1data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = i1data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if (xx < 0) xx += naxis[2];
					if (xx >= naxis[2]) xx -= naxis[2];
					i1data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = i1data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if (xx < 0) xx += naxis[3];
					if (xx >= naxis[3]) xx -= naxis[3];
					i1data[C_I(x, y, z)] = tmp[xx];
				}
			}
			free(tmp);
		}
			break;
		case I2: {
			short *tmp;
			tmp = (short *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				return FALSE;
			}
// Shift and wrap first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = i2data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if (xx < 0) xx += naxis[1];
					if (xx >= naxis[1]) xx -= naxis[1];
					i2data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = i2data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if (xx < 0) xx += naxis[2];
					if (xx >= naxis[2]) xx -= naxis[2];
					i2data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = i2data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if (xx < 0) xx += naxis[3];
					if (xx >= naxis[3]) xx -= naxis[3];
					i2data[C_I(x, y, z)] = tmp[xx];
				}
			}
			free(tmp);
		}
			break;
		case I4: {
            int *tmp;
            tmp = (int *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				return FALSE;
			}
// Shift and wrap first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = i4data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if (xx < 0) xx += naxis[1];
					if (xx >= naxis[1]) xx -= naxis[1];
					i4data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = i4data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if (xx < 0) xx += naxis[2];
					if (xx >= naxis[2]) xx -= naxis[2];
					i4data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = i4data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if (xx < 0) xx += naxis[3];
					if (xx >= naxis[3]) xx -= naxis[3];
					i4data[C_I(x, y, z)] = tmp[xx];
				}
			}
			free(tmp);
		}
			break;
        case I8: {
            long long *tmp;
            tmp = (long long *)malloc(n);
            if (tmp == NULL) {
                dp_output("Fits::wrap: Could not allocate memory\n");
                return FALSE;
            }
    // Shift and wrap first axis
            if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
                for (x = 0; x < naxis[1]; x++) tmp[x] = i8data[C_I(x, y, z)];
                for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
                    if (xx < 0) xx += naxis[1];
                    if (xx >= naxis[1]) xx -= naxis[1];
                    i8data[C_I(x, y, z)] = tmp[xx];
                }
            }
    // Shift and wrap second axis
            if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
                for (y = 0; y < naxis[2]; y++) tmp[y] = i8data[C_I(x, y, z)];
                for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
                    if (xx < 0) xx += naxis[2];
                    if (xx >= naxis[2]) xx -= naxis[2];
                    i8data[C_I(x, y, z)] = tmp[xx];
                }
            }
    // Shift and wrap third axis
            if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
                for (z = 0; z < naxis[3]; z++) tmp[z] = i8data[C_I(x, y, z)];
                for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
                    if (xx < 0) xx += naxis[3];
                    if (xx >= naxis[3]) xx -= naxis[3];
                    i8data[C_I(x, y, z)] = tmp[xx];
                }
            }
            free(tmp);
        }
            break;
        case R4: {
			float *tmp;
			tmp = (float *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				return FALSE;
			}
// Shift and wrap first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = r4data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if (xx < 0) xx += naxis[1];
					if (xx >= naxis[1]) xx -= naxis[1];
					r4data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = r4data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if (xx < 0) xx += naxis[2];
					if (xx >= naxis[2]) xx -= naxis[2];
					r4data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = r4data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if (xx < 0) xx += naxis[3];
					if (xx >= naxis[3]) xx -= naxis[3];
					r4data[C_I(x, y, z)] = tmp[xx];
				}
			}
			free(tmp);
		}
			break;
		case R8: {
			double *tmp;
			tmp = (double *)malloc(n);
			if (tmp == NULL) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				return FALSE;
			}
// Shift and wrap first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) tmp[x] = r8data[C_I(x, y, z)];
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if (xx < 0) xx += naxis[1];
					if (xx >= naxis[1]) xx -= naxis[1];
					r8data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) tmp[y] = r8data[C_I(x, y, z)];
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if (xx < 0) xx += naxis[2];
					if (xx >= naxis[2]) xx -= naxis[2];
					r8data[C_I(x, y, z)] = tmp[xx];
				}
			}
// Shift and wrap third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) tmp[z] = r8data[C_I(x, y, z)];
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if (xx < 0) xx += naxis[3];
					if (xx >= naxis[3]) xx -= naxis[3];
					r8data[C_I(x, y, z)] = tmp[xx];
				}
			}
			free(tmp);
		}
			break;
		case C16: {
			double *tmpr, *tmpi;
			tmpr = (double *)malloc(n);
			tmpi = (double *)malloc(n);
			if ((tmpr == NULL) || (tmpi == NULL)) {
				dp_output("Fits::wrap: Could not allocate memory\n");
				if (tmpr != NULL) free(tmpr);
				if (tmpi != NULL) free(tmpi);
				return FALSE;
			}
// Shift and wrap first axis
			if (sx != 0) for (y = 0; y < naxis[2]; y++) for (z = 0; z < naxis[3]; z++) {
				for (x = 0; x < naxis[1]; x++) {
					tmpr[x] = cdata[C_I(x, y, z)].r;
					tmpi[x] = cdata[C_I(x, y, z)].i;
				}
				for (x = 0, xx = sx; x < naxis[1]; x++, xx++) {
					if (xx < 0) xx += naxis[1];
					if (xx >= naxis[1]) xx -= naxis[1];
					cdata[C_I(x, y, z)].r = tmpr[xx];
					cdata[C_I(x, y, z)].i = tmpi[xx];
				}
			}
// Shift and wrap second axis
			if (sy != 0) for (x = 0; x < naxis[1]; x++) for (z = 0; z < naxis[3]; z++) {
				for (y = 0; y < naxis[2]; y++) {
					tmpr[y] = cdata[C_I(x, y, z)].r;
					tmpi[y] = cdata[C_I(x, y, z)].i;
				}
				for (y = 0, xx = sy; y < naxis[2]; y++, xx++) {
					if (xx < 0) xx += naxis[2];
					if (xx >= naxis[2]) xx -= naxis[2];
					cdata[C_I(x, y, z)].r = tmpr[xx];
					cdata[C_I(x, y, z)].i = tmpi[xx];
				}
			}
// Shift and wrap third axis
			if (sz != 0) for (x = 0; x < naxis[1]; x++) for (y = 0; y < naxis[2]; y++) {
				for (z = 0; z < naxis[3]; z++) {
					tmpr[z] = cdata[C_I(x, y, z)].r;
					tmpi[z] = cdata[C_I(x, y, z)].i;
				}
				for (z = 0, xx = sz; z < naxis[3]; z++, xx++) {
					if (xx < 0) xx += naxis[3];
					if (xx >= naxis[3]) xx -= naxis[3];
					cdata[C_I(x, y, z)].r = tmpr[xx];
					cdata[C_I(x, y, z)].i = tmpi[xx];
				}
			}
			free(tmpr);
			free(tmpi);
		}
			break;
		default:
			break;
	}
    if (updateWCS && hasRefPix()) {
		SetFloatKey("CRPIX1", getCRPIX(1) + (double)ssx);
		SetFloatKey("CRPIX2", getCRPIX(2) + (double)ssy);
		SetFloatKey("CRPIX3", getCRPIX(3) + (double)ssz);
	}
	return TRUE;
}

/*! sub-pixel shift one single line in the array */
bool Fits::lineshift(int line, float s, int axis) {
	float *xdata = NULL, frac1, frac2;
	long i, a, image, images;

	if ((axis < 1) || (axis > naxis[0])) return FALSE;
	if ((line > naxis[axis]) || (line < 1)) return FALSE;
	frac1 = fabs(s);
	frac2 = 1.0 - frac1;
	images = naxis[3];
	if (images < 2) images = 1;

	if (membits != R4) dp_output("Fits::lineshift: Bitpix being changed to R4.\n");
	if (!setType(R4)) return FALSE;

// Reserve data space for one line
	if ((xdata = (float *)malloc(naxis[axis] * sizeof(float))) == NULL) return FALSE;

// deal with first edge
	switch (axis) {
		case 1:
			for (image = 1; image <= images; image++) {
				if (s > 0.0) {
					xdata[0] = r4data[F_I(1, line, image)] * frac2;
					for (i = 1, a = F_I(2, line, image); i < naxis[1]; i++, a++) {
						xdata[i] = r4data[a] * frac2 + r4data[a - 1] * frac1;
					}
				} else {
// deal with second edge
					xdata[naxis[1] - 1] = r4data[F_I(naxis[1], line, image)] * frac1;
					for (i = 0, a = F_I(1, line, image); i < naxis[1] - 1; i++, a++) {
						xdata[i] = r4data[a + 1] * frac1 + r4data[a] * frac2;
					}
				}
				for (i = 0, a = F_I(1, line, image); i < naxis[1]; i++, a++) r4data[a] = xdata[i];
			}
			break;
		case 2:
			for (image = 1; image <= images; image++) {
				if (s > 0.0) {
					xdata[0] = r4data[F_I(line, 1, image)] * frac2;
					for (i = 1, a = F_I(line, 2, image); i < naxis[2]; i++, a += naxis[1]) {
						xdata[i] = r4data[a] * frac2 + r4data[a - naxis[1]] * frac1;
					}
				} else {
// deal with second edge
					xdata[naxis[2] - 1] = r4data[F_I(line, naxis[2], image)] * frac1;
					for (i = 0, a = F_I(line, 1, image); i < naxis[2] - 1; i++, a += naxis[1]) {
						xdata[i] = r4data[a + naxis[1]] * frac1 + r4data[a] * frac2;
					}
				}
				for (i = 0, a = F_I(line, 1, image); i < naxis[2]; i++, a += naxis[1]) r4data[a] = xdata[i];
			}
			break;
		default: break;
	}
	free(xdata);
	return TRUE;
}

/*!
sub-pixel shift the complete array

float ssx, ssy are the shift vectors
int method can have the following values:
    0: Take weighted mean of next pixels
    1: Use fft
    2...: Use n-th order polynomial approximation
*/

bool Fits::fshift(float ssx, float ssy, int method) {
	long i, j, ix, iy;

	if ((method < 0) || (method > 15)) return FALSE;
	ix = iy = 0;
	if (fabs(ssx) > 0.5) {
		ix = (long)rint(ssx);
		ssx -= (float)ix;
	}
	if (fabs(ssy) > 0.5) {
		iy = (long)rint(ssy);
		ssy -= (float)iy;
	}
	
	if ((ix) || (iy)) shift(ix, iy, 2);

	if (method == 1) {
                if (!setType(R4)) return FALSE;
                float xslope, yslope;
		float sx, sy;
		dpCOMPLEX phi0, phi1;
	
		sx = -ssx;
		sy = -ssy;

		xslope = sx * M_PI / ((float)naxis[1] / 2.0);
		yslope = sy * M_PI / ((float)naxis[2] / 2.0);
	
		if (!fft()) return FALSE;
		if (!reass()) return FALSE;
	
		for (i = 1; i <= naxis[1]; i++) {
			phi0 = Complex(cos(yslope*(i-naxis[1]/2)), sin(yslope*(i-naxis[1]/2)));
			for (j = 1; j <= naxis[2]; j++) {
				phi1 = Cmul(phi0, Complex(cos(xslope*(j-naxis[2]/2)),sin(xslope*(j-naxis[2]/2))));
				cdata[F_I(j, i)] = Cmul(cdata[F_I(j, i)], phi1);
			}
		}
	
		if (!reass()) return FALSE;
		if (!fft()) return FALSE;
		div((float)n_elements);
        } else if (method == 0) {
        // This is quick and dirty
                if (!setType(R4)) return FALSE;
                for (i = 1; i <= naxis[2]; i++)
                    lineshift(i, ssx, 1);
                for (i = 1; i <= naxis[1]; i++)
                    lineshift(i, ssy, 2);
            } else if (method == 3) {
        // Alex
        polyshift(ssx,ssy, 4); // equals to polynomial of 3rd degree
	}
	
	if (hasRefPix()) {
		SetFloatKey("CRPIX1", getCRPIX(1) + (double)ssx);
		SetFloatKey("CRPIX2", getCRPIX(2) + (double)ssy);
	}
	return TRUE;
}


// This function is used in Fits::polyshift()
// It checks if the pixel (x,y) is one the border of the image or not
bool isBorder(int mod_kernel_size, int img_size_x, int img_size_y, int border_size, int x, int y) {
    if (mod_kernel_size == 1) {
        // kernel_size uneven
        if ((x < border_size) || (y < border_size)) {
            return true;
        }
        if ((x >= img_size_x - border_size) || (y >= img_size_y - border_size)) {
            return true;
        }
    } else {
        // kernel_size even
        if ((x < border_size - 1) || (y < border_size - 1)) {
            return true;
        }
        if ((x >= img_size_x - border_size) || (y >= img_size_y - border_size)) {
            return true;
        }
    }
    return false;
}

// global definition of memory needed in loops in interpol_poly2d()
double *tmp_val;
// global definition of memory needed in loops in interpol_poly1d()
double *v;
double *w;

// This function is used in Fits::polyshift()
// x:           the coordinates of values (vector)
// f:           the values at x (vector)
// degree:      size of the kernel
// xp:          the coordinates of the desired value
// val,val_err: the desired value and its error at coordinate xp
bool interpol_poly1d(double *x, double *f, int degree, double xp, double *val, double *val_err)
{
    int i, g, h = 1;

    double l, diff, tmp_diff, lo, lp, k;

    diff = fabs(xp - x[0]);

    for (i = 1; i <= degree; i++) {
        tmp_diff = fabs(xp - x[0]);
        if (tmp_diff < diff) {
            h = i;
            diff = tmp_diff;
        }
        v[i-1] = f[i - 1];
        w[i-1] = f[i - 1];
    }

    *val = f[h - 1];
    h--;
    for (g = 1; g < degree; g++) {
        for (i = 1; i <= degree - g; i++) {
            lo = x[i -1] - xp;
            lp = x[i + g - 1] - xp;
            k  = v[i] - w[i-1];
            l = lo - lp;
            if (l == 0.0) {
                dp_output("Error: in function interpol_poly1d()");
                free(v);
                free(w);
                return false;
            }
            l = k / l;
            v[i-1] = lo * l;
            w[i-1] = lp * l;
        }

        if (2 * h < (degree - g)) {
            *val_err = v[h];
        } else {
            *val_err = w[h-1];
            h--;
        }

        *val += *val_err;
    }

    return true;
}

// This function is used in Fits::polyshift()
// x, y:            the coordinates of values (vectors)
// f:               the values at xcoord & ycoord (array)
// degree:          size of the kernel
// xp,yp:           the coordinates of the desired value
// val,val_err:     the desired value and its error at coordinates xp & yp
bool interpol_poly2d(double *x, double *y, double **f, int degree, double xp, double yp, double *val, double *val_err)
{
    int j;

    for (j = 0; j < degree; j++) {
        if (!interpol_poly1d(y, f[j], degree, yp, &tmp_val[j], val_err)) {
            free(tmp_val);
            return false;
        }
    }

    if (!interpol_poly1d(x, tmp_val, degree, xp, val, val_err)) {
        free(tmp_val);
        return false;
    }

    return true;
}

bool Fits::polyshift(double ssx, double ssy, const int kernel_size)
{
    int x = 0, y = 0, i = 0,
        x_kernel = 0, y_kernel = 0,
        half_kernel_size = kernel_size / 2,
        mod_kernel_size = kernel_size % 2,
        border_size, xxx, yyy;

    Fits *source;

    double *x_val, *y_val, **func_val,
           val, val_err;

    // set data type of FITS to double
    if (membits != R8) {
        dp_output("Fits::polyshift(): Bitpix being changed to R8.\n");
        if (!setType(R8)) {
            return false;
        }
    }

    // check here for NaN values and replace them with median value of
    // surrounding pixels
    Fits med;
    med.create(3, 3, R8);
    for (y = 1; y < naxis[2]-1; y++) {
        for (x = 1; x < naxis[1]-1; x++) {
            // check if any values are NaN, if yes
            // then take median in a 3x3-filed around the NaN-value and replace it
            if (gsl_finite(r8data[C_I(x, y)]) == FALSE) {
                med.r8data[0] = r8data[C_I(x-1, y-1)];
                med.r8data[1] = r8data[C_I(x, y-1)];
                med.r8data[2] = r8data[C_I(x+1, y-1)];
                med.r8data[3] = r8data[C_I(x-1, y)];
                med.r8data[4] = -99.9;
                med.r8data[5] = r8data[C_I(x+1, y)];
                med.r8data[6] = r8data[C_I(x-1, y+1)];
                med.r8data[7] = r8data[C_I(x, y+1)];
                med.r8data[8] = r8data[C_I(x+1, y+1)];
                val = med.get_median(-99.9);
dp_debug("Nan: %g (x: %d, y: %d)",val, x, y);
                r8data[C_I(x, y)] = val;
            }
        }
    }

    // create a copy of our FITS
    source = CreateFits();
    if (!source->copy(*this)) {
        return false;
    }

    // alloc some needed arrays
    x_val = (double*)malloc(kernel_size*sizeof(double));
    y_val = (double*)malloc(kernel_size*sizeof(double));

    func_val = (double**)malloc(kernel_size*sizeof(double*));
    for( int i = 0; i < kernel_size; i++ ){
        func_val[i] = (double*)malloc(kernel_size*sizeof(double));
    }

    // fill x- & y-vector with values
    for (i = 0; i < kernel_size; i++) {
        x_val[i] = i;
        y_val[i] = i;
    }

    // defined globally because of loops (needed in interpol_poly2d())
    tmp_val = (double*)malloc(kernel_size * sizeof(double));
    // defined globally because of loops (needed in interpol_poly1d())
    v = (double*)malloc(kernel_size * sizeof(double));
    w = (double*)malloc(kernel_size * sizeof(double));

    if (mod_kernel_size == 1) {
        // kernel_size uneven
        border_size = kernel_size / 2;
    } else {
        // kernel_size even
        border_size = kernel_size / 2;
    }

    for (y = 0; y < naxis[2]; y++) {
        for (x = 0; x < naxis[1]; x++) {
            if (isBorder(mod_kernel_size, naxis[1], naxis[2], border_size, x, y)) {
                // set the border to zero
                r8data[C_I(x, y)] = 0.0;
            } else {
                // fill kernel with values from image
                i = 0;
                for (y_kernel = 0; y_kernel < kernel_size; y_kernel++) {
                    for (x_kernel = 0; x_kernel < kernel_size; x_kernel++) {
                        if (mod_kernel_size == 1) {
                            // kernel_size uneven
                            xxx = x + x_kernel - half_kernel_size;
                            yyy = y + y_kernel - half_kernel_size;
                        } else {
                            // kernel_size even
                            xxx = x + x_kernel - half_kernel_size + 1;
                            yyy = y + y_kernel - half_kernel_size + 1;
                        }

                        func_val[x_kernel][y_kernel] = source->ValueAt(source->C_I(xxx, yyy));

                        i++;
                    }
                }

                // fit now the 2D polynomial
                if (mod_kernel_size == 1) {
                    // kernel_size uneven
                    if (!interpol_poly2d(x_val, y_val, func_val, kernel_size, half_kernel_size - ssx, half_kernel_size - ssy, &val, &val_err)) {
                        return FALSE;
                    }
                } else {
                    // kernel_size even
                    if (!interpol_poly2d(x_val, y_val, func_val, kernel_size, half_kernel_size-1-ssx, half_kernel_size-1-ssy, &val, &val_err)) {
                        return FALSE;
                    }
                }

                r8data[C_I(x,y)] = val;
            }
        }
    }
    free(tmp_val);
    free(v);
    free(w);

    // free allocated memory again
    DeleteFits(source);
    source = NULL;
    free(x_val);
    free(y_val);
    for( int i = 0; i < kernel_size; i++ ){
        free(func_val[i]);
    }
    free(func_val);

    return TRUE;
}

double Fits::interpolate_pixel(const double x, const double y, const int kernel_size)
{
    int     half_kernel_size    = kernel_size / 2,
            mod_kernel_size     = kernel_size % 2,
            border_size         = 0,
            i                   = 0,
            x_kernel            = 0,
            y_kernel            = 0,
            int_x               = 0,
            int_y               = 0;

    double  ret_val             = 0.0,
            *x_val              = NULL,
            *y_val              = NULL,
            **func_val          = NULL,
            val                 = 0.0,
            val_err             = 0.0,
            frac_x              = 0.0,
            frac_y              = 0.0;

    // get fractional part of pixel to interpolate
    // this will be fed into the interpolation-algorithm
    frac_x = x - (int)x;
    frac_y = y - (int)y;

    if ((frac_x == 0.0) && (frac_y == 0.0)) {
        // x and y are integers, just return the value
        ret_val = ValueAt(F_I(x, y));
    } else {
        // get integer part of pixel to interpolate
        // this will be used to calculate the interpolation-kernel
        // if the frctional part is greater than 0.5 we go one pixel up

        if (mod_kernel_size == 1) {
            // kernel_size uneven
            border_size = kernel_size / 2;

            int_x = (int)x;
            int_y = (int)y;
        } else {
            // kernel_size even
            border_size = kernel_size / 2;

            if ((frac_x >= 0.0) && (frac_x <= 0.5)) {
                int_x = (int)x - 1;
            } else {
                int_x = (int)x;
                frac_x -= 1;
            }

            if ((frac_y >= 0.0) && (frac_y <= 0.5)) {
                int_y = (int)y - 1;
            } else {
                int_y = (int)y;
                frac_y -= 1;
            }

        }

        // change from Fortran- to C-notation
        int_x -= 1,
        int_y -= 1;

        if (isBorder(mod_kernel_size, naxis[1], naxis[2], border_size, int_x, int_y)) {
            // we are in the border-region, return zero
            ret_val = 0.0;
        } else {
            x_val = (double*)malloc(kernel_size*sizeof(double));
            y_val = (double*)malloc(kernel_size*sizeof(double));

            func_val = (double**)malloc(kernel_size*sizeof(double*));
            for( int i = 0; i < kernel_size; i++ ){
                func_val[i] = (double*)malloc(kernel_size*sizeof(double));
            }

            // fill x- & y-vector with values
            for (i = 0; i < kernel_size; i++) {
                x_val[i] = i;
                y_val[i] = i;
            }

            // defined globally because of loops (needed in interpol_poly2d())
            tmp_val = (double*)malloc(kernel_size * sizeof(double));
            // defined globally because of loops (needed in interpol_poly1d())
            v = (double*)malloc(kernel_size * sizeof(double));
            w = (double*)malloc(kernel_size * sizeof(double));

            // fill kernel with values from image
            i = 0;
            for (y_kernel = 0; y_kernel < kernel_size; y_kernel++) {
                for (x_kernel = 0; x_kernel < kernel_size; x_kernel++) {
                    int xxx, yyy;
                    if (mod_kernel_size == 1) {
                        // kernel_size uneven
                        xxx = int_x + x_kernel - half_kernel_size;
                        yyy = int_y + y_kernel - half_kernel_size;
                    } else {
                        // kernel_size even
                        xxx = int_x + x_kernel - half_kernel_size + 1;
                        yyy = int_y + y_kernel - half_kernel_size + 1;
                    }

                    func_val[x_kernel][y_kernel] = ValueAt(C_I(xxx, yyy));
                    i++;
                }
            }

            // fit now the 2D polynomial
            if (mod_kernel_size == 1) {
                // kernel_size uneven
                if (!interpol_poly2d(x_val, y_val, func_val, kernel_size, half_kernel_size + frac_x, half_kernel_size + frac_y, &val, &val_err)) {
                    return FALSE;
                }
            } else {
                // kernel_size even
                if (!interpol_poly2d(x_val, y_val, func_val, kernel_size, half_kernel_size+frac_x, half_kernel_size+frac_y, &val, &val_err)) {
                    return FALSE;
                }
            }

            ret_val = val;

            free(tmp_val);
            free(v);
            free(w);

            // free allocated memory again
            free(x_val);
            free(y_val);
            for( int i = 0; i < kernel_size; i++ ){
                free(func_val[i]);
            }
            free(func_val);
        }
    }

    return ret_val;
}

/*!
Reassemble the array, i.e. re-arrange the quadrants after 2 fft's.
*/

bool Fits::reass() {
	bool rv;
	int n;

	n = 3;
	if (naxis[3] < 2) n = 2;
	if (naxis[2] < 2) n = 1;

	switch (n) {
        case 1: rv = wrap(naxis[1] / 2 + naxis[1] % 2, 0, 0, FALSE); break;
        case 2: rv = wrap(naxis[1] / 2 + naxis[1] % 2, naxis[2] / 2 + naxis[2] % 2, 0, FALSE); break;
        case 3: rv = wrap(naxis[1] / 2 + naxis[1] % 2, naxis[2] / 2 + naxis[2] % 2, naxis[3] / 2 + naxis[3] % 2, FALSE); break;
		default: dp_output("Fits::reass: wrong number of axes\n"); rv = FALSE; break;
	}
	return rv;
}

bool Fits::old_reass()
{
	int i, j, ii, jj, k, n, x, y;

	x = y = 0;
	n = 3;
	if (naxis[3] < 2) n = 2;
	if (naxis[2] < 2) n = 1;
	if (n > 1) y = naxis[2] % 2;
	x = naxis[1] % 2;

	switch (n) {
		case 1: switch (membits) {
			case I1: {
				if (x) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						i1data[i-1] = tmp.i1data[ii-1];
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						i1data[i-1] = tmp.i1data[ii-1];
					}
				} else {
                    unsigned char tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(i1data[i-1], i1data[i+naxis[1]/2-1]);
					}
				}
			}
				break;
			case I2: {
				if (x) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						i2data[i-1] = tmp.i2data[ii-1];
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						i2data[i-1] = tmp.i2data[ii-1];
					}
				} else {
                    short tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(i2data[i-1], i2data[i+naxis[1]/2-1]);
					}
				}
			}
				break;
			case I4: {
				if (x) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						i4data[i-1] = tmp.i4data[ii-1];
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						i4data[i-1] = tmp.i4data[ii-1];
					}
				} else {
                    long tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(i4data[i-1], i4data[i+naxis[1]/2-1]);
					}
				}
			}
				break;
            case I8: {
                if (x) {
                    Fits tmp;
                    if (!tmp.copy(*this)) return FALSE;
                    for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
                        i8data[i-1] = tmp.i8data[ii-1];
                    }
                    for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
                        i8data[i-1] = tmp.i8data[ii-1];
                    }
                } else {
                    long long tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(i8data[i-1], i8data[i+naxis[1]/2-1]);
                    }
                }
            }
                break;
            case R4: {
				if (x) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						r4data[i-1] = tmp.r4data[ii-1];
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						r4data[i-1] = tmp.r4data[ii-1];
					}
				} else {
                    float tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(r4data[i-1], r4data[i+naxis[1]/2-1]);
					}
				}
			}
				break;
			case R8: {
				if (x) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						r8data[i-1] = tmp.r8data[ii-1];
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						r8data[i-1] = tmp.r8data[ii-1];
					}
				} else {
                    double tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(r8data[i-1], r8data[i+naxis[1]/2-1]);
					}
				}
			}
				break;
			case C16: {
				if (x) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						cdata[i-1].r = tmp.cdata[ii-1].r;
						cdata[i-1].i = tmp.cdata[ii-1].i;
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						cdata[i-1].r = tmp.cdata[ii-1].r;
						cdata[i-1].i = tmp.cdata[ii-1].i;
					}
				} else {
                    double tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        SWAP(cdata[i-1].r, cdata[i+naxis[1]/2-1].r);
                        SWAP(cdata[i-1].i, cdata[i+naxis[1]/2-1].i);
					}
				}
			}
				break;
			default: return FALSE; break;
		}
			break;
		case 2: switch (membits) {
			case I1: {
				if (x || y) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							i1data[F_I(i, j)] = tmp.i1data[F_I(ii, jj)];
						}
					}
					for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							i1data[F_I(i, j)] = tmp.i1data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							i1data[F_I(i, j)] = tmp.i1data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							i1data[F_I(i, j)] = tmp.i1data[F_I(ii, jj)];
						}
					}
				} else {
                    unsigned char tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
						for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(i1data[F_I(i, j)], i1data[F_I(i+naxis[1]/2, j+naxis[2]/2)]);
                            SWAP(i1data[F_I(i+naxis[1]/2, j)], i1data[F_I(i, j+naxis[2]/2)]);
		 				}
					}
				}
				break;
			}
			case I2: {
				if (x || y) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							i2data[F_I(i, j)] = tmp.i2data[F_I(ii, jj)];
						}
					}
					for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							i2data[F_I(i, j)] = tmp.i2data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							i2data[F_I(i, j)] = tmp.i2data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							i2data[F_I(i, j)] = tmp.i2data[F_I(ii, jj)];
						}
					}
				} else {
                    short tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
						for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(i2data[F_I(i, j)], i2data[F_I(i+naxis[1]/2, j+naxis[2]/2)]);
                            SWAP(i2data[F_I(i+naxis[1]/2, j)], i2data[F_I(i, j+naxis[2]/2)]);
		 				}
					}
				}
				break;
			}
			case I4: {
				if (x || y) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							i4data[F_I(i, j)] = tmp.i4data[F_I(ii, jj)];
						}
					}
					for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							i4data[F_I(i, j)] = tmp.i4data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							i4data[F_I(i, j)] = tmp.i4data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							i4data[F_I(i, j)] = tmp.i4data[F_I(ii, jj)];
						}
					}
				} else {
                    long tempr;

					for (i = 1; i <= naxis[1]/2; i++) {
						for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(i4data[F_I(i, j)], i4data[F_I(i+naxis[1]/2, j+naxis[2]/2)]);
                            SWAP(i4data[F_I(i+naxis[1]/2, j)], i4data[F_I(i, j+naxis[2]/2)]);
			 			}
					}
				}
				break;
			}
            case I8: {
                if (x || y) {
                    Fits tmp;
                    if (!tmp.copy(*this)) return FALSE;
                    for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
                        for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
                            i8data[F_I(i, j)] = tmp.i8data[F_I(ii, jj)];
                        }
                    }
                    for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
                        for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
                            i8data[F_I(i, j)] = tmp.i8data[F_I(ii, jj)];
                        }
                    }
                    for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
                        for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
                            i8data[F_I(i, j)] = tmp.i8data[F_I(ii, jj)];
                        }
                    }
                    for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
                        for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
                            i8data[F_I(i, j)] = tmp.i8data[F_I(ii, jj)];
                        }
                    }
                } else {
                    long long tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
                        for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(i8data[F_I(i, j)], i8data[F_I(i+naxis[1]/2, j+naxis[2]/2)]);
                            SWAP(i8data[F_I(i+naxis[1]/2, j)], i8data[F_I(i, j+naxis[2]/2)]);
                        }
                    }
                }
                break;
            }
            case R4: {
				if (x || y) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							r4data[F_I(i, j)] = tmp.r4data[F_I(ii, jj)];
						}
					}
					for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							r4data[F_I(i, j)] = tmp.r4data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							r4data[F_I(i, j)] = tmp.r4data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							r4data[F_I(i, j)] = tmp.r4data[F_I(ii, jj)];
						}
					}
				} else {
                    float tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
						for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(r4data[F_I(i, j)], r4data[F_I(i+naxis[1]/2, j+naxis[2]/2)]);
                            SWAP(r4data[F_I(i+naxis[1]/2, j)], r4data[F_I(i, j+naxis[2]/2)]);
		 				}
					}
				}
				break;
			}
			case R8: {
				if (x || y) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							r8data[F_I(i, j)] = tmp.r8data[F_I(ii, jj)];
						}
					}
					for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							r8data[F_I(i, j)] = tmp.r8data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							r8data[F_I(i, j)] = tmp.r8data[F_I(ii, jj)];
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							r8data[F_I(i, j)] = tmp.r8data[F_I(ii, jj)];
						}
					}
				} else {
                    double tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
						for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(r8data[F_I(i, j)], r8data[F_I(i+naxis[1]/2, j+naxis[2]/2)]);
                            SWAP(r8data[F_I(i+naxis[1]/2, j)], r8data[F_I(i, j+naxis[2]/2)]);
		 				}
					}
				}
				break;
			}
			case C16: {
				if (x || y) {
					Fits tmp;
					if (!tmp.copy(*this)) return FALSE;
					for (i = 1, ii = naxis[1]/2 + 1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							cdata[F_I(i, j)].r = tmp.cdata[F_I(ii, jj)].r;
							cdata[F_I(i, j)].i = tmp.cdata[F_I(ii, jj)].i;
						}
					}
					for (i = 1, ii = naxis[1]/2+1; i <= naxis[1]/2+x; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							cdata[F_I(i, j)].r = tmp.cdata[F_I(ii, jj)].r;
							cdata[F_I(i, j)].i = tmp.cdata[F_I(ii, jj)].i;
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = naxis[2]/2+1+y, jj = 1; j <= naxis[2]; j++, jj++) {
							cdata[F_I(i, j)].r = tmp.cdata[F_I(ii, jj)].r;
							cdata[F_I(i, j)].i = tmp.cdata[F_I(ii, jj)].i;
						}
					}
					for (i = naxis[1]/2+1+x, ii = 1; i <= naxis[1]; i++, ii++) {
						for (j = 1, jj = naxis[2]/2+1; j <= naxis[2]/2+y; j++, jj++) {
							cdata[F_I(i, j)].r = tmp.cdata[F_I(ii, jj)].r;
							cdata[F_I(i, j)].i = tmp.cdata[F_I(ii, jj)].i;
						}
					}
				} else {
                    double tempr;

                    for (i = 1; i <= naxis[1]/2; i++) {
						for (j = 1; j <= naxis[2]/2; j++) {
                            SWAP(cdata[F_I(i, j)].r, cdata[F_I(i+naxis[1]/2, j+naxis[2]/2)].r);
                            SWAP(cdata[F_I(i+naxis[1]/2, j)].r, cdata[F_I(i, j+naxis[2]/2)].r);
                            SWAP(cdata[F_I(i, j)].i, cdata[F_I(i+naxis[1]/2, j+naxis[2]/2)].i);
                            SWAP(cdata[F_I(i+naxis[1]/2, j)].i, cdata[F_I(i, j+naxis[2]/2)].i);
			 			}
					}
				}
				break;
			}
			default: return FALSE; break;
		}
		case 3: switch (membits) {
			case I1: {
                unsigned char tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
					for (j = 1; j <= naxis[2]/2; j++) {
						for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(i1data[F_I(i, j, k)], i1data[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i1data[F_I(i+naxis[1]/2, j, k)], i1data[F_I(i, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i1data[F_I(i, j+naxis[2]/2, k)], i1data[F_I(i+naxis[1]/2, j, k+naxis[3]/2)]);
                            SWAP(i1data[F_I(i+naxis[1]/2, j+naxis[2]/2, k)], i1data[F_I(i, j, k+naxis[3]/2)]);
						}
		 			}
				}
				break;
			}
			case I2: {
                short tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
					for (j = 1; j <= naxis[2]/2; j++) {
						for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(i2data[F_I(i, j, k)], i2data[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i2data[F_I(i+naxis[1]/2, j, k)], i2data[F_I(i, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i2data[F_I(i, j+naxis[2]/2, k)], i2data[F_I(i+naxis[1]/2, j, k+naxis[3]/2)]);
                            SWAP(i2data[F_I(i+naxis[1]/2, j+naxis[2]/2, k)], i2data[F_I(i, j, k+naxis[3]/2)]);
						}
		 			}
				}
				break;
			}
			case I4: {
                long tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
					for (j = 1; j <= naxis[2]/2; j++) {
						for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(i4data[F_I(i, j, k)], i4data[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i4data[F_I(i+naxis[1]/2, j, k)], i4data[F_I(i, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i4data[F_I(i, j+naxis[2]/2, k)], i4data[F_I(i+naxis[1]/2, j, k+naxis[3]/2)]);
                            SWAP(i4data[F_I(i+naxis[1]/2, j+naxis[2]/2, k)], i4data[F_I(i, j, k+naxis[3]/2)]);
						}
		 			}
				}
				break;
			}
            case I8: {
                long long tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
                    for (j = 1; j <= naxis[2]/2; j++) {
                        for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(i8data[F_I(i, j, k)], i8data[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i8data[F_I(i+naxis[1]/2, j, k)], i8data[F_I(i, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(i8data[F_I(i, j+naxis[2]/2, k)], i8data[F_I(i+naxis[1]/2, j, k+naxis[3]/2)]);
                            SWAP(i8data[F_I(i+naxis[1]/2, j+naxis[2]/2, k)], i8data[F_I(i, j, k+naxis[3]/2)]);
                        }
                    }
                }
                break;
            }
            case R4: {
                float tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
					for (j = 1; j <= naxis[2]/2; j++) {
						for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(r4data[F_I(i, j, k)], r4data[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(r4data[F_I(i+naxis[1]/2, j, k)], r4data[F_I(i, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(r4data[F_I(i, j+naxis[2]/2, k)], r4data[F_I(i+naxis[1]/2, j, k+naxis[3]/2)]);
                            SWAP(r4data[F_I(i+naxis[1]/2, j+naxis[2]/2, k)], r4data[F_I(i, j, k+naxis[3]/2)]);
						}
		 			}
				}
				break;
			}
			case R8: {
                double tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
					for (j = 1; j <= naxis[2]/2; j++) {
						for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(r8data[F_I(i, j, k)], r8data[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(r8data[F_I(i+naxis[1]/2, j, k)], r8data[F_I(i, j+naxis[2]/2, k+naxis[3]/2)]);
                            SWAP(r8data[F_I(i, j+naxis[2]/2, k)], r8data[F_I(i+naxis[1]/2, j, k+naxis[3]/2)]);
                            SWAP(r8data[F_I(i+naxis[1]/2, j+naxis[2]/2, k)], r8data[F_I(i, j, k+naxis[3]/2)]);
						}
		 			}
				}
				break;
			}
			case C16: {
                double tempr;

                for (i = 1; i <= naxis[1]/2; i++) {
					for (j = 1; j <= naxis[2]/2; j++) {
						for (k = 1; k <= naxis[3]/2; k++) {
                            SWAP(cdata[F_I(i, j, k)].r, cdata[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)].r);
                            SWAP(cdata[F_I(i, j, k)].i, cdata[F_I(i+naxis[1]/2, j+naxis[2]/2, k+naxis[3]/2)].i);
                            SWAP(cdata[F_I(i+naxis[1]/2, j, k)].r, cdata[F_I(i, j+naxis[2]/2, k+naxis[3]/2)].r);
                            SWAP(cdata[F_I(i+naxis[1]/2, j, k)].i, cdata[F_I(i, j+naxis[2]/2, k+naxis[3]/2)].i);
                            SWAP(cdata[F_I(i, j+naxis[2]/2, k)].r, cdata[F_I(i+naxis[1]/2, j, k+naxis[3]/2)].r);
                            SWAP(cdata[F_I(i, j+naxis[2]/2, k)].i, cdata[F_I(i+naxis[1]/2, j, k+naxis[3]/2)].i);
                            SWAP(cdata[F_I(i+naxis[1]/2, j+naxis[2]/2, k)].r, cdata[F_I(i, j, k+naxis[3]/2)].r);
                            SWAP(cdata[F_I(i+naxis[1]/2, j+naxis[2]/2, k)].i, cdata[F_I(i, j, k+naxis[3]/2)].i);
						}
		 			}
				}
				break;
			}
			default: return FALSE; break;
		}
			break;
		default: return FALSE; break;
	}
	return TRUE;
}

/*!
Perform the forward Fast Fourier Transform if the array is float
Perform the inverse Fast Fourier Transform if the array is complex
This uses the FFTW package
*/
/*
In the call to the fourier transform, note that the ordering of
the axis is switched. This makes the transform work for non-quadratic
arrays (this is a feature of fftw)
*/

bool Fits::fft()
{
//@	fftwnd_plan cplan = NULL, rplan = NULL;
    fftw_plan plan = NULL;
	int i, j, n[MAXNAXIS];

	for (i = 1, j = naxis[0] - 1; i <= naxis[0]; i++, j--) n[j] = naxis[i];
	if (membits != C16) {
		if (!setType(C16)) return FALSE;
        plan = fftw_plan_dft((int)naxis[0], n, (fftw_complex *)cdata, (fftw_complex *)cdata, FFTW_FORWARD, FFTW_ESTIMATE);
        fftw_execute(plan);
        fftw_destroy_plan(plan);
/*		if (naxis[0] == 1) {
			c1plan = fftw_create_plan((int)naxis[1], FFTW_FORWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
			fftw_one(c1plan, (fftw_complex *)cdata, NULL);
			fftw_destroy_plan(c1plan);
		} else { */
//@			cplan = fftwnd_create_plan((int)naxis[0], n, FFTW_FORWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
/*			if (naxis[0] == 2) cplan = fftw2d_create_plan((int)naxis[2], (int)naxis[1], FFTW_FORWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
			else if (naxis[0] == 3) cplan = fftw3d_create_plan((int)naxis[3], (int)naxis[2], (int)naxis[1], FFTW_FORWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
			else return fits_error("Cannot compute the fft of such an array"); */
//@			fftwnd_one(cplan, (fftw_complex *)cdata, NULL);
//@			fftwnd_destroy_plan(cplan);
	} else {
        plan = fftw_plan_dft((int)naxis[0], n, (fftw_complex *)cdata, (fftw_complex *)cdata, FFTW_BACKWARD, FFTW_ESTIMATE);
        fftw_execute(plan);
        fftw_destroy_plan(plan);
/*		if (naxis[0] == 1) {
			c1plan = fftw_create_plan((int)naxis[1], FFTW_BACKWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
			fftw_one(c1plan, (fftw_complex *)cdata, NULL);
			fftw_destroy_plan(c1plan);
		} else { */
//@			rplan = fftwnd_create_plan((int)naxis[0], n, FFTW_BACKWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
/*			if (naxis[0] == 2) rplan = fftw2d_create_plan((int)naxis[2], (int)naxis[1], FFTW_BACKWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
			else if (naxis[0] == 3) rplan = fftw3d_create_plan((int)naxis[3], (int)naxis[2], (int)naxis[1], FFTW_BACKWARD, FFTW_ESTIMATE|FFTW_USE_WISDOM|FFTW_IN_PLACE);
			else return fits_error("Cannot compute the fft of such an array"); */
//@			fftwnd_one(rplan, (fftw_complex *)cdata, NULL);
//@			fftwnd_destroy_plan(rplan);
		if (!setType(R4)) return FALSE;
	}

	return TRUE;
}

/*! Median of data */

float Median(Fits & a)
{
	return(a.get_median());
}

/*!
Do a running boxcar smoothing
method == 0: average
method == 1: minimum
method == 2: maximum
method == 3: median

For Fits Cubes:
method2 == 0: Boxcar of images (default)
method2 == 1: Boxcar of spectra
*/

int Boxcar(Fits & a, int size, int method, int method2)  {
	int i, j, ii, jj, c, rv = 1;
	float v, *sortdata;
	Fits b;
	
    if (a.Naxis(3) > 1 && method2 == 0) {
        Fits sub;

        dpProgress(-a.Naxis(2), "boxcar...");
        for (int z = 1; z <= a.Naxis(3); z++) {
            a.extractRange(sub, 1, a.Naxis(1), 1, a.Naxis(2), z, z);
            Boxcar(sub, size, method);
            a.setRange(sub, 1, a.Naxis(1), 1, a.Naxis(2), z, z);
            dpProgress(z, "boxcar...");
        }
    } else if (method2 != 0) {
        Fits sub;
        switch (method2) {
            case 1:
                dpProgress(-a.Naxis(2), "boxcar...");
                for (int x = 1; x <= a.Naxis(2); x++) {
                    for (int y = 1; y <= a.Naxis(3); y++) {
                        a.extractRange(sub, -1, -1, x, x, y, y);
                        Boxcar(sub, size, method);
                        a.setRange(sub, -1, -1, x, x, y, y);
                    }
                    dpProgress(x, "boxcar...");
                }
                break;
            case 2:
                dpProgress(-a.Naxis(1), "boxcar...");
                for (int x = 1; x <= a.Naxis(1); x++) {
                    for (int y = 1; y <= a.Naxis(3); y++) {
                        a.extractRange(sub, x, x, -1, -1, y, y);
                        Boxcar(sub, size, method);
                        a.setRange(sub, x, x, -1, -1, y, y);
                    }
                    dpProgress(x, "boxcar...");
                }
                break;
            case 3:
                dpProgress(-a.Naxis(1), "boxcar...");
                for (int x = 1; x <= a.Naxis(1); x++) {
                    for (int y = 1; y <= a.Naxis(2); y++) {
                        a.extractRange(sub, x, x, y, y, -1, -1);
                        Boxcar(sub, size, method);
                        a.setRange(sub, x, x, y, y, -1, -1);
                    }
                    dpProgress(x, "boxcar...");
                }
                break;
            default:
                break;
        }
		return rv;
	}

	if (a.membits != R4) dp_output("Boxcar: Bitpix being changed to R4. Check R8 and C16.\n");
	if (!a.setType(R4)) return 0;
	b.copy(a);
	if (method == 0) {
		for (i = 1; i <= a.Naxis(1); i++) {
			for (j = 1; j <= a.Naxis(2); j++) {
				c = 0;
				v = 0.0;
				for (ii = i - size; ii <= i + size; ii++) {
					if ((ii > 0) && (ii <= a.Naxis(1))) {
						for (jj = j - size; jj <= j + size; jj++) {
							if ((jj > 0) && (jj <= a.Naxis(2))) {
								c++;
                                v += b(ii, jj);
							}
						}
					}
				}
                a.setValue(v / (float)c, i, j);
			}
		}
	} else if (method == 1) {
		for (i = 1; i <= a.Naxis(1); i++) {
			for (j = 1; j <= a.Naxis(2); j++) {
                v = b(i, j);
				for (ii = i - size; ii <= i + size; ii++) {
					if ((ii > 0) && (ii <= a.Naxis(1))) {
						for (jj = j - size; jj <= j + size; jj++) {
							if ((jj > 0) && (jj <= a.Naxis(2))) {
                                if (b(ii, jj) < v) v = b(ii, jj);
							}
						}
					}
				}
                a.setValue(v, i, j);
			}
		}
	} else if (method == 2) {
		for (i = 1; i <= a.Naxis(1); i++) {
			for (j = 1; j <= a.Naxis(2); j++) {
                v = (float)b(i, j);
				for (ii = i - size; ii <= i + size; ii++) {
					if ((ii > 0) && (ii <= a.Naxis(1))) {
						for (jj = j - size; jj <= j + size; jj++) {
							if ((jj > 0) && (jj <= a.Naxis(2))) {
                                if (b(ii, jj) > v) v = b(ii, jj);
							}
						}
					}
				}
                a.setValue(v, i, j);
			}
		}
	} else if (method == 3) {
		sortdata = (float *)malloc((size * 2 + 1) * (size * 2 + 1) * sizeof(float));
        dpint64 index;
        for (i = 0; i < a.Naxis(1); i++) {
            for (j = 0; j < a.Naxis(2); j++) {
                c = 0;
				for (ii = i - size; ii <= i + size; ii++) {
                    if ((ii >= 0) && (ii < a.Naxis(1))) {
						for (jj = j - size; jj <= j + size; jj++) {
                            if ((jj >= 0) && (jj < a.Naxis(2))) {
//                                sortdata[c] = (float)a(ii, jj);
                                sortdata[c] = b.r4data[b.C_I(ii, jj)];
                                c++;
							}
						}
                    }
                }
                std::nth_element(sortdata, sortdata + c/2, sortdata + c);
//                qsort(sortdata, c, sizeof(float), &floatcmp);
//                b.setValue(sortdata[c/2], i, j);
                a.r4data[a.C_I(i, j)] = sortdata[c/2];
            }
		}
		free(sortdata);
	}
//	a.copy(b);
	return(rv);
}

/*!
Put together images specified in the file pointed to by fname
File format: 3(4) Columns: Filename, xshift, yshift, (scale factor)
Negative Shifts result in clipping of the image
*/

bool Fits::mosaic(const char *fname) {
	int i, nlines, z;
    dpint64 n;
	char **fnames;
	float *xshift, *yshift, *scale, d, xmin, xmax, ymin, ymax;
	Fits current, mask, sc;
    dpStringList inp, line;
    dpString tmpStr;
	
// Find out how many lines there are in the file
    if (!inp.readFile(dpString(fname)))
        return FALSE;
    nlines = inp.size();
	dp_output("There are %i lines in %s\n", nlines, fname);
	
// Allocate memory, initialize and read in everything
	xshift = (float *)malloc(nlines * sizeof(float));
	yshift = (float *)malloc(nlines * sizeof(float));
	scale = (float *)malloc(nlines * sizeof(float));
	fnames = (char **)malloc(nlines * sizeof(char *));

	for (i = 0; i < nlines; i++) {
		fnames[i] = (char *)malloc(256 * sizeof(char));
		strcpy(fnames[i], "");
		xshift[i] = yshift[i] = 0.0;
		scale[i] = 1.0;
    tmpStr = inp[i].simplifyWhiteSpace();
    line = dpStringList::split(' ', tmpStr);
    if (line.size() > 0)
        strncpy(fnames[i], line[0].c_str(), 255);
    if (line.size() > 1)
        xshift[i] = line[1].toFloat();
    if (line.size() > 2)
        yshift[i] = line[2].toFloat();
    if (line.size() > 3)
        scale[i] = line[3].toFloat();
	}

// find greatest shift vectors
	xmin = xmax = xshift[0];
	ymin = ymax = yshift[0];	
	for (i = 0; i < nlines; i++) {
		if (xshift[i] < xmin) xmin = xshift[i];
		else if (xshift[i] > xmax) xmax = xshift[i];
		if (yshift[i] < ymin) ymin = yshift[i];
		else if (yshift[i] > ymax) ymax = yshift[i];
	}
	if (xmin > 0.0) xmin = 0.0;
	if (ymin > 0.0) ymin = 0.0;

// Read in the first fits file to see how big we are
	current.ReadFITS(fnames[0]);
//	naxis[1] = (int)fabs(xmax) + (int)fabs(xmin) + current.naxis[1];
//	naxis[2] = (int)fabs(ymax) + (int)fabs(ymin) + current.naxis[2];
//	naxis[3] = current.naxis[3];
//	naxis[0] = (naxis[3] > 1 ? 3 : 2);
//	type = IMAGE;
//	allocateMemory();
//	*this = 0.0;
	create((int)fabs(xmax) + (int)fabs(xmin) + current.naxis[1], (int)fabs(ymax) + (int)fabs(ymin) + current.naxis[2], current.naxis[3]);
//	CopyHeader(current);

	if (naxis[0] == 3)
		sc.create(naxis[1], naxis[2], naxis[3]);
	else
		sc.create(naxis[1], naxis[2]);

	for (i = 0; i < nlines; i++) {
		dp_output("%s %f %f %f\n", fnames[i], xshift[i] - xmin, yshift[i] - ymin, scale[i]);
		current.ReadFITS(fnames[i]);

		
		if (naxis[0] == 3) {
			if (!current.resize(naxis[1], naxis[2], naxis[3])) return FALSE;
			mask.create(naxis[1], naxis[2], naxis[3]);
		} else {
			if (!current.resize(naxis[1], naxis[2])) return FALSE;
			mask.create(naxis[1], naxis[2]);
		}

		mask = 1.0;

		if (naxis[0] == 3) {
			Fits _temp, _temp2;

			_temp.create(current.naxis[1], current.naxis[2], current.membits);
			_temp2.CubeSum(current);
			for (n = 0; n < current.Nelements(); n++) {
				if (current.ValueAt(n) == 0.0) mask.r4data[n] = 0.0;
			}
			for (n = 0; n < _temp2.Nelements(); n++) {
				if (_temp2.r4data[n] == 0.0) mask.r4data[n] = 0.0;
			}
			for (z = 1; z <= naxis[3]; z++) {
				current.extractRange(_temp, -1, -1, -1, -1, z, z);
				_temp.shift(xshift[i] - xmin, yshift[i] - ymin, 2);
				current.setRange(_temp, 1, current.naxis[1], 1, current.naxis[2], z, z);
			}
		} else {	
			for (n = 0; n < current.Nelements(); n++) {
				if (current.ValueAt(n) == 0.0) mask.r4data[n] = 0.0;
			}
			current.shift(xshift[i] - xmin, yshift[i] - ymin, 2);
		}
		mask.shift(xshift[i] - xmin, yshift[i] - ymin, 2);
		current *= scale[i];
		add(current);
		sc += mask;
		free(fnames[i]);
		if (i == 0) CopyHeader(current);
	}
	free(fnames);
	free(xshift);
	free(yshift);
	free(scale);
	sc.czero(1.0);
	div(sc);

	return TRUE;
}

/*!
Replace all values which do not represent a value (i.e. which are NaN or INF) by v.
*/
void Fits::cblank(double v) {
    dpint64 n;
	
	if (membits > R4) return;

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++)
				if (!gsl_finite(r4data[n])) r4data[n] = (float)v;
			break;
		case R8:
			for (n = 0; n < n_elements; n++)
				if (!gsl_finite(r8data[n])) r8data[n] = v;
			break;
		case C16:
			for (n = 0; n < n_elements; n++)
				if ((!gsl_finite(cdata[n].r)) || (!gsl_finite(cdata[n].i))) {
					cdata[n].r = cdata[n].i = v;
				}
			break;
		default: break;
	}
}

/*!
Replace all values which are equal to zero by v.
*/
void Fits::czero(double v) {
    dpint64 n;

	switch (membits) {
		case I1: {
			UCHAR c = (unsigned char)((v - bzero) / bscale);
			for (n = 0; n < Nelements(); n++) {
				if (ValueAt(n) == 0.0) i1data[n] = c;
			}
		}
			break;
		case I2: {
			short c = (short)((v - bzero) / bscale);
			for (n = 0; n < Nelements(); n++) {
				if (ValueAt(n) == 0.0) i2data[n] = c;
			}
		}
			break;
		case I4: {
			int c = (int)((v - bzero) / bscale);
			for (n = 0; n < Nelements(); n++) {
				if (ValueAt(n) == 0.0) i4data[n] = c;
			}
		}
			break;
        case I8: {
            long long c = (long long)((v - bzero) / bscale);
            for (n = 0; n < Nelements(); n++) {
                if (ValueAt(n) == 0.0) i8data[n] = c;
            }
        }
            break;
        case R4: {
			float c = (float)v;
			for (n = 0; n < Nelements(); n++) {
				if (r4data[n] == 0.0) r4data[n] = c;
			}
		}
			break;
		case R8: {
			for (n = 0; n < Nelements(); n++) {
				if (r8data[n] == 0.0) r8data[n] = v;
			}
		}
			break;
		default: break;
	}
}

bool Fits::correl(const Fits & a) {
	Fits Y;
	
	if (!Y.copy(a)) return FALSE;
	if (!Y.fft()) return FALSE;
	Y.conj();
	if (!fft()) return FALSE;
	mul(Y);
	if (!fft()) return FALSE;
//	div(get_flux());
	return TRUE;
}

bool Fits::correl_real(const Fits & a) {
	if (Nelements() != a.Nelements()) return FALSE;
	
	double vec1sum = 0.0, vec2sum = 0.0;
	unsigned long i, x, y, sy, ne;
	Fits acopy, result;
	
	ne = Nelements();
	if (!acopy.copy(a)) return FALSE;
	if (!result.create(ne, 1, R8)) return FALSE;
	result.CopyHeader(*this);
	
	for (i = 0; i < ne; i++) {
		vec1sum += ValueAt(i) * ValueAt(i);
		vec2sum += acopy.ValueAt(i) * acopy.ValueAt(i);
	}
	vec1sum = sqrt(vec1sum);
	vec2sum = sqrt(vec2sum);
	div(vec1sum);
	acopy.div(vec2sum);
	
	for (y = 1; y <= ne; y++) {
		for (x = 1; x <= ne; x++) {
			sy = x + y - 1;
			if (sy > ne) sy -= ne;
			result.r8data[y-1] += ValueAt(x-1) * acopy.ValueAt(sy - 1);
		}
	}
	copy(result);
	
	return TRUE;
}
