/******************************************************************
 * DPUSER - The Next Generation
 *
 * File:     libfits/fits_range.cpp
 * Purpose:  Fits class methods that deal with subarrays
 * Author:   Thomas Ott
 *
 * Revision history
 * ================
 * 05.01.2000: file created from part of fits.cpp
 ******************************************************************/

#include "fits.h"

/*!
Check the range given by integer arguments, and replace -1's by
correct values
*/

bool Fits::checkRange(int *x1, int *x2, int *y1, int *y2, int *z1, int *z2) {
	int i[6], n;
    int tempr;
	bool rv = TRUE;

	i[0] = *x1;
	i[1] = *x2;
	i[2] = *y1;
	i[3] = *y2;
	i[4] = *z1;
	i[5] = *z2;

// Replace and fix as necessary and possible
	if ((i[0] > 0) && (i[1] < 1)) i[1] = naxis[1];
	else if ((i[0] < 1) && (i[1] > 0)) i[0] = 1;
	else if ((i[0] == -1) && (i[1] == -1)) {
		i[0] = 1;
		i[1] = naxis[1];
	}
	if ((i[2] > 0) && (i[3] < 1)) i[3] = naxis[2];
	else if ((i[2] < 1) && (i[3] > 0)) i[2] = 1;
	else if ((i[2] == -1) && (i[3] == -1)) {
		i[2] = 1;
		i[3] = naxis[2];
	}
	if ((i[4] > 0) && (i[5] < 1)) i[5] = naxis[2];
	else if ((i[4] < 1) && (i[5] > 0)) i[4] = 1;
	else if ((i[4] == -1) && (i[5] == -1)) {
		i[4] = 1;
		i[5] = naxis[3];
	}
    if (i[0] > i[1]) { SWAP(i[0], i[1]); }
    if (i[2] > i[3]) { SWAP(i[2], i[3]); }
    if (i[4] > i[5]) { SWAP(i[4], i[5]); }

// Check the ranges
	for (n = 0; n < 6; n++) if (i[n] < 1) rv = FALSE;
	if ((i[0] > naxis[1]) || (i[1] > naxis[1])) rv = FALSE;
	if ((i[2] > naxis[2]) || (i[3] > naxis[2])) rv = FALSE;
	if ((i[4] > naxis[3]) || (i[5] > naxis[3])) rv = FALSE;
	if (!rv) {
		dp_output("[%i:%i, %i:%i, %i:%i] out of bounds.\n", i[0], i[1], i[2], i[3], i[4], i[5]);
		return FALSE;
	}
	
	*x1 = i[0];
	*x2 = i[1];
	*y1 = i[2];
	*y2 = i[3];
	*z1 = i[4];
	*z2 = i[5];

	return TRUE;
}

bool Fits::correctRange(int n, long *x) {
	int i;
	long y;

	if (n / 2 != naxis[0]) return FALSE;
	for (i = 0; i < n; i += 2) {
		if (x[i] < 1) x[i] = 1;
		if (x[i + 1] > naxis[i / 2 + 1]) x[i + 1] = naxis[i / 2 + 1];
		if (x[i + 1] < 1) x[i + 1] = naxis[i / 2 + 1];
		if (x[i] > x[i + 1]) {
			y = x[i];
			x[i] = x[i + 1];
			x[i + 1] = y;
		}
		if (x[i] < 1) x[i] = 1;
		if (x[i + 1] > naxis[i / 2 + 1]) x[i + 1] = naxis[i / 2 + 1];
		if (x[i + 1] < 1) x[i + 1] = naxis[i / 2 + 1];
	}

	return TRUE;
}

bool Fits::checkRange(int n, long *x) {
	int i;

	if (n / 2 != naxis[0]) return FALSE;
    for (i = 0; i < n; i += 2) {
		if (x[i] < 1) return FALSE;
        if (x[i + 1] > naxis[i / 2 + 1]) return FALSE;
        if (x[i + 1] < 1) return FALSE;
        if (x[i] > x[i + 1]) return FALSE;
    }

	return TRUE;
}

/*!
Set a range in the fits array to a certain value
*/

