/******************************************************************************
 **	Filename:	kdtree.c
 **	Purpose:	Routines for managing K-D search trees
 **	Author:		Dan Johnson
 **	History:	3/10/89, DSJ, Created.
 **			5/23/89, DSJ, Added circular feature capability.
 **			7/13/89, DSJ, Made tree nodes invisible to outside.
 **
 **	(c) Copyright Hewlett-Packard Company, 1988.
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 ** http://www.apache.org/licenses/LICENSE-2.0
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 ******************************************************************************/

/**----------------------------------------------------------------------------
          Include Files and Type Defines
----------------------------------------------------------------------------**/
#include "kdtree.h"
#include "const.h"
#include "emalloc.h"
#include "freelist.h"
#include <stdio.h>
#include <math.h>
#include <setjmp.h>

#define Magnitude(X)    ((X) < 0 ? -(X) : (X))
#define MIN(A,B)    ((A) < (B) ? (A) : (B))
#define NodeFound(N,K,D)  (( (N)->Key == (K) ) && ( (N)->Data == (D) ))

/**----------------------------------------------------------------------------
        Global Data Definitions and Declarations
----------------------------------------------------------------------------**/
#define MINSEARCH -MAX_FLOAT32
#define MAXSEARCH MAX_FLOAT32

static int NumberOfNeighbors;
static inT16 N;                  /* number of dimensions in the kd tree */

static FLOAT32 *QueryPoint;
static int MaxNeighbors;
static FLOAT32 Radius;
static int Furthest;
static char **Neighbor;
static FLOAT32 *Distance;

static int MaxDimension = 0;
static FLOAT32 *SBMin;
static FLOAT32 *SBMax;
static FLOAT32 *LBMin;
static FLOAT32 *LBMax;

static PARAM_DESC *KeyDesc;

static jmp_buf QuickExit;

static void_proc WalkAction;

// Helper function to find the next essential dimension in a cycle.
static int NextLevel(int level) {
  do {
    ++level;
    if (level >= N)
      level = 0;
  } while (KeyDesc[level].NonEssential);
  return level;
}

// Helper function to find the previous essential dimension in a cycle.
static int PrevLevel(int level) {
  do {
    --level;
    if (level < 0)
      level = N - 1;
  } while (KeyDesc[level].NonEssential);
  return level;
}

/**----------------------------------------------------------------------------
              Public Code
----------------------------------------------------------------------------**/
/*---------------------------------------------------------------------------*/
KDTREE *
MakeKDTree (inT16 KeySize, PARAM_DESC KeyDesc[]) {
/*
 **	Parameters:
 **		KeySize		# of dimensions in the K-D tree
 **		KeyDesc		array of params to describe key dimensions
 **	Globals:
 **		MaxDimension	largest # of dimensions in any K-D tree
 **		SBMin		small search region box
 **		SBMax
 **		LBMin		large search region box
 **		LBMax
 **		Key		description of key dimensions
 **	Operation:
 **		This routine allocates and returns a new K-D tree data
 **		structure.  It also reallocates the small and large
 **		search region boxes if they are not large enough to
 **		accomodate the size of the new K-D tree.  KeyDesc is
 **		an array of key descriptors that indicate which dimensions
 **		are circular and, if they are circular, what the range is.
 **	Return:
 **		Pointer to new K-D tree
 **	Exceptions:
 **		None
 **	History:
 **		3/13/89, DSJ, Created.
 */
  int i;
  void *NewMemory;
  KDTREE *KDTree;

  if (KeySize > MaxDimension) {
    NewMemory = Emalloc (KeySize * 4 * sizeof (FLOAT32));
    if (MaxDimension > 0) {
      memfree ((char *) SBMin);
      memfree ((char *) SBMax);
      memfree ((char *) LBMin);
      memfree ((char *) LBMax);
    }
    SBMin = (FLOAT32 *) NewMemory;
    SBMax = SBMin + KeySize;
    LBMin = SBMax + KeySize;
    LBMax = LBMin + KeySize;
  }

  KDTree =
    (KDTREE *) Emalloc (sizeof (KDTREE) +
    (KeySize - 1) * sizeof (PARAM_DESC));
  for (i = 0; i < KeySize; i++) {
    KDTree->KeyDesc[i].NonEssential = KeyDesc[i].NonEssential;
    KDTree->KeyDesc[i].Circular = KeyDesc[i].Circular;
    if (KeyDesc[i].Circular) {
      KDTree->KeyDesc[i].Min = KeyDesc[i].Min;
      KDTree->KeyDesc[i].Max = KeyDesc[i].Max;
      KDTree->KeyDesc[i].Range = KeyDesc[i].Max - KeyDesc[i].Min;
      KDTree->KeyDesc[i].HalfRange = KDTree->KeyDesc[i].Range / 2;
      KDTree->KeyDesc[i].MidRange = (KeyDesc[i].Max + KeyDesc[i].Min) / 2;
    }
    else {
      KDTree->KeyDesc[i].Min = MINSEARCH;
      KDTree->KeyDesc[i].Max = MAXSEARCH;
    }
  }
  KDTree->KeySize = KeySize;
  KDTree->Root.Left = NULL;
  KDTree->Root.Right = NULL;
  return (KDTree);
}                                /* MakeKDTree */


