/*
** Astrolog (Version 3.05) File: formulas.c
**
** IMPORTANT: The planetary calculation routines used in this program
** have been Copyrighted and the core of this program is basically a
** conversion to C of the routines created by James Neely as listed in
** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
** available from Matrix Software. The copyright gives us permission to
** use the routines for our own purposes but not to sell them or profit
** from them in any way.
**
** IN ADDITION: the graphics database and chart display routines used in
** this program are Copyright (C) 1991-1993 by Walter D. Pullen. Permission
** is granted to freely use and distribute these routines provided one
** doesn't sell, restrict, or profit from them in any way. Modification
** is allowed provided these notices remain with any altered or edited
** versions of the program.
*/

#include "astrolog.h"

real MC, Asc, Vtx, OB,
  A, R, B, Q, L, G, O, RA, R1, R2, AZ, FF, LO, OA,
  S, S1, S2, AP, IN, AN, XX, YY, ZZ, NU, BR, AB, C, P, T0[4];

real geo[TOTAL+1], geoalt[TOTAL+1], georet[TOTAL+1],
  helio[TOTAL+1], helioalt[TOTAL+1], helioret[TOTAL+1],
  planet1[TOTAL+1], planet2[TOTAL+1], planetalt1[TOTAL+1], planetalt2[TOTAL+1],
  house1[SIGNS+1], house2[SIGNS+1], ret1[TOTAL+1], ret2[TOTAL+1];


/*
*******************************************************************************
** Specific calculations
*******************************************************************************
*/

/* Given a month, day, and year, convert it into a single Julian day value, */
/* i.e. the number of days passed since a fixed reference date.             */

real MdyToJulian(mon, day, yea)
real mon, day, yea;
{
  long im, j;

  im = 12*((long)yea+4800)+(long)mon-3;
  j = (2*(im%12) + 7 + 365*im)/12;
  j += (long)day + im/48 - 32083;
  if (j > 2299171)                   /* Take care of dates in */
    j += im/4800 - im/1200 + 38;     /* Gregorian calendar.   */
  return (real)j;
}


/* Integer division - like the "/" operator but always rounds result down. */

long dvd(x, y)
long x, y;
{
  long z;

  if (y == 0)
    return x;
  z = x / y;
  if (((x >= 0)==(y >= 0)) || (x-z*y == 0))
    return z;
  return z - 1;
}


/* Take a Julian day value, and convert it back into the corresponding */
/* month, day, and year.                                               */

void JulianToMdy(JD, mon, day, yea)
real JD, *mon, *day, *yea;
{
  long L, N, IT, JT, K, IK;

  L  = (long)floor(JD+0.5)+68569L;
  N  = dvd(4L*L, 146097L);
  L  -= dvd(146097L*N + 3L, 4L);
  IT = dvd(4000L*(L+1L), 1461001L);
  L  -= dvd(1461L*IT, 4L) - 31L;
  JT = dvd(80L*L, 2447L);
  K  = L-dvd(2447L*JT, 80L);
  L  = dvd(JT, 11L);
  JT += 2L - 12L*L;
  IK = 100L*(N-49L) + IT + L;
  *mon = (real)JT; *day = (real)K; *yea = (real)IK;
}


/* This is a subprocedure of CastChart(). Once we have the chart parameters, */
/* calculate a few important things related to the date, i.e. the Greenwich  */
/* time, the Julian day and fractional part of the day, the offset to the    */
/* sidereal, and a couple of other things.                                   */

real ProcessInput(var)
int var;
{
  real Off, Ln;

  F = Sgn(F)*floor(dabs(F))+FRACT(dabs(F))*100.0/60.0+DecToDeg(X);
  L5 = DecToDeg(L5);
  LA = DTOR(DecToDeg(LA));
  LA = MIN(LA, 89.9999);      /* Make sure the chart isn't being cast */
  LA = MAX(LA, -89.9999);     /* on the precise north or south pole.  */

  /* if parameter 'var' isn't set, then we can assume that the true time   */
  /* has already been determined (as in a -rm switch time midpoint chart). */

  if (var) {
    JD = MdyToJulian(M, D, Y);
    if (!progress || (operation & DASHp0) > 0)
      T = ((JD-2415020.0)+F/24.0-0.5)/36525.0;
    else

      /* Determine actual time that a progressed chart is to be cast for. */

      T = (((Jdp-JD)/progday+JD)-2415020.0+F/24.0-0.5)/36525.0;
  }

  /* Compute angle that the ecliptic is inclined to the Celestial Equator */
  OB = DTOR(23.452294-0.0130125*T);

  Ln = Mod((933060-6962911*T+7.5*T*T)/3600.0);    /* Mean lunar node */
  Off = (259205536.0*T+2013816.0)/3600.0;         /* Mean Sun        */
  Off = 17.23*sin(DTOR(Ln))+1.27*sin(DTOR(Off))-(5025.64+1.11*T)*T;
  Off = (Off-84038.27)/3600.0;
  SD = operation & DASHs ? Off : 0.0;
  return Off;
}


