/*
** Astrolog (Version 4.40) File: charts3.c
**
** IMPORTANT NOTICE: The graphics database and chart display routines
** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
** (astara@u.washington.edu). 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.
**
** The main 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 personal use but not to sell them or profit from
** them in any way.
**
** The PostScript code within the core graphics routines are programmed
** and Copyright (C) 1992-1993 by Brian D. Willoughby
** (brianw@sounds.wa.com). Conditions are identical to those above.
**
** The extended accurate ephemeris databases and formulas are from the
** calculation routines in the program "Placalc" and are programmed and
** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
** (alois@azur.ch). The use of that source code is subject to
** regulations made by Astrodienst Zurich, and the code is not in the
** public domain. This copyright notice must not be changed or removed
** by any user of this program.
**
** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
** X Window graphics initially programmed 10/23-29/1991.
** PostScript graphics initially programmed 11/29-30/1992.
** Last code change made 1/29/1995.
*/

#include "astrolog.h"


/*
******************************************************************************
** Multiple Chart Scanning Routines.
******************************************************************************
*/

/* Search through a day, and print out the times of exact aspects among the  */
/* planets during that day, as specified with the -d switch, as well as the  */
/* times when a planet changes sign or direction. To do this, we cast charts */
/* for the beginning and end of the day, or a part of a day, and do a linear */
/* equation check to see if anything exciting happens during the interval.   */
/* (This is probably the single most complicated procedure in the program.)  */

