/*
** Astrolog (Version 3.05) File: charts.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"


/*
*******************************************************************************
** Display subprograms
*******************************************************************************
*/

/* This function is used by the interpretation routines to print out lines  */
/* of text with newlines inserted just before the end of screen is reached. */

void FieldWord(string)
char *string;
{
  static char line[STRING*2];
  static int cursor = 0;
  int i, j;

  /* Hack: Dump buffer if function called with a null string. */

  if (*string == 0) {
    line[cursor] = 0;
    printf("%s\n", line);
    cursor = 0;
    return;
  }
  if (cursor)
    line[cursor++] = ' ';
  for (i = 0; (line[cursor] = string[i]); i++, cursor++)
    ;

  /* When buffer overflows 80 columns, print out one line and start over. */

  while (cursor >= STRING-1) {
    for (i = STRING-1; line[i] != ' '; i--)
      ;
    line[i] = 0;
    printf("%s\n", line);
    line[0] = line[1] = ' ';
    for (j = 2; (line[j] = line[i+j-1]) != 0; j++)
      ;
    cursor -= (i-1);
  }
}


/* Print the interpretation of each planet in sign and house, as specified */
/* with the -I switch. This is basically array accessing combining the     */
/* meanings of each planet, sign, and house, and a couple of other things. */

void InterpretLocation(general)
int general;
{
  int i, j;
  char c;

  /* If parameter 'general' set, then, instead of an interpretation, just */
  /* print out what each sign, house, and planet means, as in -I0 switch. */

  if (general) {
    printf("Signs of the zodiac represent psychological characteristics.\n\n");
    for (i = 1; i <= SIGNS; i++) {
      AnsiColor(elemansi[(i-1) & 3]);
      sprintf(string, "%s is", signname[i]); FieldWord(string);
      sprintf(string, "%s, and", description[i]); FieldWord(string);
      sprintf(string, "%s.", desire[i]); FieldWord(string);
      FieldWord("");
    }
    AnsiColor(-1);
    printf("\nHouses represent different areas within one's life.\n\n");
    for (i = 1; i <= SIGNS; i++) {
      AnsiColor(elemansi[(i-1) & 3]);
      sprintf(string, "The %d%s House is the area of life dealing with",
        i, post[i]); FieldWord(string);
      sprintf(string, "%s.", lifearea[i]); FieldWord(string);
      FieldWord("");
    }
    AnsiColor(-1);
    printf("\nPlanets represent various parts of one's mind or self.\n\n");
    for (i = 1; i <= OBJECTS; i++) if (!ignore[i]) {
      AnsiColor(objectansi[i]);
      sprintf(string, "%s%s%s%s represents one's",
        i < 3 || i >= 16 ? "The " : "",
        i == 16 ? "North " : (i == 17 ? "Part of " : ""), objectname[i],
        i == 13 ? " Athena" : ""); FieldWord(string);
      sprintf(string, "%s.", mindpart[i]); FieldWord(string);
      FieldWord("");
    }
    AnsiColor(-1);
    return;
  }

  /* If parameter 'general' not set, then print out interpretation. */

  putchar('\n');
  for (i = 1; i <= OBJECTS; i++) if (!ignore[i]) {
    AnsiColor(objectansi[i]);
    j = (int) (planet[i]/30.0)+1; c = Dignify(i, j);
    sprintf(string, "%s%s%s%s in %s", ret[i] < 0.0 ? "Retrograde " : "",
      i == 16 ? "North " : (i == 17 ? "Part of " : ""), objectname[i],
      i == 13 ? " Athena" : "", signname[j]);
    FieldWord(string);
    sprintf(string, "and %d%s House:", inhouse[i], post[inhouse[i]]);
    FieldWord(string);
    FieldWord("This person's"); FieldWord(mindpart[i]); FieldWord("is");
    if (((int) planet[i]) % 30 < 10)
      FieldWord("very");
    sprintf(string, "%s, and", description[j]); FieldWord(string);
    sprintf(string, "%s.", desire[j]); FieldWord(string);
    FieldWord("Most often this manifests");
    if (ret[i] < 0.0 && i != 16)
      FieldWord("in an independent, backward, introverted manner, and");
    FieldWord("in the area of life dealing with");
    sprintf(string, "%s.", lifearea[inhouse[i]]); FieldWord(string);

    /* Extra information if planet is in its ruling, falling, etc, sign. */

    if (c == 'R')
      FieldWord("This is a major aspect of the person's psyche!");
    else if (c == 'F')
      FieldWord("(This bit plays only a minor part in the person's psyche.)");
    else if (c == 'e')
      FieldWord("It is easy for them to express this part of themself.");
    else if (c == 'd')
      FieldWord("It is difficult for them to express this part of themself.");
    FieldWord("");
  }
}


/* Print the straight listing of planet and house positions and specified */
/* by the -v switch, along with the element table, etc.                   */