/*---------------------------------------------------------------------------*/
void KDStore(KDTREE *Tree, FLOAT32 *Key, void *Data) {
/*
 **	Parameters:
 **		Tree		K-D tree in which data is to be stored
 **		Key		ptr to key by which data can be retrieved
 **		Data		ptr to data to be stored in the tree
 **	Globals:
 **		N		dimension of the K-D tree
 **		KeyDesc		descriptions of tree dimensions
 **		StoreCount	debug variables for performance tests
 **		StoreUniqueCount
 **		StoreProbeCount
 **	Operation:
 **		This routine stores Data in the K-D tree specified by Tree
 **		using Key as an access key.
 **	Return: none
 **	Exceptions: none
 **	History:	3/10/89, DSJ, Created.
 **			7/13/89, DSJ, Changed return to void.
 */
  int Level;
  KDNODE *Node;
  KDNODE **PtrToNode;

  N = Tree->KeySize;
  KeyDesc = &(Tree->KeyDesc[0]);
  PtrToNode = &(Tree->Root.Left);
  Node = *PtrToNode;
  Level = NextLevel(-1);
  while (Node != NULL) {
    if (Key[Level] < Node->BranchPoint) {
      PtrToNode = &(Node->Left);
      if (Key[Level] > Node->LeftBranch)
        Node->LeftBranch = Key[Level];
    }
    else {
      PtrToNode = &(Node->Right);
      if (Key[Level] < Node->RightBranch)
        Node->RightBranch = Key[Level];
    }
    Level = NextLevel(Level);
    Node = *PtrToNode;
  }

  *PtrToNode = MakeKDNode (Key, (char *) Data, Level);
}                                /* KDStore */