bool Fits::setRange(double value, int x1, int x2, int y1, int y2, int z1, int z2)
{
	int i, j, k;
	
/* Sanity check */
	if (!checkRange(&x1, &x2, &y1, &y2, &z1, &z2)) {
		dp_output("[%i:%i, %i:%i, %i:%i] out of bounds\n", x1, x2, y1, y2, z1, z2);
		return FALSE;
	}
	
//	printf("Fits::setRange: Bitpix changed to R4.\n");
	
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)value;
		for (i = x1; i <= x2; i++) {
			for (j = y1; j <= y2; j++) {
				for (k = z1; k <= z2; k++) {
					r4data[at(i, j, k)] = fvalue;
				}
			}
		}
	} else if (membits == R8) {
		for (i = x1; i <= x2; i++) {
			for (j = y1; j <= y2; j++) {
				for (k = z1; k <= z2; k++) {
					r8data[at(i, j, k)] = value;
				}
			}
		}
	} else return FALSE;
	
	return TRUE;
}

bool Fits::setRawRange(double value, dpint64 x1, dpint64 x2) {
    dpint64 n;

/* Sanity check */
	if ((x1 < 1) || (x1 > n_elements) || (x2 < 1) || (x2 > n_elements) || (x1 > x2)) {
		dp_output("setRawRange: Values out of bounds\n");
		return FALSE;
	}
	if (membits > -32) if (!setType(R4)) return FALSE;
	
	if (membits == R4) {
		float fvalue = (float)value;
		for (n = x1 - 1; n < x2; n++) {
			r4data[n] = fvalue;
		}
	} else if (membits == R8) {
		for (n = x1 - 1; n < x2; n++) {
			r8data[n] = value;
		}
	} else return FALSE;
	return TRUE;
}

bool Fits::setRawRange(double value, const Fits &where) {
    dpint64 n, w;

	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)value;
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r4data[w - 1] = fvalue;
			}
		}
	} else if (membits == R8) {
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r8data[w - 1] = value;
			}
		}
	} else return FALSE;
	return TRUE;
}

bool Fits::setRawRange(const Fits &value, const Fits &where) {
    if (where.Nelements() != value.Nelements()) return FALSE;
    dpint64 n, w;

    if (membits > -32) if (!setType(R4)) return FALSE;

    if (membits == R4) {
        for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
            if ((w >= 1) && (w <= n_elements)) {
                r4data[w - 1] = value.ValueAt(n);
            }
        }
    } else if (membits == R8) {
        for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
            if ((w >= 1) && (w <= n_elements)) {
                r8data[w - 1] = value.ValueAt(n);
            }
        }
    } else return FALSE;
    return TRUE;
}

bool Fits::addRawRange(double value, const Fits &where) {
    dpint64 n, w;

	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)value;
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r4data[w - 1] += fvalue;
			}
		}
	} else if (membits == R8) {
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r8data[w - 1] += value;
			}
		}
	} else return FALSE;
	return TRUE;
}

bool Fits::subRawRange(double value, const Fits &where) {
    dpint64 n, w;

	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)value;
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r4data[w - 1] -= fvalue;
			}
		}
	} else if (membits == R8) {
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r8data[w - 1] -= value;
			}
		}
	} else return FALSE;
	return TRUE;
}

bool Fits::mulRawRange(double value, const Fits &where) {
    dpint64 n, w;

	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)value;
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r4data[w - 1] *= fvalue;
			}
		}
	} else if (membits == R8) {
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r8data[w - 1] *= value;
			}
		}
	} else return FALSE;
	return TRUE;
}

bool Fits::divRawRange(double value, const Fits &where) {
    dpint64 n, w;

	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)value;
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r4data[w - 1] /= fvalue;
			}
		}
	} else if (membits == R8) {
		for (n = 0; n < where.Nelements(); n++) {
            w = (dpint64)where.ValueAt(n);
			if ((w >= 1) && (w <= n_elements)) {
				r8data[w - 1] /= value;
			}
		}
	} else return FALSE;
	return TRUE;
}

/*!
Set a range in the fits array to a certain value, range given by a file
*/