void ChartInDaySearch(fProg)
bool fProg;
{
  char sz[cchSzDef];
  int source[MAXINDAY], aspect[MAXINDAY], dest[MAXINDAY],
    sign1[MAXINDAY], sign2[MAXINDAY], D1, D2, occurcount, division, div,
    fYear, yea0, yea1, yea2, i, j, k, l, s1, s2;
  real time[MAXINDAY], divsiz, d1, d2, e1, e2, f1, f2, g;

  /* If parameter 'fProg' is set, look for changes in a progressed chart. */

  fYear = us.fInDayMonth && (Mon2 == 0);
  division = (fYear || fProg) ? 1 : us.nDivision;
  divsiz = 24.0 / (real)division*60.0;

  /* If -dY in effect, then search through a range of years. */

  yea1 = fProg ? Yea2 : Yea;
  yea2 = fYear ? (yea1 + us.nEphemYears - 1) : yea1;
  for (yea0 = yea1; yea0 <= yea2; yea0++) {

  /* If -dm in effect, then search through the whole month, day by day. */

  if (us.fInDayMonth) {
    D1 = 1;
    if (fYear) {
      Mon2 = 1; D2 = DayInYearHi(yea0);
    } else
      D2 = DayInMonth(fProg ? Mon2 : Mon, yea0);
  } else
    D1 = D2 = Day;

  /* Start searching the day or days in question for exciting stuff. */

  for (Day2 = D1; Day2 <= D2; Day2 = AddDay(Mon, Day2, yea0, 1)) {
    occurcount = 0;

    /* Cast chart for beginning of day and store it for future use. */

    SetCI(ciCore, fYear ? Mon2 : Mon, Day2, yea0, 0.0, Dst, Zon, Lon, Lat);
    if (us.fProgress = fProg) {
      is.JDp = MdytszToJulian(Mon2, DD, yea0, 0.0, Dst, Zon);
      ciCore = ciMain;
    }
    CastChart(fTrue);
    for (i = 1; i <= cSign; i++) {
      cp2.cusp[i] = house[i];
      cp2.house[i] = inhouse[i];
    }
    for (i = 1; i <= cObj; i++) {
      cp2.obj[i] = planet[i];
      cp2.dir[i] = ret[i];
    }

    /* Now divide the day into segments and search each segment in turn. */
    /* More segments is slower, but has slightly better time accuracy.   */

    for (div = 1; div <= division; div++) {

      /* Cast the chart for the ending time of the present segment. The   */
      /* beginning time chart is copied from the previous end time chart. */

      SetCI(ciCore, fYear ? Mon2 : Mon, Day2, yea0,
        DegToDec(24.0*(real)div/(real)division), Dst, Zon, Lon, Lat);
      if (fProg) {
        is.JDp = MdytszToJulian(Mon2, DD+1, yea0, 0.0, Dst, Zon);
        ciCore = ciMain;
      }
      CastChart(fTrue);
      for (i = 1; i <= cSign; i++) {
        cp1.cusp[i] = cp2.cusp[i]; cp1.house[i] = cp2.house[i];
        cp2.cusp[i] = house[i];  cp2.house[i] = inhouse[i];
      }
      for (i = 1; i <= cObj; i++) {
        cp1.obj[i] = cp2.obj[i]; cp1.dir[i] = cp2.dir[i];
        cp2.obj[i] = planet[i];  cp2.dir[i] = ret[i];
      }

      /* Now search through the present segment for anything exciting. */

      for (i = 1; i <= cObj; i++) if (!ignore[i] && (fProg || FThing(i))) {
        s1 = SFromZ(cp1.obj[i])-1;
        s2 = SFromZ(cp2.obj[i])-1;

        /* Does the current planet change into the next or previous sign? */

        if (!ignore[i] && s1 != s2 && !ignore[0]) {
          source[occurcount] = i;
          aspect[occurcount] = aSig;
          dest[occurcount] = s2+1;
          time[occurcount] = MinDistance(cp1.obj[i],
            (real)(cp1.dir[i] >= 0.0 ? s2 : s1) * 30.0) /
            MinDistance(cp1.obj[i], cp2.obj[i])*divsiz + (real)(div-1)*divsiz;
          sign1[occurcount] = sign2[occurcount] = s1+1;
          occurcount++;
        }

        /* Does the current planet go retrograde or direct? */

        if (!ignore[i] && (cp1.dir[i] < 0.0) != (cp2.dir[i] < 0.0) &&
          !ignore2[0]) {
          source[occurcount] = i;
          aspect[occurcount] = aDir;
          dest[occurcount] = cp2.dir[i] < 0.0;
          time[occurcount] = RAbs(cp1.dir[i])/(RAbs(cp1.dir[i])+
            RAbs(cp2.dir[i]))*divsiz + (real)(div-1)*divsiz;
          sign1[occurcount] = sign2[occurcount] = s1+1;
          occurcount++;
        }

        /* Now search for anything making an aspect to the current planet. */

        for (j = i+1; j <= cObj; j++) if (!ignore[j] && (fProg || FThing(j)))
          for (k = 1; k <= us.nAsp; k++) if (FAcceptAspect(i, k, j)) {
            d1 = cp1.obj[i]; d2 = cp2.obj[i];
            e1 = cp1.obj[j]; e2 = cp2.obj[j];
            if (MinDistance(d1, d2) < MinDistance(e1, e2)) {
              SwapR(&d1, &e1);
              SwapR(&d2, &e2);
            }

            /* We are searching each aspect in turn. Let's subtract the  */
            /* size of the aspect from the angular difference, so we can */
            /* then treat it like a conjunction.                         */

            if (MinDistance(e1, Mod(d1-aspectangle[k])) <
                MinDistance(e2, Mod(d2+aspectangle[k]))) {
              e1 = Mod(e1+aspectangle[k]);
              e2 = Mod(e2+aspectangle[k]);
            } else {
              e1 = Mod(e1-aspectangle[k]);
              e2 = Mod(e2-aspectangle[k]);
            }

            /* Check to see if the aspect actually occurs during our    */
            /* segment, making sure we take into account if one or both */
            /* planets are retrograde or if they cross the Aries point. */

            f1 = e1-d1;
            if (RAbs(f1) > rDegHalf)
              f1 -= RSgn(f1)*rDegMax;
            f2 = e2-d2;
            if (RAbs(f2) > rDegHalf)
              f2 -= RSgn(f2)*rDegMax;
            if (MinDistance(Midpoint(d1, d2), Midpoint(e1, e2)) < rDegQuad &&
              RSgn(f1) != RSgn(f2)) {
              source[occurcount] = i;
              aspect[occurcount] = k;
              dest[occurcount] = j;

              /* Horray! The aspect occurs sometime during the interval.   */
              /* Now we just have to solve an equation in two variables to */
              /* find out where the "lines" cross, i.e. the aspect's time. */

              f1 = d2-d1;
              if (RAbs(f1) > rDegHalf)
                f1 -= RSgn(f1)*rDegMax;
              f2 = e2-e1;
              if (RAbs(f2) > rDegHalf)
                f2 -= RSgn(f2)*rDegMax;
              g = (RAbs(d1-e1) > rDegHalf ?
                (d1-e1)-RSgn(d1-e1)*rDegMax : d1-e1)/(f2-f1);
              time[occurcount] = g*divsiz + (real)(div-1)*divsiz;
              sign1[occurcount] = (int)(Mod(cp1.obj[i]+
                RSgn(cp2.obj[i]-cp1.obj[i])*
                (RAbs(cp2.obj[i]-cp1.obj[i]) > rDegHalf ? -1 : 1)*
                RAbs(g)*MinDistance(cp1.obj[i], cp2.obj[i]))/30.0)+1;
              sign2[occurcount] = (int)(Mod(cp1.obj[j]+
                RSgn(cp2.obj[j]-cp1.obj[j])*
                (RAbs(cp2.obj[j]-cp1.obj[j]) > rDegHalf ? -1 : 1)*
                RAbs(g)*MinDistance(cp1.obj[j], cp2.obj[j]))/30.0)+1;
              occurcount++;
            }
          }
      }
    }

    /* After all the aspects, etc, in the day have been located, sort   */
    /* them by time at which they occur, so we can print them in order. */

    for (i = 1; i < occurcount; i++) {
      j = i-1;
      while (j >= 0 && time[j] > time[j+1]) {
        SwapN(source[j], source[j+1]);
        SwapN(aspect[j], aspect[j+1]);
        SwapN(dest[j], dest[j+1]);
        SwapR(&time[j], &time[j+1]);
        SwapN(sign1[j], sign1[j+1]); SwapN(sign2[j], sign2[j+1]);
        j--;
      }
    }

    /* Finally, loop through and display each aspect and when it occurs. */

    for (i = 0; i < occurcount; i++) {
      s1 = (int)time[i]/60;
      s2 = (int)time[i]-s1*60;
      j = Day2;
      if (fYear || fProg) {
        l = Mon2;
        while (j > (k = DayInMonth(l, yea0))) {
          j -= k;
          l++;
        }
      }
      SetCI(ciSave, fYear || fProg ? l : Mon, j, yea0,
        DegToDec(time[i] / 60.0), Dst, Zon, Lon, Lat);
      k = DayOfWeek(fYear || fProg ? l : Mon, j, yea0);
      AnsiColor(kRainbowA[k + 1]);
      sprintf(sz, "(%c%c%c) ", chDay3(k)); PrintSz(sz);
      AnsiColor(kDefault);
      sprintf(sz, "%s %s ",
        SzDate(fYear || fProg ? l : Mon, j, yea0, fFalse),
        SzTime(s1, s2)); PrintSz(sz);
      PrintAspect(source[i], sign1[i],
        (int)RSgn(cp1.dir[source[i]])+(int)RSgn(cp2.dir[source[i]]),
        aspect[i], dest[i], sign2[i],
        (int)RSgn(cp1.dir[dest[i]])+(int)RSgn(cp2.dir[dest[i]]),
        (char)(fProg ? 'e' : 'd'));
      PrintInDay(source[i], aspect[i], dest[i]);
    }
  }
  }

  /* Recompute original chart placements as we've overwritten them. */

  ciCore = ciMain;
  CastChart(fTrue);
}