/*---------------------------------------------------------------------------*/
void
KDDelete (KDTREE * Tree, FLOAT32 Key[], void *Data) {
/*
 **	Parameters:
 **		Tree		K-D tree to delete node from
 **		Key		key of node to be deleted
 **		Data		data contents of node to be deleted
 **	Globals:
 **		N		dimension of the K-D tree
 **		KeyDesc		description of each dimension
 **		DeleteCount	debug variables for performance tests
 **		DeleteProbeCount
 **	Operation:
 **		This routine deletes a node from Tree.  The node to be
 **		deleted is specified by the Key for the node and the Data
 **		contents of the node.  These two pointers must be identical
 **		to the pointers that were used for the node when it was
 **		originally stored in the tree.  A node will be deleted from
 **		the tree only if its key and data pointers are identical
 **		to Key and Data respectively.  The empty space left in the tree
 **		is filled by pulling a leaf up from the bottom of one of
 **		the subtrees of the node being deleted.  The leaf node will
 **		be pulled from left subtrees whenever possible (this was
 **		an arbitrary decision).  No attempt is made to pull the leaf
 **		from the deepest subtree (to minimize length).  The branch
 **		point for the replacement node is changed to be the same as
 **		the branch point of the deleted node.  This keeps us from
 **		having to rearrange the tree every time we delete a node.
 **		Also, the LeftBranch and RightBranch numbers of the
 **		replacement node are set to be the same as the deleted node.
 **		The makes the delete easier and more efficient, but it may
 **		make searches in the tree less efficient after many nodes are
 **		deleted.  If the node specified by Key and Data does not
 **		exist in the tree, then nothing is done.
 **	Return: none
 **		None
 **	Exceptions: none
 **		None
 **	History:	3/13/89, DSJ, Created.
 **			7/13/89, DSJ, Specify node indirectly by key and data.
 */
  int Level;
  KDNODE *Current;
  KDNODE *Father;
  KDNODE *Replacement;
  KDNODE *FatherReplacement;

  /* initialize search at root of tree */
  N = Tree->KeySize;
  KeyDesc = &(Tree->KeyDesc[0]);
  Father = &(Tree->Root);
  Current = Father->Left;
  Level = NextLevel(-1);

  /* search tree for node to be deleted */
  while ((Current != NULL) && (!NodeFound (Current, Key, Data))) {
    Father = Current;
    if (Key[Level] < Current->BranchPoint)
      Current = Current->Left;
    else
      Current = Current->Right;

    Level = NextLevel(Level);
  }

  if (Current != NULL) {         /* if node to be deleted was found */
    Replacement = Current;
    FatherReplacement = Father;

    /* search for replacement node (a leaf under node to be deleted */
    while (TRUE) {
      if (Replacement->Left != NULL) {
        FatherReplacement = Replacement;
        Replacement = Replacement->Left;
      }
      else if (Replacement->Right != NULL) {
        FatherReplacement = Replacement;
        Replacement = Replacement->Right;
      }
      else
        break;

      Level = NextLevel(Level);
    }

    /* compute level of replacement node's father */
    Level = PrevLevel(Level);

    /* disconnect replacement node from it's father */
    if (FatherReplacement->Left == Replacement) {
      FatherReplacement->Left = NULL;
      FatherReplacement->LeftBranch = KeyDesc[Level].Min;
    }
    else {
      FatherReplacement->Right = NULL;
      FatherReplacement->RightBranch = KeyDesc[Level].Max;
    }

    /* replace deleted node with replacement (unless they are the same) */
    if (Replacement != Current) {
      Replacement->BranchPoint = Current->BranchPoint;
      Replacement->LeftBranch = Current->LeftBranch;
      Replacement->RightBranch = Current->RightBranch;
      Replacement->Left = Current->Left;
      Replacement->Right = Current->Right;

      if (Father->Left == Current)
        Father->Left = Replacement;
      else
        Father->Right = Replacement;
    }
    FreeKDNode(Current);
  }
}                                /* KDDelete */


/*---------------------------------------------------------------------------*/
int
KDNearestNeighborSearch (KDTREE * Tree,
FLOAT32 Query[],
int QuerySize,
FLOAT32 MaxDistance,
void *NBuffer, FLOAT32 DBuffer[]) {
/*
 **	Parameters:
 **		Tree		ptr to K-D tree to be searched
 **		Query		ptr to query key (point in D-space)
 **		QuerySize	number of nearest neighbors to be found
 **		MaxDistance	all neighbors must be within this distance
 **		NBuffer		ptr to QuerySize buffer to hold nearest neighbors
 **		DBuffer		ptr to QuerySize buffer to hold distances
 **					from nearest neighbor to query point
 **	Globals:
 **		NumberOfNeighbors	# of neighbors found so far
 **		N			# of features in each key
 **		KeyDesc			description of tree dimensions
 **		QueryPoint		point in D-space to find neighbors of
 **		MaxNeighbors		maximum # of neighbors to find
 **		Radius			current distance of furthest neighbor
 **		Furthest		index of furthest neighbor
 **		Neighbor		buffer of current neighbors
 **		Distance		buffer of neighbor distances
 **		SBMin			lower extent of small search region
 **		SBMax			upper extent of small search region
 **		LBMin			lower extent of large search region
 **		LBMax			upper extent of large search region
 **		QuickExit		quick exit from recursive search
 **	Operation:
 **		This routine searches the K-D tree specified by Tree and
 **		finds the QuerySize nearest neighbors of Query.  All neighbors
 **		must be within MaxDistance of Query.  The data contents of
 **		the nearest neighbors
 **		are placed in NBuffer and their distances from Query are
 **		placed in DBuffer.
 **	Return: Number of nearest neighbors actually found
 **	Exceptions: none
 **	History:
 **		3/10/89, DSJ, Created.
 **		7/13/89, DSJ, Return contents of node instead of node itself.
 */
  int i;

  NumberOfNeighbors = 0;
  N = Tree->KeySize;
  KeyDesc = &(Tree->KeyDesc[0]);
  QueryPoint = Query;
  MaxNeighbors = QuerySize;
  Radius = MaxDistance;
  Furthest = 0;
  Neighbor = (char **) NBuffer;
  Distance = DBuffer;

  for (i = 0; i < N; i++) {
    SBMin[i] = KeyDesc[i].Min;
    SBMax[i] = KeyDesc[i].Max;
    LBMin[i] = KeyDesc[i].Min;
    LBMax[i] = KeyDesc[i].Max;
  }

  if (Tree->Root.Left != NULL) {
    if (setjmp (QuickExit) == 0)
      Search (0, Tree->Root.Left);
  }
  return (NumberOfNeighbors);
}                                /* KDNearestNeighborSearch */


