dumper.h

/* This is the header file for the Dumper module.  It is also included by the
   Cases, Odometer and Reporter modules. */

/* external functions */

extern void
Write_Matrix(
      file stream,
      area *area_pointer,
      vector_element *facts_head,
      court *court_head,
      boolean hypothetical,
      cardinal number);

extern void
Write_Year_and_Court(
      file stream,
      kase *case_pointer,
      cardinal level);

extern void
Dump_Specification(
      file dump_stream,
      file log_stream,
      case_law_specification case_law,
      boolean inputable_latex,
      boolean verbose);

dumper.c

/* This is the implementation file for the Dumper module. */

#include <stdio.h>
#include "shyster.h"
#include "cases.h"
#include "dumper.h"
#include "odometer.h"

static void
warning(
      file stream,
      const string message)
{
   Write_Warning_Message(stream, "Dumper", message, Top_Level);
}

static void
write_hierarchy_table(
      file dump_stream,
      court *court_pointer)

/* Writes a table of courts, with their ranks */

{
   boolean same_rank = FALSE;

   fprintf(dump_stream, "%s{Hierarchy}\n\n", Heading);
   Indent(dump_stream, 1);
   fprintf(dump_stream, "\\begin{small}\n");
   Indent(dump_stream, 2);
   fprintf(dump_stream, "\\begin{trivlist}\\item[]\n");
   Indent(dump_stream, 3);
   fprintf(dump_stream, "\\begin{tabular}{|r|l|}\\hline\n");
   Indent(dump_stream, 4);
   fprintf(dump_stream,
         "\\multicolumn{1}{|c|}{$c$}&"
         "\\multicolumn{1}{c|}{\\it Court\\/}\\\\\\hline\\hline");

   /* while there are still courts to list ... */

   while (court_pointer != NULL) {
      fprintf(dump_stream, "\n");
      Indent(dump_stream, 4);
      if (!same_rank)
         fprintf(dump_stream, "%u", court_pointer->rank);
      fprintf(dump_stream, "&%s\\\\", court_pointer->string);
      if (court_pointer->next != NULL)
         same_rank = (court_pointer->rank == court_pointer->next->rank);
      court_pointer = court_pointer->next;
   }

   fprintf(dump_stream, "\\hline\n");
   Indent(dump_stream, 3);
   fprintf(dump_stream, "\\end{tabular}\n");
   Indent(dump_stream, 2);
   fprintf(dump_stream, "\\end{trivlist}\n");
   Indent(dump_stream, 1);
   fprintf(dump_stream, "\\end{small}\n\n");
}

static void
write_distance(
      file stream,
      distance_subtype distance,
      boolean centre,
      boolean rule_at_right)

/* Writes distance as a cell in a table of distances, centred (if centre is
   TRUE) and with a vertical rule at the right (if rule_at_right and centre
   are both TRUE). */

{
   if (distance.infinite != 0) {

      if (Is_Zero(distance.finite)) {

         /* the distance has an infinite component, but no finite component */

         if (distance.infinite == 1)
            if (centre)
               fprintf(stream, "\\multicolumn{1}{c%s}{$\\infty$}",
                     rule_at_right ? "|" : "");
            else
               fprintf(stream, "$\\infty$");
         else if (centre)
            fprintf(stream, "\\multicolumn{1}{c%s}{$%u\\infty$}",
                  rule_at_right ? "|" : "", distance.infinite);
         else
            fprintf(stream, "$%u\\infty$", distance.infinite);

      } else {

         /* the distance has both a finite component and an infinite
            component */

         if (distance.infinite == 1) {
            fprintf(stream, "$\\infty$+");
            Write_Floating_Point(stream, distance.finite, Empty_String);
         } else {
            fprintf(stream, "$%u\\infty$+", distance.infinite);
            Write_Floating_Point(stream, distance.finite, Empty_String);
         }
      }

   } else {

      if (Is_Zero(distance.finite)) {

         /* the distance has neither a finite component nor an infinite
            component */

         if (centre)
            fprintf(stream, "\\multicolumn{1}{c%s}{--}", rule_at_right ? "|" : "");
         else
            fprintf(stream, "--");

      } else

         /* the distance has a finite component, but no infinite component */

         Write_Floating_Point(stream, distance.finite, Empty_String);
   }
}

static void
write_metrics(
      file stream,
      metrics_type metrics,
      boolean weighted_association_coefficient,
      boolean correlation_coefficients)

/* Writes, as cells in a table of distances, each of the metrics in metrics:
   the known distance, the unknown distance, the unweighted distance, the
   association coefficient, the weighted association coefficient (if
   weighted_association_coefficient is TRUE), the correlation coefficient (if
   correlation_coefficients is TRUE), and the weighted correlation
   coefficient (if correlation_coefficients is TRUE). */