int dLB(char *input)
{
	int count = 0;
	int length = strlen(input);
	
#ifdef DEBUG
	dp_output("Deleting leading blanks:\nInput is %s.\n", input);
#endif /* DEBUG */

	while ((count < length) && (input[count] == ' ')) count++;
	if (count > 0) {
		memmove((void *)input, (void *)(input+count), sizeof(char)*(length-count+1));
	}

#ifdef DEBUG
	dp_output("Output is %s.\n", input);
#endif /* DEBUG */

	return(count);
}

int Fits::setRange(float value, char *fname)
{
	int i, j, l, rv, x1, x2, y1, y2, z1, z2;
	char xrange[256], yrange[256], zrange[256], *second, input[256];
	FILE *desc;
	
	desc = fopen(fname, "rb");
	if (desc == NULL) {
		dp_output("File %s does not exist.\n", fname);
		return(-1);
	}
	
	rv = 0;
	while (rv != EOF) {
		l = 0;
		do {
			rv = fscanf(desc, "%c", &input[l++]);
		} while (input[l-1] != '\n');
		l--;
		input[l] = (char)0;
		if (rv != EOF) {
			xrange[0] = yrange[0] = zrange[0] = (char)0;
			x1 = y1 = z1 = 1;
			x2 = naxis[1];
			y2 = naxis[2];
			z2 = naxis[3];
			i = 0;
			while (input[i] == ' ') i++;
			j = 0;
			while ((input[i] != ',') && (i < l)) {
				xrange[j] = input[i];
				i++;
				j++;
			}
			xrange[j] = (char)0;
			j = 0;
			i++;
			while ((input[i] != ',') && (i < l)) {
				yrange[j] = input[i];
				i++;
				j++;
			}
			yrange[j] = (char)0;
			j = 0;
			i++;
			while ((input[i] != ',') && (i < l)) {
				zrange[j] = input[i];
				i++;
				j++;
			}
			zrange[j] = (char)0;
			dLB(xrange);
			dLB(yrange);
			dLB(zrange);
			i = 0;
			if ((strlen(xrange) > 0) && (xrange[i] != '*')) {
				x1 = atoi(xrange);
				if ((second = strchr(xrange, ':')) != NULL) {
					x2 = atoi(second + 1);
				} else x2 = x1;
			}
			i = 0;
			if ((strlen(yrange) > 0) && (yrange[i] != '*')) {
				y1 = atoi(yrange);
				if ((second = strchr(yrange, ':')) != NULL) {
					y2 = atoi(second + 1);
				} else y2 = y1;
			}
			i = 0;
			if ((strlen(zrange) > 0) && (zrange[i] != '*')) {
				z1 = atoi(zrange);
				if ((second = strchr(zrange, ':')) != NULL) {
					z2 = atoi(second + 1);
				} else z2 = z1;
			}
			setRange(value, x1, x2, y1, y2, z1, z2);
		}
	}
	fclose(desc);
	return(1);
}

/*!
Set a range in the fits array to values in another fits array
*/

bool Fits::setRange(Fits & value, int x1, int x2, int y1, int y2, int z1, int z2)
{
    dpint64 i, i1, j, j1, k, k1, n;
	
/* Sanity check */
	if (!checkRange(&x1, &x2, &y1, &y2, &z1, &z2)) return FALSE;

//	if ((x2 - x1 + 1 != value.naxis[1]) ||
//	    (y2 - y1 + 1 != value.naxis[2]) ||
//		(z2 - z1 + 1 != value.naxis[3])) {
    if ((x2 - x1 + 1) * (y2 - y1 + 1) * (z2 - z1 + 1) != value.n_elements) {
		dp_output("Arrays [%ix%ix%i] and [%ix%ix%i] are not of the same size.\n",
		x2 - x1 + 1, y2 - y1 + 1, z2 - z1 + 1, value.naxis[1], value.naxis[2], value.naxis[3]);
		return FALSE;
	}
	
//	printf("Fits::setRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	n = 0;
//	for (i = x1, i1 = 1; i <= x2; i++, i1++) {
//		for (j = y1, j1 = 1; j <= y2; j++, j1++) {
//			for (k = z1, k1 = 1; k <= z2; k++, k1++) {
//				r4data[F_I(i, j, k)] = (float)value.ValueAt(value.F_I(i1, j1, k1));
//				r4data[F_I(i, j, k)] = (float)value.ValueAt(n++);
//			}
//		}
//	}
	if (membits == R4) {
		for (k = z1, k1 = 1; k <= z2; k++, k1++) {
			for (j = y1, j1 = 1; j <= y2; j++, j1++) {
				for (i = x1, i1 = 1; i <= x2; i++, i1++) {
					r4data[F_I(i, j, k)] = (float)value.ValueAt(n++);
				}
			}
		}
	} else if (membits == R8) {
		for (k = z1, k1 = 1; k <= z2; k++, k1++) {
			for (j = y1, j1 = 1; j <= y2; j++, j1++) {
				for (i = x1, i1 = 1; i <= x2; i++, i1++) {
					r8data[F_I(i, j, k)] = value.ValueAt(n++);
				}
			}
		}
	} else return FALSE;
	
	return TRUE;
}