void ChartLocation(general)
int general;
{
  int elemode[4][3], elem[4], mo[3], pos = 0, abo = 0, lef = 0, lea = 0;
  int count = 0, i, j, k;

  if (general) {                /* If parameter 'general' set, then ignore */
    InterpretLocation(TRUE);    /* chart, and display general meanings of  */
    return;                     /* signs, houses, and planets.             */
  }
  for (i = 0; i < 4; i++)
    elem[i] = 0;
  for (j = 0; j < 3; j++)
    mo[j] = 0;
  for (i = 0; i < 4; i++)
    for (j = 0; j < 3; j++)
      elemode[i][j] = 0;

  /* Calculate number of objects in each element, mode, hemisphere, etc. */

  for (i = 1; i <= total; i++) if (!ignore[i]) {
    count++;
    j = (int) (planet[i]/30.0) + 1;
    elemode[(j-1)%4][(j-1)%3]++;
    elem[(j-1)%4]++; mo[(j-1)%3]++;
    pos += (j & 1);
    lea += (j <= 6);
    j = inhouse[i];
    abo += (j >= 7);
    lef += (j < 4 || j >= 10);
  }

  /* Print header showing time and date of the chart being displayed. */

  printf("%s (%s) chart ", appname, VERSION);
  if (Mon == -1)
    printf("(no time or space)\n");
  else if (relation == DASHrc)
    printf("(composite)\n");
  else {
    i = (int) (FRACT(dabs(Tim))*100.0+0.5);
    j = (int) (FRACT(dabs(Zon))*100.0+0.5);
    printf("for %d %s %d %.0f:%d%d (%c%.0f:%d%d GMT) ",
      (int) Day, monthname[(int) Mon], (int) Yea, floor(Tim), 
      i/10, i%10, Zon > 0.0 ? '-' : '+', dabs(Zon), j/10, j%10);
    printf("%s\n", StringLocation(Lon, Lat, 100.0));
  }
  if (interpret) {
    InterpretLocation(FALSE);    /* Print interpretation if -I in effect. */
    return;
  }
  printf("Body  Locat. Ret. Decl. Rul.      House  Rul. Veloc.    ");
  printf("%s Houses.\n\n", systemname[housesystem]);

  /* Ok, now print out each location of each object. */

  for (i = 1, j = 1; i <= BASE; i++, j++)
    if (i <= OBJECTS || (i > C_HI && !ignore[i])) {
    while (i <= OBJECTS && j <= OBJECTS && ignore[j])
      j++;
    if (i <= OBJECTS && j > OBJECTS)
      PrintTab(' ', 51);
    else {
      if (i > OBJECTS)
        j = i;
      AnsiColor(objectansi[j]);
      printf("%-4.4s: ", objectname[j]);
      PrintMinute(planet[j]);
      printf(" %c ", ret[j] >= 0.0 ? ' ' : 'R');
      if (j <= THINGS || j > OBJECTS)
        PrintAltitude(planetalt[i]);
      else
        printf("_______");
      printf(" (%c)", Dignify(j, (int)planet[i]/30+1));
      k = inhouse[j];
      AnsiColor(elemansi[(k-1) & 3]);
      printf(" [%2d%c%c house]", k, post[k][0], post[k][1]);
      AnsiColor(-1);
      printf(" [%c]", Dignify(j, k));
      if (j != 2 && j < THINGS || j > C_HI)
        printf(" %c%5.3f", ret[i] < 0.0 ? '-' : '+', RTOD(dabs(ret[j])));
      else
        printf(" ______");
    }

    /* For some lines, we have to append the house cusp positions. */

    if (i <= SIGNS) {
      printf("  -  ");
      AnsiColor(elemansi[(i-1) & 3]);
      printf("House cusp %2d: ", i);
      PrintMinute(house[i]);
    }

    /* For some lines, we have to append the element table information. */

    if (i == SIGNS+2)
      printf("     Car Fix Mut TOT");
    else if (i > SIGNS+2 && i < SIGNS+7) {
      k = i-(SIGNS+2)-1;
      AnsiColor(elemansi[k]);
      printf("  %c%c%c%3d %3d %3d %3d",
        element[k][0], element[k][1], element[k][2],
        elemode[k][0], elemode[k][1], elemode[k][2], elem[k]);
      AnsiColor(-1);
    } else if (i == SIGNS+7)
      printf("  TOT %2d %3d %3d %3d", mo[0], mo[1], mo[2], count);
    else if (i == OBJECTS)
      PrintTab(' ', 23);
    else if (i >= U_LO)
      printf("  Uranian #%d", i-U_LO+1);
    switch (i-SIGNS-1) {
    case 1: printf("   +:%2d", pos);       break;
    case 2: printf("   -:%2d", count-pos); break;
    case 3: printf("   M:%2d", abo);       break;
    case 4: printf("   N:%2d", count-abo); break;
    case 5: printf("   A:%2d", lef);       break;
    case 6: printf("   D:%2d", count-lef); break;
    case 7: printf(   "<:%2d", lea);       break;
    }
    putchar('\n');
  }

  /* Do another loop to print out the stars in their specified order. */

  if (universe) for (i = S_LO; i <= S_HI; i++) if (!ignore[i]) {
    j = BASE+starname[i-BASE];
    AnsiColor(objectansi[j]);
    printf("%.4s: ", objectname[j]);
    PrintMinute(planet[j]);
    printf("   ");
    PrintAltitude(planetalt[j]);
    k = inhouse[j];
    printf("     [%2d%c%c house]", k, post[k][0], post[k][1]);
    printf("     ______  Star #%2d: %5.2f\n", i-BASE, starbright[j-BASE]);
  }
}


/* Print an interpretation for a particular aspect in effect in a chart. */
/* This is called from the InterpretGrid and ChartAspect routines.       */

void InterpretAspect(x, y)
int x, y;
{
  int n;

  n = grid->n[x][y];
  if (n > 0 && n <= ASPECTI && x <= OBJECTS && y <= OBJECTS) {
    AnsiColor(aspectansi[n]);
    sprintf(string, "%s %s %s: This person's", objectname[x],
      aspectname[n], objectname[y]);
    FieldWord(string); FieldWord(mindpart[x]);
    sprintf(string, interact[n],
      modifier[MIN(abs(grid->v[x][y])/150, 2)][n-1]);
    FieldWord(string);
    sprintf(string, "their %s.", mindpart[y]); FieldWord(string);
    if (therefore[n][0]) {
      sprintf(string, "%s.", therefore[n]); FieldWord(string);
    }
    FieldWord("");
  }
}


/* Print the interpretation of each aspect in the aspect grid, as specified */
/* with the -g -I switch. Again, this is basically array accessing of the   */
/* meanings of the two planets in aspect and of the aspect itself.          */

void InterpretGrid(general)
int general;
{
  int i, j;

  /* Again, if parameter 'general' is set, then ignore chart and print */
  /* general meanings of each aspect as specified with the -I0 switch. */

  if (general) {
    printf("\nAspects are different relationships between planets.\n\n");
    for (i = 1; i <= MIN(aspects, ASPECTI); i++) {
      AnsiColor(aspectansi[i]);
      sprintf(string, "When planets are %s, one", aspectname[i]);
      FieldWord(string); sprintf(string, interact[i], ""); FieldWord(string);
      FieldWord("another.");
      if (therefore[i][0]) {
        sprintf(string, "%s.", therefore[i]); FieldWord(string);
      }
      FieldWord("");
    }
    return;
  }

  /* If parameter 'general' not set, then actually do the interpretation. */

  for (i = 1; i < OBJECTS; i++) if (!ignore[i])
    for (j = i+1; j <= OBJECTS; j++) if (!ignore[j])
      InterpretAspect(i, j);
}


/* Print out the aspect and midpoint grid for a chart, as specified with the */
/* -g switch. (Each grid row takes up 4 lines of text.)                      */