{
   write_distance(stream, metrics.distance.known, TRUE, FALSE);
   fprintf(stream, "&");
   write_distance(stream, metrics.distance.unknown, TRUE, TRUE);
   fprintf(stream, "&");

   if (metrics.number_of_known_pairs == 0) {

      /* there are no known pairs */

      fprintf(stream, "--&--&");
      if (weighted_association_coefficient)
         fprintf(stream, "--&");
      if (correlation_coefficients)
         fprintf(stream, "&&");

   } else {

      if (metrics.number_of_known_differences == 0) {

         /* there are no known differences */

         fprintf(stream, "--&--&");
         if (weighted_association_coefficient)
            fprintf(stream, "--&");

      } else {

         /* there are known differences */

         fprintf(stream, "%u&", metrics.number_of_known_differences);
         Write_Floating_Point(stream,
               (floating_point) metrics.number_of_known_differences /
               metrics.number_of_known_pairs, Empty_String);
         fprintf(stream, "&");
         if (weighted_association_coefficient) {
            Write_Floating_Point(stream,
                  metrics.weighted_association_coefficient, Empty_String);
            fprintf(stream, "&");
         }
      }
      if (correlation_coefficients)
         if (metrics.correlation_coefficient.meaningless)

            /* either this case (or ideal point or centroid) or the instant
               case has all attribute values equal: the correlation
               coefficients are meaningless */

            fprintf(stream, "&&");

         else {

            /* write values for the correlation coefficients */

            Write_Floating_Point(stream,
                  metrics.correlation_coefficient.unweighted, Empty_String);
            fprintf(stream, "&");
            Write_Floating_Point(stream,
                  metrics.correlation_coefficient.weighted, Empty_String);
            fprintf(stream, "&");
         }
   }
}

static boolean
all_three_equal(
      distance_subtype x,
      distance_subtype y,
      distance_subtype z)

/* Returns TRUE, iff distances x, y and z are equal. */

{
   return ((x.infinite == y.infinite) &&
         Is_Equal(x.finite, y.finite, Distance_Precision) &&
         (x.infinite == z.infinite) &&
         Is_Equal(x.finite, z.finite, Distance_Precision));
}

static void
write_result(
      file stream,
      cardinal count,
      cardinal first_result_row,
      boolean write_directions,
      result *result_pointer)

/* Writes the identifier, and the strength of every non-zero direction (if
   write_directions is TRUE), for the result pointed to by result_pointer, as
   cells in a table of distances.  Writes the identifier, then the
   directions, in consecutive rows of the "Result" column, starting with row
   first_result_row so that the information is centred vertically. */

{
   static boolean specified_to_be_written,
      ideal_point_to_be_written,
      centroid_to_be_written,
      all_to_be_written;

   if (count == first_result_row) {

      /* the result identifier should be written in this row */

      fprintf(stream, "{%s %s}", Identifier_Font, result_pointer->identifier);

      if (write_directions) {

         /* determine which directions will be written (on subsequent
            invocations of this function) */

         specified_to_be_written =
               !Is_Zero_Subdistance(result_pointer->specified_direction);
         ideal_point_to_be_written =
               !Is_Zero_Subdistance(result_pointer->ideal_point_direction);
         centroid_to_be_written =
               !Is_Zero_Subdistance(result_pointer->centroid_direction);
         all_to_be_written = (specified_to_be_written &&
               ideal_point_to_be_written && centroid_to_be_written &&
               all_three_equal(result_pointer->specified_direction,
                     result_pointer->ideal_point_direction,
                     result_pointer->centroid_direction));
      }
   } else if (write_directions && (count > first_result_row)) {

      /* write the strength of the next non-zero direction */

      if (all_to_be_written) {
         fprintf(stream, "%s\\,", All_Directions_Symbol);
         write_distance(stream, result_pointer->specified_direction, FALSE, FALSE);
         all_to_be_written = FALSE;
         specified_to_be_written = FALSE;
         ideal_point_to_be_written = FALSE;
         centroid_to_be_written = FALSE;
      } else if (specified_to_be_written) {
         fprintf(stream, "%s\\,", Specified_Direction_Symbol);
         write_distance(stream, result_pointer->specified_direction, FALSE, FALSE);
         specified_to_be_written = FALSE;
      } else if (ideal_point_to_be_written) {
         fprintf(stream, "%s\\,", Ideal_Point_Direction_Symbol);
         write_distance(stream, result_pointer->ideal_point_direction, FALSE, FALSE);
         ideal_point_to_be_written = FALSE;
      } else if (centroid_to_be_written) {
         fprintf(stream, "%s\\,", Centroid_Direction_Symbol);
         write_distance(stream, result_pointer->centroid_direction, FALSE, FALSE);
         centroid_to_be_written = FALSE;
      }
   }
}

extern void
Write_Matrix(
      file stream,
      area *area_pointer,
      vector_element *facts_head,
      court *court_head,
      boolean hypothetical,
      cardinal number)

/* Writes a matrix of attribute values and (if facts_head is TRUE) metric
   information, for the instant case, the cases in the area pointed to by
   area_pointer, the ideal points in that area, and each result's centroid
   (if they have been calculated).  If number is not zero then the instant
   case is actually a hypothetical (if hypothetical is TRUE) or an
   instantiation, and number is its number. */