/* Convert polar to rectangular coordinates. */

void PolToRec(A, R, X, Y)
real A, R, *X, *Y;
{
  if (A == 0.0)
    A = 1.7453E-09;
  *X = R*cos(A);
  *Y = R*sin(A);
}


/* Convert rectangular to polar coordinates. */

void RecToPol(X, Y, A, R)
real X, Y, *A, *R;
{
  if (Y == 0.0)
    Y = 1.7453E-09;
  *R = sqrt(X*X+Y*Y);
  *A = atan(Y/X);
  if (*A < 0.0)
    *A += PI;
  if (Y < 0.0)
    *A += PI;
}


/* Convert rectangular to spherical coordinates. */

void RecToSph()
{
  A = B; R = 1.0;
  PolToRec(A, R, &X, &Y);
  Q = Y; R = X; A = L;
  PolToRec(A, R, &X, &Y);
  G = X; X = Y; Y = Q;
  RecToPol(X, Y, &A, &R);
  A += O;
  PolToRec(A, R, &X, &Y);
  Q = ASIN(Y);
  Y = X; X = G;
  RecToPol(X, Y, &A, &R);
  if (A < 0.0)
    A += 2*PI;
  G = A;
}


/* Do a coordinate transformation: Given a longitude and latitude value,    */
/* return the new longitude and latitude values that the same location      */
/* would have, were the equator tilted by a specified number of degrees.    */
/* In other words, do a pole shift! This is used to convert among ecliptic, */
/* equatorial, and local coordinates, each of which have zero declination   */
/* in different planes. In other words, take into account the Earth's axis. */

void CoorXform(azi, alt, tilt)
real *azi, *alt, tilt;
{
  real x, y, a1, l1;

  x = cos(*alt)*sin(*azi)*cos(tilt);
  y = sin(*alt)*sin(tilt);
  x -= y;
  a1 = cos(*alt);
  y = cos(*alt)*cos(*azi);
  l1 = (y == 0.0 ? Sgn(x)*PI/2.0 : atan(x/y));
  if (l1 < 0.0)
    l1 += PI;
  if (x < 0.0)
    l1 += PI;
  a1 = a1*sin(*azi)*sin(tilt)+sin(*alt)*cos(tilt);
  a1 = ASIN(a1);
  *azi = l1; *alt = a1;
}


/* This is another subprocedure of CastChart(). Calculate a few variables */
/* corresponding to the chart parameters that are used later on. The      */
/* astrological vertex (object number twenty) is also calculated here.    */

void ComputeVariables()
{
  RA = DTOR(Mod((6.6460656+2400.0513*T+2.58E-5*T*T+F)*15.0-L5));
  R2 = RA; O = -OB; B = LA; A = R2; R = 1.0;
  PolToRec(A, R, &X, &Y);
  X *= cos(O);
  RecToPol(X, Y, &A, &R);
  MC = Mod(SD+RTOD(A));            /* Midheaven */
  L = R2;
  RecToSph();
  AZ = Mod(SD+Mod(G+PI/2.0));      /* Ascendant */
  L= R2+PI; B = PI/2.0-dabs(B);
  if (LA < 0.0)
    B = -B;
  RecToSph();
  Vtx = Mod(SD+RTOD(G+PI/2.0));    /* Vertex */
}


/*
*******************************************************************************
** House cusp calculations
*******************************************************************************
*/


/* This is a subprocedure of HousePlace(). Given a zodiac position, return */
/* which of the twelve houses it falls in. Remember that a special check   */
/* has to be done for the house that spans 0 degrees Aries.                */

int HousePlaceIn(point)
real point;
{
  int i = 0;

  point = Mod(point + 0.5/60.0);
  do {
    i++;
  } while (!(i >= SIGNS ||
      (point >= house[i] && point < house[Mod12(i+1)]) ||
      (house[i] > house[Mod12(i+1)] &&
      (point >= house[i] || point < house[Mod12(i+1)]))));
  return i;
}


/* For each object in the chart, determine what house it belongs in. */