/*---------------------------------------------------------------------------*/
void KDWalk(KDTREE *Tree, void_proc Action) {
/*
 **	Parameters:
 **		Tree	ptr to K-D tree to be walked
 **		Action	ptr to function to be executed at each node
 **	Globals:
 **		WalkAction	action to be performed at every node
 **	Operation:
 **		This routine stores the desired action in a global
 **		variable and starts a recursive walk of Tree.  The walk
 **		is started at the root node.
 **	Return:
 **		None
 **	Exceptions:
 **		None
 **	History:
 **		3/13/89, DSJ, Created.
 */
  WalkAction = Action;
  if (Tree->Root.Left != NULL)
    Walk (Tree->Root.Left, NextLevel(-1));
}                                /* KDWalk */


/*---------------------------------------------------------------------------*/
void FreeKDTree(KDTREE *Tree) {
/*
 **	Parameters:
 **		Tree	tree data structure to be released
 **	Globals: none
 **	Operation:
 **		This routine frees all memory which is allocated to the
 **		specified KD-tree.  This includes the data structure for
 **		the kd-tree itself plus the data structures for each node
 **		in the tree.  It does not include the Key and Data items
 **		which are pointed to by the nodes.  This memory is left
 **		untouched.
 **	Return: none
 **	Exceptions: none
 **	History:
 **		5/26/89, DSJ, Created.
 */
  FreeSubTree (Tree->Root.Left);
  memfree(Tree);
}                                /* FreeKDTree */


/**----------------------------------------------------------------------------
              Private Code
----------------------------------------------------------------------------**/
/*---------------------------------------------------------------------------*/
int
Equal (FLOAT32 Key1[], FLOAT32 Key2[]) {
/*
 **	Parameters:
 **		Key1,Key2	search keys to be compared for equality
 **	Globals:
 **		N		number of parameters per key
 **	Operation:
 **		This routine returns TRUE if Key1 = Key2.
 **	Return:
 **		TRUE if Key1 = Key2, else FALSE.
 **	Exceptions:
 **		None
 **	History:
 **		3/11/89, DSJ, Created.
 */
  int i;

  for (i = N; i > 0; i--, Key1++, Key2++)
    if (*Key1 != *Key2)
      return (FALSE);
  return (TRUE);
}                                /* Equal */


/*---------------------------------------------------------------------------*/
KDNODE *
MakeKDNode (FLOAT32 Key[], char *Data, int Index) {
/*
 **	Parameters:
 **		Key	Access key for new node in KD tree
 **		Data	ptr to data to be stored in new node
 **		Index	index of Key to branch on
 **	Globals:
 **		KeyDesc	descriptions of key dimensions
 **	Operation:
 **		This routine allocates memory for a new K-D tree node
 **		and places the specified Key and Data into it.  The
 **		left and right subtree pointers for the node are
 **		initialized to empty subtrees.
 **	Return:
 **		pointer to new K-D tree node
 **	Exceptions:
 **		None
 **	History:
 **		3/11/89, DSJ, Created.
 */
  KDNODE *NewNode;

  NewNode = (KDNODE *) Emalloc (sizeof (KDNODE));

  NewNode->Key = Key;
  NewNode->Data = Data;
  NewNode->BranchPoint = Key[Index];
  NewNode->LeftBranch = KeyDesc[Index].Min;
  NewNode->RightBranch = KeyDesc[Index].Max;
  NewNode->Left = NULL;
  NewNode->Right = NULL;

  return (NewNode);
}                                /* MakeKDNode */