/*!
extract part of the array
*/

bool Fits::extractRange(Fits &result, int x1, int x2, int y1, int y2, int z1, int z2) {
	int i, j, k, i1, j1, k1;
	int n1, n2;

	if (!checkRange(&x1, &x2, &y1, &y2, &z1, &z2)) {
		return FALSE;
	}

// Check if this would be the original array
	if ((x1 == 1) && (y1 == 1) && (z1 == 1) && (x2 == naxis[1]) && (y2 == naxis[2]) && (z2 == naxis[3]))
		return result.copy(*this);
	
// go through the array and set values
	n1 = x2 - x1 + 1;
	n2 = y2 - y1 + 1;
	result.create(n1, n2, z2 - z1 + 1, membits);
	result.CopyHeader(*this);
	if (membits > R4) {
		result.bscale = bscale;
		result.bzero = bzero;
	}

	switch (membits) {
		case I1:
			for (i = x1, i1 = 1; i <= x2; i++, i1++) {
				for (j = y1, j1 = 1; j <= y2; j++, j1++) {
					for (k = z1, k1 = 1; k <= z2; k++, k1++) {
						result.i1data[result.at(i1, j1, k1)] = i1data[at(i, j, k)];
					}
				}
			}
			break;
		case I2:
			for (i = x1, i1 = 1; i <= x2; i++, i1++) {
				for (j = y1, j1 = 1; j <= y2; j++, j1++) {
					for (k = z1, k1 = 1; k <= z2; k++, k1++) {
						result.i2data[result.at(i1, j1, k1)] = i2data[at(i, j, k)];
					}
				}
			}
			break;
		case I4:
			for (i = x1, i1 = 1; i <= x2; i++, i1++) {
				for (j = y1, j1 = 1; j <= y2; j++, j1++) {
					for (k = z1, k1 = 1; k <= z2; k++, k1++) {
						result.i4data[result.at(i1, j1, k1)] = i4data[at(i, j, k)];
					}
				}
			}
			break;
        case I8:
            for (i = x1, i1 = 1; i <= x2; i++, i1++) {
                for (j = y1, j1 = 1; j <= y2; j++, j1++) {
                    for (k = z1, k1 = 1; k <= z2; k++, k1++) {
                        result.i8data[result.at(i1, j1, k1)] = i8data[at(i, j, k)];
                    }
                }
            }
            break;
        case R4:
			for (i = x1, i1 = 1; i <= x2; i++, i1++) {
				for (j = y1, j1 = 1; j <= y2; j++, j1++) {
					for (k = z1, k1 = 1; k <= z2; k++, k1++) {
                        result.r4data[result.at(i1, j1, k1)] = r4data[at(i, j, k)];
					}
				}
			}
			break;
		case R8:
			for (i = x1, i1 = 1; i <= x2; i++, i1++) {
				for (j = y1, j1 = 1; j <= y2; j++, j1++) {
					for (k = z1, k1 = 1; k <= z2; k++, k1++) {
						result.r8data[result.at(i1, j1, k1)] = r8data[at(i, j, k)];
					}
				}
			}
			break;
		case C16:
			for (i = x1, i1 = 1; i <= x2; i++, i1++) {
				for (j = y1, j1 = 1; j <= y2; j++, j1++) {
					for (k = z1, k1 = 1; k <= z2; k++, k1++) {
						result.cdata[result.at(i1, j1, k1)].r = cdata[at(i, j, k)].r;
						result.cdata[result.at(i1, j1, k1)].i = cdata[at(i, j, k)].i;
					}
				}
			}
			break;
		default: break;
	}
	if (result.hasRefPix()) {
		result.SetFloatKey("CRPIX1", result.getCRPIX(1) - (double)(x1 - 1));
		result.SetFloatKey("CRPIX2", result.getCRPIX(2) - (double)(y1 - 1));
		result.SetFloatKey("CRPIX3", result.getCRPIX(3) - (double)(z1 - 1));
	}
	result.deflate();
	return TRUE;
}

