/*
 * File:   libfits/dp_trigonometry.cpp
 * Author: Thomas Ott
 * Description: Trigonometric functions for the Fits class library
 */

#include "fits.h"

/*!
Sine of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Sin(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	if (radians) switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = sin(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = sin(r8data[n]);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				re = sin(cdata[n].r) * cosh(cdata[n].i);
				cdata[n].i = cos(cdata[n].r) * sinh(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	} else switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = sin(r4data[n] * M_PI / 180.);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = sin(r8data[n] * M_PI / 180.);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				cdata[n].r *= M_PI / 180.;
				cdata[n].i *= M_PI / 180.;
				re = sin(cdata[n].r) * cosh(cdata[n].i);
				cdata[n].i = cos(cdata[n].r) * sinh(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	}

	return TRUE;
}

/*!
Cosine of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Cos(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	if (radians) switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = cos(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = cos(r8data[n]);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				re = cos(cdata[n].r) * cosh(cdata[n].i);
				cdata[n].i = -sin(cdata[n].r) * sinh(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	} else switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = cos(r4data[n] * M_PI / 180.);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = cos(r8data[n] * M_PI / 180.);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				cdata[n].r *= M_PI / 180.;
				cdata[n].i *= M_PI / 180.;
				re = cos(cdata[n].r) * cosh(cdata[n].i);
				cdata[n].i = -sin(cdata[n].r) * sinh(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	}

	return TRUE;
}

/*!
Tangent of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Tan(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	if (radians) switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = tan(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = tan(r8data[n]);
			break;
		case C16: {
			double r, i;
		
			for (n = 0; n < n_elements; n++) {
				r = 2.0 * cdata[n].r;
				i = 2.0 * cdata[n].i;
				cdata[n].r = sin(r) / (cos(r) + cosh(i));
				cdata[n].i = sinh(i) / (cos(r) + cosh(i));
			}
		}
			break;
		default: return FALSE; break;
	} else switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = tan(r4data[n] * M_PI / 180.);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = tan(r8data[n] * M_PI / 180.);
			break;
		case C16: {
			double r, i;
		
			for (n = 0; n < n_elements; n++) {
				r = 2.0 * cdata[n].r * M_PI / 180.;
				i = 2.0 * cdata[n].i * M_PI / 180.;
				cdata[n].r = sin(r) / (cos(r) + cosh(i));
				cdata[n].i = sinh(i) / (cos(r) + cosh(i));
			}
		}
			break;
		default: return FALSE; break;
	}

	return TRUE;
}

/*!
Sine hyperbolicus of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Sinh(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	if (radians) switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = sinh(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = sinh(r8data[n]);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				re = sinh(cdata[n].r) * cos(cdata[n].i);
				cdata[n].i = cosh(cdata[n].r) * sin(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	} else switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = sinh(r4data[n] * M_PI / 180.);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = sinh(r8data[n] * M_PI / 180.);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				cdata[n].r *= M_PI / 180.;
				cdata[n].i *= M_PI / 180.;
				re = sinh(cdata[n].r) * cos(cdata[n].i);
				cdata[n].i = cosh(cdata[n].r) * sin(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	}

	return TRUE;
}

/*!
Cosine hyperbolicus of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Cosh(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	if (radians) switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = cosh(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = cosh(r8data[n]);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				re = cosh(cdata[n].r) * cos(cdata[n].i);
				cdata[n].i = sinh(cdata[n].r) * sin(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	} else switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = cosh(r4data[n] * M_PI / 180.);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = cosh(r8data[n] * M_PI / 180.);
			break;
		case C16: {
			double re;
		
			for (n = 0; n < n_elements; n++) {
				cdata[n].r *= M_PI / 180.;
				cdata[n].i *= M_PI / 180.;
				re = cosh(cdata[n].r) * cos(cdata[n].i);
				cdata[n].i = sinh(cdata[n].r) * sin(cdata[n].i);
				cdata[n].r = re;
			}
		}
			break;
		default: return FALSE; break;
	}

	return TRUE;
}

/*!
Tangent hyperbolicus of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Tanh(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	if (radians) switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = tanh(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = tanh(r8data[n]);
			break;
		case C16: {
			double r, i;
		
			for (n = 0; n < n_elements; n++) {
				r = 2.0 * cdata[n].r;
				i = 2.0 * cdata[n].i;
				cdata[n].r = sinh(r) / (cosh(r) + cos(i));
				cdata[n].i = sin(i) / (cos(r) + cos(i));
			}
		}
			break;
		default: return FALSE; break;
	} else switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = tanh(r4data[n] * M_PI / 180.);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = tanh(r8data[n] * M_PI / 180.);
			break;
		case C16: {
			double r, i;
		
			for (n = 0; n < n_elements; n++) {
				r = 2.0 * cdata[n].r * M_PI / 180.;
				i = 2.0 * cdata[n].i * M_PI / 180.;
				cdata[n].r = sinh(r) / (cosh(r) + cos(i));
				cdata[n].i = sin(i) / (cos(r) + cos(i));
			}
		}
			break;
		default: return FALSE; break;
	}

	return TRUE;
}

/*!Arcus Sine of all array elements.
If the array is of integer type, it will be changed to float (R4).
If the minimum value of array elements is < -1.0 or the maximum is > 1.0, the array
type will be changed to complex (C16).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Asin(bool radians) {
	ULONG n;
	double min, max;
	
	get_minmax(&min, &max);
	if ((min < -1.0) || (max > 1.0)) if (!setType(C16)) return FALSE;
	if (membits > R4) if (!setType(R4)) return FALSE;

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = asin(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = asin(r8data[n]);
			break;
		case C16: {
			dpComplex c;

			for (n = 0; n < n_elements; n++) {
				c = complex_asin(dpComplex(cdata[n].r, cdata[n].i));
				cdata[n].r = real(c);
				cdata[n].i = imag(c);
			}
		}
			break;
		default: return FALSE; break;
	}
	if (!radians) mul(180. / M_PI);

	return TRUE;
}

/*!
Arcus Cosine of all array elements.
If the array is of integer type, it will be changed to float (R4).
If the minimum value of array elements is < -1.0 or the maximum is > 1.0, the array
type will be changed to complex (C16).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Acos(bool radians) {
	ULONG n;
	double min, max;
	
	get_minmax(&min, &max);
	if ((min < -1.0) || (max > 1.0)) if (!setType(C16)) return FALSE;

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

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = acos(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = acos(r8data[n]);
			break;
		case C16: {
			dpComplex c;

			for (n = 0; n < n_elements; n++) {
				c = complex_acos(dpComplex(cdata[n].r, cdata[n].i));
				cdata[n].r = real(c);
				cdata[n].i = imag(c);
			}
		}
			break;
		default: return FALSE; break;
	}
	if (!radians) mul(180. / M_PI);

	return TRUE;
}

/*!Arcus Tangent of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Atan(bool radians) {
	ULONG n;

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

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = atan(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = atan(r8data[n]);
			break;
		case C16: {
			dpComplex c;

			for (n = 0; n < n_elements; n++) {
				c = complex_atan(dpComplex(cdata[n].r, cdata[n].i));
				cdata[n].r = real(c);
				cdata[n].i = imag(c);
			}
		}
			break;
		default: return FALSE; break;
	}
	if (!radians) mul(180. / M_PI);

	return TRUE;
}

/*!
Arcus Sine hyperbolicus of all array elements.
If the array is of integer type, it will be changed to float (R4).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Asinh(bool radians) {
	ULONG n;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = asinh(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = asinh(r8data[n]);
			break;
		case C16: {
			dpComplex c;

			for (n = 0; n < n_elements; n++) {
				c = complex_asinh(dpComplex(cdata[n].r, cdata[n].i));
				cdata[n].r = real(c);
				cdata[n].i = imag(c);
			}
		}
			break;
		default: return FALSE; break;
	}
	if (!radians) mul(180. / M_PI);

	return TRUE;
}

/*!
Arcus Cosine hyperbolicus of all array elements.
If the array is of integer type, it will be changed to float (R4).
If the minimum value of array elements is < 1.0 the array
type will be changed to complex (C16).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Acosh(bool radians) {
	ULONG n;
	double min, max;
	
	get_minmax(&min, &max);
	if (min < 1.0) if (!setType(C16)) return FALSE;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = acosh(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = acosh(r8data[n]);
			break;
		case C16: {
			dpComplex c;

			for (n = 0; n < n_elements; n++) {
				c = complex_acosh(dpComplex(cdata[n].r, cdata[n].i));
				cdata[n].r = real(c);
				cdata[n].i = imag(c);
			}
		}
			break;
		default: return FALSE; break;
	}
	if (!radians) mul(180. / M_PI);

	return TRUE;
}

/*!
Arcus Tangent hyperbolicus of all array elements.
If the array is of integer type, it will be changed to float (R4).
If the minimum value of array elements is <= -1.0 or the maximum is >= 1.0, the array
type will be changed to complex (C16).
\returns TRUE if successful, FALSE otherwise.
*/

bool Fits::Atanh(bool radians) {
	ULONG n;
	double min, max;
	
	get_minmax(&min, &max);
	if ((min <= -1.0) || (max >= 1.0)) if (!setType(C16)) return FALSE;
	
	if (membits > R4) if (!setType(R4)) return FALSE;

	switch (membits) {
		case R4:
			for (n = 0; n < n_elements; n++) r4data[n] = atanh(r4data[n]);
			break;
		case R8:
			for (n = 0; n < n_elements; n++) r8data[n] = atanh(r8data[n]);
			break;
		case C16: {
			dpComplex c;

			for (n = 0; n < n_elements; n++) {
				c = complex_atanh(dpComplex(cdata[n].r, cdata[n].i));
				cdata[n].r = real(c);
				cdata[n].i = imag(c);
			}
		}
			break;
		default: return FALSE; break;
	}
	if (!radians) mul(180. / M_PI);

	return TRUE;
}