void ChartGrid(general)
int general;
{
  int i, j, k, l, temp;

  if (general || interpret) {                   /* If -I or -I0 in effect, */
    InterpretGrid(general);                     /* ignore chart and print  */
    return;                                     /* interpretation.         */
  }
  for (j = 1; j <= total; j++) if (!ignore[j])
    for (k = 1; k <= 4; k++) {
      for (l = 0, i = 1; i <= total; i++) if (!ignore[i]) {
        if (i > 1 && j+k > 2)
          putchar(k > 1 ? BOXV : BOXC);
        if (k > 1) {
          temp = grid->n[i][j];

          /* Print aspect rows. */

          if (i < j) {
            if (temp);
              AnsiColor(aspectansi[temp]);
            if (k == 2)
              printf("%s", aspectabbrev[temp]);
            else if (!temp)
              printf("   ");
            else
              if (k == 3) {
                if (grid->v[i][j] < 600)
                  printf("%c%2d", exdisplay & DASHga ?
                    (grid->v[i][j] < 0 ? 'a' : 's') :
                    (grid->v[i][j] < 0 ? '-' : '+'), abs(grid->v[i][j])/60);
                else
                  printf("%3d", abs(grid->v[i][j])/60);
              } else {
                temp = abs(grid->v[i][j])%60;
                printf("%d%d'", temp/10, temp%10);
              }

          /* Print midpoint rows. */

          } else if (i > j) {
            AnsiColor(elemansi[(temp-1) & 3]);
            if (k == 2) {
              temp = grid->n[i][j];
              printf("%c%c%c", SIGNAM(temp));
            } else if (k == 3) {
              printf("%2d%c", grid->v[i][j]/60, DEGR2);
            } else {
              temp = grid->v[i][j]%60;
              printf("%d%d'", temp/10, temp%10);
            }

          /* Print the diagonal of object names. */

          } else {
            if (k == 2) {
              AnsiColor(objectansi[j]);
              printf("%c%c%c", OBJNAM(j));
            } else {
              temp = (int)(planet[j]/30.0)+1;
              AnsiColor(elemansi[(i-1) & 3]);
              if (k == 3)
                printf("%2d%c", (int)planet[j] - (temp-1)*30, DEGR2);
              else
                printf("%c%c%c", SIGNAM(temp));
            }
          }
          AnsiColor(-1);
        } else
          if (j > 1)
            PrintTab(BOXH, 3);
        l++;
        if (column80 && l >= 20)
          i = total;
      }
      if (j+k > 2)
        putchar('\n');
    }
}


/* This is a subprocedure of DisplayGrands(). Here we print out one aspect */
/* configuration found by the parent procedure.                            */

void PrintGrand(nam, i1, i2, i3, i4)
char nam;
int i1, i2, i3, i4;
{
  switch (nam) {
  case '.': printf("Stellium   "); break;
  case 't': printf("Grand Trine"); break;
  case 's': printf("T-Square   "); break;
  case 'y': printf("Yod        "); break;
  case 'g': printf("Grand Cross"); break;
  case 'c': printf("Cradle     "); break;
  default: ;
  }
  printf(" %s ", nam == '.' || nam == 't' || nam == 'g' ? "with" : "from");
  printf("%c%c%c: ", OBJNAM(i1));
  PrintMinute(planet[i1]);
  printf(" %s %c%c%c: ", nam == '.' || nam == 't' ? "and" : "to ", OBJNAM(i2));
  PrintMinute(planet[i2]);
  printf(" %s %c%c%c: ", nam == 'g' || nam == 'c' ? "to " : "and", OBJNAM(i3));
  PrintMinute(planet[i3]);
  if (nam == 'g' || nam == 'c') {
    printf(" to %c%c%c: ", OBJNAM(i4));
    PrintMinute(planet[i4]);
  }
  printf("\n");
}


/* Scan the aspect grid of a chart and print out any major configurations, */
/* as specified with the -g0 switch.                                       */

void DisplayGrands()
{
  int count = 0, i, j, k, l=0;

  for (i = 1; i <= OBJECTS; i++) if (!ignore[i])
    for (j = 1; j <= OBJECTS; j++) if (j != i && !ignore[j])
      for (k = 1; k <= OBJECTS; k++) if (k != i && k != j && !ignore[k]) {

        /* Is there a Stellium among the current three planets? */

        if (i < j && j < k && grid->n[i][j] == 1 &&
            grid->n[i][k] == 1 && grid->n[j][k] == 1) {
          count++;
          PrintGrand('.', i, j, k, l);

        /* Is there a Grand Trine? */

        } else if (i < j && j < k && grid->n[i][j] == 4 &&
            grid->n[i][k] == 4 && grid->n[j][k] == 4) {
          count++;
          PrintGrand('t', i, j, k, l);

        /* Is there a T-Square? */

        } else if (j < k && grid->n[j][k] == 2 &&
            grid->n[MIN(i, j)][MAX(i, j)] == 3 &&
            grid->n[MIN(i, k)][MAX(i, k)] == 3) {
          count++;
          PrintGrand('s', i, j, k, l);

        /* Is there a Yod? */

        } else if (j < k && grid->n[j][k] == 5 &&
            grid->n[MIN(i, j)][MAX(i, j)] == 6 &&
            grid->n[MIN(i, k)][MAX(i, k)] == 6) {
          count++;
          PrintGrand('y', i, j, k, l);
        }
        for (l = 1; l <= OBJECTS; l++) if (!ignore[l]) {

          /* Is there a Grand Cross among the current four planets? */

          if (i < j && i < k && i < l && j < l && grid->n[i][j] == 3 &&
              grid->n[MIN(j, k)][MAX(j, k)] == 3 &&
              grid->n[MIN(k, l)][MAX(k, l)] == 3 &&
              grid->n[i][l] == 3 && MinDistance(planet[i], planet[k]) > 150.0
              && MinDistance(planet[j], planet[l]) > 150.0) {
            count++;
            PrintGrand('g', i, j, k, l);

          /* Is there a Cradle? */

          } else if (i < l && grid->n[MIN(i, j)][MAX(i, j)] == 5 &&
              grid->n[MIN(j, k)][MAX(j, k)] == 5 &&
              grid->n[MIN(k, l)][MAX(k, l)] == 5 &&
              MinDistance(planet[i], planet[l]) > 150.0) {
            count++;
            PrintGrand('c', i, j, k, l);
          }
        }
      }
  if (!count)
    printf("No major configurations in aspect grid.\n");
}


byte wheel[SIGNS][WHEELROWS];    /* Array used by ChartWheel(). */

/* This is a subprocedure of ChartWheel(). Here we print out one line in a */
/* particular house cell (which may be blank).                             */

void PrintWheelSlot(house, row)
int house, row;
{
  int i;

  i = wheel[house-1][row];
  if (i) {
    AnsiColor(objectansi[i]);
    printf(" %c%c%c ", OBJNAM(i));    /* Print planet and its position. */
    PrintMinute(planet[i]);
    printf("%c ", ret[i] < 0.0 ? 'r' : ' ');
    PrintTab(' ', WHEELCOLS-14-1);
  } else
    PrintTab(' ', WHEELCOLS-1);       /* This particular line is blank. */
}


