/* This is the header file for the Odometer module. It is also included by
the Cases, Dumper and Reporter modules. */
/* external functions */
extern relative_distance_type
Relative_Distance(
distance_type x,
distance_type y);
extern void
Calculate_Distances(
file distances_stream,
file log_stream,
area *area_pointer,
case_law_specification case_law,
vector_element *facts_head,
boolean hypothetical,
cardinal number,
cardinal level);
/* This is the implementation file for the Odometer module. */
#include <stdio.h>
#include <math.h>
#include "shyster.h"
#include "cases.h"
#include "odometer.h"
#include "dumper.h"
#include "scales.h"
static void
error_exit(
file stream,
const string message)
{
Write_Error_Message_And_Exit(stream, "Odometer", message);
}
static void
warning(
file stream,
const string message,
cardinal level)
{
Write_Warning_Message(stream, "Odometer", message, level);
}
static void
zero_subdistance(
distance_subtype *subdistance_pointer)
/* Sets the subdistance pointed to by subdistance_pointer to zero. */
{
subdistance_pointer->infinite = 0;
subdistance_pointer->finite = 0.0;
}
static void
zero_distance(
distance_type *distance_pointer)
/* Sets the distance pointed to by distance_pointer to zero. */
{
zero_subdistance(&distance_pointer->known);
zero_subdistance(&distance_pointer->unknown);
}
static void
zero_correlation(
correlation_type *correlation_pointer)
/* Sets the correlation coefficient pointed to by correlation_pointer to
zero. */
{
correlation_pointer->meaningless = FALSE;
correlation_pointer->unweighted = 0.0;
correlation_pointer->weighted = 0.0;
}
static void
zero_metrics(
metrics_type *metrics_pointer)
/* Sets the metrics pointed to by metrics_pointer to zero. */
{
zero_distance(&metrics_pointer->distance);
metrics_pointer->number_of_known_differences = 0;
metrics_pointer->number_of_known_pairs = 0;
metrics_pointer->weighted_association_coefficient = 0.0;
zero_correlation(&metrics_pointer->correlation_coefficient);
}
static relative_distance_type
relative_subdistance(
distance_subtype x,
distance_subtype y)
/* Returns NEARER, if the subdistance x is less than the subdistance y;
returns FURTHER, if x is greater than y; returns EQUIDISTANT, otherwise. */
{
if ((x.infinite == y.infinite) && Is_Equal(x.finite, y.finite, Distance_Precision))
return EQUIDISTANT;
else if ((x.infinite < y.infinite) ||
((x.infinite == y.infinite) && Is_Less(x.finite, y.finite, Distance_Precision)))
return NEARER;
else
return FURTHER;
}
extern relative_distance_type
Relative_Distance(
distance_type x,
distance_type y)
/* Returns NEARER, if the distance x is less than the distance y; returns
FURTHER, if x is greater than y; returns EQUIDISTANT, otherwise. */
{
if ((x.known.infinite + x.unknown.infinite ==
y.known.infinite + y.unknown.infinite) &&
Is_Equal(x.known.finite + x.unknown.finite,
y.known.finite + y.unknown.finite, Distance_Precision))
return EQUIDISTANT;
else if ((x.known.infinite + x.unknown.infinite <
y.known.infinite + y.unknown.infinite) ||
((x.known.infinite + x.unknown.infinite ==
y.known.infinite + y.unknown.infinite) &&
Is_Less(x.known.finite + x.unknown.finite,
y.known.finite + y.unknown.finite, Distance_Precision)))
return NEARER;
else
return FURTHER;
}
static void
add_weight(
file log_stream,
attribute_value_type x,
attribute_value_type y,
weight_type weight,
metrics_type *metrics_pointer,
cardinal level)
/* Adds weight to the known distance in the metrics pointed to by
metrics_pointer, if the attribute values x and y are known and different.
Adds weight to the unknown distance in the metrics pointed to by
metrics_pointer, if either of the attribute values x or y is UNKNOWN. */
{
if (!weight.infinite && Is_Zero(weight.finite) && ((x != UNKNOWN) || (y != UNKNOWN)))
warning(log_stream, "known attribute value for weightless attribute", level);
else if ((x == UNKNOWN) || (y == UNKNOWN))
if (weight.infinite)
metrics_pointer->distance.unknown.infinite++;
else
metrics_pointer->distance.unknown.finite += weight.finite;
else {
if (x != y) {
if (weight.infinite)
metrics_pointer->distance.known.infinite++;
else
metrics_pointer->distance.known.finite += weight.finite;
}
}
}
static void
sum_pair(
attribute_value_type x,
attribute_value_type y,
weight_type weight,
metrics_type *sum_pointer_X,
correlation_type *sum_pointer_Y)
/* Adds the attribute values (weighted and unweighted) of both x and y to
various of the metrics of sum_pointer_X and sum_pointer_Y, if both x and y
are known. (These metrics are used here as temporary storage; the final
sums are later used to calculate association and correlation
coefficients.) */
{
floating_point temp;
if ((x != UNKNOWN) && (y != UNKNOWN)) {
sum_pointer_X->number_of_known_pairs++;
if (x != y)
sum_pointer_X->number_of_known_differences++;
sum_pointer_X->weighted_association_coefficient += weight.finite;
if (weight.infinite)
/* one of the attributes is infinitely weighted, so use a
"pseudo-infinite" weight for the calculation of weighted
correlation coefficients */
weight.finite = Very_Heavy_Indeed;
(void) Attribute_Value(x, &temp);
sum_pointer_X->correlation_coefficient.unweighted += temp;
sum_pointer_X->correlation_coefficient.weighted += temp * weight.finite;
(void) Attribute_Value(y, &temp);
sum_pointer_Y->unweighted += temp;
sum_pointer_Y->weighted += temp * weight.finite;
}
}
static void
correlate_pair(
attribute_value_type x,
attribute_value_type y,
floating_point mean_X,
floating_point mean_Y,
floating_point *numerator,
floating_point *left_denominator,
floating_point *right_denominator,
weight_type weight)
/* Updates *numerator, *left_denominator and right_denominator appropriately,
if both x and y are known: *left_denominator is incremented by the square
of the weighted value of x less mean_X; right_denominator is incremented
by the square of the weighted value of y less mean_Y; *numerator is
incremented by the product of the weighted value of x less mean_X and the
weighted value of y less mean_Y. These variables are later used in the
calculation of the weighted correlation coefficient r'. */
{
floating_point temp_X,
temp_Y;
if ((x != UNKNOWN) && (y != UNKNOWN)) {
if (weight.infinite)
/* one of the attributes is infinitely weighted, so use a
"pseudo-infinite" weight for the calculation of weighted
correlation coefficients */
weight.finite = Very_Heavy_Indeed;
(void) Attribute_Value(x, &temp_X);
(void) Attribute_Value(y, &temp_Y);
*numerator += (temp_X * weight.finite - mean_X) *
(temp_Y * weight.finite - mean_Y);
*left_denominator += (temp_X * weight.finite - mean_X) *
(temp_X * weight.finite - mean_X);
*right_denominator += (temp_Y * weight.finite - mean_Y) *
(temp_Y * weight.finite - mean_Y);
}
}
static void
calculate_case_means(
matrix_element *matrix_pointer,
vector_element *vector_pointer,
attribute *attribute_pointer,
metrics_type *metrics_pointer,
correlation_type *correlation_pointer)
/* Calculates the mean attribute values for a leading case and the instant
case (their attribute values are pointed to by matrix_pointer and
vector_pointer, respectively) and stores them as the correlation
coefficients in *metrics_pointer and *correlation_pointer, respectively.
(These correlation coefficients are used here as temporary storage; their
values are later used to calculate the actual correlation coefficients.)
*attribute_pointer is the head of the list of attributes for this area. */
{
while (matrix_pointer != NULL) {
sum_pair(matrix_pointer->attribute_value, vector_pointer->attribute_value,
attribute_pointer->weight, metrics_pointer, correlation_pointer);
matrix_pointer = matrix_pointer->case_next;
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
if (metrics_pointer->number_of_known_pairs != 0) {
metrics_pointer->correlation_coefficient.unweighted /=
metrics_pointer->number_of_known_pairs;
metrics_pointer->correlation_coefficient.weighted /=
metrics_pointer->number_of_known_pairs;
correlation_pointer->unweighted /=
metrics_pointer->number_of_known_pairs;
correlation_pointer->weighted /=
metrics_pointer->number_of_known_pairs;
}
}
static void
calculate_case_metrics(
file log_stream,
matrix_element *matrix_pointer,
vector_element *vector_pointer,
attribute *attribute_pointer,
metrics_type *metrics_pointer,
boolean *correlation_coefficients,
cardinal level)
/* Calculates the metrics for a leading case and the instant case (their
attribute values are pointed to by matrix_pointer and vector_pointer,
respectively) and stores them in *metrics_pointer. *attribute_pointer is
the head of the list of attributes for this area.
*correlation_coefficients is set to TRUE, if the correlation coefficients
are meaningful (i.e. neither of the two cases has all attribute values
equal). */
{
correlation_type vector_means,
numerator,
left_denominator,
right_denominator;
weight_type unit_weight = { FALSE, 1.0 };
zero_correlation(&vector_means);
zero_correlation(&numerator);
zero_correlation(&left_denominator);
zero_correlation(&right_denominator);
calculate_case_means(matrix_pointer, vector_pointer, attribute_pointer,
metrics_pointer, &vector_means);
while (attribute_pointer != NULL) {
add_weight(log_stream, matrix_pointer->attribute_value,
vector_pointer->attribute_value,
attribute_pointer->weight, metrics_pointer, level);
correlate_pair(matrix_pointer->attribute_value, vector_pointer->attribute_value,
metrics_pointer->correlation_coefficient.unweighted, vector_means.unweighted,
&numerator.unweighted, &left_denominator.unweighted,
&right_denominator.unweighted, unit_weight);
correlate_pair(matrix_pointer->attribute_value, vector_pointer->attribute_value,
metrics_pointer->correlation_coefficient.weighted, vector_means.weighted,
&numerator.weighted, &left_denominator.weighted,
&right_denominator.weighted, attribute_pointer->weight);
matrix_pointer = matrix_pointer->case_next;
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
metrics_pointer->weighted_association_coefficient =
metrics_pointer->distance.known.finite /
metrics_pointer->weighted_association_coefficient;
if (Is_Zero(left_denominator.unweighted * right_denominator.unweighted))
/* either this case or the instant case has all attribute values equal:
the correlation coefficients are meaningless */
metrics_pointer->correlation_coefficient.meaningless = TRUE;
else {
metrics_pointer->correlation_coefficient.unweighted = numerator.unweighted /
(floating_point) sqrt((double)
(left_denominator.unweighted * right_denominator.unweighted));
metrics_pointer->correlation_coefficient.weighted = numerator.weighted /
(floating_point) sqrt((double)
(left_denominator.weighted * right_denominator.weighted));
*correlation_coefficients = TRUE;
}
}
static void
calculate_ideal_point_means(
vector_element *vector_pointer_X,
vector_element *vector_pointer_Y,
attribute *attribute_pointer,
metrics_type *metrics_pointer,
correlation_type *correlation_pointer)
/* Calculates the mean attribute values for an ideal point and the instant
case (their attribute values are pointed to by vector_pointer_X and
vector_pointer_Y, respectively) and stores them as the correlation
coefficients in *metrics_pointer and correlation_pointer, respectively.
(These correlation coefficients are used here as temporary storage; their
values are later used to calculate the actual correlation coefficients.)
attribute_pointer is the head of the list of attributes for this area. */
{
while (vector_pointer_X != NULL) {
sum_pair(vector_pointer_X->attribute_value, vector_pointer_Y->attribute_value,
attribute_pointer->weight, metrics_pointer, correlation_pointer);
vector_pointer_X = vector_pointer_X->next;
vector_pointer_Y = vector_pointer_Y->next;
attribute_pointer = attribute_pointer->next;
}
if (metrics_pointer->number_of_known_pairs != 0) {
metrics_pointer->correlation_coefficient.unweighted /=
metrics_pointer->number_of_known_pairs;
metrics_pointer->correlation_coefficient.weighted /=
metrics_pointer->number_of_known_pairs;
correlation_pointer->unweighted /=
metrics_pointer->number_of_known_pairs;
correlation_pointer->weighted /=
metrics_pointer->number_of_known_pairs;
}
}
static void
calculate_ideal_point_metrics(
file log_stream,
vector_element *vector_pointer_X,
vector_element *vector_pointer_Y,
attribute *attribute_pointer,
metrics_type *metrics_pointer,
boolean *correlation_coefficients,
cardinal level)
/* Calculates the metrics for an ideal point and the instant case (their
attribute values are pointed to by vector_pointer_X and vector_pointer_Y,
respectively) and stores them in metrics_pointer. *attribute_pointer is
the head of the list of attributes for this area.
*correlation_coefficients is set to TRUE, if the correlation coefficients
are meaningful (i.e. neither the ideal point nor the instant case has all
attribute values equal). */
{
correlation_type vector_means,
numerator,
left_denominator,
right_denominator;
weight_type unit_weight = { FALSE, 1.0 };
zero_correlation(&vector_means);
zero_correlation(&numerator);
zero_correlation(&left_denominator);
zero_correlation(&right_denominator);
calculate_ideal_point_means(vector_pointer_X, vector_pointer_Y, attribute_pointer,
metrics_pointer, &vector_means);
while (attribute_pointer != NULL) {
add_weight(log_stream, vector_pointer_X->attribute_value,
vector_pointer_Y->attribute_value,
attribute_pointer->weight, metrics_pointer, level);
correlate_pair(vector_pointer_X->attribute_value, vector_pointer_Y->attribute_value,
metrics_pointer->correlation_coefficient.unweighted, vector_means.unweighted,
&numerator.unweighted, &left_denominator.unweighted,
&right_denominator.unweighted, unit_weight);
correlate_pair(vector_pointer_X->attribute_value, vector_pointer_Y->attribute_value,
metrics_pointer->correlation_coefficient.weighted, vector_means.weighted,
&numerator.weighted, &left_denominator.weighted,
&right_denominator.weighted, attribute_pointer->weight);
vector_pointer_X = vector_pointer_X->next;
vector_pointer_Y = vector_pointer_Y->next;
attribute_pointer = attribute_pointer->next;
}
metrics_pointer->weighted_association_coefficient =
metrics_pointer->distance.known.finite /
metrics_pointer->weighted_association_coefficient;
if (Is_Zero(left_denominator.unweighted * right_denominator.unweighted))
/* either this ideal point or the instant case has all attribute values
equal: the correlation coefficients are meaningless */
metrics_pointer->correlation_coefficient.meaningless = TRUE;
else {
metrics_pointer->correlation_coefficient.unweighted = numerator.unweighted /
(floating_point) sqrt((double)
(left_denominator.unweighted * right_denominator.unweighted));
metrics_pointer->correlation_coefficient.weighted = numerator.weighted /
(floating_point) sqrt((double)
(left_denominator.weighted * right_denominator.weighted));
*correlation_coefficients = TRUE;
}
}
static void
calculate_centroid_means(
centroid_element *centroid_pointer,
vector_element *vector_pointer,
attribute *attribute_pointer,
metrics_type *metrics_pointer,
correlation_type *correlation_pointer)
/* Calculates the mean attribute values for a centroid and the instant case
(their attribute values are pointed to by centroid_pointer and
vector_pointer, respectively) and stores them as the correlation
coefficients in *metrics_pointer and *correlation_pointer, respectively.
(These correlation coefficients are used here as temporary storage; their
values are later used to calculate the actual correlation coefficients.)
*attribute_pointer is the head of the list of attributes for this area. */
{
floating_point temp;
weight_type weight;
while (centroid_pointer != NULL) {
if ((!centroid_pointer->unknown) && (vector_pointer->attribute_value != UNKNOWN)) {
metrics_pointer->number_of_known_pairs++;
if (Nearest_Attribute_Value(centroid_pointer->value) !=
vector_pointer->attribute_value)
metrics_pointer->number_of_known_differences++;
weight = attribute_pointer->weight;
metrics_pointer->weighted_association_coefficient += weight.finite;
/* use the actual centroid value, not the nearest attribute value
(as is done for leading cases and ideal points) */
metrics_pointer->correlation_coefficient.unweighted += centroid_pointer->value;
metrics_pointer->correlation_coefficient.weighted +=
centroid_pointer->value * weight.finite;
(void) Attribute_Value(vector_pointer->attribute_value, &temp);
correlation_pointer->unweighted += temp;
correlation_pointer->weighted += temp * weight.finite;
}
centroid_pointer = centroid_pointer->next;
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
if (metrics_pointer->number_of_known_pairs != 0) {
metrics_pointer->correlation_coefficient.unweighted /=
metrics_pointer->number_of_known_pairs;
metrics_pointer->correlation_coefficient.weighted /=
metrics_pointer->number_of_known_pairs;
correlation_pointer->unweighted /=
metrics_pointer->number_of_known_pairs;
correlation_pointer->weighted /=
metrics_pointer->number_of_known_pairs;
}
}
static void
calculate_centroid_metrics(
file log_stream,
centroid_element *centroid_pointer,
vector_element *vector_pointer,
attribute *attribute_pointer,
metrics_type *metrics_pointer,
boolean *correlation_coefficients,
cardinal level)
/* Calculates the metrics for a centroid and the instant case (their
attribute values are pointed to by centroid_pointer and vector_pointer,
respectively) and stores them in *metrics_pointer. *attribute_pointer is
the head of the list of attributes for this area.
*correlation_coefficients is set to TRUE, if the correlation coefficients
are meaningful (i.e. neither the centroid nor the instant case has all
attribute values equal). */
{
correlation_type vector_means,
numerator,
left_denominator,
right_denominator;
floating_point temp,
weight;
zero_correlation(&vector_means);
zero_correlation(&numerator);
zero_correlation(&left_denominator);
zero_correlation(&right_denominator);
calculate_centroid_means(centroid_pointer, vector_pointer, attribute_pointer,
metrics_pointer, &vector_means);
while (attribute_pointer != NULL) {
if (centroid_pointer->unknown)
add_weight(log_stream, UNKNOWN, vector_pointer->attribute_value,
attribute_pointer->weight, metrics_pointer, level);
else {
add_weight(log_stream, Nearest_Attribute_Value(centroid_pointer->value),
vector_pointer->attribute_value, attribute_pointer->weight,
metrics_pointer, level);
if (vector_pointer->attribute_value != UNKNOWN) {
if (attribute_pointer->weight.infinite)
/* one of the attributes is infinitely weighted, so use a
"pseudo-infinite" weight for the calculation of weighted
correlation coefficients */
weight = Very_Heavy_Indeed;
else
weight = attribute_pointer->weight.finite;
(void) Attribute_Value(vector_pointer->attribute_value, &temp);
/* use the actual centroid value, not the nearest attribute value
(as is done for leading cases and ideal points) */
numerator.unweighted += (centroid_pointer->value -
metrics_pointer->correlation_coefficient.unweighted) *
(temp - vector_means.unweighted);
left_denominator.unweighted += (centroid_pointer->value -
metrics_pointer->correlation_coefficient.unweighted) *
(centroid_pointer->value -
metrics_pointer->correlation_coefficient.unweighted);
right_denominator.unweighted += (temp - vector_means.unweighted) *
(temp - vector_means.unweighted);
numerator.weighted += (centroid_pointer->value * weight -
metrics_pointer->correlation_coefficient.weighted) *
(temp * weight - vector_means.weighted);
left_denominator.weighted += (centroid_pointer->value * weight -
metrics_pointer->correlation_coefficient.weighted) *
(centroid_pointer->value * weight -
metrics_pointer->correlation_coefficient.weighted);
right_denominator.weighted += (temp * weight - vector_means.weighted) *
(temp * weight - vector_means.weighted);
}
}
centroid_pointer = centroid_pointer->next;
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
metrics_pointer->weighted_association_coefficient =
metrics_pointer->distance.known.finite /
metrics_pointer->weighted_association_coefficient;
if (Is_Zero(left_denominator.unweighted * right_denominator.unweighted))
/* either this centroid or the instant case has all attribute values
equal: the correlation coefficients are meaningless */
metrics_pointer->correlation_coefficient.meaningless = TRUE;
else {
metrics_pointer->correlation_coefficient.unweighted = numerator.unweighted /
(floating_point) sqrt((double)
(left_denominator.unweighted * right_denominator.unweighted));
metrics_pointer->correlation_coefficient.weighted = numerator.weighted /
(floating_point) sqrt((double)
(left_denominator.weighted * right_denominator.weighted));
*correlation_coefficients = TRUE;
}
}
static weight_list_element *
result_weight(
weight_list_element *weights_pointer,
result *result_pointer,
result *target_result_pointer)
/* Returns the result weight (from the list of result weights pointed to by
weights_pointer) which corresponds to the result pointed to by
target_result_pointer. (*result_pointer is the head of the list of
results for this area.) */
{
while (result_pointer != target_result_pointer) {
weights_pointer = weights_pointer->next;
result_pointer = result_pointer->next;
}
return weights_pointer;
}
static void
calculate_specified_directions(
attribute *attribute_pointer,
vector_element *vector_pointer,
result *result_head)
/* For every attribute, if the attribute value in the instant case is
directed towards a result, adds the weight of the attribute to the
specified direction for that result. *attribute_pointer is the head of
the list of attributes for this area. The attribute values of the instant
case are pointed to by vector_pointer. */
{
direction_list_element *direction_pointer;
weight_list_element *weights_pointer;
while (attribute_pointer != NULL) {
switch (vector_pointer->attribute_value) {
case YES:
direction_pointer = attribute_pointer->yes_direction_head;
break;
case NO:
direction_pointer = attribute_pointer->no_direction_head;
break;
case UNKNOWN:
direction_pointer = attribute_pointer->unknown_direction_head;
break;
}
while (direction_pointer != NULL) {
weights_pointer = result_weight(attribute_pointer->weights_head,
result_head, direction_pointer->result);
if (weights_pointer->weight.infinite)
direction_pointer->result->specified_direction.infinite++;
else
direction_pointer->result->specified_direction.finite +=
weights_pointer->weight.finite;
direction_pointer = direction_pointer->next;
}
vector_pointer = vector_pointer->next;
attribute_pointer = attribute_pointer->next;
}
}
static void
calculate_other_directions(
area *area_pointer,
vector_element *vector_pointer)
/* For every attribute, if only one ideal point has an attribute value
matching that of the instant case, adds the weight of the attribute to the
ideal point direction for that ideal point's result. Similarly, for every
attribute, if only one centroid has an attribute value matching that of
the instant case, adds the weight of the attribute to the centroid
direction for that centroid's result. The attribute values of the instant
case are pointed to by vector_pointer.
UNKNOWN values are ignored when counting matches. This differs from the
calculation of specified directions (in calculate_specified_directions())
because an UNKNOWN value in an ideal point could mean "don't know," while
in a centroid it just indicates an absence of values; by contrast, an
UNKNOWN specified direction means "an UNKNOWN value for this attribute
suggests this result." */
{
result *result_pointer,
*ideal_point_matching_result,
*centroid_matching_result;
attribute *attribute_pointer = area_pointer->attribute_head;
vector_element *ideal_point_pointer;
centroid_element *centroid_pointer;
cardinal count,
ideal_point_matches_count,
centroid_matches_count;
weight_list_element *weights_pointer;
while (vector_pointer != NULL) {
if (vector_pointer->attribute_value != UNKNOWN) {
/* count the number of ideal points and centroids with the same
value for this attribute as has the instant case */
ideal_point_matches_count = 0;
centroid_matches_count = 0;
result_pointer = area_pointer->result_head;
while (result_pointer != NULL) {
ideal_point_pointer = result_pointer->ideal_point_head;
centroid_pointer = result_pointer->centroid_head;
for (count = 1; count < attribute_pointer->number; count++) {
if (ideal_point_pointer != NULL)
ideal_point_pointer = ideal_point_pointer->next;
if (centroid_pointer != NULL)
centroid_pointer = centroid_pointer->next;
}
if ((ideal_point_pointer != NULL) &&
(ideal_point_pointer->attribute_value ==
vector_pointer->attribute_value)) {
ideal_point_matches_count++;
ideal_point_matching_result = result_pointer;
}
if ((centroid_pointer != NULL) &&
((centroid_pointer->unknown &&
(vector_pointer->attribute_value == UNKNOWN)) ||
(Nearest_Attribute_Value(centroid_pointer->value) ==
vector_pointer->attribute_value))) {
centroid_matches_count++;
centroid_matching_result = result_pointer;
}
result_pointer = result_pointer->next;
}
if (ideal_point_matches_count == 1) {
/* add the weight of the attribute to the ideal point direction
for the matching ideal point's result */
weights_pointer = result_weight(attribute_pointer->weights_head,
area_pointer->result_head, ideal_point_matching_result);
if (weights_pointer->weight.infinite)
ideal_point_matching_result->ideal_point_direction.infinite++;
else
ideal_point_matching_result->ideal_point_direction.finite +=
weights_pointer->weight.finite;
}
if (centroid_matches_count == 1) {
/* add the weight of the attribute to the centroid direction for
the matching centroid's result */
weights_pointer = result_weight(attribute_pointer->weights_head,
area_pointer->result_head, centroid_matching_result);
if (weights_pointer->weight.infinite)
centroid_matching_result->centroid_direction.infinite++;
else
centroid_matching_result->centroid_direction.finite +=
weights_pointer->weight.finite;
}
}
attribute_pointer = attribute_pointer->next;
vector_pointer = vector_pointer->next;
}
}
static void
find_nearest_and_strongest(area *area_pointer)
/* Finds the nearest result, nearest ideal point, nearest centroid, and
strongest directions (specified, ideal point, and centroid) in the area
pointed to by area_pointer, and adjusts various pointers in area_pointer
to point to them. */
{
result *result_pointer,
*equidistant_pointer,
*nearest_result = NULL;
relative_distance_type relative_distance;
kase *nearest_neighbour;
area_pointer->nearest_result = NULL;
area_pointer->nearest_ideal_point = NULL;
area_pointer->nearest_centroid = NULL;
area_pointer->strongest_specified_direction = NULL;
area_pointer->strongest_ideal_point_direction = NULL;
area_pointer->strongest_centroid_direction = NULL;
/* for every result ... */
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next) {
result_pointer->equidistant_next = NULL;
if (result_pointer->nearest_known_compared_with_unknown == FURTHER)
nearest_neighbour = result_pointer->nearest_unknown_case;
else
nearest_neighbour = result_pointer->nearest_known_case;
if (nearest_neighbour != NULL)
/* this result has a nearest neighbour (i.e. it has at least one
case) */
if (nearest_result == NULL)
/* no nearest result has been found yet, so this result is the
nearest so far */
nearest_result = result_pointer;
else {
/* a nearest result has previously been found, so compare the
nearest neighbour for this result with the nearest neighbour
for that nearest result */
if (nearest_result->nearest_known_compared_with_unknown == FURTHER)
relative_distance = Relative_Distance(nearest_neighbour->metrics.distance,
nearest_result->nearest_unknown_case->metrics.distance);
else
relative_distance = Relative_Distance(nearest_neighbour->metrics.distance,
nearest_result->nearest_known_case->metrics.distance);
if (relative_distance == EQUIDISTANT) {
/* the two cases are equidistant from the instant case, so add
this result to the end of the list of equidistant results */
for (equidistant_pointer = nearest_result;
equidistant_pointer->equidistant_next != NULL;
equidistant_pointer = equidistant_pointer->equidistant_next);
equidistant_pointer->equidistant_next = result_pointer;
} else if (relative_distance == NEARER)
/* the nearest neighbour for this result is nearer to the
instant case than the previous nearest neighbour, so this
result becomes the nearest result */
nearest_result = result_pointer;
}
/* check whether this result has the nearest ideal point */
result_pointer->equidistant_ideal_point_next = NULL;
if (result_pointer->ideal_point_head != NULL) {
if (area_pointer->nearest_ideal_point == NULL)
area_pointer->nearest_ideal_point = result_pointer;
else if ((relative_distance =
Relative_Distance(result_pointer->ideal_point_metrics.distance,
area_pointer->nearest_ideal_point->
ideal_point_metrics.distance)) == EQUIDISTANT) {
for (equidistant_pointer = area_pointer->nearest_ideal_point;
equidistant_pointer->equidistant_ideal_point_next != NULL;
equidistant_pointer = equidistant_pointer->equidistant_ideal_point_next);
equidistant_pointer->equidistant_ideal_point_next = result_pointer;
} else if (relative_distance == NEARER)
area_pointer->nearest_ideal_point = result_pointer;
}
/* check whether this result has the nearest centroid */
result_pointer->equidistant_centroid_next = NULL;
if (result_pointer->centroid_head != NULL) {
if (area_pointer->nearest_centroid == NULL)
area_pointer->nearest_centroid = result_pointer;
else if ((relative_distance =
Relative_Distance(result_pointer->centroid_metrics.distance,
area_pointer->nearest_centroid->
centroid_metrics.distance)) == EQUIDISTANT) {
for (equidistant_pointer = area_pointer->nearest_centroid;
equidistant_pointer->equidistant_centroid_next != NULL;
equidistant_pointer = equidistant_pointer->equidistant_centroid_next);
equidistant_pointer->equidistant_centroid_next = result_pointer;
} else if (relative_distance == NEARER)
area_pointer->nearest_centroid = result_pointer;
}
/* check whether this result has the strongest specified direction */
result_pointer->equidistant_specified_direction_next = NULL;
if (area_pointer->strongest_specified_direction == NULL)
area_pointer->strongest_specified_direction = result_pointer;
else if ((relative_distance = relative_subdistance(result_pointer->specified_direction,
area_pointer->strongest_specified_direction->
specified_direction)) == EQUIDISTANT) {
for (equidistant_pointer = area_pointer->strongest_specified_direction;
equidistant_pointer->equidistant_specified_direction_next != NULL;
equidistant_pointer =
equidistant_pointer->equidistant_specified_direction_next);
equidistant_pointer->equidistant_specified_direction_next = result_pointer;
} else if (relative_distance == FURTHER)
area_pointer->strongest_specified_direction = result_pointer;
/* check whether this result has the strongest ideal point direction */
result_pointer->equidistant_ideal_point_direction_next = NULL;
if (area_pointer->strongest_ideal_point_direction == NULL)
area_pointer->strongest_ideal_point_direction = result_pointer;
else if ((relative_distance = relative_subdistance(result_pointer->ideal_point_direction,
area_pointer->strongest_ideal_point_direction->
ideal_point_direction)) == EQUIDISTANT) {
for (equidistant_pointer = area_pointer->strongest_ideal_point_direction;
equidistant_pointer->equidistant_ideal_point_direction_next != NULL;
equidistant_pointer =
equidistant_pointer->equidistant_ideal_point_direction_next);
equidistant_pointer->equidistant_ideal_point_direction_next = result_pointer;
} else if (relative_distance == FURTHER)
area_pointer->strongest_ideal_point_direction = result_pointer;
/* check whether this result has the strongest centroid direction */
result_pointer->equidistant_centroid_direction_next = NULL;
if (area_pointer->strongest_centroid_direction == NULL)
area_pointer->strongest_centroid_direction = result_pointer;
else if ((relative_distance = relative_subdistance(result_pointer->centroid_direction,
area_pointer->strongest_centroid_direction->
centroid_direction)) == EQUIDISTANT) {
for (equidistant_pointer = area_pointer->strongest_centroid_direction;
equidistant_pointer->equidistant_centroid_direction_next != NULL;
equidistant_pointer =
equidistant_pointer->equidistant_centroid_direction_next);
equidistant_pointer->equidistant_centroid_direction_next = result_pointer;
} else if (relative_distance == FURTHER)
area_pointer->strongest_centroid_direction = result_pointer;
}
area_pointer->nearest_result = nearest_result;
/* ensure that none of the strongest directions is so small as to be
effectively zero */
if (Is_Zero_Subdistance(area_pointer->strongest_specified_direction->
specified_direction))
area_pointer->strongest_specified_direction = NULL;
if (Is_Zero_Subdistance(area_pointer->strongest_ideal_point_direction->
ideal_point_direction))
area_pointer->strongest_ideal_point_direction = NULL;
if (Is_Zero_Subdistance(area_pointer->strongest_centroid_direction->
centroid_direction))
area_pointer->strongest_centroid_direction = NULL;
}
static void
resolve_equidistant_results(
file log_stream,
area *area_pointer,
cardinal level)
/* Chooses between two or more nearest results (and sets
area_pointer->nearest_result appropriately) by reference to the rank of
the courts involved in the nearest neighbours, and the recentness of those
cases. Issues a warning as to how the equidistance has been resolved. */
{
result *result_pointer,
*highest_ranking_result,
*most_recent_result;
cardinal highest_ranking_court = 0,
most_recent_year = 0;
kase *nearest_neighbour;
boolean one_highest_ranking_court = FALSE;
boolean one_most_recent_year = FALSE;
/* for each equidistant result ... */
for (result_pointer = area_pointer->nearest_result; result_pointer != NULL;
result_pointer = result_pointer->equidistant_next) {
/* find the nearest neighbour for this result */
if (result_pointer->nearest_known_compared_with_unknown == FURTHER)
nearest_neighbour = result_pointer->nearest_unknown_case;
else
nearest_neighbour = result_pointer->nearest_known_case;
if ((nearest_neighbour->court_string != NULL) &&
(nearest_neighbour->court_rank != 0)) {
/* this case has a court, with a rank */
if ((highest_ranking_court == 0) ||
(nearest_neighbour->court_rank < highest_ranking_court)) {
/* this is the first court for any result, or this court is more
important than the highest ranking court yet found */
highest_ranking_result = result_pointer;
highest_ranking_court = nearest_neighbour->court_rank;
one_highest_ranking_court = TRUE;
/* this must also be the most recent case for this rank so far */
most_recent_result = result_pointer;
most_recent_year = nearest_neighbour->year;
one_most_recent_year = TRUE;
} else if (nearest_neighbour->court_rank == highest_ranking_court) {
/* there are two or more equidistant cases with courts of this
rank */
one_highest_ranking_court = FALSE;
if (nearest_neighbour->year > most_recent_year) {
/* this is the most recent case for this rank so far */
most_recent_result = result_pointer;
most_recent_year = nearest_neighbour->year;
one_most_recent_year = TRUE;
} else if (nearest_neighbour->year == most_recent_year)
/* this case is exactly as old as the most recent case yet
found for this rank */
one_most_recent_year = FALSE;
}
} else {
/* this case has no court, or a court with no rank */
if ((most_recent_year == 0) ||
(nearest_neighbour->year > most_recent_year)) {
/* this is the first case for any result, or this case is more
recent than the most recent yet found */
most_recent_result = result_pointer;
most_recent_year = nearest_neighbour->year;
one_most_recent_year = TRUE;
} else if (nearest_neighbour->year == most_recent_year)
/* this decision is exactly as old as the most recent decision
yet found */
one_most_recent_year = FALSE;
}
}
if (one_highest_ranking_court) {
/* there was only one highest ranking court, so choose that case's
result */
area_pointer->nearest_result = highest_ranking_result;
warning(log_stream,
"equidistant results; nearest result chosen "
"on the basis of rank", level);
return;
}
if (one_most_recent_year) {
/* there was only one case decided as recently as this, so choose that
case's result */
area_pointer->nearest_result = most_recent_result;
warning(log_stream,
"equidistant results; nearest result chosen "
"on the basis of recentness", level);
return;
}
error_exit(log_stream, "can't choose between equidistant results");
}
extern void
Calculate_Distances(
file distances_stream,
file log_stream,
area *area_pointer,
case_law_specification case_law,
vector_element *facts_head,
boolean hypothetical,
cardinal number,
cardinal level)
/* Calculates the distances between the instant case and every leading case,
ideal point and centroid in the area pointed to by area_pointer, and
writes a table of distances to distances_stream (if it is not NULL). 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,
*equidistant_pointer;
relative_distance_type relative_distance;
/* for every result ... */
for (result_pointer = area_pointer->result_head; result_pointer != NULL;
result_pointer = result_pointer->next) {
/* initialize result details */
result_pointer->nearest_known_case = NULL;
result_pointer->nearest_unknown_case = NULL;
zero_metrics(&result_pointer->ideal_point_metrics);
zero_metrics(&result_pointer->centroid_metrics);
zero_subdistance(&result_pointer->specified_direction);
zero_subdistance(&result_pointer->ideal_point_direction);
zero_subdistance(&result_pointer->centroid_direction);
/* for every case with this result ... */
for (case_pointer = result_pointer->case_head; case_pointer != NULL;
case_pointer = case_pointer->next) {
/* initialize case details */
zero_metrics(&case_pointer->metrics);
case_pointer->equidistant_known_next = NULL;
case_pointer->equidistant_unknown_next = NULL;
calculate_case_metrics(log_stream, case_pointer->matrix_head,
facts_head, area_pointer->attribute_head, &case_pointer->metrics,
&area_pointer->correlation_coefficients, level);
if (!Is_Zero_Subdistance(case_pointer->metrics.distance.unknown)) {
/* the case has unknown distance, so check to see whether it is
the nearest unknown neighbour */
if (result_pointer->nearest_unknown_case == NULL)
result_pointer->nearest_unknown_case = case_pointer;
else if ((relative_distance = Relative_Distance(case_pointer->metrics.distance,
result_pointer->nearest_unknown_case->
metrics.distance)) == EQUIDISTANT) {
for (equidistant_pointer = result_pointer->nearest_unknown_case;
equidistant_pointer->equidistant_unknown_next != NULL;
equidistant_pointer
= equidistant_pointer->equidistant_unknown_next);
equidistant_pointer->equidistant_unknown_next = case_pointer;
} else if (relative_distance == NEARER)
result_pointer->nearest_unknown_case = case_pointer;
} else {
/* the case has no unknown distance, so check to see whether it
is the nearest known neighbour */
if (result_pointer->nearest_known_case == NULL)
result_pointer->nearest_known_case = case_pointer;
else if ((relative_distance = Relative_Distance(case_pointer->metrics.distance,
result_pointer->nearest_known_case->
metrics.distance)) == EQUIDISTANT) {
for (equidistant_pointer = result_pointer->nearest_known_case;
equidistant_pointer->equidistant_known_next != NULL;
equidistant_pointer = equidistant_pointer->equidistant_known_next);
equidistant_pointer->equidistant_known_next = case_pointer;
} else if (relative_distance == NEARER)
result_pointer->nearest_known_case = case_pointer;
}
}
/* note which of the nearest known neighbour and the nearest unknown
neighbour is the nearest neighbour */
if ((result_pointer->nearest_known_case == NULL) &&
(result_pointer->nearest_unknown_case == NULL))
result_pointer->nearest_known_compared_with_unknown = EQUIDISTANT;
else if (result_pointer->nearest_known_case == NULL)
result_pointer->nearest_known_compared_with_unknown = FURTHER;
else if (result_pointer->nearest_unknown_case == NULL)
result_pointer->nearest_known_compared_with_unknown = NEARER;
else
result_pointer->nearest_known_compared_with_unknown =
Relative_Distance(result_pointer->nearest_known_case->metrics.distance,
result_pointer->nearest_unknown_case->metrics.distance);
if (result_pointer->ideal_point_head != NULL) {
calculate_ideal_point_metrics(log_stream, result_pointer->ideal_point_head,
facts_head, area_pointer->attribute_head,
&result_pointer->ideal_point_metrics,
&area_pointer->correlation_coefficients, level);
}
calculate_centroid_metrics(log_stream, result_pointer->centroid_head,
facts_head, area_pointer->attribute_head, &result_pointer->centroid_metrics,
&area_pointer->correlation_coefficients, level);
}
calculate_specified_directions(area_pointer->attribute_head, facts_head,
area_pointer->result_head);
calculate_other_directions(area_pointer, facts_head);
find_nearest_and_strongest(area_pointer);
if (area_pointer->nearest_result->equidistant_next != NULL)
/* there are two or more equidistant results, so choose one of them */
resolve_equidistant_results(log_stream, area_pointer, level);
if (distances_stream != NULL) {
if (number == 0)
/* the instant case is the uninstantiated and unhypothesized instant
case */
fprintf(distances_stream, "%s{%s area}\n\n"
"%s{Instant case}\n\n", Heading, area_pointer->identifier, Subheading);
else if (hypothetical)
/* the instant case is hypothetical number */
fprintf(distances_stream, "%s{Hypothetical %u}\n\n", Subheading, number);
else
/* the instant case is instantiation number */
fprintf(distances_stream, "%s{Instantiation %u}\n\n", Subheading, number);
Write_Matrix(distances_stream, area_pointer, facts_head,
case_law.court_head, hypothetical, number);
}
}
Other SHYSTER modules: Shyster, Statutes, Cases, Tokenizer, Parser, Dumper, Checker, Scales, Adjuster, Consultant and Reporter.