/* This is the header file for the Reporter module. It is also included by
the Cases module. */
/* external function */
extern void
Write_Report(
file report_stream,
file log_stream,
area *area_pointer,
vector_element *facts_head,
vector_element *original_facts,
boolean verbose,
boolean hypothetical,
boolean same_result,
cardinal number,
cardinal level);
/* This is the implementation file for the Reporter module. */
#include <stdio.h>
#include "shyster.h"
#include "cases.h"
#include "reporter.h"
#include "dumper.h"
#include "odometer.h"
static void
warning(
file stream,
const string message,
cardinal level)
{
Write_Warning_Message(stream, "Reporter", message, level);
}
static void
list_facts(
file report_stream,
vector_element *vector_pointer,
attribute *attribute_pointer,
cardinal count)
/* Lists the facts pointed to by vector_pointer by writing the appropriate
string (YES, NO, or UNKNOWN) for each attribute. attribute_pointer is the
head of the list of attributes for this area. count is the number of
attributes. */
{
/* while there are still facts to list ... */
while ((attribute_pointer != NULL) && (count != 0)) {
if (count == 1)
Write(report_stream, vector_pointer->attribute_value == YES ?
attribute_pointer->yes : vector_pointer->attribute_value == NO ?
attribute_pointer->no : attribute_pointer->unknown, ".", 1, Hang);
else if (count == 2)
Write(report_stream, vector_pointer->attribute_value == YES ?
attribute_pointer->yes : vector_pointer->attribute_value == NO ?
attribute_pointer->no : attribute_pointer->unknown, "; and", 1, Hang);
else
Write(report_stream, vector_pointer->attribute_value == YES ?
attribute_pointer->yes : vector_pointer->attribute_value == NO ?
attribute_pointer->no : attribute_pointer->unknown, ";", 1, Hang);
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
count--;
}
}
static void
list_equidistant_cases_known_first(
file report_stream,
kase *known_case_pointer,
kase *unknown_case_pointer,
boolean short_names,
boolean and)
/* Lists the case pointed to by known_case_pointer (and any known equidistant
cases), then the case pointed to by unknown_case_pointer (and any unknown
equidistant cases). Writes short case names, if short_names is TRUE.
Writes "and" between the last two cases in the list, if and is TRUE;
writes "or", otherwise. */
{
/* while there are still known cases to list ... */
while (known_case_pointer != NULL) {
/* write the case name with appropriate trailing characters */
fprintf(report_stream, "{\\it %s", short_names ?
known_case_pointer->short_name : known_case_pointer->name);
if ((known_case_pointer->equidistant_known_next == NULL) &&
(unknown_case_pointer == NULL))
/* this is the last case to list */
fprintf(report_stream, "\\/}");
else if (((known_case_pointer->equidistant_known_next != NULL) &&
(known_case_pointer->equidistant_known_next->
equidistant_known_next == NULL) &&
(unknown_case_pointer == NULL)) ||
((known_case_pointer->equidistant_known_next == NULL) &&
(unknown_case_pointer != NULL) &&
(unknown_case_pointer->equidistant_unknown_next == NULL)))
/* this is the penultimate case to list */
fprintf(report_stream, "\\/} %s\n", and ? "and" : "or");
else
fprintf(report_stream, "},\n");
known_case_pointer = known_case_pointer->equidistant_known_next;
}
/* while there are still unknown cases to list ... */
while (unknown_case_pointer != NULL) {
/* write the case name with appropriate trailing characters */
fprintf(report_stream, "{\\it %s", short_names ?
unknown_case_pointer->short_name : unknown_case_pointer->name);
if (unknown_case_pointer->equidistant_unknown_next == NULL)
/* this is the last case to list */
fprintf(report_stream, "\\/}");
else if (unknown_case_pointer->equidistant_unknown_next->
equidistant_unknown_next == NULL)
/* this is the penultimate case to list */
fprintf(report_stream, "\\/} %s\n", and ? "and" : "or");
else
fprintf(report_stream, "},\n");
unknown_case_pointer = unknown_case_pointer->equidistant_unknown_next;
}
}
static void
list_equidistant_cases_unknown_first(
file report_stream,
kase *known_case_pointer,
kase *unknown_case_pointer,
boolean short_names,
boolean and)
/* Lists the case pointed to by unknown_case_pointer (and any unknown
equidistant cases), then the case pointed to by known_case_pointer (and
any known equidistant cases). Writes short case names, if short_names is
TRUE. Writes "and" between the last two cases in the list, if and is
TRUE; writes "or", otherwise. */
{
/* while there are still unknown cases to list ... */
while (unknown_case_pointer != NULL) {
/* write the case name with appropriate trailing characters */
fprintf(report_stream, "{\\it %s", short_names ?
unknown_case_pointer->short_name : unknown_case_pointer->name);
if ((unknown_case_pointer->equidistant_unknown_next == NULL) &&
(known_case_pointer == NULL))
/* this is the last case to list */
fprintf(report_stream, "\\/}");
else if (((unknown_case_pointer->equidistant_unknown_next != NULL) &&
(unknown_case_pointer->equidistant_unknown_next->
equidistant_unknown_next == NULL) &&
(known_case_pointer == NULL)) ||
((unknown_case_pointer->equidistant_unknown_next == NULL) &&
(known_case_pointer != NULL) &&
(known_case_pointer->equidistant_known_next == NULL)))
/* this is the penultimate case to list */
fprintf(report_stream, "\\/} %s\n", and ? "and" : "or");
else
fprintf(report_stream, "},\n");
unknown_case_pointer = unknown_case_pointer->equidistant_unknown_next;
}
/* while there are still known cases to list ... */
while (known_case_pointer != NULL) {
/* write the case name with appropriate trailing characters */
fprintf(report_stream, "{\\it %s", short_names ?
known_case_pointer->short_name : known_case_pointer->name);
if (known_case_pointer->equidistant_known_next == NULL)
/* this is the last case to list */
fprintf(report_stream, "\\/}");
else if (known_case_pointer->equidistant_known_next->equidistant_known_next == NULL)
/* this is the penultimate case to list */
fprintf(report_stream, "\\/} %s\n", and ? "and" : "or");
else
fprintf(report_stream, "},\n");
known_case_pointer = known_case_pointer->equidistant_known_next;
}
}
static void
list_equidistant_cases(
file report_stream,
kase *known_case_pointer,
kase *unknown_case_pointer,
boolean unknown_first,
boolean short_names,
boolean and)
/* Lists the case pointed to by known_case_pointer (and any known equidistant
cases) and the case pointed to by unknown_case_pointer (and any unknown
equidistant cases). Lists the unknown cases first, if unknown_first is
TRUE. Writes short case names, if short_names is TRUE. Writes "and"
between the last two cases in the list, if and is TRUE; writes "or",
otherwise. */
{
if (unknown_first)
list_equidistant_cases_unknown_first(report_stream, known_case_pointer,
unknown_case_pointer, short_names, and);
else
list_equidistant_cases_known_first(report_stream, known_case_pointer,
unknown_case_pointer, short_names, and);
}
static void
state_opinion(
file report_stream,
result *result_pointer,
kase *known_case_pointer,
kase *unknown_case_pointer,
boolean unknown_first)
/* States its opinion: that, following the cases pointed to by
known_case_pointer and unknown_case_pointer, the result will be that which
is pointed to by result_pointer. Lists the unknown cases first, if
unknown_first is TRUE. */
{
fprintf(report_stream, "---following \\frenchspacing\n");
list_equidistant_cases(report_stream, known_case_pointer, unknown_case_pointer,
unknown_first, FALSE, TRUE);
fprintf(report_stream, "\\nonfrenchspacing---%%\n"
"%s.\n\n", result_pointer->string);
}
static void
state_counter_opinion(
file report_stream,
result *result_pointer,
kase *known_case_pointer,
kase *unknown_case_pointer,
boolean unknown_first)
/* States a counter opinion: that, if the cases pointed to by
known_case_pointer and unknown_case_pointer are followed, the result will
be that which is pointed to by result_pointer. Lists the unknown cases
first, if unknown_first is TRUE. */
{
boolean plural = FALSE;
fprintf(report_stream, "%s If \\frenchspacing\n", Skip);
list_equidistant_cases(report_stream, known_case_pointer, unknown_case_pointer,
unknown_first, FALSE, FALSE);
if (known_case_pointer != NULL) {
if (unknown_case_pointer != NULL)
plural = TRUE;
else if (known_case_pointer->equidistant_known_next != NULL)
plural = TRUE;
} else if (unknown_case_pointer != NULL)
if (unknown_case_pointer->equidistant_unknown_next != NULL)
plural = TRUE;
fprintf(report_stream, " \\nonfrenchspacing\n"
"%s followed then %s.\n\n",
plural ? "are" : "is", result_pointer->string);
}
static cardinal
number_of_similarities(
matrix_element *matrix_pointer,
vector_element *vector_pointer)
/* Returns the number of similarities in attribute value pairs between a
leading case and the instant case (their attribute values are pointed to
by matrix_pointer and vector_pointer, respectively), where both cases have
known attribute values. */
{
cardinal count = 0;
/* while there are still attribute values to compare ... */
while (matrix_pointer != NULL) {
if ((matrix_pointer->attribute_value != UNKNOWN) &&
(matrix_pointer->attribute_value == vector_pointer->attribute_value))
/* the corresponding attribute values are identical and known */
count++;
matrix_pointer = matrix_pointer->case_next;
vector_pointer = vector_pointer->next;
}
return count;
}
static void
list_similarities(
file report_stream,
matrix_element *matrix_pointer,
vector_element *vector_pointer,
attribute *attribute_pointer,
cardinal count)
/* Lists the similarities between a leading case and the instant case (their
attribute values are pointed to by matrix_pointer and vector_pointer,
respectively), where both cases have known attribute values, by writing
the appropriate string (YES or NO) for the similar attributes.
*attribute_pointer is the head of the list of attributes for this area.
count is the number of similarities. */
{
/* while there are still attribute values to compare, and not all of the
similarities have been listed ... */
while ((attribute_pointer != NULL) && (count != 0)) {
if ((matrix_pointer->attribute_value != UNKNOWN) &&
(matrix_pointer->attribute_value == vector_pointer->attribute_value)) {
/* the corresponding attribute values are identical and known, so
write the relevant string with appropriate trailing characters */
if (count == 1)
Write(report_stream, matrix_pointer->attribute_value == YES ?
attribute_pointer->yes : attribute_pointer->no, ".\n", 1, Hang);
else if (count == 2)
Write(report_stream, matrix_pointer->attribute_value == YES ?
attribute_pointer->yes : attribute_pointer->no, "; and", 1, Hang);
else
Write(report_stream, matrix_pointer->attribute_value == YES ?
attribute_pointer->yes : attribute_pointer->no, ";", 1, Hang);
count--;
}
matrix_pointer = matrix_pointer->case_next;
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
}
static cardinal
number_of_known_differences(
matrix_element *matrix_pointer,
vector_element *vector_pointer)
/* Returns the number of differences in attribute value pairs between a
leading case and the instant case (their attribute values are pointed to
by matrix_pointer and vector_pointer, respectively), where the leading
case has a known attribute value. */
{
cardinal count = 0;
/* while there are still attribute values to compare ... */
while (matrix_pointer != NULL) {
if ((matrix_pointer->attribute_value != UNKNOWN) &&
(matrix_pointer->attribute_value != vector_pointer->attribute_value))
/* the corresponding attribute values are different, and the leading
case's value is known, so increment the count */
count++;
matrix_pointer = matrix_pointer->case_next;
vector_pointer = vector_pointer->next;
}
return count;
}
static void
list_known_differences(
file report_stream,
matrix_element *matrix_pointer,
vector_element *vector_pointer,
attribute *attribute_pointer,
cardinal count)
/* Lists the differences between a leading case and the instant case (their
attribute values are pointed to by matrix_pointer and vector_pointer,
respectively), where the leading case has a known attribute value, by
writing the appropriate string (YES or NO) for the different attributes.
*attribute_pointer is the head of the list of attributes for this area.
count is the number of differences. */
{
/* while there are still attribute values to compare, and not all of the
differences have been listed ... */
while ((attribute_pointer != NULL) && (count != 0)) {
if ((matrix_pointer->attribute_value != UNKNOWN) &&
(matrix_pointer->attribute_value != vector_pointer->attribute_value)) {
/* the corresponding attribute values are different, and the leading
case's value is known, so write the relevant string with
appropriate trailing characters */
if (count == 1)
Write(report_stream, matrix_pointer->attribute_value == YES ?
attribute_pointer->yes : attribute_pointer->no, ".", 1, Hang);
else if (count == 2)
Write(report_stream, matrix_pointer->attribute_value == YES ?
attribute_pointer->yes : attribute_pointer->no, "; and", 1, Hang);
else
Write(report_stream, matrix_pointer->attribute_value == YES ?
attribute_pointer->yes : attribute_pointer->no, ";", 1, Hang);
count--;
}
matrix_pointer = matrix_pointer->case_next;
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
}
static cardinal
number_of_unknowns(
matrix_element *matrix_pointer)
/* Returns the number of UNKNOWNs in the leading case whose attribute values
are pointed to by matrix_pointer. */
{
cardinal count = 0;
/* while there are still attribute values to check ... */
while (matrix_pointer != NULL) {
if (matrix_pointer->attribute_value == UNKNOWN)
count++;
matrix_pointer = matrix_pointer->case_next;
}
return count;
}
static void
list_unknowns(
file report_stream,
matrix_element *matrix_pointer,
attribute *attribute_pointer,
cardinal count)
/* Lists the UNKNOWN string for each unknown attribute in the leading case
whose attribute values are pointed to by matrix_pointer. attribute_pointer
is the head of the list of attributes for this area. count is the number
of UNKNOWNs. */
{
/* while there are still attribute values to compare, and not all of the
UNKNOWNs have been listed ... */
while ((attribute_pointer != NULL) && (count != 0)) {
if (matrix_pointer->attribute_value == UNKNOWN) {
if (count == 1)
Write(report_stream, attribute_pointer->unknown, ".", 1, Hang);
else if (count == 2)
Write(report_stream, attribute_pointer->unknown, "; and", 1, Hang);
else
Write(report_stream, attribute_pointer->unknown, ";", 1, Hang);
count--;
}
matrix_pointer = matrix_pointer->case_next;
attribute_pointer = attribute_pointer->next;
}
}
static cardinal
number_of_differences(
vector_element *vector_pointer_X,
vector_element *vector_pointer_Y)
/* Returns the number of differences in attribute value pairs between two
fact vectors. */
{
cardinal count = 0;
/* while there are still attribute values to compare ... */
while (vector_pointer_X != NULL) {
if (vector_pointer_X->attribute_value != vector_pointer_Y->attribute_value)
count++;
vector_pointer_X = vector_pointer_X->next;
vector_pointer_Y = vector_pointer_Y->next;
}
return count;
}
static void
list_new_differences(
file report_stream,
vector_element *vector_pointer,
vector_element *original_vector_pointer,
attribute *attribute_pointer,
cardinal count)
/* Lists the differences between an instantiation or hypothetical and the
uninstantiated and unhypothesized instant case (their attribute values are
pointed to by vector_pointer and original_vector_pointer, respectively),
by writing the appropriate string (YES, NO or UNKNOWN) for the different
attributes. *attribute_pointer is the head of the list of attributes for
this area. count is the number of differences. */
{
/* while there are still attribute values to compare, and not all of the
differences have been listed ... */
while ((attribute_pointer != NULL) && (count != 0)) {
if (vector_pointer->attribute_value != original_vector_pointer->attribute_value) {
/* the corresponding attribute values are different, so write the
relevant string with appropriate trailing characters */
if (count == 1)
Write(report_stream, vector_pointer->attribute_value == YES ?
attribute_pointer->yes : vector_pointer->attribute_value == NO ?
attribute_pointer->no : attribute_pointer->unknown, ".", 1, Hang);
else if (count == 2)
Write(report_stream, vector_pointer->attribute_value == YES ?
attribute_pointer->yes : vector_pointer->attribute_value == NO ?
attribute_pointer->no : attribute_pointer->unknown, "; and", 1, Hang);
else
Write(report_stream, vector_pointer->attribute_value == YES ?
attribute_pointer->yes : vector_pointer->attribute_value == NO ?
attribute_pointer->no : attribute_pointer->unknown, ";", 1, Hang);
count--;
}
vector_pointer = vector_pointer->next;
original_vector_pointer = original_vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
}
static void
summarize_case(
file report_stream,
kase *case_pointer,
boolean written_linking_paragraph,
boolean verbose)
/* Writes the name of the case pointed to by case_pointer with its citation
in a footnote. Summarizes the case, if verbose is TRUE. If
written_linking_paragraph is TRUE, a brief paragraph was just written
linking the previous case with this case (the two cases are equidistant). */
{
if (!case_pointer->summarized) {
/* the case has not been summarized yet */
if (written_linking_paragraph)
/* the case's citation has already been footnoted in the linking
paragraph */
fprintf(report_stream, "In \\frenchspacing\n"
"{\\it %s}\\nonfrenchspacing,\n",
case_pointer->short_name);
else {
fprintf(report_stream, "In \\frenchspacing\n"
"{\\it %s}\\nonfrenchspacing,%%\n"
"\\footnote{%s.}\n",
case_pointer->name, case_pointer->citation);
Write_Year_and_Court(report_stream, case_pointer, 1);
fprintf(report_stream, ",\n");
}
if (verbose) {
if (case_pointer->summary != NULL) {
Write(report_stream, case_pointer->summary, "\n", 1, Hang);
case_pointer->summarized = TRUE;
} else
fprintf(report_stream, "\n");
} else
Write(report_stream, "[summary].\n", Empty_String, 1, Hang);
} else
/* the case has already been summarized */
fprintf(report_stream, "Details of \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"are summarized above.\n",
case_pointer->short_name);
}
static void
list_similarities_and_differences(
file report_stream,
kase *case_pointer,
attribute *attribute_head,
vector_element *facts_head,
string instant_case_type,
boolean neighbour,
boolean written_linking_paragraph,
boolean unknown_list_to_follow,
boolean verbose)
/* Cites the case pointed to by case_pointer, and lists the similarities
between that case and the instant case (whose attribute values are pointed
to by facts_head). Summarizes the case, if verbose is TRUE.
instant_case_type is either "instant", "instantiated" or "hypothetical".
If neighbour is TRUE, this case is a nearest neighbour; otherwise, it is a
nearest other. If written_linking_paragraph is TRUE, a brief paragraph
was just written linking the previous case with this case (the two cases
are equidistant). If unknown_list_to_follow is TRUE, an invocation of
list_unknowns() will immediately follow this invocation of
list_similarities_and_differences(). */
{
cardinal count;
if (!written_linking_paragraph)
/* a linking paragraph has not just been written */
fprintf(report_stream, "%s ", Skip);
if (case_pointer->summary != NULL)
/* this case has a summary, so write the case name (with its citation
in a footnote) and the summary */
summarize_case(report_stream, case_pointer, written_linking_paragraph, verbose);
if (Is_Zero_Subdistance(case_pointer->metrics.distance.known))
/* there is no known distance between this case and the instant case */
if (Is_Zero_Subdistance(case_pointer->metrics.distance.unknown)) {
/* there is no unknown distance between this case and the instant
case */
fprintf(report_stream,
"The %s case is on all fours with \\frenchspacing\n",
instant_case_type);
if (case_pointer->summary != NULL)
fprintf(report_stream,
"{\\it %s}\\null\\nonfrenchspacing.\n",
case_pointer->short_name);
else
/* the case has no summary, so its citation has not yet been
footnoted */
fprintf(report_stream,
"{\\it %s}\\null\\nonfrenchspacing.%%\n"
"\\footnote{%s.}\n",
case_pointer->name, case_pointer->citation);
} else {
/* there is some unknown distance between this case and the instant
case */
fprintf(report_stream, "The %s case {\\it may\\/} be "
"on all fours with \\frenchspacing\n", instant_case_type);
if (case_pointer->summary != NULL)
fprintf(report_stream, "{\\it %s\\/}\\nonfrenchspacing",
case_pointer->short_name);
else
/* the case has no summary, so its citation has not yet been
footnoted */
fprintf(report_stream, "{\\it %s\\/}\\nonfrenchspacing%%\n"
"\\footnote{%s.}",
case_pointer->name, case_pointer->citation);
if (unknown_list_to_follow)
/* a list of unknown differences follows immediately */
fprintf(report_stream, "---but\n");
else
/* a statement that this case would have been followed (instead
of the nearest neighbour) follows - and a list of unknown
differences follows that */
fprintf(report_stream, " and");
} else {
/* there is some known and/or unknown distance between this case and
the instant case */
count = number_of_similarities(case_pointer->matrix_head, facts_head);
if (count != 0) {
fprintf(report_stream, "There ");
if (neighbour)
/* characterize the similarities as "extremely significant"
(one), "very significant" (two), or just "significant" (three
or more) */
switch (count) {
case 1:
fprintf(report_stream,
"is one extremely significant similarity\n");
break;
case 2:
fprintf(report_stream,
"are two very significant similarities\n");
break;
default:
fprintf(report_stream,
"are several significant similarities\n");
break;
}
else
switch (count) {
case 1:
fprintf(report_stream,
"is one similarity\n");
break;
case 2:
fprintf(report_stream,
"are two similarities\n");
break;
default:
fprintf(report_stream,
"are several similarities\n");
break;
}
fprintf(report_stream, "between the %s case and \\frenchspacing\n",
instant_case_type);
if (case_pointer->summary != NULL)
fprintf(report_stream, "{\\it %s\\/}\\null\\nonfrenchspacing:\n",
case_pointer->short_name);
else
/* the case has no summary, so its citation has not yet been
footnoted */
fprintf(report_stream, "{\\it %s\\/}\\null\\nonfrenchspacing:%%\n"
"\\footnote{%s.}\n",
case_pointer->name, case_pointer->citation);
/* list the similarities between this case and the instant case */
list_similarities(report_stream, case_pointer->matrix_head, facts_head,
attribute_head, count);
}
count = number_of_known_differences(case_pointer->matrix_head, facts_head);
if (neighbour)
fprintf(report_stream,
"However, the %s case is not on all fours "
"with \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.\n",
instant_case_type, case_pointer->short_name);
else {
if (count != 0) {
/* characterize the differences as "extremely significant" (one),
"very significant" (two), or just "significant" (three or
more) */
fprintf(report_stream, "However, there ");
switch (count) {
case 1:
fprintf(report_stream,
"is one extremely significant difference\n");
break;
case 2:
fprintf(report_stream,
"are two very significant differences\n");
break;
default:
fprintf(report_stream,
"are several significant differences\n");
break;
}
fprintf(report_stream, "between the %s case and \\frenchspacing\n",
instant_case_type);
fprintf(report_stream,
"{\\it %s}\\null\\nonfrenchspacing.\n",
case_pointer->short_name);
}
}
fprintf(report_stream, "In that case\n");
/* list the differences between this case and the instant case */
list_known_differences(report_stream, case_pointer->matrix_head, facts_head,
attribute_head, count);
}
}
static boolean
has_less_known_distance(
kase *case_pointer_X,
kase *case_pointer_Y)
/* Returns TRUE, iff the case pointed to by case_pointer_X has less known
distance than does that pointed to by case_pointer_Y. */
{
return ((case_pointer_X != NULL) && (case_pointer_Y != NULL) &&
((case_pointer_X->metrics.distance.known.infinite <
case_pointer_Y->metrics.distance.known.infinite) ||
((case_pointer_X->metrics.distance.known.infinite ==
case_pointer_Y->metrics.distance.known.infinite) &&
Is_Less(case_pointer_X->metrics.distance.known.finite,
case_pointer_Y->metrics.distance.known.finite,
Distance_Precision))));
}
static void
state_confidence(
file report_stream,
string short_name)
/* States its confidence that the case called short_name should still be
followed. */
{
fprintf(report_stream, "Nevertheless, I believe that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"should be followed.\n\n", short_name);
}
static boolean
write_number_as_word(
file report_stream,
cardinal number)
/* Writes number: as a word, if number <= 10; as a number, otherwise.
Returns TRUE, iff number is not 1: i.e. if the noun to follow should be
plural. */
{
switch (number) {
case 1:
fprintf(report_stream, "one");
break;
case 2:
fprintf(report_stream, "two");
break;
case 3:
fprintf(report_stream, "three");
break;
case 4:
fprintf(report_stream, "four");
break;
case 5:
fprintf(report_stream, "five");
break;
case 6:
fprintf(report_stream, "six");
break;
case 7:
fprintf(report_stream, "seven");
break;
case 8:
fprintf(report_stream, "eight");
break;
case 9:
fprintf(report_stream, "nine");
break;
case 10:
fprintf(report_stream, "ten");
break;
default:
fprintf(report_stream, "%u", number);
break;
}
return (number != 1);
}
static void
state_intransigence(
file report_stream,
kase *nearest_neighbour,
kase *nearest_other)
/* Restates its opinion that the case pointed to by nearest_neighbour should
be followed, and compares the relative importance of the courts that
decided that case and the case pointed to by nearest_other. */
{
if ((nearest_neighbour->court_string == NULL) || (nearest_other->court_string == NULL))
/* the nearest neighbour or the nearest other has no court, so make no
comment about each case's relative importance */
fprintf(report_stream, "\nConsequently, ");
else if (nearest_neighbour->court_rank < nearest_other->court_rank)
/* the nearest neighbour was decided by a more important court than was
the nearest other */
fprintf(report_stream, "Note%s that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is only a decision of\n"
"%s\n"
"and not as good authority as a case decided by\n"
"%s%%\n"
"---like \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.\n\n"
"Consequently, ",
Is_Zero_Subdistance(nearest_other->metrics.distance.known) &&
Is_Zero_Subdistance(nearest_other->metrics.distance.unknown) ?
", however," : " also",
nearest_other->short_name, nearest_other->court_string,
nearest_neighbour->court_string, nearest_neighbour->short_name);
else if (nearest_neighbour->court_rank > nearest_other->court_rank)
/* the nearest other was decided by a more important court than was the
nearest neighbour */
fprintf(report_stream, "\nDespite the fact that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is a decision of\n"
"%s\n"
"(and better authority than a case decided by\n"
"%s%%\n"
"---like \\frenchspacing\n"
"{\\it %s\\/}\\nonfrenchspacing),\n",
nearest_other->short_name, nearest_other->court_string,
nearest_neighbour->court_string, nearest_neighbour->short_name);
else if (nearest_neighbour->court_string == nearest_other->court_string)
/* the nearest neighbour and the nearest other were decided by the same
court */
fprintf(report_stream, "\nDespite the fact that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"and \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"are both decisions of\n"
"%s,\n", nearest_other->short_name,
nearest_neighbour->short_name, nearest_other->court_string);
else
/* the nearest neighbour and the nearest other were decided by
different courts of the same rank */
fprintf(report_stream, "\nDespite the fact that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is a decision of\n"
"%s\n"
"(and as good authority as a case decided by\n"
"%s%%\n"
"---like \\frenchspacing\n"
"{\\it %s\\/}\\nonfrenchspacing),\n",
nearest_other->short_name, nearest_other->court_string,
nearest_neighbour->court_string, nearest_neighbour->short_name);
fprintf(report_stream, "there is nothing in \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"to warrant any change in my conclusion.\n\n",
nearest_other->short_name);
}
static boolean
write_linking_paragraph(
file report_stream,
kase *previous_case_pointer,
kase *next_case_pointer)
/* Writes a brief paragraph linking the previous case with the next case (the
two cases are equidistant), if there is a next case. Puts the two cases
into context (i.e. explains which is more important and why). Returns
TRUE, if a paragraph is written, which means that the next paragraph
should not include year and court information - information included in
this linking paragraph. */
{
if ((next_case_pointer != NULL) &&
(previous_case_pointer->court_string != NULL) &&
(next_case_pointer->court_string != NULL)) {
/* the previous case and the next case are equidistant and both have a
court string, so write a linking paragraph */
fprintf(report_stream, "%s In %u,\n", Skip, next_case_pointer->year);
if (previous_case_pointer->court_string == next_case_pointer->court_string) {
/* the previous case and the next case were decided by the same
court */
if (previous_case_pointer->year == next_case_pointer->year)
/* the previous case and the next case were decided in the same
year */
fprintf(report_stream, "the same year in which \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"was decided,\n",
previous_case_pointer->short_name);
fprintf(report_stream, "%s\n"
"also decided \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.%%\n"
"\\footnote{%s.}\n",
next_case_pointer->court_string, next_case_pointer->name,
next_case_pointer->citation);
if (previous_case_pointer->year != next_case_pointer->year) {
/* the previous case and the next case were decided in a
different year; the previous case must be more recent than the
next case (otherwise the next case would be earlier in the
list than the previous case) */
fprintf(report_stream, "(Note, however, that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is ", previous_case_pointer->short_name);
if (write_number_as_word(report_stream,
previous_case_pointer->year - next_case_pointer->year))
fprintf(report_stream, " years");
else
fprintf(report_stream, " year");
fprintf(report_stream, " more recent than \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.)\n",
next_case_pointer->short_name);
}
} else if (previous_case_pointer->court_rank == next_case_pointer->court_rank) {
/* the previous case and the next case were decided by different
courts of the same rank */
if (previous_case_pointer->year == next_case_pointer->year)
/* the previous case and the next case were decided in the same
year */
fprintf(report_stream, "the same year in which \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"was decided by\n"
"%s, ",
previous_case_pointer->short_name,
previous_case_pointer->court_string);
fprintf(report_stream, "\\frenchspacing\n"
"{\\it %s\\/}\\nonfrenchspacing%%\n"
"\\footnote{%s.}\n"
"was decided by\n"
"%s.\n"
"(A case decided by\n"
"%s\n"
"is as good authority as a case decided by\n"
"%s", next_case_pointer->name, next_case_pointer->citation,
next_case_pointer->court_string, next_case_pointer->court_string,
previous_case_pointer->court_string);
if (previous_case_pointer->year == next_case_pointer->year)
/* the previous case and the next case were decided in the same
year */
fprintf(report_stream, ".)\n");
else {
/* the previous case and the next case were decided in a
different year; the previous case must be more recent than the
next case (otherwise the next case would be earlier in the
list than the previous case) */
fprintf(report_stream, "%%\n"
"---like \\frenchspacing\n"
"{\\it %s\\/}\\nonfrenchspacing;\n"
"note, however, that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is ",
previous_case_pointer->short_name,
previous_case_pointer->short_name);
if (write_number_as_word(report_stream,
previous_case_pointer->year - next_case_pointer->year))
fprintf(report_stream, " years");
else
fprintf(report_stream, " year");
fprintf(report_stream, " more recent than \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.)\n",
next_case_pointer->short_name);
}
} else {
/* the previous case and the next case were decided by different
courts of different ranks; the previous case must be more
important than the next case, otherwise the next case would be
earlier in the list than the previous case) */
if (previous_case_pointer->year == next_case_pointer->year)
/* the previous case and the next case were decided in the same
year */
fprintf(report_stream, "the same year in which \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"was decided by\n"
"%s, ",
previous_case_pointer->short_name,
previous_case_pointer->court_string);
fprintf(report_stream, "\\frenchspacing\n"
"{\\it %s\\/}\\nonfrenchspacing%%\n"
"\\footnote{%s.}\n"
"was decided by\n"
"%s.\n"
"(A case decided by\n"
"%s\n"
"is not as good authority as a case decided by\n"
"%s", next_case_pointer->name, next_case_pointer->citation,
next_case_pointer->court_string, next_case_pointer->court_string,
previous_case_pointer->court_string);
if (previous_case_pointer->year == next_case_pointer->year)
/* the previous case and the next case were decided in the same
year */
fprintf(report_stream, ".)\n");
else
fprintf(report_stream, "%%\n"
"---like \\frenchspacing\n"
"{\\it %s\\/}\\nonfrenchspacing;\n",
previous_case_pointer->short_name);
if (previous_case_pointer->year > next_case_pointer->year) {
/* the previous case is more recent than the next case */
fprintf(report_stream, "furthermore \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is ", next_case_pointer->short_name);
if (write_number_as_word(report_stream,
previous_case_pointer->year - next_case_pointer->year))
fprintf(report_stream, " years");
else
fprintf(report_stream, " year");
fprintf(report_stream, " older than \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.)\n",
previous_case_pointer->short_name);
} else if (previous_case_pointer->year < next_case_pointer->year) {
/* the next case is more recent than the previous case */
fprintf(report_stream, "though \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"is ", next_case_pointer->short_name);
if (write_number_as_word(report_stream,
next_case_pointer->year - previous_case_pointer->year))
fprintf(report_stream, " years");
else
fprintf(report_stream, " year");
fprintf(report_stream, " more recent than \\frenchspacing\n"
"{\\it %s}\\null\\nonfrenchspacing.)\n",
previous_case_pointer->short_name);
}
}
fprintf(report_stream, "\n");
return TRUE;
} else
/* no linking paragraph has been written */
return FALSE;
}
static void
handle_near_unknown(
file report_stream,
result *result_pointer,
result *nearest_result_pointer,
kase *nearest_neighbour,
boolean nearest_neighbour_is_known,
area *area_pointer,
vector_element *facts_head,
string instant_case_type,
boolean neighbour,
boolean verbose)
/* Argues that the nearest unknown case of the result pointed to by
result_pointer would have been followed but for its unknown distance. */
{
kase *case_pointer;
boolean written_linking_paragraph = FALSE;
if (result_pointer == nearest_result_pointer) {
fprintf(report_stream, "%s\\frenchspacing\n", Skip);
list_equidistant_cases(report_stream, NULL, result_pointer->nearest_unknown_case,
FALSE, FALSE, TRUE);
fprintf(report_stream, " \\nonfrenchspacing\n"
"%s in which %s.\n\n",
result_pointer->nearest_unknown_case->equidistant_unknown_next ==
NULL ? "is another case" : "are other cases", result_pointer->string);
}
/* for each nearest unknown case ... */
for (case_pointer = result_pointer->nearest_unknown_case; case_pointer != NULL;
case_pointer = case_pointer->equidistant_unknown_next) {
list_similarities_and_differences(report_stream, case_pointer,
area_pointer->attribute_head, facts_head, instant_case_type, neighbour,
written_linking_paragraph, FALSE, verbose);
fprintf(report_stream, "\n"
"I would have suggested that \\frenchspacing\n"
"{\\it %s\\/} \\nonfrenchspacing\n"
"be followed (instead of \\frenchspacing\n",
case_pointer->short_name);
if (nearest_neighbour_is_known)
list_equidistant_cases(report_stream, nearest_neighbour, NULL,
FALSE, TRUE, TRUE);
else
list_equidistant_cases(report_stream, NULL, nearest_neighbour,
FALSE, TRUE, TRUE);
fprintf(report_stream, "\\nonfrenchspacing)\n"
"except that\n");
list_unknowns(report_stream, case_pointer->matrix_head,
area_pointer->attribute_head, number_of_unknowns(case_pointer->matrix_head));
if (!neighbour)
state_intransigence(report_stream, nearest_neighbour, case_pointer);
written_linking_paragraph = write_linking_paragraph(report_stream, case_pointer,
case_pointer->equidistant_unknown_next);
}
}
static void
handle_nearest_known(
file report_stream,
result *result_pointer,
result *nearest_result_pointer,
kase *nearest_neighbour,
area *area_pointer,
vector_element *facts_head,
string instant_case_type,
boolean verbose)
/* Argues that the result should be same as that of the nearest known
neighbour. Uses the nearest unknown neighbour too, if (but for its
unknown distance) it would be the nearest neighbour. */
{
kase *case_pointer;
boolean written_linking_paragraph = FALSE;
if (has_less_known_distance(result_pointer->nearest_unknown_case,
result_pointer->nearest_known_case))
/* if not for its unknown distance, the nearest unknown neighbour would
be the nearest neighbour, so base the argument on the nearest known
and the nearest unknown neighbours */
state_opinion(report_stream, result_pointer,
result_pointer->nearest_known_case,
result_pointer->nearest_unknown_case, FALSE);
else
/* base the argument only on the nearest known neighbours */
state_opinion(report_stream, result_pointer,
result_pointer->nearest_known_case, NULL, FALSE);
/* for each nearest known neighbour ... */
for (case_pointer = result_pointer->nearest_known_case; case_pointer != NULL;
case_pointer = case_pointer->equidistant_known_next) {
list_similarities_and_differences(report_stream, case_pointer,
area_pointer->attribute_head, facts_head, instant_case_type, TRUE,
written_linking_paragraph, FALSE, verbose);
fprintf(report_stream, "\n");
if (!Is_Zero_Distance(case_pointer->metrics.distance))
/* the instant case is not on all fours with the case */
state_confidence(report_stream, case_pointer->short_name);
written_linking_paragraph = write_linking_paragraph(report_stream, case_pointer,
case_pointer->equidistant_known_next);
}
if (has_less_known_distance(result_pointer->nearest_unknown_case,
result_pointer->nearest_known_case)) {
/* if not for its unknown distance, the nearest unknown neighbour would
be the nearest neighbour */
handle_near_unknown(report_stream, result_pointer, nearest_result_pointer,
nearest_neighbour, TRUE, area_pointer,
facts_head, instant_case_type, TRUE, verbose);
fprintf(report_stream, "\n");
}
}
static void
handle_nearest_unknown(
file report_stream,
result *result_pointer,
area *area_pointer,
vector_element *facts_head,
string instant_case_type,
boolean verbose)
/* Argues that the result should be same as that in the nearest unknown
neighbour. Uses the nearest known neighbour too. */
{
kase *case_pointer;
boolean written_linking_paragraph = FALSE;
cardinal count;
/* base the argument on the nearest unknown and the nearest known
neighbours */
state_opinion(report_stream, result_pointer,
result_pointer->nearest_known_case,
result_pointer->nearest_unknown_case, TRUE);
/* for every nearest unknown neighbour ... */
for (case_pointer = result_pointer->nearest_unknown_case; case_pointer != NULL;
case_pointer = case_pointer->equidistant_unknown_next) {
list_similarities_and_differences(report_stream, case_pointer,
area_pointer->attribute_head, facts_head, instant_case_type, TRUE,
written_linking_paragraph, TRUE, verbose);
count = number_of_unknowns(case_pointer->matrix_head);
if (count != 0) {
fprintf(report_stream, "Furthermore, \n");
list_unknowns(report_stream, case_pointer->matrix_head,
area_pointer->attribute_head, count);
}
fprintf(report_stream, "\n");
state_confidence(report_stream, case_pointer->short_name);
written_linking_paragraph = write_linking_paragraph(report_stream, case_pointer,
case_pointer->equidistant_unknown_next);
}
/* for every nearest known neighbour ... */
written_linking_paragraph = FALSE;
for (case_pointer = result_pointer->nearest_known_case; case_pointer != NULL;
case_pointer = case_pointer->equidistant_known_next) {
list_similarities_and_differences(report_stream, case_pointer,
area_pointer->attribute_head, facts_head, instant_case_type, TRUE,
written_linking_paragraph, FALSE, verbose);
fprintf(report_stream, "\n");
if (!Is_Zero_Distance(case_pointer->metrics.distance))
state_confidence(report_stream, case_pointer->short_name);
written_linking_paragraph = write_linking_paragraph(report_stream, case_pointer,
case_pointer->equidistant_known_next);
}
}
static void
handle_nearest_others(
file report_stream,
result *result_pointer,
result *nearest_result_pointer,
kase *nearest_other,
boolean nearest_other_is_known,
area *area_pointer,
vector_element *facts_head,
string instant_case_type,
boolean verbose)
/* Make a counter argument that the result should be same as that of the
nearest known other. Uses this result's nearest unknown other too, if
(but for its unknown distance) it would be the nearest neighbour. */
{
kase *case_pointer;
boolean written_linking_paragraph = FALSE;
cardinal count;
if ((result_pointer->nearest_known_compared_with_unknown == FURTHER) ||
has_less_known_distance(result_pointer->nearest_unknown_case,
nearest_other)) {
/* the nearest unknown other is the nearest other or if not for its
unknown distance, the nearest unknown other would be the nearest
neighbour */
state_counter_opinion(report_stream, result_pointer,
result_pointer->nearest_known_case,
result_pointer->nearest_unknown_case, TRUE);
if (has_less_known_distance(result_pointer->nearest_unknown_case,
nearest_other)) {
/* if not for its unknown distance, the nearest unknown other would
be the nearest neighbour */
handle_near_unknown(report_stream, result_pointer, nearest_result_pointer,
nearest_other, nearest_other_is_known, area_pointer,
facts_head, instant_case_type, FALSE, verbose);
} else
/* for every nearest unknown other ... */
for (case_pointer = result_pointer->nearest_unknown_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_unknown_next) {
list_similarities_and_differences(report_stream, case_pointer,
area_pointer->attribute_head, facts_head, instant_case_type, FALSE,
written_linking_paragraph, FALSE, verbose);
count = number_of_unknowns(case_pointer->matrix_head);
if (count != 0) {
fprintf(report_stream, "Furthermore, \n");
list_unknowns(report_stream, case_pointer->matrix_head,
area_pointer->attribute_head, count);
}
state_intransigence(report_stream, nearest_other, case_pointer);
written_linking_paragraph = write_linking_paragraph(report_stream, case_pointer,
case_pointer->equidistant_unknown_next);
}
} else
/* the nearest known other is the nearest other, so base the
counter-opinion only on the nearest known other */
state_counter_opinion(report_stream, result_pointer,
result_pointer->nearest_known_case, NULL, FALSE);
written_linking_paragraph = FALSE;
for (case_pointer = result_pointer->nearest_known_case; case_pointer != NULL;
case_pointer = case_pointer->equidistant_known_next) {
/* for every nearest known other ... */
list_similarities_and_differences(report_stream, case_pointer,
area_pointer->attribute_head, facts_head, instant_case_type, FALSE,
written_linking_paragraph, FALSE, verbose);
state_intransigence(report_stream, nearest_other, case_pointer);
written_linking_paragraph = write_linking_paragraph(report_stream, case_pointer,
case_pointer->equidistant_known_next);
}
}
static void
initialize_nearest_metrics(
cardinal *minimum_known_differences,
floating_point *minimum_association_coefficient,
floating_point *minimum_weighted_association_coefficient,
floating_point *max_correlation_coefficient,
floating_point *max_weighted_correlation_coefficient,
cardinal number_of_attributes)
/* Initializes the minimum and maximum metric variables. */
{
*minimum_known_differences = number_of_attributes;
*minimum_association_coefficient = 1.0;
*minimum_weighted_association_coefficient = 1.0;
*max_correlation_coefficient = -1.0;
*max_weighted_correlation_coefficient = -1.0;
}
static void
find_nearest_metrics(
metrics_type metrics,
cardinal *minimum_known_differences,
floating_point *minimum_association_coefficient,
floating_point *minimum_weighted_association_coefficient,
floating_point *max_correlation_coefficient,
floating_point *max_weighted_correlation_coefficient,
boolean weighted_association_coefficient)
/* Checks metrics against the various minimum and maximum values found so
far, and changes each minimum/maximum if the relevant metric is less/more. */
{
if (metrics.number_of_known_differences < *minimum_known_differences)
*minimum_known_differences = metrics.number_of_known_differences;
if (metrics.number_of_known_pairs == 0) {
*minimum_known_differences = 0;
*minimum_association_coefficient = 0.0;
*minimum_weighted_association_coefficient = 0.0;
} else {
if (Is_Less((floating_point) metrics.number_of_known_differences /
metrics.number_of_known_pairs,
*minimum_association_coefficient, Precision))
*minimum_association_coefficient =
(floating_point) metrics.number_of_known_differences /
metrics.number_of_known_pairs;
if (weighted_association_coefficient)
if (Is_Less(metrics.weighted_association_coefficient,
*minimum_weighted_association_coefficient, Precision))
*minimum_weighted_association_coefficient =
metrics.weighted_association_coefficient;
if (!metrics.correlation_coefficient.meaningless) {
if (Is_Less(*max_correlation_coefficient,
metrics.correlation_coefficient.unweighted, Precision))
*max_correlation_coefficient =
metrics.correlation_coefficient.unweighted;
if (Is_Less(*max_weighted_correlation_coefficient,
metrics.correlation_coefficient.weighted, Precision))
*max_weighted_correlation_coefficient =
metrics.correlation_coefficient.weighted;
}
}
}
static boolean
matches_nearest_neighbour(
kase *case_pointer,
kase **nearest_known_neighbour_pointer,
kase **nearest_unknown_neighbour_pointer)
/* Returns TRUE (and adjusts the appropriate pointer so as to point to the
next equidistant case), if case_pointer points to a nearest neighbour
(known or unknown). */
{
if (case_pointer == *nearest_known_neighbour_pointer) {
*nearest_known_neighbour_pointer =
(*nearest_known_neighbour_pointer)->equidistant_known_next;
return TRUE;
} else if (case_pointer == *nearest_unknown_neighbour_pointer) {
*nearest_unknown_neighbour_pointer =
(*nearest_unknown_neighbour_pointer)->equidistant_unknown_next;
return TRUE;
} else
return FALSE;
}
static void
log_case_number_and_name(
file log_stream,
cardinal case_number,
string case_name)
{
fprintf(log_stream, "C%u%s %s", case_number, case_number < 10 ? " " : "", case_name);
}
static void
log_case(
file log_stream,
kase *case_pointer,
result *result_pointer,
boolean additional,
cardinal level,
string *subheading)
/* Writes, to the log file, details of the case pointed to by case_pointer.
Writes a character before the case name: "*", if additional is TRUE and
result_pointer is not NULL (i.e. the case is suggested by an extra metric,
but is not a neighbour, and has a different result to the nearest result);
"+", if additional is TRUE (i.e. the case is suggested by an extra metric,
but is not a neighbour); "-", otherwise (i.e. the case is not suggested by
the metric, but is a nearest neighbour). Writes the result in parentheses
after the case name, if result_pointer is not NULL. */
{
if (*subheading != NULL) {
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", *subheading);
*subheading = NULL;
}
Indent(log_stream, level + 1);
fprintf(log_stream, " %s ", additional ? result_pointer != NULL ? "*" : "+" : "-");
log_case_number_and_name(log_stream, case_pointer->number, case_pointer->short_name);
if (result_pointer != NULL)
fprintf(log_stream, " (%s)", result_pointer->identifier);
fprintf(log_stream, "\n");
}
static void
log_case_if_necessary(
file log_stream,
result *result_pointer,
result *nearest_result_pointer,
kase *case_pointer,
kase **nearest_known_case_pointer,
kase **nearest_unknown_case_pointer,
cardinal level,
boolean neighbour,
string *heading,
string *subheading,
boolean *different_result)
/* Writes, to the log file, details of those cases about which the
known/unknown distance and the extra similarity measures disagree. Sets
different_result to TRUE, if an alternative metric suggests, as a nearest
neighbour, a case which is not a nearest neighbour and which has a result
different from the nearest result. */
{
if (neighbour) {
/* the alternative metric suggests this as a nearest neighbour */
if (!matches_nearest_neighbour(case_pointer,
nearest_known_case_pointer, nearest_unknown_case_pointer)) {
/* it isn't a nearest neighbour, so log it as an additional case */
if (*heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", *heading);
*heading = NULL;
}
if (result_pointer != nearest_result_pointer) {
/* the result of this case is not the nearest result, so include
the result in the log */
log_case(log_stream, case_pointer, result_pointer, TRUE, level, subheading);
if (different_result != NULL)
*different_result = TRUE;
} else
/* the result of this case is the nearest result, so don't
include it in the log */
log_case(log_stream, case_pointer, NULL, TRUE, level, subheading);
}
} else if (matches_nearest_neighbour(case_pointer,
nearest_known_case_pointer, nearest_unknown_case_pointer)) {
/* the alternative metric does not suggest this case as a nearest
neighbour, but it is a nearest neighbour, so log it as a missing
case */
if (*heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", *heading);
*heading = NULL;
}
/* the result of this case is the nearest result, so don't include it
in the log */
log_case(log_stream, case_pointer, NULL, FALSE, level, subheading);
}
}
static void
implement_safeguards(
file log_stream,
area *area_pointer,
string instant_case_type,
cardinal level)
/* Implements the safeguards based on the extra similarity measures, and
issues a warning in each of the following circumstances: the weighted
association coefficients suggest that a case, with a different result than
that of the nearest neighbour, ought to be the nearest neighbour; the
weighted correlation coefficients suggest that a case, with a different
result than that of the nearest neighbour, ought to be the nearest
neighbour; an ideal point suggesting a different result is at least as
near to the instant case as is the nearest neighbour; a centroid
suggesting a different result is at least as near to the instant case as
is the nearest neighbour; or the specified directions suggest a different
result or results. */
{
result *result_pointer,
*equidistant_pointer;
kase *case_pointer,
*nearest_known_case_pointer,
*nearest_unknown_case_pointer;
cardinal minimum_known_differences;
floating_point minimum_association_coefficient,
minimum_weighted_association_coefficient,
max_correlation_coefficient,
max_weighted_correlation_coefficient;
string heading = "Safeguards",
subheading = NULL;
char message[Max_Error_Message_Length];
boolean weighted_different_result = FALSE,
ideal_point_different_result = FALSE,
centroid_different_result = FALSE,
specified_direction_different_result = FALSE;
initialize_nearest_metrics(&minimum_known_differences,
&minimum_association_coefficient, &minimum_weighted_association_coefficient,
&max_correlation_coefficient, &max_weighted_correlation_coefficient,
area_pointer->number_of_attributes);
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next)
find_nearest_metrics(case_pointer->metrics, &minimum_known_differences,
&minimum_association_coefficient,
&minimum_weighted_association_coefficient, &max_correlation_coefficient,
&max_weighted_correlation_coefficient, !area_pointer->infinite_weight);
subheading = "Distance measures";
nearest_known_case_pointer =
area_pointer->nearest_result->nearest_known_case;
nearest_unknown_case_pointer =
area_pointer->nearest_result->nearest_unknown_case;
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next)
log_case_if_necessary(log_stream, result_pointer, area_pointer->nearest_result,
case_pointer, &nearest_known_case_pointer,
&nearest_unknown_case_pointer, level,
case_pointer->metrics.number_of_known_differences ==
minimum_known_differences,
&heading, &subheading, NULL);
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Association coefficients";
nearest_known_case_pointer =
area_pointer->nearest_result->nearest_known_case;
nearest_unknown_case_pointer =
area_pointer->nearest_result->nearest_unknown_case;
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next)
log_case_if_necessary(log_stream, result_pointer, area_pointer->nearest_result,
case_pointer, &nearest_known_case_pointer,
&nearest_unknown_case_pointer, level,
Is_Equal((floating_point) case_pointer->
metrics.number_of_known_differences /
case_pointer->metrics.number_of_known_pairs,
minimum_association_coefficient, Precision),
&heading, &subheading, NULL);
if (subheading == NULL)
fprintf(log_stream, "\n");
if (!area_pointer->infinite_weight) {
/* none of the weights is infinite, so the values obtained for the
weighted association coefficients are meaningful */
subheading = "Weighted association coefficients";
nearest_known_case_pointer =
area_pointer->nearest_result->nearest_known_case;
nearest_unknown_case_pointer =
area_pointer->nearest_result->nearest_unknown_case;
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next)
log_case_if_necessary(log_stream, result_pointer, area_pointer->nearest_result,
case_pointer, &nearest_known_case_pointer,
&nearest_unknown_case_pointer, level,
Is_Equal(case_pointer->metrics.weighted_association_coefficient,
minimum_weighted_association_coefficient, Precision),
&heading, &subheading, &weighted_different_result);
if (subheading == NULL)
fprintf(log_stream, "\n");
}
subheading = "Correlation coefficients";
nearest_known_case_pointer =
area_pointer->nearest_result->nearest_known_case;
nearest_unknown_case_pointer =
area_pointer->nearest_result->nearest_unknown_case;
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next)
if (!case_pointer->metrics.correlation_coefficient.meaningless)
log_case_if_necessary(log_stream, result_pointer, area_pointer->nearest_result,
case_pointer, &nearest_known_case_pointer,
&nearest_unknown_case_pointer, level,
Is_Equal(max_correlation_coefficient,
case_pointer->metrics.correlation_coefficient.unweighted,
Precision),
&heading, &subheading, NULL);
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Weighted correlation coefficients";
nearest_known_case_pointer =
area_pointer->nearest_result->nearest_known_case;
nearest_unknown_case_pointer =
area_pointer->nearest_result->nearest_unknown_case;
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next)
if (!case_pointer->metrics.correlation_coefficient.meaningless)
log_case_if_necessary(log_stream, result_pointer, area_pointer->nearest_result,
case_pointer, &nearest_known_case_pointer,
&nearest_unknown_case_pointer, level,
Is_Equal(max_weighted_correlation_coefficient,
case_pointer->metrics.correlation_coefficient.weighted, Precision),
&heading, &subheading, &weighted_different_result);
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Ideal points";
for (equidistant_pointer = area_pointer->nearest_ideal_point; equidistant_pointer != NULL;
equidistant_pointer = equidistant_pointer->equidistant_ideal_point_next)
if (equidistant_pointer != area_pointer->nearest_result) {
if (heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", heading);
heading = NULL;
}
if (subheading != NULL) {
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", subheading);
subheading = NULL;
}
Indent(log_stream, level + 2);
fprintf(log_stream, "%s\n", equidistant_pointer->identifier);
if (area_pointer->nearest_result->nearest_known_case != NULL)
if (Relative_Distance(equidistant_pointer->ideal_point_metrics.distance,
area_pointer->nearest_result->nearest_known_case->
metrics.distance) != FURTHER)
ideal_point_different_result = TRUE;
if (area_pointer->nearest_result->nearest_unknown_case != NULL)
if (Relative_Distance(equidistant_pointer->ideal_point_metrics.distance,
area_pointer->nearest_result->nearest_unknown_case->
metrics.distance) != FURTHER)
ideal_point_different_result = TRUE;
}
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Centroids";
for (equidistant_pointer = area_pointer->nearest_centroid; equidistant_pointer != NULL;
equidistant_pointer = equidistant_pointer->equidistant_centroid_next)
if (equidistant_pointer != area_pointer->nearest_result) {
if (heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", heading);
heading = NULL;
}
if (subheading != NULL) {
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", subheading);
subheading = NULL;
}
Indent(log_stream, level + 2);
fprintf(log_stream, "%s\n", equidistant_pointer->identifier);
if (area_pointer->nearest_result->nearest_known_case != NULL)
if (Relative_Distance(equidistant_pointer->centroid_metrics.distance,
area_pointer->nearest_result->nearest_known_case->
metrics.distance) != FURTHER)
centroid_different_result = TRUE;
if (area_pointer->nearest_result->nearest_unknown_case != NULL)
if (Relative_Distance(equidistant_pointer->centroid_metrics.distance,
area_pointer->nearest_result->nearest_unknown_case->
metrics.distance) != FURTHER)
centroid_different_result = TRUE;
}
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Specified directions";
for (equidistant_pointer = area_pointer->strongest_specified_direction;
equidistant_pointer != NULL;
equidistant_pointer = equidistant_pointer->equidistant_specified_direction_next)
if (equidistant_pointer != area_pointer->nearest_result) {
if (heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", heading);
heading = NULL;
}
if (subheading != NULL) {
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", subheading);
subheading = NULL;
}
Indent(log_stream, level + 2);
fprintf(log_stream, "%s\n", equidistant_pointer->identifier);
specified_direction_different_result = TRUE;
}
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Ideal point directions";
for (equidistant_pointer = area_pointer->strongest_ideal_point_direction;
equidistant_pointer != NULL;
equidistant_pointer = equidistant_pointer->equidistant_ideal_point_direction_next)
if (equidistant_pointer != area_pointer->nearest_result) {
if (heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", heading);
heading = NULL;
}
if (subheading != NULL) {
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", subheading);
subheading = NULL;
}
Indent(log_stream, level + 2);
fprintf(log_stream, "%s\n", equidistant_pointer->identifier);
}
if (subheading == NULL)
fprintf(log_stream, "\n");
subheading = "Centroid directions";
for (equidistant_pointer = area_pointer->strongest_centroid_direction;
equidistant_pointer != NULL;
equidistant_pointer = equidistant_pointer->equidistant_centroid_direction_next)
if (equidistant_pointer != area_pointer->nearest_result) {
if (heading != NULL) {
Indent(log_stream, level);
fprintf(log_stream, "%s:\n\n", heading);
heading = NULL;
}
if (subheading != NULL) {
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", subheading);
subheading = NULL;
}
Indent(log_stream, level + 2);
fprintf(log_stream, "%s\n", equidistant_pointer->identifier);
}
if (subheading == NULL)
fprintf(log_stream, "\n");
/* issue warnings if necessary */
if (weighted_different_result)
warning(log_stream,
"one or both of the weighted safeguard metrics suggest "
"that a case (or cases) with a different result should "
"be the nearest neighbour (or neighbours)", level);
if (ideal_point_different_result) {
sprintf(message,
"one or more ideal points with a different result are at "
"least as near to the %s case as is the nearest neighbour",
instant_case_type);
warning(log_stream, message, level);
}
if (centroid_different_result) {
sprintf(message,
"one or more centroids with a different result are at "
"least as near to the %s case as is the nearest neighbour",
instant_case_type);
warning(log_stream, message, level);
}
if (specified_direction_different_result)
warning(log_stream,
"the specified directions suggest a different result or results",
level);
}
extern void
Write_Report(
file report_stream,
file log_stream,
area *area_pointer,
vector_element *facts_head,
vector_element *original_facts,
boolean verbose,
boolean hypothetical,
boolean same_result,
cardinal number,
cardinal level)
/* Writes SHYSTER's legal opinion about the facts pointed to by facts_head to
report_stream (if it is not NULL). Cases are summarized in full, and
opening and closing strings are written in full, if verbose is TRUE.
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. If
the instant case is an instantiation or a hypothetical, original_facts
points to the facts of the uninstantiated and unhypothesized instant case.
If the instant case is a hypothetical and same_result is TRUE, the
hypothetical has the same result as the unhypothesized instant case. */
{
result *result_pointer;
kase *case_pointer,
*nearest_neighbour;
string instant_case_type;
if (number == 0) {
/* the instant case is the uninstantiated and unhypothesized instant
case */
instant_case_type = "instant";
if (report_stream != NULL) {
fprintf(report_stream, "%s{%s area}\n\n"
"%s{Instant case}\n\n",
Heading, area_pointer->identifier, Subheading);
/* write the opening string */
if (area_pointer->opening != NULL) {
if (verbose)
Write(report_stream, area_pointer->opening, "\n", Top_Level, Hang);
else
Write(report_stream, "[Opening.]", "\n", Top_Level, Hang);
fprintf(report_stream, "%s ", Skip);
}
/* write the facts of the instant case */
fprintf(report_stream, "In the instant case,\n");
list_facts(report_stream, facts_head, area_pointer->attribute_head,
area_pointer->number_of_attributes);
fprintf(report_stream, "\n%s In my opinion", Skip);
}
} else if (!hypothetical) {
/* the instant case is instantiation number */
instant_case_type = "instantiated";
if (report_stream != NULL) {
fprintf(report_stream, "%s{Instantiation %u}\n\n", Subheading, number);
fprintf(report_stream,
"It may be that the following is true of the instant case:\n");
list_new_differences(report_stream, facts_head, original_facts,
area_pointer->attribute_head,
number_of_differences(facts_head, original_facts));
fprintf(report_stream, "\n%s If that is so then in my opinion", Skip);
}
} else {
/* the instant case is hypothetical number */
instant_case_type = "hypothetical";
if (report_stream != NULL) {
fprintf(report_stream, "%s{Hypothetical %u}\n\n", Subheading, number);
fprintf(report_stream, "Consider the instant case changed "
"so that the following is true:\n");
list_new_differences(report_stream, facts_head, original_facts,
area_pointer->attribute_head,
number_of_differences(facts_head, original_facts));
if (same_result)
/* this hypothetical has the same result as does the instant case */
fprintf(report_stream,
"\n%s If that were so then I would be "
"more strongly of the\n"
"opinion that", Skip);
else
/* this hypothetical has a different result to that of the
instant case */
fprintf(report_stream,
"\n%s If that were so then my opinion would be\n"
"that", Skip);
}
}
Indent(log_stream, level);
fprintf(log_stream, "Nearest neighbours:\n\n");
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", area_pointer->nearest_result->identifier);
if (area_pointer->nearest_result->nearest_known_compared_with_unknown != FURTHER) {
/* the nearest known neighbour is the nearest neighbour (although there
may be an equidistant case with an unknown distance) */
nearest_neighbour = area_pointer->nearest_result->nearest_known_case;
/* for every nearest known case with this result ... */
for (case_pointer = area_pointer->nearest_result->nearest_known_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_known_next) {
/* log the case (a nearest known neighbour) */
Indent(log_stream, level + 2);
log_case_number_and_name(log_stream, case_pointer->number,
case_pointer->short_name);
if (Is_Zero_Subdistance(case_pointer->metrics.distance.known))
fprintf(log_stream, " (identical)");
fprintf(log_stream, "\n");
}
if (has_less_known_distance(area_pointer->nearest_result->nearest_unknown_case,
area_pointer->nearest_result->nearest_known_case))
/* if not for its unknown distance, the nearest unknown neighbour
would be the nearest neighbour, so for every nearest unknown case
with this result ... */
for (case_pointer = area_pointer->nearest_result->nearest_unknown_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_unknown_next) {
/* log the case (a nearest unknown neighbour) */
Indent(log_stream, level + 2);
log_case_number_and_name(log_stream, case_pointer->number,
case_pointer->short_name);
fprintf(log_stream, "\n");
} else
/* the nearest unknown neighbours should be ignored */
area_pointer->nearest_result->nearest_unknown_case = NULL;
fprintf(log_stream, "\n");
if (report_stream != NULL)
handle_nearest_known(report_stream, area_pointer->nearest_result,
area_pointer->nearest_result, nearest_neighbour,
area_pointer, facts_head, instant_case_type, verbose);
} else {
/* the nearest unknown neighbour is the nearest neighbour */
nearest_neighbour = area_pointer->nearest_result->nearest_unknown_case;
/* for every nearest unknown case with this result ... */
for (case_pointer = area_pointer->nearest_result->nearest_unknown_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_unknown_next) {
/* log the case (a nearest unknown neighbour) */
Indent(log_stream, level + 2);
log_case_number_and_name(log_stream, case_pointer->number,
case_pointer->short_name);
fprintf(log_stream, "\n");
}
/* for every nearest known case with this result ... */
for (case_pointer = area_pointer->nearest_result->nearest_known_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_known_next) {
/* log the case (a nearest known neighbour) */
Indent(log_stream, level + 2);
log_case_number_and_name(log_stream, case_pointer->number,
case_pointer->short_name);
fprintf(log_stream, "\n");
}
fprintf(log_stream, "\n");
if (report_stream != NULL)
handle_nearest_unknown(report_stream, area_pointer->nearest_result,
area_pointer, facts_head, instant_case_type, verbose);
}
Indent(log_stream, level);
fprintf(log_stream, "Nearest others:\n\n");
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next)
/* for every result ... */
if (result_pointer != area_pointer->nearest_result) {
/* this result is not the nearest result */
Indent(log_stream, level + 1);
fprintf(log_stream, "%s:\n", result_pointer->identifier);
if ((result_pointer->nearest_known_case != NULL) ||
(result_pointer->nearest_unknown_case != NULL)) {
/* this result has a nearest case (i.e. it has at least one case) */
if ((result_pointer->nearest_known_compared_with_unknown == FURTHER) ||
has_less_known_distance(result_pointer->nearest_unknown_case,
nearest_neighbour))
/* the nearest unknown other is the nearest other or, if not
for its unknown distance, the nearest unknown other would
be the nearest neighbour, so for every nearest unknown case
with this result ... */
for (case_pointer = result_pointer->nearest_unknown_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_unknown_next) {
/* log the case (a nearest unknown other) */
Indent(log_stream, level + 2);
log_case_number_and_name(log_stream, case_pointer->number,
case_pointer->short_name);
fprintf(log_stream, "\n");
}
/* for every nearest known case with this result ... */
for (case_pointer = result_pointer->nearest_known_case;
case_pointer != NULL;
case_pointer = case_pointer->equidistant_known_next) {
/* log the case (a nearest known other) */
Indent(log_stream, level + 2);
log_case_number_and_name(log_stream, case_pointer->number,
case_pointer->short_name);
fprintf(log_stream, "\n");
}
if (report_stream != NULL)
handle_nearest_others(report_stream, result_pointer,
area_pointer->nearest_result, nearest_neighbour,
area_pointer->nearest_result->
nearest_known_compared_with_unknown != FURTHER,
area_pointer, facts_head, instant_case_type, verbose);
}
fprintf(log_stream, "\n");
}
implement_safeguards(log_stream, area_pointer, instant_case_type, level);
/* log the nearest result */
Indent(log_stream, level - 1);
fprintf(log_stream, "Nearest result for ");
if (number == 0)
/* this is the unhypothesized, uninstantiated instant case */
fprintf(log_stream, "the instant case");
else if (!hypothetical)
/* this is an instantiation */
fprintf(log_stream, "instantiation %u", number);
else
/* this is a hypothetical */
fprintf(log_stream, "hypothetical %u", number);
fprintf(log_stream, " is %s.\n\n", area_pointer->nearest_result->identifier);
if ((report_stream != NULL) && (number == 0) && (area_pointer->closing != NULL)) {
/* write the closing string */
fprintf(report_stream, "%s\n", Skip);
if (verbose)
Write(report_stream, area_pointer->closing, "\n", Top_Level, Hang);
else
Write(report_stream, "[Closing.]", "\n", Top_Level, Hang);
}
}
Other SHYSTER modules: Shyster, Statutes, Cases, Tokenizer, Parser, Dumper, Checker, Scales, Adjuster, Consultant and Odometer.