void HousePlace()
{
  int i;

  for (i = 1; i <= total; i++)
    inhouse[i] = HousePlaceIn(planet[i]);
}


/* The following two functions calculate the midheaven and ascendant of  */
/* the chart in question, based on time and location. They are also used */
/* in some of the house cusp calculation routines as a quick way to get  */
/* the 10th and 1st house cusps.                                         */

real CuspMidheaven()
{
  real MC;

  MC = atan(tan(RA)/cos(OB));
  if (MC < 0.0)
    MC += PI;
  if (RA > PI)
    MC += PI;
  return Mod(RTOD(MC)+SD);
}

real CuspAscendant()
{
  real Asc;

  Asc = atan(cos(RA)/(-sin(RA)*cos(OB)-tan(LA)*sin(OB)));
  if (Asc < 0.0)
    Asc += PI;
  if (cos(RA) < 0.0)
    Asc += PI;
  return Mod(RTOD(Asc)+SD);
}


/* These are various different algorithms for calculating the house cusps: */

void CuspPlacidus()
{
  int i;

  if (Y == 1.0)
    X = 1.0;
  else
    X = -1.0;
  for (i = 1; i <= 10; i++) {

    /* This formula works except at 0 latitude (LA == 0.0). */

    XX = X*sin(R1)*tan(OB)*tan(LA == 0.0 ? 0.0001 : LA);
    XX = ACOS(XX);
    if (XX < 0.0)
      XX += PI;
    if (Y == 1.0)
      R2 = RA+PI-(XX/FF);
    else
      R2 = RA+(XX/FF);
    R1 = R2;
  }
  LO = atan(tan(R1)/cos(OB));
  if (LO < 0.0)
    LO += PI;
  if (sin(R1) < 0.0)
    LO += PI;
  LO = RTOD(LO);
}

void HousePlacidus()
{
  int i;

  Y = 0.0;
  house[4] = Mod(MC+180.0-SD);
  house[1] = Mod(Asc-SD);
  R1 = RA+DTOR(30.0);  FF=3.0; CuspPlacidus(); house[5]=Mod(LO+180.0);
  R1 = RA+DTOR(60.0);  FF=1.5; CuspPlacidus(); house[6]=Mod(LO+180.0);
  R1 = RA+DTOR(120.0); Y=1.0;  CuspPlacidus(); house[2]=LO;
  R1 = RA+DTOR(150.0); FF=3.0; CuspPlacidus(); house[3]=LO;
  for (i = 1; i <= SIGNS; i++) {
    if (i > 6)
      house[i] = Mod(house[i-6]+180.0);
    else
      house[i] = Mod(house[i]+SD);
  }
}

void HouseKoch()
{
  real A1, A2, A3, KN;
  int i;

  A1 = sin(RA)*tan(LA)*tan(OB);
  A1 = ASIN(A1);
  for (i = 1; i <= SIGNS; i++) {
    D = Mod(60.0+30.0*(real)i);
    A2 = D/90.0-1.0; KN = 1.0;
    if (D >= 180.0) {
      KN = -1.0;
      A2 = D/90.0-3.0;
    }
    A3 = DTOR(Mod(RTOD(RA)+D+A2*RTOD(A1)));
    X = atan(sin(A3)/(cos(A3)*cos(OB)-KN*tan(LA)*sin(OB)));
    if (X < 0.0)
      X += PI;
    if (sin(A3) < 0.0)
      X += PI;
    house[i] = Mod(RTOD(X)+SD);
  }
}

void HouseEqual()
{
  int i;

  for (i = 1; i <= SIGNS; i++) {
    house[i] = Mod(Asc-30.0+30.0*(real)i);
  }
}

void HouseCampanus()
{
  real KO, DN;
  int i;

  for (i = 1; i <= SIGNS; i++) {
    KO = DTOR(60.000001+30.0*(real)i);
    DN = atan(tan(KO)*cos(LA));
    if (DN < 0.0)
      DN += PI;
    if (sin(KO) < 0.0)
      DN += PI;
    Y = sin(RA+DN);
    X = cos(RA+DN)*cos(OB)-sin(DN)*tan(LA)*sin(OB);
    X = atan(Y/X);
    if (X < 0.0)
      X += PI;
    if (Y < 0.0)
      X += PI;
    house[i] = Mod(RTOD(X)+SD);
  }
}

void HouseMeridian()
{
  int i;

  for (i = 1; i <= SIGNS; i++) {
    D = DTOR(60.0+30.0*(real)i);
    Y = sin(RA+D);
    X = atan(Y/(cos(RA+D)*cos(OB)));
    if (X < 0.0)
      X += PI;
    if (Y < 0.0)
      X += PI;
    house[i] = Mod(RTOD(X)+SD);
  }
}