/* Search through a month, year, or years, and print out the times of exact */
/* transits where planets in the time frame make aspect to the planets in   */
/* some other chart, as specified with the -t switch. To do this, we cast   */
/* charts for the start and end of each month, or within a month, and do an */
/* equation check for aspects to the other base chart during the interval.  */

void ChartTransitSearch(fProg)
bool fProg;
{
  real planet3[objMax], house3[cSign+1], ret3[objMax], time[MAXINDAY];
  char sz[cchSzDef];
  int source[MAXINDAY], aspect[MAXINDAY], dest[MAXINDAY], sign[MAXINDAY],
    isret[MAXINDAY], M1, M2, Y1, Y2, occurcount, div, i, j, k, s1, s2, s3;
  real divsiz, daysiz, d, e1, e2, f1, f2;

  for (i = 1; i <= cSign; i++)
    house3[i] = house[i];
  for (i = 1; i <= cObj; i++) {
    planet3[i] = planet[i];
    ret3[i] = ret[i];
  }

  /* Hacks: Searching month number zero means to search the whole year     */
  /* instead, month by month. Searching a negative month means to search   */
  /* multiple years, with the span off the year stored in the "day" field. */

  Y1 = Y2 = Yea2;
  M1 = M2 = Mon2;
  if (Mon2 < 1) {
    M1 = 1; M2 = 12;
    if (Mon2 < 0) {
      if (Day2 < 1) {
        Y1 = Yea2 + Day2 + 1; Y2 = Yea2;
      } else {
        Y1 = Yea2; Y2 = Yea2 + Day2 - 1;
      }
    }
  }

  /* Start searching the year or years in question for any transits. */

  for (Yea2 = Y1; Yea2 <= Y2; Yea2++)

  /* Start searching the month or months in question for any transits. */

  for (Mon2 = M1; Mon2 <= M2; Mon2++) {
    daysiz = (real)DayInMonth(Mon2, Yea2)*24.0*60.0;
    divsiz = daysiz / (real)us.nDivision;

    /* Cast chart for beginning of month and store it for future use. */

    SetCI(ciCore, Mon2, 1, Yea2, 0.0, Dst2, Zon2, Lon2, Lat2);
    if (us.fProgress = fProg) {
      is.JDp = MdytszToJulian(MM, DD, YY, 0.0, Dst2, Zon2);
      ciCore = ciMain;
    }
    for (i = 1; i <= oNorm; i++)
      SwapN(ignore[i], ignore2[i]);
    CastChart(fTrue);
    for (i = 1; i <= oNorm; i++)
      SwapN(ignore[i], ignore2[i]);
    for (i = 1; i <= cSign; i++)
      cp2.cusp[i] = house[i];
    for (i = 1; i <= oCore; i++) {
      cp2.obj[i] = planet[i];
      cp2.dir[i] = ret[i];
    }

    /* Divide our month into segments and then search each segment in turn. */

    for (div = 1; div <= us.nDivision; div++) {
      occurcount = 0;

      /* Cast the chart for the ending time of the present segment, and */
      /* copy the start time chart from the previous end time chart.    */

      d = 1.0 + (daysiz/24.0/60.0)*(real)div/(real)us.nDivision;
      SetCI(ciCore, Mon2, (int)d, Yea2,
        DegToDec(RFract(d)*24.0), Dst2, Zon2, Lon2, Lat2);
      if (fProg) {
        is.JDp = MdytszToJulian(MM, DD, YY, 0.0, Dst2, Zon2);
        ciCore = ciMain;
      }
      for (i = 1; i <= oNorm; i++)
        SwapN(ignore[i], ignore2[i]);
      CastChart(fTrue);
      for (i = 1; i <= oNorm; i++)
        SwapN(ignore[i], ignore2[i]);
      for (i = 1; i <= cSign; i++) {
        cp1.cusp[i] = cp2.cusp[i]; cp2.cusp[i] = house[i];
      }
      for (i = 1; i <= oCore; i++) {
        cp1.obj[i] = cp2.obj[i]; cp1.dir[i] = cp2.dir[i];
        cp2.obj[i] = planet[i];  cp2.dir[i] = ret[i];
      }

      /* Now search through the present segment for any transits. Note that */
      /* stars can be transited, but they can't make transits themselves.   */

      for (i = 1; i <= cObj; i++) if (!ignore[i])
        for (j = 1; j <= oNorm; j++) if (!ignore2[j] && (fProg || FThing(j)))

          /* Between each pair of planets, check if they make any aspects. */

          for (k = 1; k <= us.nAsp; k++) if (FAcceptAspect(i, k, j)) {
            d = planet3[i]; e1 = cp1.obj[j]; e2 = cp2.obj[j];
            if (MinDistance(e1, Mod(d-aspectangle[k])) <
                MinDistance(e2, Mod(d+aspectangle[k]))) {
              e1 = Mod(e1+aspectangle[k]);
              e2 = Mod(e2+aspectangle[k]);
            } else {
              e1 = Mod(e1-aspectangle[k]);
              e2 = Mod(e2-aspectangle[k]);
            }

            /* Check to see if the present aspect actually occurs during the */
            /* segment, making sure we check any Aries point crossings.      */

            f1 = e1-d;
            if (RAbs(f1) > rDegHalf)
              f1 -= RSgn(f1)*rDegMax;
            f2 = e2-d;
            if (RAbs(f2) > rDegHalf)
              f2 -= RSgn(f2)*rDegMax;
            if (MinDistance(d, Midpoint(e1, e2)) < rDegQuad &&
              RSgn(f1) != RSgn(f2) && occurcount < MAXINDAY) {

              /* Ok, we have found a transit. Now determine the time */
              /* and save this transit in our list to be printed.    */

              source[occurcount] = j;
              aspect[occurcount] = k;
              dest[occurcount] = i;
              time[occurcount] = RAbs(f1)/(RAbs(f1)+RAbs(f2))*divsiz +
                (real)(div-1)*divsiz;
              sign[occurcount] = (int)(Mod(
                MinDistance(cp1.obj[j], Mod(d-aspectangle[k])) <
                MinDistance(cp2.obj[j], Mod(d+aspectangle[k])) ?
                d-aspectangle[k] : d+aspectangle[k])/30.0)+1;
              isret[occurcount] = (int)RSgn(cp1.dir[j]) +
                (int)RSgn(cp2.dir[j]);
              occurcount++;
            }
          }

      /* After all transits located, sort them by time at which they occur. */

      for (i = 1; i < occurcount; i++) {
        j = i-1;
        while (j >= 0 && time[j] > time[j+1]) {
          SwapN(source[j], source[j+1]);
          SwapN(aspect[j], aspect[j+1]);
          SwapN(dest[j], dest[j+1]);
          SwapR(&time[j], &time[j+1]);
          SwapN(sign[j], sign[j+1]);
          SwapN(isret[j], isret[j+1]);
          j--;
        }
      }

      /* Now loop through list and display all the transits. */

      for (i = 0; i < occurcount; i++) {
        s1 = (_int)time[i]/24/60;
        s3 = (_int)time[i]-s1*24*60;
        s2 = s3/60;
        s3 = s3-s2*60;
        SetCI(ciSave, Mon2, s1+1, Yea2, DegToDec((real)
          ((_int)time[i]-s1*24*60) / 60.0), Dst2, Zon2, Lon2, Lat2);
        sprintf(sz, "%s %s ",
          SzDate(Mon2, s1+1, Yea2, fFalse), SzTime(s2, s3)); PrintSz(sz);
        PrintAspect(source[i], sign[i], isret[i], aspect[i],
          dest[i], SFromZ(planet3[dest[i]]), (int)RSgn(ret3[dest[i]]),
          (char)(fProg ? 'u' : 't'));

        /* Check for a Solar, Lunar, or any other return. */

        if (aspect[i] == aCon && source[i] == dest[i]) {
          AnsiColor(kWhite);
          sprintf(sz, " (%s Return)", source[i] == oSun ? "Solar" :
            (source[i] == oMoo ? "Lunar" : szObjName[source[i]]));
          PrintSz(sz);
        }
        PrintL();
#ifdef INTERPRET
        if (us.fInterpret)
          InterpretTransit(source[i], aspect[i], dest[i]);
#endif
        AnsiColor(kDefault);
      }
    }
  }
}