/* Another subprocedure of ChartWheel(). Here we print out the location */
/* of a particular house cusp as well as what house cusp number it is.  */

void PrintHouse(i, left)
int i, left;
{
  if (!left)
    PrintMinute(house[i]);
  AnsiColor(elemansi[(i-1) & 3]);
  printf("<%d>", i);
  if (left)
    PrintMinute(house[i]);
  else
    AnsiColor(-1);
}


/* Display all the objects in a wheel format on the screen, as specified */
/* with the -w switch. The wheel is divided into the 12 houses and the   */
/* planets are placed accordingly.                                       */

void ChartWheel()
{
  int i, j, k, l, count = 0;

  for (i = 0; i < SIGNS; i++)
    for (j = 0; j < wheelrows; j++)    /* Clear out array from the */
      wheel[i][j] = 0;                 /* last time we used it.    */

  /* This section of code places each object in the wheel house array. */

  for (i = 1; i <= total && count < wheelrows*12; i++) {
    if (ignore[i] || !(i < 18 || i == 20 || i > C_HI))
      continue;

    /* Try to put object in its proper house. If no room, */
    /* then overflow over to the succeeding house.        */

    for (j = inhouse[i]-1; j < SIGNS; j = j < SIGNS ? (j+1)%SIGNS : j) {

      /* Now try to find the proper place in the house to put the object. */
      /* This is in sorted order, although a check is made for 0 Aries.   */

      if (wheel[j][wheelrows-1] > 0)
        continue;
      l = house[j+1] > house[Mod12(j+2)];
      for (k = 0; wheel[j][k] > 0 &&
           (planet[i] >= planet[wheel[j][k]] ||
            (l && planet[i] < 180.0 && planet[wheel[j][k]] > 180.0)) &&
           !(l && planet[i] > 180.0 && planet[wheel[j][k]] < 180.0); k++)
        ;

      /* Actually insert object in proper place. */

      if (wheel[j][k] <= 0)
        wheel[j][k] = i;
      else {
        for (l = wheelrows-1; l > k; l--)
          wheel[j][l] = wheel[j][l-1];
        wheel[j][k] = i;
      }
      count++;
      j = SIGNS;
    }
  }

  /* Now, if this is really the -w switch and not -w0, then reverse the */
  /* order of objects in western houses for more intuitive reading.     */

  if (!(exdisplay & DASHw0))
    for (i = 3; i < 9; i++)
      for (j = 0; j < wheelrows/2; j++) {
        k = wheelrows-1-j;
        l = wheel[i][j]; wheel[i][j] = wheel[i][k]; wheel[i][k] = l;
      }

  /* Here we actually print the wheel and the objects in it. */

  putchar(BOXNW); PrintTab(BOXH, WHEELCOLS-8); PrintHouse(11, TRUE);
  PrintTab(BOXH, WHEELCOLS-11); PrintHouse(10, TRUE);
  PrintTab(BOXH, WHEELCOLS-10); PrintHouse(9, TRUE);
  PrintTab(BOXH, WHEELCOLS-4); printf("%c\n", BOXNE);
  for (i = 0; i < wheelrows; i++) {
    for (j = 11; j >= 8; j--) {
      putchar(BOXV); PrintWheelSlot(j, i);
    }
    printf("%c\n", BOXV);
  }
  PrintHouse(12, TRUE); PrintTab(BOXH, WHEELCOLS-11);
  putchar(BOXC); PrintTab(BOXH, WHEELCOLS-1); putchar(BOXJN);
  PrintTab(BOXH, WHEELCOLS-1); putchar(BOXC); PrintTab(BOXH, WHEELCOLS-10);
  PrintHouse(8, FALSE); putchar('\n');
  for (i = 0; i < wheelrows; i++) {
    putchar(BOXV); PrintWheelSlot(12, i); putchar(BOXV);

    /* For some rows, we have to insert the chart header information. */

    if (i) {
      PrintTab(' ', WHEELCOLS-11);
      if (i == 1)
        printf("%s (%s) chart", appname, VERSION);
      else if (i == 2) {
        j = (int) Mon;
        k = ((long) MdyToJulian(Mon, Day, Yea) + 1) % 7;
        printf("%c%c%c %2d %c%c%c ",
          dayname[k][0], dayname[k][1], dayname[k][2], (int) Day,
          monthname[j][0], monthname[j][1], monthname[j][2]);
        k = (int) (FRACT(dabs(Tim))*100.0+0.5);
        printf("%4d %2.0f:%d%d", (int) Yea, floor(Tim), k/10, k%10);
      } else if (i == 3) {
        printf("%c%d%d:", Zon > 0.0 ? '-' : '+',
          (int)dabs(Zon)/10, (int)dabs(Zon)%10);
        j = (int) (FRACT(dabs(Zon))*100.0+0.5);
        printf("%d%d %s", j/10, j%10, StringLocation(Lon, Lat, 100.0));
      } else
        PrintTab(' ', 21);
      PrintTab(' ', WHEELCOLS-11);

    } else
      PrintTab(' ', WHEELCOLS*2-1);
    putchar(BOXV); PrintWheelSlot(7, i); printf("%c\n", BOXV);
  }
  PrintHouse(1, TRUE); PrintTab(BOXH, WHEELCOLS-10);
  putchar(BOXJW); PrintTab(' ', WHEELCOLS-11);
  printf("%s", systemname[housesystem]);
  PrintTab(' ', 14-StringLen(systemname[housesystem]));
  printf("Houses."); PrintTab(' ', WHEELCOLS-11); putchar(BOXJE);
  PrintTab(BOXH, WHEELCOLS-10); PrintHouse(7, FALSE); putchar('\n');
  for (i = 0; i < wheelrows; i++) {
    putchar(BOXV); PrintWheelSlot(1, i); putchar(BOXV);
    PrintTab(' ', WHEELCOLS*2-1); putchar(BOXV); PrintWheelSlot(6, i);
    printf("%c\n", BOXV);
  }
  PrintHouse(2, TRUE); PrintTab(BOXH, WHEELCOLS-10);
  putchar(BOXC); PrintTab(BOXH, WHEELCOLS-1); putchar(BOXJS);
  PrintTab(BOXH, WHEELCOLS-1); putchar(BOXC);
  PrintTab(BOXH, WHEELCOLS-10); PrintHouse(6, FALSE); putchar('\n');
  for (i = 0; i < wheelrows; i++) {
    for (j = 2; j <= 5; j++) {
      putchar(BOXV); PrintWheelSlot(j, i);
    }
    printf("%c\n", BOXV);
  }
  putchar(BOXSW); PrintTab(BOXH, WHEELCOLS-4); PrintHouse(3, FALSE);
  PrintTab(BOXH, WHEELCOLS-10); PrintHouse(4, FALSE);
  PrintTab(BOXH, WHEELCOLS-10); PrintHouse(5, FALSE);
  PrintTab(BOXH, WHEELCOLS-7); printf("%c\n", BOXSE);
}


