odometer.h

/* 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);

odometer.c

/* 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.
Copyright noticeValid HTML 4.0
Home page:  <http://www.popple.net/james/>
E-mail:  <james@popple.net>
Last modified:  30 April 1995