/*
   File:        distutils.c
   Author:      Andrew W. Moore
   Created:     June 2000
   Description: Utilities for measuring metric distances between points
                and rectangles.

   Copyright 2000, Schenley Park Research
*/

#include "distutils.h"

#define MIN_DIST 0
#define MAX_DIST 1
#define MID_DIST 2

int Num_pt_dists = 0;
int Num_hr_dists = 0;

double hrect_metric_dsqd(dyv *metric,hrect *a,hrect *b,int dist_type)
{
  double result = 0.0;
  int i;
  for ( i = 0 ; i < dyv_size(metric) ; i++ )
  {
    double delta;
    double alo = hrect_lo_ref(a,i);
    double ahi = hrect_hi_ref(a,i);
    double blo = hrect_lo_ref(b,i);
    double bhi = hrect_hi_ref(b,i);
    if ( dist_type == MIN_DIST )
    {
      if ( ahi < blo )
        delta = blo - ahi;
      else if ( bhi < alo )
        delta = alo - bhi;
      else
        delta = 0.0;
    }
    else if ( dist_type == MAX_DIST )
      delta = real_max(ahi-blo,bhi-alo);
    else
      delta = ((ahi+alo) - (bhi+blo))/2.0;

    delta /= dyv_ref(metric,i);
    result += delta * delta;
  }
  Num_hr_dists += 1;

  return result;
}

double hrect_metric_dist(dyv *metric,hrect *a,hrect *b,int dist_type)
{
  return sqrt(hrect_metric_dsqd(metric,a,b,dist_type));
}

double hrect_midpoint_metric_dsqd(dyv *metric,hrect *a,hrect *b)
{
  return hrect_metric_dsqd(metric,a,b,MID_DIST);
}

double hrect_midpoint_metric_dist(dyv *metric,hrect *a,hrect *b)
{
  return hrect_metric_dist(metric,a,b,MID_DIST);
}

double hrect_min_metric_dist(dyv *metric,hrect *a,hrect *b)
{
  return hrect_metric_dist(metric,a,b,MIN_DIST);
}

double hrect_max_metric_dist(dyv *metric,hrect *a,hrect *b)
{
  return hrect_metric_dist(metric,a,b,MAX_DIST);
}

double hrect_min_metric_dsqd(dyv *metric,hrect *a,hrect *b)
{
  return hrect_metric_dsqd(metric,a,b,MIN_DIST);
}

double hrect_max_metric_dsqd(dyv *metric,hrect *a,hrect *b)
{
  return hrect_metric_dsqd(metric,a,b,MAX_DIST);
}

double metric_dsqd(dyv *metric,dyv *x1,dyv *x2)
{
  double result = 0.0;
  int i;
  for ( i = dyv_size(x1)-1 ; i >= 0 ; i-- )
  {
    double d = ( dyv_ref(x1,i) - dyv_ref(x2,i) ) / dyv_ref(metric,i);
    result += d * d;
  }
  Num_pt_dists += 1;
  return result;
}
double row_metric_dsqd(dym *x1,dym *x2,dyv *metric,int row1,int row2)
{
  double result = 0.0;
  int i;
  for ( i = dym_cols(x1)-1 ; i >= 0 ; i-- )
  {
    double d = ( dym_ref(x1,row1,i) - dym_ref(x2,row2,i) ) / dyv_ref(metric,i);
    result += d * d;
  }
  Num_pt_dists += 1;
  return result;
}

double row_dyv_metric_dsqd(dym *x,dyv *metric,int row,dyv *query)
{
  double result = 0.0;
  int i;
  for ( i = dym_cols(x)-1 ; i >= 0 ; i-- )
  {
    double d = ( dym_ref(x,row,i) - dyv_ref(query,i) ) / dyv_ref(metric,i);
    result += d * d;
  }
  return result;
}

double hrect_dyv_metric_dsqd(dyv *metric,hrect *hr,dyv *x,int dist_type)
{
  double result = 0.0;
  int i;
  for ( i = 0 ; i < dyv_size(metric) ; i++ )
  {
    double delta;
    double alo = hrect_lo_ref(hr,i);
    double ahi = hrect_hi_ref(hr,i);
    double b = dyv_ref(x,i);

    if ( dist_type == MIN_DIST )
    {
      if ( ahi < b )
        delta = b - ahi;
      else if ( b < alo )
        delta = alo - b;
      else
        delta = 0.0;
    }
    else if ( dist_type == MAX_DIST )
      delta = real_max(ahi-b,b-alo);
    else
      delta = ((ahi+alo) - b)/2.0;

    delta /= dyv_ref(metric,i);
    result += delta * delta;
  }

  return result;
}

double hrect_dyv_min_metric_dsqd(dyv *metric,hrect *hr,dyv *x)
{
  return hrect_dyv_metric_dsqd(metric,hr,x,MIN_DIST);
}

double hrect_dyv_max_metric_dsqd(dyv *metric,hrect *hr,dyv *x)
{
  return hrect_dyv_metric_dsqd(metric,hr,x,MAX_DIST);
}