{
   result *result_pointer;
   kase *case_pointer;
   matrix_element *matrix_pointer;
   vector_element *vector_pointer;
   centroid_element *centroid_pointer;
   cardinal count,
      first_result_row;

   Indent(stream, 1);
   fprintf(stream, "\\begin{small}\n");
   Indent(stream, 2);
   fprintf(stream, "\\begin{tabular}{*{2}{|c}");

   if (area_pointer->number_of_attributes > 1)
      fprintf(stream, "*{%u}{@{\\hspace{%s}}c}|",
            area_pointer->number_of_attributes - 1, Matrix_Column_Separation);

   if (court_head != NULL)

      /* there will be a column for the rank of each case's court */

      fprintf(stream, "r|");

   if (facts_head != NULL) {

      /* there will be columns for metric information */

      fprintf(stream, "r@{\\hspace{%s}}r|r|", Column_Separation);

      if (area_pointer->infinite_weight)

         /* at least one of the attribute's weights is infinite, so the
            values obtained for S' are meaningless: there will be no S'
            column */

         fprintf(stream, "c|");

      else

         /* there will be an S' column */

         fprintf(stream, "c@{\\hspace{%s}}c|", Column_Separation);

      if (area_pointer->correlation_coefficients)

         /* the instant case does not have all attribute values the same, and
            not every case, ideal point and centroid has all attribute values
            the same: there will be columns for the correlation coefficients
            r and r' (if either of these conditions does not hold, all the
            values of r and r' are meaningless) */

         fprintf(stream, "r@{\\hspace{%s}}r|", Column_Separation);
   }
   fprintf(stream, "c|}\\hline\n");

   /* write the column headings */

   Indent(stream, 3);
   fprintf(stream, "&\\multicolumn{%u}{|c|}{\\it Attributes\\/}&",
         area_pointer->number_of_attributes);

   if (court_head != NULL)
      fprintf(stream, "&");

   if (facts_head != NULL) {
      fprintf(stream, "&&&&");
      if (!area_pointer->infinite_weight)
         fprintf(stream, "&");
      if (area_pointer->correlation_coefficients)
         fprintf(stream, "&&");
   }
   fprintf(stream, "\\\\\n");
   Indent(stream, 3);

   fprintf(stream, "\\smash{\\raisebox{%s}{\\it Case\\/}}&", Raise_Height);

   for (count = 1; count <= area_pointer->number_of_attributes; count++)
      fprintf(stream, "$A_{%u}$&", count);

   if (court_head != NULL)
      fprintf(stream, "\\multicolumn{1}{c|}{\\smash{\\raisebox{%s}{$c$}}}&",
            Raise_Height);

   if (facts_head != NULL) {
      fprintf(stream,
            "\\multicolumn{1}{c}{\\smash{\\raisebox{%s}{$d_{\\rm K}$}}}&"
            "\\multicolumn{1}{c|}{\\smash{\\raisebox{%s}{$d_{\\rm U}$}}}&"
            "\\multicolumn{1}{c|}{\\smash{\\raisebox{%s}{$\\Delta$}}}&"
            "\\smash{\\raisebox{%s}{$S$}}&",
            Raise_Height, Raise_Height, Raise_Height, Raise_Height);

      if (!area_pointer->infinite_weight)
         fprintf(stream, "\\smash{\\raisebox{%s}{$S'$}}&",
               Raise_Height);

      if (area_pointer->correlation_coefficients)
         fprintf(stream, "\\multicolumn{1}{c}{\\smash{\\raisebox{%s}{$r$}}}&"
               "\\multicolumn{1}{c|}{\\smash{\\raisebox{%s}{$r'$}}}&",
               Raise_Height, Raise_Height);
   }
   fprintf(stream, "\\smash{\\raisebox{%s}{\\it Result\\/}}\\\\\\hline",
         Raise_Height);

   if (facts_head != NULL) {

      /* write details of the instant case */

      fprintf(stream, "\\hline\n");
      Indent(stream, 3);

      if (number == 0)

         /* the instant case is the uninstantiated and unhypothesized instant
            case */

         fprintf(stream, "$C_{\\rm Instant}$&");

      else if (hypothetical)

         /* the instant case is hypothetical number */

         fprintf(stream, "$C_{\\mbox{\\scriptsize Hypo-%u}}$&", number);

      else

         /* the instant case is instantiation number */

         fprintf(stream, "$C_{\\mbox{\\scriptsize Inst-%u}}$&", number);

      /* write the attribute values for the instant case */

      for (vector_pointer = facts_head; vector_pointer != NULL;
            vector_pointer = vector_pointer->next)
         fprintf(stream, "%s&",
               vector_pointer->attribute_value == YES ? Yes_Symbol :
               vector_pointer->attribute_value == NO ? No_Symbol :
               Unknown_Symbol);

      /* leave an appropriate number of columns empty */

      fprintf(stream, "\\multicolumn{%u}{c|}{}\\\\\\hline",
            court_head == NULL ?
            area_pointer->infinite_weight ?
            area_pointer->correlation_coefficients ? 7 : 5 :
            area_pointer->correlation_coefficients ? 8 : 6 :
            area_pointer->infinite_weight ?
            area_pointer->correlation_coefficients ? 8 : 6 :
            area_pointer->correlation_coefficients ? 9 : 7);
   }

   /* for every result ... */

   for (result_pointer = area_pointer->result_head; result_pointer != NULL;
         result_pointer = result_pointer->next) {

      /* determine the first row in which information should appear, for this
         result, in the "Result" column so that the information is centred
         vertically */

      first_result_row = 0;
      for (case_pointer = result_pointer->case_head; case_pointer != NULL;
            case_pointer = case_pointer->next)
         first_result_row++;
      if (result_pointer->ideal_point_head != NULL)
         first_result_row++;
      if (result_pointer->centroid_head != NULL)
         first_result_row++;
      if (first_result_row != 0) {
         if ((facts_head != NULL) && (first_result_row > 1) &&
               !Is_Zero_Subdistance(result_pointer->specified_direction)) {
            first_result_row--;

            if (!all_three_equal(result_pointer->specified_direction,
                        result_pointer->ideal_point_direction,
                        result_pointer->centroid_direction)) {
               if ((first_result_row > 1) &&
                     !Is_Zero_Subdistance(result_pointer->ideal_point_direction))
                  first_result_row--;
               if ((first_result_row > 1) &&
                     !Is_Zero_Subdistance(result_pointer->centroid_direction))
                  first_result_row--;
            }
         }
         first_result_row = (first_result_row + 1) / 2;

         count = 1;
         fprintf(stream, "\\hline");

         /* for every case with this result ... */

         for (case_pointer = result_pointer->case_head; case_pointer != NULL;
               case_pointer = case_pointer->next) {
            fprintf(stream, "\n");
            Indent(stream, 3);
            fprintf(stream, "$C_{%u}$&", case_pointer->number);

            /* write the attribute values for this case */

            for (matrix_pointer = case_pointer->matrix_head; matrix_pointer != NULL;
                  matrix_pointer = matrix_pointer->case_next)
               fprintf(stream, "%s&",
                     matrix_pointer->attribute_value == YES ? Yes_Symbol :
                     matrix_pointer->attribute_value == NO ? No_Symbol :
                     Unknown_Symbol);

            if (court_head != NULL)

               /* write the rank of the case's court */

               if ((case_pointer->court_string != NULL) &&
                     (case_pointer->court_rank != 0))
                  fprintf(stream, "%u&", case_pointer->court_rank);
               else
                  fprintf(stream, "\\footnotesize?&");

            if (facts_head != NULL)
               write_metrics(stream, case_pointer->metrics,
                     !area_pointer->infinite_weight, area_pointer->correlation_coefficients);

            write_result(stream, count++, first_result_row, facts_head != NULL,
                  result_pointer);
            fprintf(stream, "\\\\");
         }

         /* write a line (an appropriate number of columns wide) under the
            cases for this result */

         if ((result_pointer->case_head != NULL) &&
               ((result_pointer->ideal_point_head != NULL) ||
                     (result_pointer->centroid_head != NULL)))
            fprintf(stream, "\\cline{2-%u}", facts_head == NULL ?
                  court_head == NULL ?
                  area_pointer->number_of_attributes + 1 :
                  area_pointer->number_of_attributes + 2 :
                  court_head == NULL ?
                  area_pointer->infinite_weight ?
                  area_pointer->correlation_coefficients ?
                  area_pointer->number_of_attributes + 7 :
                  area_pointer->number_of_attributes + 5 :
                  area_pointer->correlation_coefficients ?
                  area_pointer->number_of_attributes + 8 :
                  area_pointer->number_of_attributes + 6 :
                  area_pointer->infinite_weight ?
                  area_pointer->correlation_coefficients ?
                  area_pointer->number_of_attributes + 8 :
                  area_pointer->number_of_attributes + 6 :
                  area_pointer->correlation_coefficients ?
                  area_pointer->number_of_attributes + 9 :
                  area_pointer->number_of_attributes + 7);

         if (result_pointer->ideal_point_head != NULL) {

            /* this result has an ideal point, so write its details */

            fprintf(stream, "\n");
            Indent(stream, 3);
            fprintf(stream, "$I_{\\mbox{\\scriptsize%s %s}}$&",
                  Identifier_Font, result_pointer->identifier);

            /* write the attribute values for this ideal point */

            for (vector_pointer = result_pointer->ideal_point_head;
                  vector_pointer != NULL; vector_pointer = vector_pointer->next)
               fprintf(stream, "%s&", (vector_pointer->attribute_value == YES ?
                           Yes_Symbol : (vector_pointer->attribute_value == NO ?
                                 No_Symbol : Unknown_Symbol)));

            if (court_head != NULL)
               fprintf(stream, "&");

            if (facts_head != NULL)
               write_metrics(stream, result_pointer->ideal_point_metrics,
                     !area_pointer->infinite_weight, area_pointer->correlation_coefficients);

            write_result(stream, count++, first_result_row, facts_head != NULL,
                  result_pointer);
            fprintf(stream, "\\\\");
         }
         if (result_pointer->centroid_head != NULL) {

            /* this result has a centroid, so write its details */

            fprintf(stream, "\n");
            Indent(stream, 3);
            fprintf(stream, "$\\mu_{\\mbox{\\scriptsize%s %s}}$&",
                  Identifier_Font, result_pointer->identifier);

            /* write the attribute values for this centroid */

            for (centroid_pointer = result_pointer->centroid_head;
                  centroid_pointer != NULL; centroid_pointer = centroid_pointer->next)
               fprintf(stream, "%s&",
                     centroid_pointer->unknown ? Unknown_Symbol :
                     Nearest_Attribute_Value(centroid_pointer->value) == YES ?
                     Yes_Symbol : No_Symbol);

            if (court_head != NULL)
               fprintf(stream, "&");

            if (facts_head != NULL)
               write_metrics(stream, result_pointer->centroid_metrics,
                     !area_pointer->infinite_weight, area_pointer->correlation_coefficients);

            write_result(stream, count++, first_result_row, facts_head != NULL,
                  result_pointer);
            fprintf(stream, "\\\\");
         }
         fprintf(stream, "\\hline");
      }
   }
   fprintf(stream, "\n");
   Indent(stream, 2);
   fprintf(stream, "\\end{tabular}\n");
   Indent(stream, 1);
   fprintf(stream, "\\end{small}\n\n");
}

