/*  Utility programs for unit conversions and calendar
 */

/*double PI = 3.14159265358979323846;*/
#include "kep.h"
#if __STDC__
double caltoj (long, int, double);
#else
double caltoj();
#endif

char *intfmt = "%d";
char *lngfmt = "%ld";
char *dblfmt = "%lf";
char *strfmt = "%s";

/* Display Right Ascension and Declination
 * from input equatorial rectangular unit vector.
 * Output vector pol[] contains R.A., Dec., and radius.
 */
int showrd( msg, p, pol )
char *msg;
double p[], pol[];
{
double x, y, r;
int i;

r = 0.0;
for( i=0; i<3; i++ )
	{
	x = p[i];
	r += x * x;
	}
r = sqrt(r);

x = zatan2( p[0], p[1] );
pol[0] = x;

y = asin( p[2]/r );
pol[1] = y;

pol[2] = r;

if (prtflg != 0)
  {
    printf( "%s  R.A. ", msg );
    hms( x );
    printf( "Dec. " );
    dms( y );
    printf( "\n" );
  }
return(0);
}




/* Display magnitude of correction vector
 * in arc seconds
 */
int showcor( strng, p, dp )
char *strng;
double p[], dp[];
{
double p1[3], dr, dd;
int i;

if( prtflg == 0 )
	return(0);

for( i=0; i<3; i++ )
	p1[i] = p[i] + dp[i];

deltap( p, p1, &dr, &dd );
printf( "%s dRA %.3fs dDec %.2f\"\n", strng, RTS*dr/15.0, RTS*dd );
return(0);
}



/* Radians to degrees, minutes, seconds
 */
int dms( x )
double x;
{
double s;
int d, m;

s = x * RTD;
if( s < 0.0 )
	{
	printf( " -" );
	s = -s;
	}
else
	printf( "  " );
d = (int) s;
s -= d;
s *= 60;
m = (int) s;
s -= m;
s *= 60;
printf( "%3dd %02d\' %05.2f\"  ", d, m, s );
return(0);
}




/* Radians to hours, minutes, seconds
 */
#define RTOH (12.0/PI)

int hms( x )
double x;
{
int h, m;
long sint, sfrac;
double s;

s = x * RTOH;
if( s < 0.0 )
	s += 24.0;
h = (int) s;
s -= h;
s *= 60;
m = (int) s;
s -= m;
s *= 60;
/* Handle shillings and pence roundoff. */
sfrac = (long) (1000.0 * s + 0.5);
if( sfrac >= 60000L )
  {
    sfrac -= 60000L;
    m += 1;
    if( m >= 60 )
      {
	m -= 60;
	h += 1;
      }
  }
sint = sfrac / 1000;
sfrac -= sint * 1000;
printf( "%3dh %02dm %02ld.%03lds  ", h, m, sint, sfrac );
return(0);
}

/*		julian.c
 *
 * This program calculates Julian day number from calendar
 * date, and the date and day of the week from Julian day.
 * The Julian date is double precision floating point
 * with the origin used by astronomers.  The calendar output
 * converts fractions of a day into hours, minutes, and seconds.
 * There is no year 0.  Enter B.C. years as negative; i.e.,
 * 2 B.C. = -2.
 *
 * The approximate range of dates handled is 4713 B.C. to
 * 54,078 A.D.  This should be adequate for most applications.
 *
 * B.C. dates are calculated by extending the Gregorian sequence
 * of leap years and century years into the past.  This seems
 * the only sensible definition, but I don't know if it is
 * the official one.
 *
 * Note that the astronomical Julian day starts at noon on
 * the previous calendar day.  Thus at midnight in the morning
 * of the present calendar day the Julian date ends in .5;
 * it rolls over to tomorrow at noon today.
 *
 * The month finding algorithm is attributed to Meeus.
 *
 * - Steve Moshier
 */


char *months[12] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};

char *days[7] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};

long cyear = 1986L;
extern long cyear;
static int month = 1;
static double day = 1.0;
short yerend = 0;
extern short yerend;