/*---------------------------------------------------------------------------*/
void FreeKDNode(KDNODE *Node) {
/*
 **	Parameters:
 **		Node	ptr to node data structure to be freed
 **	Globals:
 **		None
 **	Operation:
 **		This routine frees up the memory allocated to Node.
 **	Return:
 **		None
 **	Exceptions:
 **		None
 **	History:
 **		3/13/89, DSJ, Created.
 */
  memfree ((char *) Node);
}                                /* FreeKDNode */


/*---------------------------------------------------------------------------*/
void Search(int Level, KDNODE *SubTree) {
/*
 **	Parameters:
 **		Level		level in tree of sub-tree to be searched
 **		SubTree		sub-tree to be searched
 **	Globals:
 **		NumberOfNeighbors	# of neighbors found so far
 **		N			# of features in each key
 **		KeyDesc			description of key dimensions
 **		QueryPoint		point in D-space to find neighbors of
 **		MaxNeighbors		maximum # of neighbors to find
 **		Radius			current distance of furthest neighbor
 **		Furthest		index of furthest neighbor
 **		Neighbor		buffer of current neighbors
 **		Distance		buffer of neighbor distances
 **		SBMin			lower extent of small search region
 **		SBMax			upper extent of small search region
 **		LBMin			lower extent of large search region
 **		LBMax			upper extent of large search region
 **		QuickExit		quick exit from recursive search
 **	Operation:
 **		This routine searches SubTree for those entries which are
 **		possibly among the MaxNeighbors nearest neighbors of the
 **		QueryPoint and places their data in the Neighbor buffer and
 **		their distances from QueryPoint in the Distance buffer.
 **	Return: none
 **	Exceptions: none
 **	History:
 **		3/11/89, DSJ, Created.
 **		7/13/89, DSJ, Save node contents, not node, in neighbor buffer
 */
  FLOAT32 d;
  FLOAT32 OldSBoxEdge;
  FLOAT32 OldLBoxEdge;

  if (Level >= N)
    Level = 0;

  d = ComputeDistance (N, KeyDesc, QueryPoint, SubTree->Key);
  if (d < Radius) {
    if (NumberOfNeighbors < MaxNeighbors) {
      Neighbor[NumberOfNeighbors] = SubTree->Data;
      Distance[NumberOfNeighbors] = d;
      NumberOfNeighbors++;
      if (NumberOfNeighbors == MaxNeighbors)
        FindMaxDistance();
    }
    else {
      Neighbor[Furthest] = SubTree->Data;
      Distance[Furthest] = d;
      FindMaxDistance();
    }
  }
  if (QueryPoint[Level] < SubTree->BranchPoint) {
    OldSBoxEdge = SBMax[Level];
    SBMax[Level] = SubTree->LeftBranch;
    OldLBoxEdge = LBMax[Level];
    LBMax[Level] = SubTree->RightBranch;
    if (SubTree->Left != NULL)
      Search (NextLevel(Level), SubTree->Left);
    SBMax[Level] = OldSBoxEdge;
    LBMax[Level] = OldLBoxEdge;
    OldSBoxEdge = SBMin[Level];
    SBMin[Level] = SubTree->RightBranch;
    OldLBoxEdge = LBMin[Level];
    LBMin[Level] = SubTree->LeftBranch;
    if ((SubTree->Right != NULL) && QueryIntersectsSearch ())
      Search (NextLevel(Level), SubTree->Right);
    SBMin[Level] = OldSBoxEdge;
    LBMin[Level] = OldLBoxEdge;
  }
  else {
    OldSBoxEdge = SBMin[Level];
    SBMin[Level] = SubTree->RightBranch;
    OldLBoxEdge = LBMin[Level];
    LBMin[Level] = SubTree->LeftBranch;
    if (SubTree->Right != NULL)
      Search (NextLevel(Level), SubTree->Right);
    SBMin[Level] = OldSBoxEdge;
    LBMin[Level] = OldLBoxEdge;
    OldSBoxEdge = SBMax[Level];
    SBMax[Level] = SubTree->LeftBranch;
    OldLBoxEdge = LBMax[Level];
    LBMax[Level] = SubTree->RightBranch;
    if ((SubTree->Left != NULL) && QueryIntersectsSearch ())
      Search (NextLevel(Level), SubTree->Left);
    SBMax[Level] = OldSBoxEdge;
    LBMax[Level] = OldLBoxEdge;
  }
  if (QueryInSearch ())
    longjmp (QuickExit, 1);
}                                /* Search */