static void
write_opening_and_closing(
      file dump_stream,
      area *area_pointer,
      boolean verbose)

/* Writes the opening and closing strings for the area pointed to by
   area_pointer.  Writes each string in full only if verbose is TRUE. */

{
   if (area_pointer->opening != NULL) {
      fprintf(dump_stream, "%s{Opening}\n\n", Subheading);
      Indent(dump_stream, 1);
      fprintf(dump_stream, "\\begin{list}{}{\\leftmargin=0mm}\\item[]\n");
      if (verbose)
         Write(dump_stream, area_pointer->opening, Empty_String, 2, Hang);
      else
         Write(dump_stream, "[Opening.]", Empty_String, 2, Hang);
      Indent(dump_stream, 1);
      fprintf(dump_stream, "\\end{list}\n\n");
   }
   if (area_pointer->closing != NULL) {
      fprintf(dump_stream, "%s{Closing}\n\n", Subheading);
      Indent(dump_stream, 1);
      fprintf(dump_stream, "\\begin{list}{}{\\leftmargin=0mm}\\item[]\n");
      if (verbose)
         Write(dump_stream, area_pointer->closing, Empty_String, 2, Hang);
      else
         Write(dump_stream, "[Closing.]", Empty_String, 2, Hang);
      Indent(dump_stream, 1);
      fprintf(dump_stream, "\\end{list}\n\n");
   }
}