double zgetdate()
{
double J;

/* Get operator to type in a date.
 */
getnum( "Calendar date: Year", &cyear, lngfmt );

if( (cyear > 53994L) || (cyear < -4713L) )
	{
	printf( "Year out of range.\n" );
	goto err;
	}
if( cyear == 0 )
	{
	printf( "There is no year 0.\n" );
err:	J = 0.0;
	goto pdate;
	}

getnum( "Month (1-12)", &month, intfmt);
getnum( "Day.fraction", &day, dblfmt );

/* Find the Julian day. */
J = caltoj(cyear,month,day);
/*printf( "Julian day %.1f\n", J );*/

pdate:
/* Convert back to calendar date. */
/* jtocal( J ); */
return(J);
}



/*     Calculate Julian day from Gregorian calendar date
 */
double caltoj( year, month, day )
long year;
int month;
double day;
{
long y, a, b, c, e, m;
double J;


/* The origin should be chosen to be a century year
 * that is also a leap year.  We pick 4801 B.C.
 */
y = year + 4800;
if( year < 0 )
	{
	y += 1;
	}

/* The following magic arithmetic calculates a sequence
 * whose successive terms differ by the correct number of
 * days per calendar month.  It starts at 122 = March; January
 * and February come after December.
 */
m = month;
if( m <= 2 )
	{
	m += 12;
	y -= 1;
	}
e = (306 * (m+1))/10;

a = y/100;	/* number of centuries */
if( year <= 1582L )
	{
	if( year == 1582L )
		{
		if( month < 10 )
			goto julius;
		if( month > 10)
			goto gregor;
		if( day >= 15 )
			goto gregor;
		}
julius:
	printf( " Julian Calendar assumed.\n" );
	b = -38;
	}
else
	{ /* -number of century years that are not leap years */
gregor:
	b = (a/4) - a;
	}

c = (36525L * y)/100; /* Julian calendar years and leap years */

/* Add up these terms, plus offset from J 0 to 1 Jan 4801 B.C.
 * Also fudge for the 122 days from the month algorithm.
 */
J = b + c + e + day - 32167.5;
return( J );
}




/* Calculate month, day, and year from Julian date */

int jtocal( J )
double J;
{
int month, day;
long year, a, c, d, x, y, jd;
int BC;
double dd;

if( J < 1721425.5 ) /* January 1.0, 1 A.D. */
	BC = 1;
else
	BC = 0;

jd = (long) (J + 0.5); /* round Julian date up to integer */

/* Find the number of Gregorian centuries
 * since March 1, 4801 B.C.
 */
a = (100*jd + 3204500L)/3652425L;

/* Transform to Julian calendar by adding in Gregorian century years
 * that are not leap years.
 * Subtract 97 days to shift origin of JD to March 1.
 * Add 122 days for magic arithmetic algorithm.
 * Add four years to ensure the first leap year is detected.
 */
c = jd + 1486;
if( jd >= 2299160.5 )
	c += a - a/4;
else
	c += 38;
/* Offset 122 days, which is where the magic arithmetic
 * month formula sequence starts (March 1 = 4 * 30.6 = 122.4).
 */
d = (100*c - 12210L)/36525L;
/* Days in that many whole Julian years */
x = (36525L * d)/100L;

/* Find month and day. */
y = ((c-x)*100L)/3061L;
day = (int) (c - x - ((306L*y)/10L));
month = (int) (y - 1);
if( y > 13 )
	month -= 12;

/* Get the year right. */
year = d - 4715;
if( month > 2 )
	year -= 1;

/* Day of the week. */
a = (jd + 1) % 7;

/* Fractional part of day. */
dd = day + J - jd + 0.5;

/* post the year. */
cyear = year;

if( BC )
	{
	year = -year + 1;
	cyear = -year;
	if( prtflg )
		printf( "%ld B.C. ", year );
	}
else
	{
	if( prtflg )
		printf( "%ld ", year );
	}
day = (int) dd;
if( prtflg )
	printf( "%s %d %s", months[month-1], day, days[(int) a] );

/* Flag last or first day of year */
if( ((month == 1) && (day == 1))
	|| ((month == 12) && (day == 31)) )
	yerend = 1;
else
	yerend = 0;

/* Display fraction of calendar day
 * as clock time.
 */
a = (long) dd;
dd = dd - a;
if( prtflg )
	{
	hms( 2.0*PI*dd );
	if( J == TDT )
		printf( "TDT\n" ); /* Indicate Terrestrial Dynamical Time */
	else if( J == UT )
		printf( "UT\n" ); /* Universal Time */
	else
		printf( "\n" );
	}
return(0);
}