void HouseRegiomontanus()
{
  int i;

  for (i = 1; i <= SIGNS; i++) {
    D = DTOR(60.0+30.0*i);
    Y = sin(RA+D);
    X = atan(Y/(cos(RA+D)*cos(OB)-sin(D)*tan(LA)*sin(OB)));
    if (X < 0.0)
      X += PI;
    if (Y < 0.0)
      X += PI;
    house[i] = Mod(RTOD(X)+SD);
  }
}

void HousePorphyry()
{
  int i;

  X = Asc-MC;
  if (X < 0.0)
    X += 360;
  Y = X/3.0;
  for (i = 1; i <= 2; i++)
    house[i+4] = Mod(180.0+MC+i*Y);
  X = Mod(180.0+MC)-Asc;
  if (X < 0.0)
    X += 360;
  house[1]=Asc;
  Y = X/3.0;
  for (i = 1; i <= 3; i++)
    house[i+1] = Mod(Asc+i*Y);
  for (i = 1; i <= 6; i++)
    house[i+6] = Mod(house[i]+180.0);
}

void HouseMorinus()
{
  int i;

  for (i = 1; i <= SIGNS; i++) {
    D = DTOR(60.0+30.0*(real)i);
    Y = sin(RA+D)*cos(OB);
    X = atan(Y/cos(RA+D));
    if (X < 0.0)
      X += PI;
    if (Y < 0.0)
      X += PI;
    house[i] = Mod(RTOD(X)+SD);
  }
}

void CuspTopocentric()
{
  X = atan(tan(LA)/cos(OA));
  Y = X+OB;
  LO = atan(cos(X)*tan(OA)/cos(Y));
  if (LO < 0.0)
    LO += PI;
  if (sin(OA) < 0.0)
    LO += PI;
}

void HouseTopocentric()
{
  real TL, P1, P2, LT;
  int i;

  modulus = 2.0*PI;
  house[4] = Mod(DTOR(MC+180.0-SD));
  TL = tan(LA); P1 = atan(TL/3.0); P2 = atan(TL/1.5); LT = LA;
  LA = P1; OA = Mod(RA+DTOR(30.0)); CuspTopocentric(); house[5] = Mod(LO+PI);
  LA = P2; OA = Mod(OA+DTOR(30.0)); CuspTopocentric(); house[6] = Mod(LO+PI);
  LA = LT; OA = Mod(OA+DTOR(30.0)); CuspTopocentric(); house[1] = LO;
  LA = P2; OA = Mod(OA+DTOR(30.0)); CuspTopocentric(); house[2] = LO;
  LA = P1; OA = Mod(OA+DTOR(30.0)); CuspTopocentric(); house[3] = LO;
  LA = LT; modulus = DEGREES;
  for (i = 1; i <= 6; i++) {
    house[i] = Mod(RTOD(house[i])+SD);
    house[i+6] = Mod(house[i]+180.0);
  }
}


/* In "null" houses, the cusps are always fixed to start at their            */
/* corresponding sign, i.e. the 1st house is always at 0 degrees Aries, etc. */

void HouseNull()
{
  int i;

  for (i = 1; i <= SIGNS; i++)
    house[i] = Mod((real)(i-1)*30.0+SD);
}


/* Calculate the house cusp positions, using the specified algorithm. */

void Houses(housesystem)
int housesystem;
{
  switch (housesystem) {
  case  1: HouseKoch();          break;
  case  2: HouseEqual();         break;
  case  3: HouseCampanus();      break;
  case  4: HouseMeridian();      break;
  case  5: HouseRegiomontanus(); break;
  case  6: HousePorphyry();      break;
  case  7: HouseMorinus();       break;
  case  8: HouseTopocentric();   break;
  case  9: HouseNull();          break;
  default: HousePlacidus();
  }
}


/*
*******************************************************************************
** Planetary position calculations
*******************************************************************************
*/

/* Read the next three values from the planet data stream, and return them */
/* combined as the coefficients of a quadratic equation in the chart time. */

real ReadThree()
{
  S = ReadPlanetData(FALSE); S1 = ReadPlanetData(FALSE);
  S2 = ReadPlanetData(FALSE);
  return S = DTOR(S+S1*T+S2*T*T);
}


/* Another coordinate transformation. This one is used by the planets() */
/* procedure to rotate rectangular coordinates by a certain amount.     */