static void
write_result_list(
      file dump_stream,
      result *result_pointer)

/* Writes details of each result in the list of results pointed to by
   result_pointer. */

{
   fprintf(dump_stream, "%s{Results}\n\n", Subheading);
   Indent(dump_stream, 1);

   fprintf(dump_stream, "\\begin{description}\n\n");

   /* while there are still results ... */

   while (result_pointer != NULL) {
      Indent(dump_stream, 2);
      fprintf(dump_stream, "\\item[\\rm{%s %s}:]\n",
            Identifier_Font, result_pointer->identifier);
      Write(dump_stream, result_pointer->string, ".\n", 3, No_Hang);
      result_pointer = result_pointer->next;
   }
   Indent(dump_stream, 1);
   fprintf(dump_stream, "\\end{description}\n\n");
}

static void
write_attribute_list(
      file dump_stream,
      attribute *attribute_pointer)

/* Writes details of each attribute in the list of attributes pointed to by
   attribute_pointer. */

{
   direction_list_element *direction_list_pointer;
   identifier_list_element *identifier_list_pointer;

   fprintf(dump_stream, "%s{Attributes}\n\n", Subheading);
   Indent(dump_stream, 1);

   fprintf(dump_stream, "\\begin{description}\n\n");

   /* while there are still attributes ... */

   while (attribute_pointer != NULL) {

      Indent(dump_stream, 2);
      fprintf(dump_stream, "\\item[\\rm$A_{%u}$:]\n", attribute_pointer->number);

      if (attribute_pointer->external_attribute) {

         /* the attribute is external, so indicate the area to which it is
            linked */

         Indent(dump_stream, 3);
         fprintf(dump_stream, "%s {%s %s} area\n", External_Area_Symbol,
               Identifier_Font, attribute_pointer->details.external.area_identifier);

      } else

         /* the attribute is local, so write the attribute's question */

         Write(dump_stream, attribute_pointer->details.local.question, "?", 3, No_Hang);

      fprintf(dump_stream, "\n");

      Indent(dump_stream, 3);
      fprintf(dump_stream, "\\begin{description}\n\n");

      if (attribute_pointer->yes != NULL) {

         /* the attribute has a YES string, so write it */

         Indent(dump_stream, 4);
         fprintf(dump_stream, "\\item[\\sc yes:]\n");
         Write(dump_stream, attribute_pointer->yes, ".\n", 5, No_Hang);

         if (attribute_pointer->external_attribute) {

            /* the attribute is external, so indicate the association of
               results from the external area with YES values of this
               attribute */

            identifier_list_pointer = attribute_pointer->details.external.yes_identifier_head;
            while (identifier_list_pointer != NULL) {
               if (identifier_list_pointer ==
                     attribute_pointer->details.external.yes_identifier_head) {

                  /* this is the first identifier to write */

                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "%s {%s %s}", External_Result_Symbol,
                        Identifier_Font, identifier_list_pointer->identifier);
               } else {
                  fprintf(dump_stream, "~%s\n", Disjunction_Symbol);
                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "{%s %s}", Identifier_Font,
                        identifier_list_pointer->identifier);
               }
               identifier_list_pointer = identifier_list_pointer->next;
            }
            if (attribute_pointer->details.external.yes_identifier_head != NULL)
               fprintf(dump_stream, "\n\n");
         }

         /* indicate the specified direction for YES values of this attribute */

         direction_list_pointer = attribute_pointer->yes_direction_head;
         while (direction_list_pointer != NULL) {
            if (direction_list_pointer == attribute_pointer->yes_direction_head) {

               /* this is the first identifier to write */

               Indent(dump_stream, 5);
               fprintf(dump_stream, "%s {%s %s}", Specified_Direction_Symbol,
                     Identifier_Font, direction_list_pointer->result->identifier);
            } else {
               fprintf(dump_stream, "~%s\n", Disjunction_Symbol);
               Indent(dump_stream, 5);
               fprintf(dump_stream, "{%s %s}", Identifier_Font,
                     direction_list_pointer->result->identifier);
            }
            direction_list_pointer = direction_list_pointer->next;
         }
         if (attribute_pointer->yes_direction_head != NULL)
            fprintf(dump_stream, "\n\n");
      }
      if (attribute_pointer->no != NULL) {

         /* the attribute has a NO string, so write it */

         Indent(dump_stream, 4);
         fprintf(dump_stream, "\\item[\\sc no:]\n");
         Write(dump_stream, attribute_pointer->no, ".\n", 5, No_Hang);

         if (attribute_pointer->external_attribute) {

            /* the attribute is external, so indicate the association of
               results from the external area with NO values of this
               attribute */

            identifier_list_pointer = attribute_pointer->details.external.no_identifier_head;
            while (identifier_list_pointer != NULL) {
               if (identifier_list_pointer ==
                     attribute_pointer->details.external.no_identifier_head) {

                  /* this is the first identifier to write */

                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "%s {%s %s}", External_Result_Symbol,
                        Identifier_Font, identifier_list_pointer->identifier);
               } else {
                  fprintf(dump_stream, "~%s\n", Disjunction_Symbol);
                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "{%s %s}", Identifier_Font,
                        identifier_list_pointer->identifier);
               }
               identifier_list_pointer = identifier_list_pointer->next;
            }
            if (attribute_pointer->details.external.no_identifier_head != NULL)
               fprintf(dump_stream, "\n\n");
         }

         /* indicate the specified direction for NO values of this attribute */

         direction_list_pointer = attribute_pointer->no_direction_head;
         while (direction_list_pointer != NULL) {
            if (direction_list_pointer == attribute_pointer->no_direction_head) {

               /* this is the first identifier to write */

               Indent(dump_stream, 5);
               fprintf(dump_stream, "%s {%s %s}", Specified_Direction_Symbol,
                     Identifier_Font, direction_list_pointer->result->identifier);
            } else {
               fprintf(dump_stream, "~%s\n", Disjunction_Symbol);
               Indent(dump_stream, 5);
               fprintf(dump_stream, "{%s %s}", Identifier_Font,
                     direction_list_pointer->result->identifier);
            }
            direction_list_pointer = direction_list_pointer->next;
         }
         if (attribute_pointer->no_direction_head != NULL)
            fprintf(dump_stream, "\n\n");
      }
      if (attribute_pointer->unknown != NULL) {

         /* the attribute has an UNKNOWN string, so write it */

         Indent(dump_stream, 4);
         fprintf(dump_stream, "\\item[\\sc unknown:]\n");
         Write(dump_stream, attribute_pointer->unknown, ".\n", 5, No_Hang);

         if (attribute_pointer->external_attribute) {

            /* the attribute is external, so indicate the association of
               results from the external area with UNKNOWN values of this
               attribute */

            identifier_list_pointer =
                  attribute_pointer->details.external.unknown_identifier_head;
            while (identifier_list_pointer != NULL) {
               if (identifier_list_pointer ==
                     attribute_pointer->details.external.unknown_identifier_head) {

                  /* this is the first identifier to write */

                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "%s {%s %s}", External_Result_Symbol,
                        Identifier_Font, identifier_list_pointer->identifier);
               } else {
                  fprintf(dump_stream, "~%s\n", Disjunction_Symbol);
                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "{%s %s}", Identifier_Font,
                        identifier_list_pointer->identifier);
               }
               identifier_list_pointer = identifier_list_pointer->next;
            }
            if (attribute_pointer->details.external.unknown_identifier_head != NULL)
               fprintf(dump_stream, "\n\n");
         }

         /* indicate the specified direction for UNKNOWN values of this
            attribute */

         direction_list_pointer = attribute_pointer->unknown_direction_head;
         while (direction_list_pointer != NULL) {
            if (direction_list_pointer == attribute_pointer->unknown_direction_head) {

               /* this is the first identifier to write */

               Indent(dump_stream, 5);
               fprintf(dump_stream, "%s {%s %s}", Specified_Direction_Symbol,
                     Identifier_Font, direction_list_pointer->result->identifier);
            } else {
               fprintf(dump_stream, "~%s\n", Disjunction_Symbol);
               Indent(dump_stream, 5);
               fprintf(dump_stream, "{%s %s}", Identifier_Font,
                     direction_list_pointer->result->identifier);
            }
            direction_list_pointer = direction_list_pointer->next;
         }

         if (attribute_pointer->unknown_direction_head != NULL)
            fprintf(dump_stream, "\n\n");
      }
      Indent(dump_stream, 3);
      fprintf(dump_stream, "\\end{description}\n\n");

      if (!attribute_pointer->external_attribute &&
            (attribute_pointer->details.local.help != NULL))

         /* the attribute has a help string, so write it */

         Write(dump_stream, attribute_pointer->details.local.help, "\n", 3, No_Hang);

      attribute_pointer = attribute_pointer->next;
   }
   Indent(dump_stream, 1);
   fprintf(dump_stream, "\\end{description}\n\n");
}