/* Reduce x modulo 360 degrees
 */
double mod360(x)
double x;
{
long k;
double y;

k = (long) (x/360.0);
y = x  -  k * 360.0;
while( y < 0.0 )
	y += 360.0;
while( y > 360.0 )
	y -= 360.0;
return(y);
}




/* Reduce x modulo 2 pi
 */
#define TPI (2.0*PI)
double modtp(x)
double x;
{
double y;

y = floor( x/TPI );
y = x - y * TPI;
while( y < 0.0 )
	y += TPI;
while( y >= TPI )
	y -= TPI;
return(y);
}


/* Get operator to type in hours, minutes, and seconds
 */
static int hours = 0;
static int minutes = 0;
static double seconds = 0.0;

double gethms()
{
double t;

getnum( "Time: Hours", &hours, intfmt );
getnum( "Minutes", &minutes, intfmt );
getnum( "Seconds", &seconds, dblfmt );
t = (3600.0*hours + 60.0*minutes + seconds)/86400.0;
return(t);
}

int getnum( msg, num, format )
char *msg;
void *num;
char *format;
{
char s[40];

printf( "%s (", msg );
if( format == strfmt )
	printf( format, (char *) num );
else if( format == dblfmt )
	printf( format, *(double *)num );
else if( format == intfmt )
	printf( format, *(int *)num );
else if( format == lngfmt )
	printf( format, *(long *)num );
else
	printf( "Illegal input format\n"  );
printf( ") ? ");
gets(s);
if( s[0] != '\0' )
	sscanf( s, format, num );
return(0);
}



/*
 * Convert change in rectangular coordinatates to change
 * in right ascension and declination.
 * For changes greater than about 0.1 degree, the
 * coordinates are converted directly to R.A. and Dec.
 * and the results subtracted.  For small changes,
 * the change is calculated to first order by differentiating
 *   tan(R.A.) = y/x
 * to obtain
 *    dR.A./cos**2(R.A.) = dy/x  -  y dx/x**2
 * where
 *    cos**2(R.A.)  =  1/(1 + (y/x)**2).
 *
 * The change in declination arcsin(z/R) is
 *   d asin(u) = du/sqrt(1-u**2)
 *   where u = z/R.
 *
 * p0 is the initial object - earth vector and
 * p1 is the vector after motion or aberration.
 *
 */

int deltap( p0, p1, dr, dd )
double p0[], p1[];
double *dr, *dd;
{
double dp[3], A, B, P, Q, x, y, z;
int i;

P = 0.0;
Q = 0.0;
z = 0.0;
for( i=0; i<3; i++ )
	{
	x = p0[i];
	y = p1[i];
	P += x * x;
	Q += y * y;
	y = y - x;
	dp[i] = y;
	z += y*y;
	}

A = sqrt(P);
B = sqrt(Q);

if( (A < 1.e-7) || (B < 1.e-7) || (z/(P+Q)) > 5.e-7 )
	{
	P = zatan2( p0[0], p0[1] );
	Q = zatan2( p1[0], p1[1] );
	Q = Q - P;
	while( Q < -PI )
		Q += 2.0*PI;
	while( Q > PI )
		Q -= 2.0*PI;
	*dr = Q;
	P = asin( p0[2]/A );
	Q = asin( p1[2]/B );
	*dd = Q - P;
	return(0);
	}


x = p0[0];
y = p0[1];
if( x == 0.0 )
	{
	*dr = 1.0e38;
	}
else
	{
	Q = y/x;
	Q = (dp[1]  -  dp[0]*y/x)/(x * (1.0 + Q*Q));
	*dr = Q;
	}

x = p0[2]/A;
P = sqrt( 1.0 - x*x );
*dd = (p1[2]/B - x)/P;
return(0);
}