/*---------------------------------------------------------------------------*/
FLOAT32
ComputeDistance (register int N,
register PARAM_DESC Dim[],
register FLOAT32 p1[], register FLOAT32 p2[]) {
/*
 **	Parameters:
 **		N		number of dimensions in K-D space
 **		Dim		descriptions of each dimension
 **		p1,p2		two different points in K-D space
 **	Globals:
 **		None
 **	Operation:
 **		This routine computes the euclidian distance
 **		between p1 and p2 in K-D space (an N dimensional space).
 **	Return:
 **		Distance between p1 and p2.
 **	Exceptions:
 **		None
 **	History:
 **		3/11/89, DSJ, Created.
 */
  register FLOAT32 TotalDistance;
  register FLOAT32 DimensionDistance;
  FLOAT32 WrapDistance;

  TotalDistance = 0;
  for (; N > 0; N--, p1++, p2++, Dim++) {
    if (Dim->NonEssential)
      continue;

    DimensionDistance = *p1 - *p2;

    /* if this dimension is circular - check wraparound distance */
    if (Dim->Circular) {
      DimensionDistance = Magnitude (DimensionDistance);
      WrapDistance = Dim->Max - Dim->Min - DimensionDistance;
      DimensionDistance = MIN (DimensionDistance, WrapDistance);
    }

    TotalDistance += DimensionDistance * DimensionDistance;
  }
  return ((FLOAT32) sqrt ((FLOAT64) TotalDistance));
}                                /* ComputeDistance */


/*---------------------------------------------------------------------------*/
void FindMaxDistance() {
/*
 **	Parameters:
 **		None
 **	Globals:
 **		MaxNeighbors		maximum # of neighbors to find
 **		Radius			current distance of furthest neighbor
 **		Furthest		index of furthest neighbor
 **		Distance		buffer of neighbor distances
 **	Operation:
 **		This routine searches the Distance buffer for the maximum
 **		distance, places this distance in Radius, and places the
 **		index of this distance in Furthest.
 **	Return:
 **		None
 **	Exceptions:
 **		None
 **	History:
 **		3/11/89, DSJ, Created.
 */
  int i;

  Radius = Distance[Furthest];
  for (i = 0; i < MaxNeighbors; i++) {
    if (Distance[i] > Radius) {
      Radius = Distance[i];
      Furthest = i;
    }
  }
}                                /* FindMaxDistance */


/*---------------------------------------------------------------------------*/
int QueryIntersectsSearch() {
/*
 **	Parameters:
 **		None
 **	Globals:
 **		N			# of features in each key
 **		KeyDesc			descriptions of each dimension
 **		QueryPoint		point in D-space to find neighbors of
 **		Radius			current distance of furthest neighbor
 **		SBMin			lower extent of small search region
 **		SBMax			upper extent of small search region
 **	Operation:
 **		This routine returns TRUE if the query region intersects
 **		the current smallest search region.  The query region is
 **		the circle of radius Radius centered at QueryPoint.
 **		The smallest search region is the box (in N dimensions)
 **		whose edges in each dimension are specified by SBMin and SBMax.
 **		In the case of circular dimensions, we must also check the
 **		point which is one wrap-distance away from the query to
 **		see if it would intersect the search region.
 **	Return:
 **		TRUE if query region intersects search region, else FALSE
 **	Exceptions:
 **		None
 **	History:
 **		3/11/89, DSJ, Created.
 */
  register int i;
  register FLOAT32 *Query;
  register FLOAT32 *Lower;
  register FLOAT32 *Upper;
  register FLOAT64 TotalDistance;
  register FLOAT32 DimensionDistance;
  register FLOAT64 RadiusSquared;
  register PARAM_DESC *Dim;
  register FLOAT32 WrapDistance;

  RadiusSquared = Radius * Radius;
  Query = QueryPoint;
  Lower = SBMin;
  Upper = SBMax;
  TotalDistance = 0.0;
  Dim = KeyDesc;
  for (i = N; i > 0; i--, Dim++, Query++, Lower++, Upper++) {
    if (Dim->NonEssential)
      continue;

    if (*Query < *Lower)
      DimensionDistance = *Lower - *Query;
    else if (*Query > *Upper)
      DimensionDistance = *Query - *Upper;
    else
      DimensionDistance = 0;

    /* if this dimension is circular - check wraparound distance */
    if (Dim->Circular) {
      if (*Query < *Lower)
        WrapDistance = *Query + Dim->Max - Dim->Min - *Upper;
      else if (*Query > *Upper)
        WrapDistance = *Lower - (*Query - (Dim->Max - Dim->Min));
      else
        WrapDistance = MAX_FLOAT32;

      DimensionDistance = MIN (DimensionDistance, WrapDistance);
    }

    TotalDistance += DimensionDistance * DimensionDistance;
    if (TotalDistance >= RadiusSquared)
      return (FALSE);
  }
  return (TRUE);
}                                /* QueryIntersectsSearch */