/* Display all aspects between objects in the chart, one per line, in       */
/* sorted order based on the total "power" of the aspect, as specified with */
/* the -m0 switch. The same influences used for -I charts are used here.    */

void ChartAspect()
{
  int pcut = 30000, icut, jcut, phi, ihi, jhi, ahi, p, i, j, k, count = 0;
  real ip, jp;

  while (TRUE) {
    phi = -1;

    /* Search for the next most powerful aspect in the aspect grid. */

    for (i = 2; i <= TOTAL; i++) if (!ignore[i])
      for (j = 1; j < i; j++) if (!ignore[j])
        if (k = grid->n[j][i]) {
          ip = i <= OBJECTS ? objectinf[i] : 2.5;
          jp = j <= OBJECTS ? objectinf[j] : 2.5;
          p = (int) (aspectinf[k]*(ip+jp)/2.0*
            (1.0-dabs((real)(grid->v[j][i]))/60.0/aspectorb[k])*1000.0);
          if ((p < pcut || (p == pcut && (i > icut ||
            (i == icut && j > jcut)))) && p > phi) {
            ihi = i; jhi = j; phi = p; ahi = k;
          }
        }
    if (phi < 0)    /* Exit when no less powerful aspect found. */
      break;
    pcut = phi; icut = ihi; jcut = jhi;
    count++;                               /* Display the current aspect. */
    if (interpret)
      InterpretAspect(jhi, ihi);
    else {
      printf("%3d:", count);
      AnsiColor(objectansi[jhi]);
      printf(" %7.7s ", objectname[jhi]);
      k = (int) planet[jhi]/30;
      AnsiColor(elemansi[k & 3]);
      printf("%c%c%c%c%c", ret[jhi] >= 0.0 ? '(' : '[', SIGNAM(k+1),
        ret[jhi] >= 0.0 ? ')' : ']');
      AnsiColor(aspectansi[ahi]);
      printf(" %s ", aspectabbrev[ahi]);
      k = (int) planet[ihi]/30.0;
      AnsiColor(elemansi[k & 3]);
      printf("%c%c%c%c%c", ret[ihi] >= 0.0 ? '(' : '[', SIGNAM(k+1),
        ret[ihi] >= 0.0 ? ')' : ']');
      AnsiColor(objectansi[ihi]);
      printf(" %s", objectname[ihi]);
      PrintTab(' ', 11-StringLen(objectname[ihi]));
      AnsiColor(-1);
      k = grid->v[jhi][ihi];
      printf("- orb: %c%d,%d%d' - power:%6.2f\n",
        exdisplay & DASHga ? (k < 0 ? 'a' : 's') : (k < 0 ? '-' : '+'),
        abs(k)/60, abs(k)%60/10, abs(k)%60%10, (real) phi/1000.0);
    }
  }
}


/* Display locations of all midpoints between objects in the chart, */
/* one per line, in sorted zodiac order from zero Aries onward, as  */
/* specified with the -m switch.                                    */

void ChartMidpoint()
{
  int mcut = -1, icut, jcut, mlo, ilo, jlo, m, i, j, k, count = 0;

  if (exdisplay & DASHm0) {
    ChartAspect();
    return;
  }
  while (TRUE) {
    mlo = 21600;

    /* Search for the next closest midpoint farther down in the zodiac. */ 

    for (i = 1; i < TOTAL; i++) if (!ignore[i])
      for (j = i+1; j <= TOTAL; j++) if (!ignore[j]) {
        m = (grid->n[j][i]-1)*30*60 + grid->v[j][i];
        if ((m > mcut || (m == mcut && (i > icut ||
          (i == icut && j > jcut)))) && m < mlo) {
          ilo = i; jlo = j; mlo = m;
        }
      }
    if (mlo >= 21600)    /* Exit when no midpoint farther in zodiac found. */
      break;
    mcut = mlo; icut = ilo; jcut = jlo;
    count++;                               /* Display the current midpoint. */
    printf("%4d: ", count);
    PrintMinute((real) mlo/60.0);
    AnsiColor(objectansi[ilo]);
    printf(" %7.7s ", objectname[ilo]);
    k = (int) planet[ilo]/30;
    AnsiColor(elemansi[k & 3]);
    printf("%c%c%c%c%c", ret[ilo] >= 0.0 ? '(' : '[', SIGNAM(k+1),
      ret[ilo] >= 0.0 ? ')' : ']');
    AnsiColor(WHITE);
    printf(" & ");
    k = (int) planet[jlo]/30.0;
    AnsiColor(elemansi[k & 3]);
    printf("%c%c%c%c%c", ret[jlo] >= 0.0 ? '(' : '[', SIGNAM(k+1),
      ret[jlo] >= 0.0 ? ')' : ']');
    AnsiColor(objectansi[jlo]);
    printf(" %s", objectname[jlo]);
    PrintTab(' ', 11-StringLen(objectname[jlo]));
    AnsiColor(-1);
    printf("-%4d degree span.\n", (int) MinDistance(planet[ilo], planet[jlo]));
  }
}


/* Display locations of the objects on the screen with respect to the local */
/* horizon, as specified with the -Z switch.                                */