void RecToSph2()
{
  RecToPol(X, Y, &A, &R); A += AP; PolToRec(A, R, &X, &Y);
  D = X; X = Y; Y = 0.0; RecToPol(X, Y, &A, &R);
  A += IN; PolToRec(A, R, &X, &Y);
  G = Y; Y = X; X = D; RecToPol(X, Y, &A, &R); A += AN;
  if (A < 0.0)
    A += 2.0*PI;
  PolToRec(A, R, &X, &Y);
}


/* Calculate some harmonic delta error correction factors to add onto the */
/* coordinates of Jupiter through Pluto, for better accuracy.             */

void ErrorCorrect(ind)
int ind;
{
  real U, V, W;
  int IK, IJ, errorindex;

  errorindex = errorcount[ind];
  for (IK = 1; IK <= 3; IK++) {
    if (ind == 6 && IK == 3) {
      T0[3] = 0;
      return;
    }
    if (IK == 3)
      errorindex--;
    ReadThree(); A = 0.0;
    for (IJ = 1; IJ <= errorindex; IJ++) {
      U = ReadPlanetData(FALSE); V = ReadPlanetData(FALSE);
      W = ReadPlanetData(FALSE);
      A = A+DTOR(U)*cos((V*T+W)*PI/180.0);
    }
    T0[IK] = RTOD(S+A);
  }
}


/* Another subprocedure of the planets() routine. Convert the final        */
/* rectangular coordinates of a planet to zodiac position and declination. */

void ProcessPlanet(ind)
int ind;
{
  X = XX; Y = YY; RecToPol(X, Y, &A, &R);
  C = RTOD(A)+NU-BR;
  if (ind == 1 && AB == 1.0)
    C = Mod(C+180.0);
  C = Mod(C+SD); Y = ZZ; X = R; RecToPol(X, Y, &A, &R);
  P = RTOD(A);
}


/* This is probably the heart of the whole program of Astrolog. Calculate  */
/* the position of each body that orbits the Sun. A heliocentric chart is  */
/* most natural; extra calculation is needed to have other central bodies. */