extern void
Write_Year_and_Court(
      file stream,
      kase *case_pointer,
      cardinal level)

/* Describes the case pointed to by case_pointer as "a year decision of
   court". */

{
   cardinal hundreds = case_pointer->year / 100;

   Indent(stream, level);
   if ((hundreds == 8) || (hundreds == 11) || (hundreds == 18))
      fprintf(stream, "an");
   else
      fprintf(stream, "a");
   fprintf(stream, " %u decision", case_pointer->year);

   if (case_pointer->court_string != NULL) {
      fprintf(stream, " of\n");
      Indent(stream, level);
      fprintf(stream, "  %s", case_pointer->court_string);
   }
}

static void
write_case_list(
      file dump_stream,
      file log_stream,
      area *area_pointer,
      boolean verbose)

/* Writes details of each case in the area pointed to by area_pointer.
   Summarizes each case in full only if verbose is TRUE. */

{
   result *result_pointer = area_pointer->result_head;
   kase *case_pointer;
   attribute *attribute_pointer;
   matrix_element *matrix_pointer;
   vector_element *vector_pointer;
   string attribute_string;
   char message[Max_Error_Message_Length];

   /* while there are still results ... */

   while (result_pointer != NULL) {

      if ((result_pointer->case_head != NULL) ||
            (result_pointer->ideal_point_head != NULL)) {

         /* this result has a case or an ideal point */

         fprintf(dump_stream, "%s{Cases in which %s}\n\n", Subheading,
               result_pointer->string);
         Indent(dump_stream, 1);
         fprintf(dump_stream, "\\begin{description}\n\n", result_pointer->string);

         /* for every case with this result ... */

         for (case_pointer = result_pointer->case_head; case_pointer != NULL;
               case_pointer = case_pointer->next) {

            Indent(dump_stream, 2);
            fprintf(dump_stream,
                  "\\item[\\rm$C_{%u}$:]\\frenchspacing\n", case_pointer->number);
            Indent(dump_stream, 3);
            fprintf(dump_stream,
                  "{\\it %s\\/} \\nonfrenchspacing\n", case_pointer->name);
            Indent(dump_stream, 3);
            fprintf(dump_stream, "%s\n", case_pointer->citation);

            if (case_pointer->short_name != case_pointer->name) {
               Indent(dump_stream, 3);
               fprintf(dump_stream, "(``\\frenchspacing\n");
               Indent(dump_stream, 3);
               fprintf(dump_stream, "{\\it %s\\/}\\nonfrenchspacing'')\n",
                     case_pointer->short_name);
            }
            fprintf(dump_stream, "\n");

            attribute_pointer = area_pointer->attribute_head;
            matrix_pointer = case_pointer->matrix_head;
            Indent(dump_stream, 3);
            fprintf(dump_stream, "\\begin{description}\n\n");

            /* while there are still attributes ... */

            while (attribute_pointer != NULL) {

               /* write the strings corresponding to each attribute value for
                  the case */

               attribute_string = matrix_pointer->attribute_value == YES ?
                     attribute_pointer->yes : matrix_pointer->attribute_value == NO ?
                     attribute_pointer->no : attribute_pointer->unknown;
               Indent(dump_stream, 4);
               fprintf(dump_stream, "\\item[\\rm$A_{%u}$:]\n",
                     attribute_pointer->number);

               if (attribute_string == NULL) {
                  sprintf(message,
                        "A%u in C%u in %s area has a value "
                        "for which there is no string",
                        attribute_pointer->number, case_pointer->number,
                        area_pointer->identifier);
                  warning(log_stream, message);
                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "%s\n", Null_String);
               } else
                  Write(dump_stream, attribute_string, ".", 5, No_Hang);
               fprintf(dump_stream, "\n");

               matrix_pointer = matrix_pointer->case_next;
               attribute_pointer = attribute_pointer->next;
            }

            /* summarize the case */

            Indent(dump_stream, 3);
            fprintf(dump_stream, "\\end{description}\n\n");
            if (case_pointer->summary != NULL) {
               Indent(dump_stream, 3);
               fprintf(dump_stream, "In \\frenchspacing\n");
               Indent(dump_stream, 3);
               fprintf(dump_stream, "{\\it %s}\\nonfrenchspacing,%%\n",
                     case_pointer->name);
               Indent(dump_stream, 3);
               fprintf(dump_stream, "\\footnote{%s.}\n", case_pointer->citation);
               if (verbose) {
                  Write_Year_and_Court(dump_stream, case_pointer, 3);
                  fprintf(dump_stream, ",\n");
                  if (case_pointer->summary != NULL)
                     Write(dump_stream, case_pointer->summary, Empty_String, 3, Hang);
               } else
                  Write(dump_stream, "[summary].\n", Empty_String, 3, Hang);
               fprintf(dump_stream, "\n");
            }
         }

         if (result_pointer->ideal_point_head != NULL) {

            /* this result has an ideal point, so write its details */

            Indent(dump_stream, 2);
            fprintf(dump_stream, "\\item[$I_{\\mbox{\\scriptsize%s %s}}$]\n",
                  Identifier_Font, result_pointer->identifier);
            Indent(dump_stream, 3);
            fprintf(dump_stream, "(the ideal case in which\n");
            Indent(dump_stream, 3);
            fprintf(dump_stream, "%s):\n\n", result_pointer->string);

            attribute_pointer = area_pointer->attribute_head;
            vector_pointer = result_pointer->ideal_point_head;
            Indent(dump_stream, 3);
            fprintf(dump_stream, "\\begin{description}\n\n");

            /* while there are still attributes ... */

            while (attribute_pointer != NULL) {

               /* write the strings corresponding to each attribute value for
                  the ideal point */

               attribute_string = vector_pointer->attribute_value == YES ?
                     attribute_pointer->yes : vector_pointer->attribute_value == NO ?
                     attribute_pointer->no : attribute_pointer->unknown;
               Indent(dump_stream, 4);
               fprintf(dump_stream, "\\item[\\rm$A_{%u}$:]\n",
                     attribute_pointer->number);
               if (attribute_string == NULL) {
                  sprintf(message,
                        "A%u in ideal point %s in %s area has a value "
                        "for which there is no string",
                        attribute_pointer->number, result_pointer->identifier,
                        area_pointer->identifier);
                  warning(log_stream, message);
                  Indent(dump_stream, 5);
                  fprintf(dump_stream, "%s\n", Null_String);
               } else
                  Write(dump_stream, attribute_string, ".", 5, No_Hang);
               fprintf(dump_stream, "\n");

               vector_pointer = vector_pointer->next;
               attribute_pointer = attribute_pointer->next;
            }
            Indent(dump_stream, 3);
            fprintf(dump_stream, "\\end{description}\n\n");
         }
         Indent(dump_stream, 1);
         fprintf(dump_stream, "\\end{description}\n\n");
      }
      result_pointer = result_pointer->next;
   }
}