void ChartHorizon()
{
  real lon, lat, sx, sy, vx, vy,
    lonz[TOTAL+1], latz[TOTAL+1], azi[TOTAL+1], alt[TOTAL+1];
  int i, j, k;

  lon = DTOR(Mod(Lon)); lat = DTOR(Lat);

  /* First find zenith location on Earth of each object. */

  for (i = 1; i <= total; i++) {
    lonz[i] = DTOR(planet[i]); latz[i] = DTOR(planetalt[i]);
    ecltoequ(&lonz[i], &latz[i]);
  }

  /* Then, convert this to local horizon altitude and azimuth. */

  for (i = 1; i <= total; i++) if (i != 18) {
    lonz[i] = DTOR(Mod(RTOD(lonz[18]-lonz[i]+lon)));
    lonz[i] = DTOR(Mod(RTOD(lonz[i]-lon+PI/2.0)));
    equtolocal(&lonz[i], &latz[i], PI/2.0-lat);
    azi[i] = DEGREES-RTOD(lonz[i]); alt[i] = RTOD(latz[i]);
  }

  /* Now, actually print the location of each object. */

  printf("Body Altitude Azimuth  Azi. Vector   %s Vector    Moon Vector\n\n",
    centerplanet ? " Sun" : " Earth");
  for (k = 1; k <= total; k++) {
    i = k <= BASE ? k : BASE+starname[k-BASE];
    if (!ignore[i] && (i <= THINGS || i > C_HI)) {
      AnsiColor(objectansi[i]);
      printf("%-4.4s: ", objectname[i]);
      PrintAltitude(alt[i]);

      /* Determine directional vector based on azimuth. */

      j = (int) (FRACT(azi[i])*60.0);
      printf(" %3d%c%d%d'", (int) azi[i], DEGR1, j/10, j%10);
      sx = cos(DTOR(azi[i])); sy = sin(DTOR(azi[i]));
      if (dabs(sx) < dabs(sy)) {
        vx = dabs(sx / sy); vy = 1.0;
      } else {
        vy = dabs(sy / sx); vx = 1.0;
      }
      printf(" (%.2f%c %.2f%c)",
        vy, sy < 0.0 ? 's' : 'n', vx, sx > 0.0 ? 'e' : 'w');

      /* Determine distance vector of current object from Sun and Moon. */

      vx = azi[1]-azi[i]; vy = azi[2]-azi[i];
      printf(" [%6.1f%6.1f] [%6.1f%6.1f]",
        dabs(vx) < 180.0 ? vx : Sgn(vx)*(DEGREES-dabs(vx)), alt[1]-alt[i],
        dabs(vy) < 180.0 ? vy : Sgn(vy)*(DEGREES-dabs(vy)), alt[2]-alt[i]);
      if (i >= U_LO) {
        if (i <= U_HI)
          printf("  Uranian #%d", i-U_LO+1);
        else
          printf("  Star #%2d", i-S_LO+1);
      }
      putchar('\n');
    }
  }
  AnsiColor(-1);
}


/* Display x,y,z locations of each body (in AU) with respect to the Sun */
/* (or whatever the specified center planet is), as in the -S switch.   */
/* These values were already determined when calculating the planet     */
/* positions themselves, so this procedure is basically just a loop.    */

void ChartSpace()
{
  real x, y, z;
  int i;

  printf("Body     Angle    X axis    Y axis    Z axis    Length\n");
  for (i = 0; i <= BASE; i++)
    if (!ignore[i] && i != 2 && (i < THINGS || i > C_HI)) {
      AnsiColor(objectansi[i]);
      printf("%c%c%c%c: ", OBJNAM(i),
        objectname[i][3] ? objectname[i][3] : ' ');
      x = spacex[i]; y = spacey[i]; z = spacez[i];
      printf("[%7.2f] [%7.2f] [%7.3f] [%7.3f] [%7.3f]",
        planet[i], x, y, z, sqrt(x*x+y*y+z*z));
      if (i >= U_LO) {
        if (i <= U_HI)
          printf("  Uranian #%d", i-U_LO+1);
        else
          printf("  Star #%2d", i-S_LO+1);
      }
      printf("\n");
    }
  AnsiColor(-1);
}


/* This is a subprocedure of ChartInfluence(). Based on the values in the */
/* array parameter 'value', store numbers in array 'rank' reflecting the  */
/* relative order, e.g. value[x] 2nd greatest array value -> rank[x] = 2. */

void SortRank(value, rank, size)
real *value;
int *rank;
int size;
{
  int h, i, j, k;

  value[0] = -1.0;
  for (i = 1; i <= size; i++)
    rank[i] = -1;
  for (h = 1, i = 0; h <= size; h++) if (!ignore[h] || size == SIGNS) {
    i++;
    k = 0;
    for (j = 1; j <= size; j++) if (!ignore[j] || size == SIGNS)
      if (value[j] > value[k] && rank[j] < 0)
        k = j;

        /* 'k' is the current position of the 'i'th place planet. */

    rank[k] = i;
  }
}


/* Print out a list of power values and relative rankings, based on the */
/* placements of the planets, and their aspects in the aspect grid, as  */
/* specified with the -j "find influences" switch.                      */