void Planets()
{
  real AU, E, EA, E1, XK, XW, YW, XH[BASE+1], YH[BASE+1], ZH[BASE+1];
  int ind = 1, i;

  ReadPlanetData(TRUE);
  while (ind <= (operation & DASHu ? BASE : PLANETS+1)) {
    modulus = 2.0*PI;
    EA = M = Mod(ReadThree());     /* Calculate mean anomaly */
    E = RTOD(ReadThree());         /* Calculate eccentricity */
    for (i = 1; i <= 5; i++)
      EA = M+E*sin(EA);            /* Solve Keplar's equation */
    AU = ReadPlanetData(FALSE);    /* Semi-major axis         */
    E1 = 0.01720209/(pow(AU,1.5)*
      (1.0-E*cos(EA)));                    /* Begin velocity coordinates */
    XW = -AU*E1*sin(EA);                   /* Perifocal coordinates      */
    YW = AU*E1*pow(1.0-E*E,0.5)*cos(EA);
    AP = ReadThree(); AN = ReadThree();
    IN = ReadThree();                      /* Calculate inclination       */
    X = XW; Y = YW; RecToSph2();           /* Rotate velocity coordinates */
    XH[ind] = X; YH[ind] = Y; ZH[ind] = G; /* Helio ecliptic rectangtular */
    modulus = DEGREES;
    if (ind > 1) {
      XW = XH[ind]-XH[1]; YW = YH[ind]-YH[1];
    }
    X = AU*(cos(EA)-E);                 /* Perifocal coordinates for        */
    Y = AU*sin(EA)*pow(1.0-E*E,0.5);    /* rectangular position coordinates */
    RecToSph2();                        /* Rotate for rectangular */
    XX = X; YY = Y; ZZ = G;             /* position coordinates   */
    if (ind >= 6 && ind <= 10) {
      ErrorCorrect(ind); XX += T0[2]; YY += T0[1]; ZZ += T0[3];
    }
    helioret[ind] = XK =                        /* Helio daily motion */
      (XX*YH[ind]-YY*XH[ind])/(XX*XX+YY*YY);
    spacex[ind] = XX; spacey[ind] = YY; spacez[ind] = ZZ;
    BR = 0.0; ProcessPlanet(ind); AB = 1.0;  /* Convert helio rectangular */
    helio[ind] = C; helioalt[ind] = P;       /* to spherical coords.      */
    if (ind > 1) {
      XX -= spacex[1]; YY -= spacey[1]; ZZ -= spacez[1];  /* Helio to geo */
      XK = (XX*YW-YY*XW)/(XX*XX+YY*YY);                   /* Geo daily    */
    }
    BR = 0.0057756*sqrt(XX*XX+YY*YY+ZZ*ZZ)*RTOD(XK);      /* Aberration     */
    georet[ind] = XK; ProcessPlanet(ind);                 /* Rectangular to */
    geo[ind] = C; geoalt[ind] = P;                        /* Spherical      */
    if (!centerplanet) {
      planet[ind] = helio[ind]; planetalt[ind] = helioalt[ind];
      ret[ind] = helioret[ind];
    } else {
      planet[ind] = geo[ind]; planetalt[ind] = geoalt[ind];
      ret[ind] = georet[ind];
    }
    if (!(exdisplay & DASHv0))                    /* Use relative velocity   */
      ret[ind] = DTOR(ret[ind]/helioret[ind]);    /* unless -v0 is in effect */
    ind += (ind == 1 ? 2 : (ind != PLANETS+1 ? 1 : 10));
  }
  spacex[0] = spacey[0] = spacez[0] = 0.0;

  /* A second loop is needed for central bodies other than the Sun or Earth. */
  /* For example, we can't find the position of Mercury in relation to Pluto */
  /* until we know the position of Pluto in relation to the Sun, and since   */
  /* Mercury is calculated before Pluto, another pass needed. (Since Earth   */
  /* is the first object, a geocentric chart can be done "on the fly".)      */

  if (ind = centerplanet) {
    for (i = 0; i <= BASE; i++) if (i != 2 && i != ind) {
      spacex[i] -= spacex[ind]; spacey[i] -= spacey[ind];
      spacez[i] -= spacez[ind];
    }
    spacex[ind] = spacey[ind] = spacez[ind] = 0.0;
    SwapReal(&spacex[0], &spacex[ind]);
    SwapReal(&spacey[0], &spacey[ind]);    /* Do some swapping - we want   */
    SwapReal(&spacez[0], &spacez[ind]);    /* the central body to be in    */
    SwapReal(&spacex[1], &spacex[ind]);    /* object position number zero. */
    SwapReal(&spacey[1], &spacey[ind]);
    SwapReal(&spacez[1], &spacez[ind]);
  }
  if (ind > 2)
    for (i = 1; i <= (operation & DASHu ? BASE : PLANETS+1);
         i += (i == 1 ? 2 : (i != PLANETS+1 ? 1 : 10))) {
      XX = spacex[i]; YY = spacey[i]; ZZ = spacez[i];
      AB = 0.0; BR = 0.0; ProcessPlanet(i);
      planet[i] = C; planetalt[i] = P;
      ret[i] = (XX*(YH[i]-YH[ind])-YY*(XH[i]-XH[ind]))/(XX*XX+YY*YY);
    }
}


/*
*******************************************************************************
** Lunar position calculations
*******************************************************************************
*/

/* Calculate the position and declination of the Moon, and the Moon's North  */
/* Node. This has to be done separately from the other planets, because they */
/* all orbit the Sun, while the Moon orbits the Earth.                       */