/*---------------------------------------------------------------------------*/
int QueryInSearch() {
/*
 **	Parameters:
 **		None
 **	Globals:
 **		N			# of features in each key
 **		KeyDesc			descriptions of each dimension
 **		QueryPoint		point in D-space to find neighbors of
 **		Radius			current distance of furthest neighbor
 **		LBMin			lower extent of large search region
 **		LBMax			upper extent of large search region
 **	Operation:
 **		This routine returns TRUE if the current query region is
 **		totally contained in the current largest search region.
 **		The query region is the circle of
 **		radius Radius centered at QueryPoint.  The search region is
 **		the box (in N dimensions) whose edges in each
 **		dimension are specified by LBMin and LBMax.
 **	Return:
 **		TRUE if query region is inside search region, else FALSE
 **	Exceptions:
 **		None
 **	History:
 **		3/11/89, DSJ, Created.
 */
  register int i;
  register FLOAT32 *Query;
  register FLOAT32 *Lower;
  register FLOAT32 *Upper;
  register PARAM_DESC *Dim;

  Query = QueryPoint;
  Lower = LBMin;
  Upper = LBMax;
  Dim = KeyDesc;

  for (i = N - 1; i >= 0; i--, Dim++, Query++, Lower++, Upper++) {
    if (Dim->NonEssential)
      continue;

    if ((*Query < *Lower + Radius) || (*Query > *Upper - Radius))
      return (FALSE);
  }
  return (TRUE);
}                                /* QueryInSearch */


/*---------------------------------------------------------------------------*/
void Walk(KDNODE *SubTree, inT32 Level) {
/*
 **	Parameters:
 **		SubTree		ptr to root of subtree to be walked
 **		Level		current level in the tree for this node
 **	Globals:
 **		WalkAction	action to be performed at every node
 **	Operation:
 **		This routine walks thru the specified SubTree and invokes
 **		WalkAction at each node.  WalkAction is invoked with three
 **		arguments as follows:
 **			WalkAction( NodeData, Order, Level )
 **		Data is the data contents of the node being visited,
 **		Order is either preorder,
 **		postorder, endorder, or leaf depending on whether this is
 **		the 1st, 2nd, or 3rd time a node has been visited, or
 **		whether the node is a leaf.  Level is the level of the node in
 **		the tree with the root being level 0.
 **	Return: none
 **	Exceptions: none
 **	History:
 **		3/13/89, DSJ, Created.
 **		7/13/89, DSJ, Pass node contents, not node, to WalkAction().
 */
  if ((SubTree->Left == NULL) && (SubTree->Right == NULL))
    (*WalkAction) (SubTree->Data, leaf, Level);
  else {
    (*WalkAction) (SubTree->Data, preorder, Level);
    if (SubTree->Left != NULL)
      Walk (SubTree->Left, NextLevel(Level));
    (*WalkAction) (SubTree->Data, postorder, Level);
    if (SubTree->Right != NULL)
      Walk (SubTree->Right, NextLevel(Level));
    (*WalkAction) (SubTree->Data, endorder, Level);
  }
}                                /* Walk */


/*---------------------------------------------------------------------------*/
void FreeSubTree(KDNODE *SubTree) {
/*
 **	Parameters:
 **		SubTree		ptr to root node of sub-tree to be freed
 **	Globals: none
 **	Operation:
 **		This routine recursively frees the memory allocated to
 **		to the specified subtree.
 **	Return: none
 **	Exceptions: none
 **	History: 7/13/89, DSJ, Created.
 */
  if (SubTree != NULL) {
    FreeSubTree (SubTree->Left);
    FreeSubTree (SubTree->Right);
    memfree(SubTree);
  }
}                                /* FreeSubTree */