void ChartInfluence()
{
  real power[OBJECTS+1], power1[OBJECTS+1], power2[OBJECTS+1],
    total, total1, total2, x;
  int rank[OBJECTS+1], rank1[OBJECTS+1], rank2[OBJECTS+1], i, j, k, l;
  char c;

  for (i = 1; i <= OBJECTS; i++) {
    power1[i] = power2[i] = 0.0;
  }
  total = total1 = total2 = 0.0;

  /* First, for each object, find its power based on its placement alone. */

  for (i = 1; i <= THINGS; i++) {
    j = (int)planet[i]/30+1;
    power1[i] += objectinf[i];            /* Influence of planet itself. */
    power1[i] += houseinf[inhouse[i]];    /* Influence of house it's in. */
    c = Dignify(i, j);
    switch (c) {
    case 'R': x = objectinf[21]; break;    /* Planets in signs they rule or  */
    case 'e': x = objectinf[22]; break;    /* are exalted in have influence. */
    default:  x =  0.0;
    }
    c = Dignify(i, inhouse[i]);
    switch (c) {
    case 'R': x += houseinf[13]; break;  /* Planet in house corresponding to */
    case 'e': x += houseinf[14]; break;  /* sign it rules has influence.     */
    default: ;
    }
    power1[i] += x;
    if (i != rules[j])                         /* The planet ruling the sign */
      power1[rules[j]] += objectinf[i]/2.0;    /* and the house that the     */
    if (i != (j = rules[inhouse[i]]))          /* current planet is in, gets */
      power1[j] += objectinf[i]/2.0;           /* extra influence.           */
  }
  for (i = 1; i <= SIGNS; i++) {        /* Various planets get influence */
    j = (int)(house[i]/30.0)+1;         /* if house cusps fall in signs  */
    power1[rules[j]] += houseinf[i];    /* they rule.                    */
  }

  /* Second, for each object, find its power based on aspects it makes. */

  CreateGrid(FALSE);
  for (j = 1; j <= OBJECTS; j++) if (!ignore[j])
    for (i = 1; i <= OBJECTS; i++) if (!ignore[i] && i != j) {
      k = grid->n[MIN(i, j)][MAX(i, j)];
      if (k) {
        l = grid->v[MIN(i, j)][MAX(i, j)];
        power2[j] += aspectinf[k]*objectinf[i]*
          (1.0-dabs((real)l)/60.0/aspectorb[k]);
      }
    }

  /* Calculate total power of each planet. */

  for (i = 1; i <= THINGS; i++) if (!ignore[i]) {
    power[i] = power1[i]+power2[i]; total1 += power1[i]; total2 += power2[i];
  }
  total = total1+total2;

  /* Finally, determine ranks of the arrays, then print everything out. */

  SortRank(power1, rank1, THINGS); SortRank(power2, rank2, THINGS);
  SortRank(power, rank, THINGS);
  printf("  Planet:    Position      Aspects    Total Rank  Percent\n");
  for (i = 1; i <= THINGS; i++) if (!ignore[i]) {
    AnsiColor(objectansi[i]);
    printf("%8.8s: ", objectname[i]);
    printf("%6.1f (%2d) +%6.1f (%2d) =%7.1f (%2d) /%6.1f%%\n",
      power1[i], rank1[i], power2[i], rank2[i],
      power[i], rank[i], power[i]/total*100.0);
  }
  AnsiColor(-1);
  printf("   Total: %6.1f      +%6.1f      =%7.1f      / 100.0%%\n",
    total1, total2, total);

  /* Now, print out a list of power values and relative rankings, based on  */
  /* the power of each sign of the zodiac, as indicated by the placement of */
  /* the planets above, in the chart, as specified with the -j0 switch.     */

  if (!(exdisplay & DASHj0))
    return;
  for (i = 1; i <= SIGNS; i++) {
    power1[i] = 0.0;
  }

  /* For each sign, determine its power based on the power of the object. */

  for (i = 1; i <= OBJECTS; i++) if (!ignore[i]) {
    power1[(int)planet[i]/30+1] +=
      (i <= THINGS ? power[i] : objectinf[i]) / 2.0;
    power1[inhouse[i]]  += (i <= THINGS ? power[i] : objectinf[i]) / 4.0;
    power1[ruler1[i]]   += (i <= THINGS ? power[i] : objectinf[i]) / 3.0;
    if (ruler2[i])
      power1[ruler2[i]] += (i <= THINGS ? power[i] : objectinf[i]) / 4.0;
  }
  if (!ignore[THINGS]) {
    power1[Mod12((int)planet[THINGS]/30+7)] += power[THINGS] / 2.0; /* South */
    power1[Mod12(inhouse[THINGS]+6)]        += power[THINGS] / 4.0; /* Node. */
  }
  total1 = 0.0;
  for (i = 1; i <= SIGNS; i++)
    total1 += power1[i];

  /* Again, determine ranks in the array, and print everything out. */

  SortRank(power1, rank1, SIGNS);
  printf("\n       Sign:  Power Rank  Percent  - Element  Power  Percent\n");
  for (i = 1; i <= SIGNS; i++) {
    AnsiColor(elemansi[i-1 & 3]);
    printf("%11.11s: ", signname[i]);
    printf("%6.1f (%2d) /%6.1f%%",
      power1[i], rank1[i], power1[i]/total*100.0);
    if (i <= 4) {
      printf("  -%7.7s:", element[i-1]);
      total2 = 0.0;
      for (j = 1; j < SIGNS; j += 4)
        total2 += power1[i+j-1];
      printf("%7.1f /%6.1f%%", total2, total2/total1*100.0);
    }
    putchar('\n');
  }
  AnsiColor(-1);
  printf("      Total:%7.1f      / 100.0%%\n", total1);
}


/* Print the locations of the astro-graph lines on the Earth as specified */
/* with the -L switch. This includes Midheaven and Nadir lines, zenith    */
/* positions, and locations of Ascendant and Descendant lines.            */