void Lunar(moonlo, moonla, nodelo, nodela)
real *moonlo, *moonla, *nodelo, *nodela;
{
  real LL, G, N, G1, D, L, ML, L1, MB, M = 3600.0 /*, TN*/;

  LL = 973563.0+1732564379.0*T-4.0*T*T;  /* Compute mean lunar longitude     */
  G = 1012395.0+6189.0*T;                /* Sun's mean longitude of perigee  */
  N = 933060.0-6962911.0*T+7.5*T*T;      /* Compute mean lunar node          */
  G1 = 1203586.0+14648523.0*T-37.0*T*T;  /* Mean longitude of lunar perigee  */
  D = 1262655.0+1602961611.0*T-5.0*T*T;  /* Mean elongation of Moon from Sun */
  L = (LL-G1)/M; L1 = ((LL-D)-G)/M;      /* Some auxiliary angles            */
  F = (LL-N)/M; D = D/M; Y = 2.0*D;

  /* Compute Moon's perturbations. */

  ML = 22639.6*SIND(L)-4586.4*SIND(L-Y)+2369.9*SIND(Y)+769.0*SIND(2.0*L)-
    669.0*SIND(L1)-411.6*SIND(2.0*F)-212.0*SIND(2.0*L-Y)-206.0*SIND(L+L1-Y);
  ML += 192.0*SIND(L+Y)-165.0*SIND(L1-Y)+148.0*SIND(L-L1)-125.0*SIND(D)-
    110.0*SIND(L+L1)-55.0*SIND(2.0*F-Y)-45.0*SIND(L+2.0*F)+40.0*SIND(L-2.0*F);

  *moonlo = G = Mod((LL+ML)/M+SD);       /* Lunar longitude */

  /* Compute lunar latitude. */

  MB = 18461.5*SIND(F)+1010.0*SIND(L+F)-999.0*SIND(F-L)-624.0*SIND(F-Y)+
    199.0*SIND(F+Y-L)-167.0*SIND(L+F-Y);
  MB += 117.0*SIND(F+Y)+62.0*SIND(2.0*L+F)-
    33.0*SIND(F-Y-L)-32.0*SIND(F-2.0*L)-30.0*SIND(L1+F-Y);
  *moonla = MB =
    Sgn(MB)*((dabs(MB)/M)/DEGREES-floor((dabs(MB)/M)/DEGREES))*DEGREES;

  /* Compute position of the north node. One can compute the true North */
  /* Node here if they like, but the Mean Node seems to be the one used */
  /* in astrology, so that's the one Astrolog returns.                  */

  /*TN = N+5392.0*SIND(2.0*F-Y)-541.0*SIND(L1)-442.0*SIND(Y)+423.0*SIND(2.0*F)-
    291.0*SIND(2.0*L-2.0*F);
  TN = Mod(TN/M);*/
  *nodelo = N = Mod(N/M+SD);
  *nodela = 0.0;
}


/*
*******************************************************************************
** Star position calculations
*******************************************************************************
*/

/* Return whether one string is greater than another. */

int StrCmp(s1, s2)
char *s1, *s2;
{
  while (*s1 == *s2)
    s1++, s2++;
  return *s1 > *s2;
}


/* This is used by the chart calculation routine to calculate the positions */
/* of the fixed stars. Since the stars don't move in the sky over time,     */
/* getting their positions is mostly just reading in a data stream and      */
/* converting it to the correct reference frame. However, we have to add in */
/* the correct precession for the tropical zodiac, and sort the final index */
/* list based on what order the stars are supposed to be printed in.        */

void CastStar(SD)
real SD;
{
  int i, j;
  real x, y, z;
  ReadStarData(TRUE);

  /* Read in star positions. */

  for (i = 1; i <= STARS; i++) {
    x = ReadStarData(FALSE); y = ReadStarData(FALSE); z = ReadStarData(FALSE);
    planet[BASE+i] = DTOR(x*DEGREES/24.0+y*15.0/60.0+z*0.25/60.0);
    x = ReadStarData(FALSE); y = ReadStarData(FALSE); z = ReadStarData(FALSE);
    planetalt[BASE+i] = DTOR(x+y/60.0+z/60.0/60.0);
    equtoecl(&planet[BASE+i], &planetalt[BASE+i]);           /* Convert to */
    planet[BASE+i] = Mod(RTOD(planet[BASE+i])+SD2000+SD);    /* ecliptic.  */
    planetalt[BASE+i] = RTOD(planetalt[BASE+i]);
    starname[i] = i;
  }

  /* Sort the index list if -Uz, -Ul, -Un, or -Ub switch in effect. */

  if (universe > 1) for (i = 2; i <= STARS; i++) {
    j = i-1;

    /* Compare star names for -Un switch. */

    if (universe == 'n') while (j > 0 && StrCmp(
      objectname[BASE+starname[j]], objectname[BASE+starname[j+1]])) {
      SWAP(starname[j], starname[j+1]);
      j--;

    /* Compare star brightnesses for -Ub switch. */

    } else if (universe == 'b') while (j > 0 &&
      starbright[starname[j]] > starbright[starname[j+1]]) {
      SWAP(starname[j], starname[j+1]);
      j--;

    /* Compare star zodiac locations for -Uz switch. */

    } else if (universe == 'z') while (j > 0 &&
      planet[BASE+starname[j]] > planet[BASE+starname[j+1]]) {
      SWAP(starname[j], starname[j+1]);
      j--;

    /* Compare star declinations for -Ul switch. */

    } else if (universe == 'l') while (j > 0 &&
      planetalt[BASE+starname[j]] < planetalt[BASE+starname[j+1]]) {
      SWAP(starname[j], starname[j+1]);
      j--;
    }
  }
}


/*
*******************************************************************************
** Calculate chart for specific time
*******************************************************************************
*/