extern void
Dump_Specification(
      file dump_stream,
      file log_stream,
      case_law_specification case_law,
      boolean inputable_latex,
      boolean verbose)

/* Writes to dump_stream a formatted version of the specification case_law.
   Writes LaTeX code that can be included in another LaTeX document (i.e. not
   stand-alone code), if inputable_latex is TRUE.  Summarizes cases in full,
   and writes opening and closing strings in full, if verbose is TRUE. */

{
   area *area_pointer;

   fprintf(dump_stream, "%% Dump file\n\n");
   Write_LaTeX_Header(dump_stream, inputable_latex);

   if (case_law.court_head != NULL)
      write_hierarchy_table(dump_stream, case_law.court_head);

   /* for every area ... */

   for (area_pointer = case_law.area_head; area_pointer != NULL;
         area_pointer = area_pointer->next) {

      fprintf(dump_stream, "%s{%s area}\n\n", Heading,
            area_pointer->identifier);
      Write_Matrix(dump_stream, area_pointer, NULL, case_law.court_head, FALSE, 0);
      write_opening_and_closing(dump_stream, area_pointer, verbose);
      write_result_list(dump_stream, area_pointer->result_head);
      write_attribute_list(dump_stream, area_pointer->attribute_head);
      write_case_list(dump_stream, log_stream, area_pointer, verbose);
   }
   Write_LaTeX_Trailer(dump_stream, inputable_latex);
}

Other SHYSTER modules: Shyster, Statutes, Cases, Tokenizer, Parser, Checker, Scales, Adjuster, Consultant, Odometer and Reporter.