void ChartAstroGraph()
{
  real planet1[TOTAL+1], planet2[TOTAL+1], lat[MAXCROSS], lon[MAXCROSS],
    mc[TOTAL+1], ic[TOTAL+1], as[TOTAL+1], ds[TOTAL+1], as1[TOTAL+1],
    ds1[TOTAL+1], lo = Lon, longm, w, x, y, z, ad, oa, am, od, dm;
  int obj1[MAXCROSS], obj2[MAXCROSS], occurcount = 0, tot = total,
    i, j, k, l, m, n;

  for (i = 1; i <= TOTAL; i++) {
    planet1[i] = DTOR(planet[i]);
    planet2[i] = DTOR(planetalt[i]);      /* Calculate zenith location on */
    ecltoequ(&planet1[i], &planet2[i]);   /* Earth of each object.        */
  }

  /* Print header. */

  printf("Object :");
  for (j = 0, i = 1; i <= total; i++)
    if (!ignore[i] && (i <= THINGS || i > C_HI)) {
      AnsiColor(objectansi[i]);
      printf(" %c%c%c", OBJNAM(i));
      j++;
      if (column80 && j >= 17) {
        tot = i;
        i = total;
      }
    }
  AnsiColor(-1);
  printf("\n------ :");
  for (i = 1; i <= tot; i++)
    if (!ignore[i] && (i <= THINGS || i > C_HI))
      printf(" ###");

  /* Print the longitude locations of the Midheaven lines. */

  printf("\nMidheav: ");
  if (lo < 0.0)
    lo += DEGREES;
  for (i = 1; i <= tot; i++)
    if (!ignore[i] && (i <= THINGS || i > C_HI)) {
    AnsiColor(objectansi[i]);
    x = DTOR(MC)-planet1[i];
    if (x < 0.0)
      x += 2.0*PI;
    if (x > PI)
      x -= 2.0*PI;
    z = lo+RTOD(x);
    if (z > 180.0)
      z -= DEGREES;
    mc[i] = z;
    printf("%3.0f%c", dabs(z), z < 0.0 ? 'e' : 'w');
  }
  AnsiColor(-1);

  /* The Nadir lines are just always 180 degrees away from the Midheaven. */

  printf("\nNadir  : ");
  for (i = 1; i <= tot; i++)
    if (!ignore[i] && (i <= THINGS || i > C_HI)) {
    AnsiColor(objectansi[i]);
    z = mc[i] + 180.0;
    if (z > 180.0)
      z -= DEGREES;
    ic[i] = z;
    printf("%3.0f%c", dabs(z), z < 0.0 ? 'e' : 'w');
  }
  AnsiColor(-1);

  /* Print the Zenith latitude locations. */

  printf("\nZenith : ");
  for (i = 1; i <= tot; i++)
    if (!ignore[i] && (i <= THINGS || i > C_HI)) {
      AnsiColor(objectansi[i]);
      y = RTOD(planet2[i]);
      printf("%3.0f%c", dabs(y), y < 0.0 ? 's' : 'n');
      as[i] = ds[i] = as1[i] = ds1[i] = 1000.0;
    }
  printf("\n\n");

  /* Now print the locations of Ascendant and Descendant lines. Since these */
  /* are curvy, we loop through the latitudes, and for each object at each  */
  /* latitude, print the longitude location of the line in question.        */

  longm = DTOR(Mod(MC+lo));
  for (j = 80; j >= -80; j -= graphstep) {
    AnsiColor(-1); 
    printf("Asc@%2d%c: ", j >= 0 ? j : -j, j < 0 ? 's' : 'n');
    for (i = 1; i <= tot; i++)
      if (!ignore[i] && (i <= THINGS || i > C_HI)) {
      AnsiColor(objectansi[i]);
      ad = tan(planet2[i])*tan(DTOR(j));
      if (ad*ad > 1.0) {
        printf(" -- ");
        as1[i] = ds1[i] = ret2[i] = 1000.0;
      } else {
        ad = ASIN(ad);
        oa = planet1[i]-ad;
        if (oa < 0.0)
          oa += 2.0*PI;
        am = oa-PI/2.0;
        if (am < 0.0)
          am += 2.0*PI;
        z = longm-am;
        if (z < 0.0)
          z += 2.0*PI;
        if (z > PI)
          z -= 2.0*PI;
        as1[i] = as[i];
        as[i] = z = RTOD(z);
        ret2[i] = ad;
        printf("%3.0f%c", dabs(z), z < 0.0 ? 'e' : 'w');
      }
    }

    /* Again, the Descendant position is related to the Ascendant's,  */
    /* being a mirror image, so it can be calculated somewhat easier. */

    AnsiColor(-1);
    printf("\nDsc@%2d%c: ", j >= 0 ? j : -j, j < 0 ? 's' : 'n');
    for (i = 1; i <= tot; i++)
      if (!ignore[i] && (i <= THINGS || i > C_HI)) {
      AnsiColor(objectansi[i]);
      ad = ret2[i];
      if (ad == 1000.0)
        printf(" -- ");
      else {
        od = planet1[i]+ad;
        dm = od+PI/2.0;
        z = longm-dm;
        if (z < 0.0)
          z += 2.0*PI;
        if (z > PI)
          z -= 2.0*PI;
        ds1[i] = ds[i];
        ds[i] = z = RTOD(z);
        printf("%3.0f%c", dabs(z), z < 0.0 ? 'e' : 'w');
      }
    }
    putchar('\n');

    /* Now, if the -L0 switch is in effect, then take these line positions, */
    /* which we saved in an array above as we were printing them, and       */
    /* calculate and print the latitude crossings.                          */

    if (exdisplay & DASHL0)
      for (l = 1; l <= total; l++) if (!ignore[l] && (l <= THINGS || l > C_HI))
        for (k = 1; k <= total; k++)
        if (!ignore[k] && (k <= THINGS || k > C_HI))
        for (n = 0; n <= 1; n++) {
        x = n ? ds1[l] : as1[l];
        y = n ? ds[l] : as[l];
        for (m = 0; m <= 1; m++) {

        /* Check if Ascendant/Descendant cross Midheaven/Nadir. */

        z = m ? ic[k] : mc[k];
        if (occurcount < MAXCROSS &&
          dabs(x-y) < 180.0 && Sgn(z-x) != Sgn(z-y)) {
          obj1[occurcount] = n ? -l : l;
          obj2[occurcount] = m ? -k : k;
          lat[occurcount] = (real)j+5.0*dabs(z-y)/dabs(x-y);
          lon[occurcount] = z;
          occurcount++;
        }

        /* Check if Ascendant/Descendant cross another Asc/Des. */

        w = m ? ds1[k] : as1[k];
        z = m ? ds[k] : as[k];
        if (occurcount < MAXCROSS && k > l &&
            dabs(x-y)+dabs(w-z) < 180.0 && Sgn(w-x) != Sgn(z-y)) {
          obj1[occurcount] = n ? -l : l;
          obj2[occurcount] = 100+(m ? -k : k);
          lat[occurcount] = (real)j+5.0*
            dabs(y-z)/(dabs(x-w)+dabs(y-z));
          lon[occurcount] = MIN(x, y)+dabs(x-y)*
            dabs(y-z)/(dabs(x-w)+dabs(y-z));
          occurcount++;
        }
      }
    }
  }
  if ((exdisplay & DASHL0) == 0)
    return;
  putchar('\n');

  /* Now, print out all the latitude crossings we found.  */
  /* First, we sort them in order of decreasing latitude. */

  for (i = 1; i < occurcount; i++) {
    j = i-1;
    while (j >= 0 && lat[j] < lat[j+1]) {
      SWAP(obj1[j], obj1[j+1]); SWAP(obj2[j], obj2[j+1]);
      SwapReal(&lat[j], &lat[j+1]); SwapReal(&lon[j], &lon[j+1]);
      j--;
    }
  }
  for (i = 1; i < occurcount; i++) {
    j = abs(obj1[i]);
    AnsiColor(objectansi[j]);
    printf("%c%c%c ", OBJNAM(j));
    AnsiColor(elemansi[obj1[i] > 0 ? 0 : 2]);
    printf("%s ", obj1[i] > 0 ? "Ascendant " : "Descendant");
    AnsiColor(WHITE);
    printf("crosses ");
    j = abs(obj2[i] - (obj2[i] < 50 ? 0 : 100));
    AnsiColor(objectansi[j]);
    printf("%c%c%c ", OBJNAM(j));
    AnsiColor(elemansi[obj2[i] < 50 ?
      (obj2[i] > 0 ? 1 : 3) : (obj2[i] > 100 ? 0 : 2)]);
    printf("%s ", obj2[i] < 50 ? (obj2[i] > 0 ? "Midheaven " :
      "Nadir     ") : (obj2[i] > 100 ? "Ascendant " : "Descendant"));
    j = (int) (FRACT(dabs(lon[i]))*60.0);
    AnsiColor(-1);
    printf("at %3d%c%d%d'%c, ", (int) dabs(lon[i]), DEGR2,
      j/10, j%10, lon[i] < 0.0 ? 'E' : 'W');
    j = (int) (FRACT(dabs(lat[i]))*60.0);
    printf("%2d%c%d%d'%c\n", (int) dabs(lat[i]), DEGR2,
      j/10, j%10, lat[i] < 0.0 ? 'S' : 'N');
  }
  if (!occurcount) {
    AnsiColor(-1);
    printf("No latitude crossings.\n");
  }
}

/* charts.c */