/* Display a list of planetary rising times relative to the local horizon */
/* for the day indicated in the chart information, as specified with the  */
/* -Zd switch. For the day, the time each planet rises (transits horizon  */
/* in East half of sky), sets (transits horizon in West), reaches its     */
/* zenith point (transits meridian in South half of sky), and nadirs      */
/* transits meridian in North), is displayed.                             */

void ChartInDayHorizon()
{
  char sz[cchSzDef];
  int source[MAXINDAY], type[MAXINDAY], sign[MAXINDAY],
    fRet[MAXINDAY], occurcount, division, div, s1, s2, i, j, fT;
  real time[MAXINDAY], rgalt1[objMax], rgalt2[objMax], azialt[MAXINDAY],
    azi1, azi2, alt1, alt2, lon, lat, mc1, mc2, xA, yA, xV, yV, d, k;

  fT = us.fSiderial; us.fSiderial = fFalse;
  lon = RFromD(Mod(Lon)); lat = RFromD(Lat);
  division = us.nDivision * 4;
  occurcount = 0;

  ciCore = ciMain; ciCore.tim = 0.0;
  CastChart(fTrue);
  mc2 = RFromD(planet[oMC]); k = RFromD(planetalt[oMC]);
  EclToEqu(&mc2, &k);
  for (i = 1; i <= cSign; i++) {
    cp2.cusp[i] = house[i];
    cp2.house[i] = inhouse[i];
  }
  for (i = 1; i <= cObj; i++) {
    cp2.obj[i] = planet[i];
    rgalt2[i] = planetalt[i];
    cp2.dir[i] = ret[i];
  }

  /* Loop thorough the day, dividing it into a certain number of segments. */
  /* For each segment we get the planet positions at its endpoints.        */

  for (div = 1; div <= division; div++) {
    ciCore = ciMain; ciCore.tim = DegToDec(24.0*(real)div/(real)division);
    CastChart(fTrue);
    mc1 = mc2;
    mc2 = RFromD(planet[oMC]); k = RFromD(planetalt[oMC]);
    EclToEqu(&mc2, &k);
    for (i = 1; i <= cSign; i++) {
      cp1.cusp[i] = cp2.cusp[i]; cp1.house[i] = cp2.house[i];
      cp2.cusp[i] = house[i];    cp2.house[i] = inhouse[i];
    }
    for (i = 1; i <= cObj; i++) {
      cp1.obj[i] = cp2.obj[i]; cp2.obj[i] = planet[i];
      rgalt1[i] = rgalt2[i]; rgalt2[i] = planetalt[i];
      cp1.dir[i] = cp2.dir[i]; cp2.dir[i] = ret[i];
    }

    /* For our segment, check to see if each planet during it rises, sets, */
    /* reaches its zenith, or reaches its nadir.                           */

    for (i = 1; i <= cObj; i++) if (!ignore[i] && FThing(i)) {
      EclToHorizon(&azi1, &alt1, cp1.obj[i], rgalt1[i], lon, lat, mc1);
      EclToHorizon(&azi2, &alt2, cp2.obj[i], rgalt2[i], lon, lat, mc2);
      j = 0;

      /* Check for transits to the horizon. */
      if ((alt1 > 0.0) != (alt2 > 0.0)) {
        d = RAbs(alt1)/(RAbs(alt1)+RAbs(alt2));
        k = Mod(azi1 + d*MinDifference(azi1, azi2));
        j = 1 + 2*(MinDistance(k, rDegHalf) < rDegQuad);

      /* Check for transits to the meridian. */
      } else if (RSgn(MinDifference(azi1, rDegQuad)) !=
        RSgn(MinDifference(azi2, rDegQuad))) {
        j = 2 + 2*(MinDistance(azi1, rDegQuad) < rDegQuad);
        d = RAbs(azi1 - (j > 2 ? rDegQuad : 270.0))/MinDistance(azi1, azi2);
        k = alt1 + d*(alt2-alt1);
      }
      if (j && occurcount < MAXINDAY) {
        source[occurcount] = i;
        type[occurcount] = j;
        time[occurcount] = 24.0*((real)(div-1)+d)/(real)division*60.0;
        sign[occurcount] = (int)Mod(cp1.obj[i] +
          d*MinDifference(cp1.obj[i], cp2.obj[i]))/30 + 1;
        fRet[occurcount] = (int)RSgn(cp1.dir[i]) + (int)RSgn(cp2.dir[i]);
        azialt[occurcount] = k;
        ciSave = ciMain;
        ciSave.tim = DegToDec(time[occurcount] / 60.0);
        occurcount++;
      }
    }
  }

  /* Sort each event in order of time when it happens during the day. */

  for (i = 1; i < occurcount; i++) {
    j = i-1;
    while (j >= 0 && time[j] > time[j+1]) {
      SwapN(source[j], source[j+1]);
      SwapN(type[j], type[j+1]);
      SwapR(&time[j], &time[j+1]);
      SwapN(sign[j], sign[j+1]);
      SwapN(fRet[j], fRet[j+1]);
      SwapR(&azialt[j], &azialt[j+1]);
      j--;
    }
  }

  /* Finally display the list showing each event and when it occurs. */

  for (i = 0; i < occurcount; i++) {
    ciSave = ciMain;
    ciSave.tim = DegToDec(time[i] / 60.0);
    j = DayOfWeek(Mon, Day, Yea);
    AnsiColor(kRainbowA[j + 1]);
    sprintf(sz, "(%c%c%c) ", chDay3(j)); PrintSz(sz);
    AnsiColor(kDefault);
    s1 = (int)time[i]/60;
    s2 = (int)time[i]-s1*60;
    sprintf(sz, "%s %s ", SzDate(Mon, Day, Yea, fFalse), SzTime(s1, s2));
    PrintSz(sz);
    AnsiColor(kObjA[source[i]]);
    sprintf(sz, "%7.7s ", szObjName[source[i]]); PrintSz(sz);
    AnsiColor(kSignA(sign[i]));
    sprintf(sz, "%c%c%c%c%c ",
      fRet[i] > 0 ? '(' : (fRet[i] < 0 ? '[' : '<'), chSig3(sign[i]),
      fRet[i] > 0 ? ')' : (fRet[i] < 0 ? ']' : '>')); PrintSz(sz);
    AnsiColor(kElemA[type[i]-1]);
    if (type[i] == 1)
      PrintSz("rises  ");
    else if (type[i] == 2)
      PrintSz("zeniths");
    else if (type[i] == 3)
      PrintSz("sets   ");
    else
      PrintSz("nadirs ");
    AnsiColor(kDefault);
    PrintSz(" at ");
    if (type[i] & 1) {
      j = (int)(RFract(azialt[i])*60.0);
      sprintf(sz, "%3d%c%02d'", (int)azialt[i], chDeg1, j); PrintSz(sz);

      /* For rising and setting events, we'll also display a direction  */
      /* vector to make the 360 degree azimuth value thought of easier. */

      xA = RCosD(azialt[i]); yA = RSinD(azialt[i]);
      if (RAbs(xA) < RAbs(yA)) {
        xV = RAbs(xA / yA); yV = 1.0;
      } else {
        yV = RAbs(yA / xA); xV = 1.0;
      }
      sprintf(sz, " (%.2f%c %.2f%c)",
        yV, yA < 0.0 ? 's' : 'n', xV, xA > 0.0 ? 'e' : 'w'); PrintSz(sz);
    } else
      PrintAltitude(azialt[i]);
    PrintL();
  }

  /* Recompute original chart placements as we've overwritten them. */

  ciCore = ciMain;
  us.fSiderial = fT;
  CastChart(fTrue);
}