/*!
extract part of the array, indices given by another FITS
*/

bool Fits::extractRange(Fits &result, Fits &indices) {
    dpint64 i;
	double min, max;

// check values of indices
	indices.get_minmax(&min, &max);
    if ((nint(min) < 1) || (nint(max) > n_elements)) {
		dp_output("Fits::extractRange: indices out of range\n");
		return FALSE;
	}
	result.create(indices.n_elements, 1, membits);
	if (membits > R4) {
		result.bscale = bscale;
		result.bzero = bzero;
	}

	switch (membits) {
		case I1:
			for (i = 0; i < indices.n_elements; i++) {
				result.i1data[i] = i1data[nint(indices.ValueAt(i))-1];
			}
			break;
		case I2:
			for (i = 0; i < indices.n_elements; i++) {
				result.i2data[i] = i2data[nint(indices.ValueAt(i))-1];
			}
			break;
		case I4:
			for (i = 0; i < indices.n_elements; i++) {
				result.i4data[i] = i4data[nint(indices.ValueAt(i))-1];
			}
			break;
        case I8:
            for (i = 0; i < indices.n_elements; i++) {
                result.i8data[i] = i8data[nint(indices.ValueAt(i))-1];
            }
            break;
        case R4:
			for (i = 0; i < indices.n_elements; i++) {
				result.r4data[i] = r4data[nint(indices.ValueAt(i))-1];
			}
			break;
		case R8:
			for (i = 0; i < indices.n_elements; i++) {
				result.r8data[i] = r8data[nint(indices.ValueAt(i))-1];
			}
			break;
		case C16:
			for (i = 0; i < indices.n_elements; i++) {
				result.cdata[i].r = cdata[nint(indices.ValueAt(i))-1].r;
				result.cdata[i].i = cdata[nint(indices.ValueAt(i))-1].i;
			}
			break;
		default: break;
	}

	return TRUE;
}

bool Fits::incRange(int n, long *x) {
	long i, j, k;
	
	if (!checkRange(n, x)) return FALSE;

//	printf("Fits::incRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) {
				r4data[i]++;
			}
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r4data[F_I(i, j)]++;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r4data[F_I(i, j, k)]++;
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) {
				r8data[i]++;
			}
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r8data[F_I(i, j)]++;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r8data[F_I(i, j, k)]++;
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::decRange(int n, long *x) {
	long i, j, k;
	
	if (!checkRange(n, x)) return FALSE;

//	printf("Fits::decRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) {
				r4data[i]--;
			}
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r4data[F_I(i, j)]--;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r4data[F_I(i, j, k)]--;
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) {
				r8data[i]--;
			}
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r8data[F_I(i, j)]--;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r8data[F_I(i, j, k)]--;
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

/*!
Add the constant v to a part of the array.
*/

bool Fits::addRange(double v, int n, long *x) {
	long i, j, k;
	
	if (!checkRange(n, x)) return FALSE;

//	printf("Fits::addRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)v;
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) {
				r4data[i] += fvalue;
			}
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r4data[F_I(i, j)] += fvalue;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r4data[F_I(i, j, k)] += fvalue;
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) {
				r8data[i] += v;
			}
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r8data[F_I(i, j)] += v;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r8data[F_I(i, j, k)] += v;
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

/*!
Add the Fits f to a part of the array.
*/