/* This is probably the main routine in all of Astrolog. It generates a   */
/* chart, calculating the positions of all the celestial bodies and house */
/* cusps, based on the current chart information, and saves them for use  */
/* by any of the display routines.                                        */

real CastChart(var)
int var;
{
  int i, k;

  real housetemp[SIGNS+1], Off = 0.0, j;
  if (M == -1.0) {

    /* Hack: If month is negative, then we know chart was read in through a  */
    /* -o0 position file, so the planet positions are already in the arrays. */

    MC = planet[18]; Asc = planet[19]; Vtx = planet[20];
  } else {
    Off = ProcessInput(var);
    ComputeVariables();
    if (operation & DASHG)          /* Check for -G geodetic chart. */
      RA = DTOR(Mod(-L5));
    MC  = CuspMidheaven();          /* Calculate our Ascendant & Midheaven. */
    Asc = CuspAscendant();
    Houses(housesystem);            /* Go calculate house cusps. */
    for (i = 1; i <= total; i++)
      ret[i] = 1.0;                 /* Assume direct until we say otherwise. */

    /* Go calculate planet, Moon, and North Node positions. */

    Planets();
    Lunar(&planet[2], &planetalt[2], &planet[16], &planetalt[16]);
    ret[16] = -1.0;

    /* Calculate position of Part of Fortune. */

    j = planet[2]-planet[1];
    j = dabs(j) < 90.0 ? j : j - Sgn(j)*DEGREES;
    planet[17] = Mod(j+Asc);

    /* Fill in "planet" positions corresponding to house cusps. */

    planet[18] = MC; planet[19] = Asc; planet[20] = Vtx;
    planet[21] = house[11]; planet[22] = house[12];
    planet[23] = house[2];  planet[24] = house[3];
  }
  if (universe)                               /* Go calculate star positions */
    CastStar(operation & DASHs ? 0.0 : -Off); /* if -U switch in effect.     */

  /* Now, we may have to modify the base positions we calculated above based */
  /* on what type of chart we are generating.                                */

  if (operation & DASHp0) {         /* Are we doing a -p0 solar arc chart?   */
    for (i = 1; i <= total; i++)
      planet[i] = Mod(planet[i] + (Jdp - JD) / progday);
    for (i = 1; i <= SIGNS; i++)
      house[i]  = Mod(house[i]  + (Jdp - JD) / progday);
    }
  if (multiplyfactor > 1)           /* Are we doing a -x harmonic chart?     */
    for (i = 1; i <= total; i++)
      planet[i] = Mod(planet[i] * (real)multiplyfactor);
  if (onasc) {
    if (onasc > 0)                  /* Is -1 put on Ascendant in effect?     */
      j = planet[onasc]-Asc;
    else                            /* Or -2 put object on Midheaven switch? */
      j = planet[-onasc]-MC;
    for (i = 1; i <= SIGNS; i++)    /* If so, rotate the houses accordingly. */
      house[i] = Mod(house[i]+j);
  }

  /* Check to see if we are -F forcing any objects to be particular values. */

  for (i = 1; i <= total; i++)
    if (force[i] != 0.0) {
      planet[i] = force[i]-DEGREES;
      planetalt[i] = ret[i] = 0.0;
    }
  HousePlace();                /* Figure out what house everything falls in. */

  /* If -f domal chart switch in effect, switch planet and house positions. */

  if (operation & DASHf) {
    for (i = 1; i <= total; i++) {
      k = inhouse[i];
      inhouse[i] = (int) (planet[i]/30.0)+1;
      planet[i] = (real)(k-1)*30.0+MinDistance(house[k], planet[i])/
        MinDistance(house[k], house[Mod12(k+1)])*30.0;
    }
    for (i = 1; i <= SIGNS; i++) {
      k = HousePlaceIn((real) (i-1)*30.0);
      housetemp[i] = (real)(k-1)*30.0+MinDistance(house[k],
        (real) (i-1)*30.0)/MinDistance(house[k], house[Mod12(k+1)])*30.0;
    }
    for (i = 1; i <= SIGNS; i++)
      house[i] = housetemp[i];
  }

  /* If -3 decan chart switch in effect, edit planet positions accordingly. */

  if (operation & DASH3)
    for (i = 1; i <= total; i++) {
      k = (int) (planet[i]/30.0)+1;
      j = planet[i] - (real)((k-1)*30);
      k = Mod12(k + 4*((int)floor(j/10.0)));
      j = (j - floor(j/10.0)*10.0)*3.0;
      planet[i] = (real)(k-1)*30.0+j;
      HousePlace();
    }
  return T;
}

/* formulas.c */