/* Print out an ephemeris - the positions of the planets (at the time in the */
/* current chart) each day during a specified month, as done with the -E     */
/* switch. Display the ephemeris for the whole year if -Ey is in effect.     */

void ChartEphemeris()
{
  char sz[cchSzDef];
  int yea, yea1, yea2, mon, mon1, mon2, daysiz, i, j, s, d, m;

  /* If -Ey is in effect, then loop through all months in the whole year. */

  if (us.nEphemYears) {
    yea1 = Yea; yea2 = yea1 + us.nEphemYears - 1; mon1 = 1; mon2 = 12;
  } else {
    yea1 = yea2 = Yea; mon1 = mon2 = Mon;
  }

  /* Loop through the year or years in question. */

  for (yea = yea1; yea <= yea2; yea++)

  /* Loop through the month or months in question, printing each ephemeris. */

  for (mon = mon1; mon <= mon2; mon++) {
    daysiz = DayInMonth(mon, yea);
    PrintSz(us.fEuroDate ? "Dy/Mo/Yr" : "Mo/Dy/Yr");
    for (j = 1; j <= cObj; j++) {
      if (!ignore[j] && FThing(j)) {
        sprintf(sz, "  %s%c%c%c%c%s ", is.fSeconds ? "  " : "", chObj3(j),
          szObjName[j][3] != 0 ? szObjName[j][3] : ' ',
          is.fSeconds ? "   " : ""); PrintSz(sz);
      }
    }
    PrintL();
    for (i = 1; i <= daysiz; i = AddDay(mon, i, yea, 1)) {

      /* Loop through each day in the month, casting a chart for that day. */

      SetCI(ciCore, mon, i, yea, Tim, Dst, Zon, Lon, Lat);
      CastChart(fTrue);
      PrintSz(SzDate(mon, i, yea, -1));
      PrintCh(' ');
      for (j = 1; j <= cObj; j++)
        if (!ignore[j] && FThing(j)) {
          if (is.fSeconds)
            PrintZodiac(planet[j]);
          else {
            AnsiColor(kObjA[j]);
            s = SFromZ(planet[j]);
            d = (int)planet[j] - (s-1)*30;
            m = (int)(RFract(planet[j])*60.0);
            sprintf(sz, "%2d%s%02d", d, szSignAbbrev[s], m); PrintSz(sz);
          }
          PrintCh((char)(ret[j] >= 0.0 ? ' ' : '.'));
        }
      PrintL();
      AnsiColor(kDefault);
    }
    if (mon < mon2 || yea < yea2)
      PrintL();
  }
}

/* charts3.c */