bool Fits::addRange(const Fits & f, int n, long *x) {
	long i, j, k, l;
	
	if (!checkRange(n, x)) return FALSE;
//	if (n / 2 != f.naxis[0]) return FALSE;

//	printf("Fits::addRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	l = 0;
	if (membits == R4) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] += (float)f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r4data[F_I(i, j)] += (float)f.ValueAt(l++);
				}
			}
		} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r4data[F_I(i, j, k)] += (float)f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r8data[i] += f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r8data[F_I(i, j)] += f.ValueAt(l++);
				}
			}
		} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r8data[F_I(i, j, k)] += f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::subRange(double v, int n, long *x) {
	long i, j, k;
	
	if (!checkRange(n, x)) return FALSE;

//	printf("Fits::subRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)v;
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] -= fvalue;
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r4data[F_I(i, j)] -= fvalue;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r4data[F_I(i, j, k)] -= fvalue;
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) r8data[i] -= v;
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r8data[F_I(i, j)] -= v;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r8data[F_I(i, j, k)] -= v;
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::subRange(const Fits & f, int n, long *x) {
	long i, j, k, l;
	
	if (!checkRange(n, x)) return FALSE;
//	if (n / 2 != f.naxis[0]) return FALSE;

//	printf("Fits::addRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	l = 0;
	if (membits == R4) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] -= (float)f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r4data[F_I(i, j)] -= (float)f.ValueAt(l++);
				}
			}
		} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r4data[F_I(i, j, k)] -= (float)f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r8data[i] -= f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r8data[F_I(i, j)] -= f.ValueAt(l++);
				}
			}
		} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r8data[F_I(i, j, k)] -= f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::mulRange(double v, int n, long *x) {
	long i, j, k;
	
	if (!checkRange(n, x)) return FALSE;

//	printf("Fits::mulRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)v;
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] *= fvalue;
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r4data[F_I(i, j)] *= fvalue;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r4data[F_I(i, j, k)] *= fvalue;
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) r8data[i] *= v;
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r8data[F_I(i, j)] *= v;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r8data[F_I(i, j, k)] *= v;
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::mulRange(const Fits & f, int n, long *x) {
	long i, j, k, l;
	
	if (!checkRange(n, x)) return FALSE;
//	if (n / 2 != f.naxis[0]) return FALSE;

//	printf("Fits::addRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	l = 0;
	if (membits == R4) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] *= (float)f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r4data[F_I(i, j)] *= (float)f.ValueAt(l++);
				}
			}
		} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r4data[F_I(i, j, k)] *= (float)f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r8data[i] *= f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r8data[F_I(i, j)] *= f.ValueAt(l++);
				}
			}
		} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r8data[F_I(i, j, k)] *= f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::divRange(double v, int n, long *x) {
	long i, j, k;
	
	if (!checkRange(n, x)) return FALSE;

//	printf("Fits::divRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	if (membits == R4) {
		float fvalue = (float)v;
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] /= fvalue;
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r4data[F_I(i, j)] /= fvalue;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r4data[F_I(i, j, k)] /= fvalue;
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
			for (i = x[0] - 1; i < x[1]; i++) r8data[i] /= v;
		} else if (n == 4) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					r8data[F_I(i, j)] /= v;
				}
			}
		} else if (n == 6) {
			for (i = x[0]; i <= x[1]; i++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (k = x[4]; k <= x[5]; k++) {
						r8data[F_I(i, j, k)] /= v;
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

bool Fits::divRange(const Fits & f, int n, long *x) {
	long i, j, k, l;
	
	if (!checkRange(n, x)) return FALSE;
//	if (n / 2 != f.naxis[0]) return FALSE;

//	printf("Fits::addRange: Bitpix being changed to R4.\n");
	if (membits > -32) if (!setType(R4)) return FALSE;

	l = 0;
	if (membits == R4) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] /= (float)f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r4data[F_I(i, j)] /= (float)f.ValueAt(l++);
				}
			}
	} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r4data[F_I(i, j, k)] /= (float)f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else if (membits == R8) {
		if (n == 2) {
            if ((dpint64)(x[1] - x[0] + 1) != f.n_elements) return FALSE;
			for (i = x[0] - 1; i < x[1]; i++) r4data[i] /= f.ValueAt(l++);
		} else if (n == 4) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) != f.n_elements) return FALSE;
			for (j = x[2]; j <= x[3]; j++) {
				for (i = x[0]; i <= x[1]; i++) {
					r4data[F_I(i, j)] /= f.ValueAt(l++);
				}
			}
	} else if (n == 6) {
            if ((dpint64)(x[1] - x[0] + 1) * (dpint64)(x[3] - x[2] + 1) * (dpint64)(x[5] - x[4] + 1) != f.n_elements) return FALSE;
			for (k = x[4]; k <= x[5]; k++) {
				for (j = x[2]; j <= x[3]; j++) {
					for (i = x[0]; i <= x[1]; i++) {
						r4data[F_I(i, j, k)] /= f.ValueAt(l++);
					}
				}
			}
		} else return FALSE;
	} else return FALSE;
	return TRUE;
}

/*!
Remove all axis information of axes whose length is 1.
*/

void Fits::deflate(void) {
	int i, j;
	bool done = TRUE;
	char *ctype[10], *cunit[10];
	char key[80];
	
    for (i = 1; i <= naxis[0]; i++) {
        if (naxis[i] < 2) {
            done = FALSE;
        }
    }
	if (Nelements() == 1) return;
	if (done) return;

	for (i = 0; i < 10; i++) {
		ctype[i] = (char *)malloc(256);
		sprintf(ctype[i], "");
		cunit[i] = (char *)malloc(256);
		sprintf(cunit[i], "");
	}
	GetStringKeys("CTYPE", (char **)ctype, 9);
	GetStringKeys("CUNIT", (char **)cunit, 9);

	i = 1;
	while (!done) {
		char key[256];
		if (naxis[i] < 2) {
			naxis[0]--;
			for (j = i; j <= naxis[0]; j++) {
				naxis[j] = naxis[j + 1];

                char key2[256];
                sprintf(key, "CD%i_%i", j, j);
                sprintf(key2, "CD%i_%i", j+1, j+1);

                double dummy;
                if (GetFloatKey(key2, &dummy)) {
                    SetFloatKey(key, dummy);
                }

                sprintf(key, "CDELT%i", j);
                sprintf(key2, "CDELT%i", j+1);
                if (GetFloatKey(key2, &dummy)) {
                    SetFloatKey(key, getCDELT(j+1));
                }
				sprintf(key, "CRPIX%i", j);
				SetFloatKey(key, getCRPIX(j+1));
				sprintf(key, "CRVAL%i", j);
				SetFloatKey(key, getCRVAL(j+1));
				strcpy(ctype[j-1], ctype[j]);
				strcpy(cunit[j-1], cunit[j]);
			}
		} else i++;
		done = (naxis[0] == i);
	}
	for (i = naxis[0] + 1; i <= MAXNAXIS; i++) {
		naxis[i] = 1;
		sprintf(key, "CUNIT%i", i);
		DeleteKey(key);
		sprintf(key, "CTYPE%i", i);
		DeleteKey(key);
		sprintf(key, "CRPIX%i", i);
		DeleteKey(key);
		sprintf(key, "CRVAL%i", i);
		DeleteKey(key);
		sprintf(key, "CDELT%i", i);
		DeleteKey(key);
	}
	sprintf(crtype, "");
	if (naxis[3] < 2) naxis[0] = 2;
	if (naxis[2] < 2) naxis[0] = 1;

	SetStringKeys("CTYPE", (char **)ctype, naxis[0]);
	SetStringKeys("CUNIT", (char **)cunit, naxis[0]);
	
	for (i = 0; i < 10; i++) {
		free(ctype[i]);
		free(cunit[i]);
	}
}

bool Fits::reindex(const Fits &indices) {
	unsigned long n;
	long index;

	if (indices.Nelements() > Nelements()) {
		dp_output("Fits::reindex: too many indices.\n");
		return FALSE;
	}

	Fits temp;
	if (!temp.copy(*this)) return FALSE;

	for (n = 0; n < indices.Nelements(); n++) {
		index = nint(indices.ValueAt(indices.F_I(n + 1, 1))) - 1;
		setValue(temp.ValueAt(index), n + 1, 1);
	}
	resize(indices.Nelements());

	return TRUE;
}

dpint64 Fits::mask2index() {
    dpint64 rv = 0;
    setType(I4);
    for (dpint64 i = 0; i < Nelements(); i++) {
        if (i4data[i]) {
            i4data[rv] = i+1;
            rv++;
        }
    }
    resize(rv);
    return rv;
}
