//----------------------------------------------------------------------
// File:			kd_tree.h
// Programmer:		Sunil Arya and David Mount
// Description:		Declarations for standard kd-tree routines
// Last modified:	05/03/05 (Version 1.1)
//----------------------------------------------------------------------
// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
// David Mount.  All Rights Reserved.
//
// This software and related documentation is part of the Approximate
// Nearest Neighbor Library (ANN).  This software is provided under
// the provisions of the Lesser GNU Public License (LGPL).  See the
// file ../ReadMe.txt for further information.
//
// The University of Maryland (U.M.) and the authors make no
// representations about the suitability or fitness of this software for
// any purpose.  It is provided "as is" without express or implied
// warranty.
//----------------------------------------------------------------------
// History:
//	Revision 0.1  03/04/98
//		Initial release
//	Revision 1.1  05/03/05
//		Added fixed radius kNN search
//----------------------------------------------------------------------

#ifndef ANN_kd_tree_H
#define ANN_kd_tree_H

#include <ANN/ANNx.h> // all ANN includes

using namespace std; // make std:: available

//----------------------------------------------------------------------
//	Generic kd-tree node
//
//		Nodes in kd-trees are of two types, splitting nodes which contain
//		splitting information (a splitting hyperplane orthogonal to one
//		of the coordinate axes) and leaf nodes which contain point
//		information (an array of points stored in a bucket).  This is
//		handled by making a generic class kd_node, which is essentially an
//		empty shell, and then deriving the leaf and splitting nodes from
//		this.
//----------------------------------------------------------------------

class ANNkd_node
{ // generic kd-tree node (empty shell)
public:
  virtual ~ANNkd_node() = default; // virtual distroyer

  virtual void ann_search(ANNdist) = 0;     // tree search
  virtual void ann_pri_search(ANNdist) = 0; // priority search
  virtual void ann_FR_search(ANNdist) = 0;  // fixed-radius search

  virtual void
  getStats(                     // get tree statistics
    int           dim,          // dimension of space
    ANNkdStats &  st,           // statistics
    ANNorthRect & bnd_box) = 0; // bounding box
                                // print node
  virtual void
  print(int level, ostream & out) = 0;
  virtual void
  dump(ostream & out) = 0; // dump node

  friend class ANNkd_tree; // allow kd-tree to access us
};

//----------------------------------------------------------------------
//	kd-splitting function:
//		kd_splitter is a pointer to a splitting routine for preprocessing.
//		Different splitting procedures result in different strategies
//		for building the tree.
//----------------------------------------------------------------------

typedef void (*ANNkd_splitter)( // splitting routine for kd-trees
  ANNpointArray       pa,       // point array (unaltered)
  ANNidxArray         pidx,     // point indices (permuted on return)
  const ANNorthRect & bnds,     // bounding rectangle for cell
  int                 n,        // number of points
  int                 dim,      // dimension of space
  int &               cut_dim,  // cutting dimension (returned)
  ANNcoord &          cut_val,  // cutting value (returned)
  int &               n_lo);                  // num of points on low side (returned)

//----------------------------------------------------------------------
//	Leaf kd-tree node
//		Leaf nodes of the kd-tree store the set of points associated
//		with this bucket, stored as an array of point indices.  These
//		are indices in the array points, which resides with the
//		root of the kd-tree.  We also store the number of points
//		that reside in this bucket.
//----------------------------------------------------------------------

class ANNkd_leaf : public ANNkd_node // leaf node for kd-tree
{
  int         n_pts; // no. points in bucket
  ANNidxArray bkt;   // bucket of points
public:
  ANNkd_leaf(      // constructor
    int         n, // number of points
    ANNidxArray b) // bucket
  {
    n_pts = n; // number of points in bucket
    bkt = b;   // the bucket
  }

  ~ANNkd_leaf() override = default; // destructor (none)

  void
  getStats(                          // get tree statistics
    int           dim,               // dimension of space
    ANNkdStats &  st,                // statistics
    ANNorthRect & bnd_box) override; // bounding box
  void
  print(int level, ostream & out) override; // print node
  void
  dump(ostream & out) override; // dump node

  void ann_search(ANNdist) override;     // standard search
  void ann_pri_search(ANNdist) override; // priority search
  void ann_FR_search(ANNdist) override;  // fixed-radius search
};

//----------------------------------------------------------------------
//		KD_TRIVIAL is a special pointer to an empty leaf node. Since
//		some splitting rules generate many (more than 50%) trivial
//		leaves, we use this one shared node to save space.
//
//		The pointer is initialized to NULL, but whenever a kd-tree is
//		created, we allocate this node, if it has not already been
//		allocated. This node is *never* deallocated, so it produces
//		a small memory leak.
//----------------------------------------------------------------------

extern ANNkd_leaf * KD_TRIVIAL; // trivial (empty) leaf node

//----------------------------------------------------------------------
//	kd-tree splitting node.
//		Splitting nodes contain a cutting dimension and a cutting value.
//		These indicate the axis-parellel plane which subdivide the
//		box for this node. The extent of the bounding box along the
//		cutting dimension is maintained (this is used to speed up point
//		to box distance calculations) [we do not store the entire bounding
//		box since this may be wasteful of space in high dimensions].
//		We also store pointers to the 2 children.
//----------------------------------------------------------------------

class ANNkd_split : public ANNkd_node // splitting node of a kd-tree
{
  int      cut_dim;    // dim orthogonal to cutting plane
  ANNcoord cut_val;    // location of cutting plane
  ANNcoord cd_bnds[2]; // lower and upper bounds of
                       // rectangle along cut_dim
  ANNkd_ptr child[2];  // left and right children
public:
  ANNkd_split(    // constructor
    int       cd, // cutting dimension
    ANNcoord  cv, // cutting value
    ANNcoord  lv,
    ANNcoord  hv, // low and high values
    ANNkd_ptr lc = NULL,
    ANNkd_ptr hc = NULL) // children
  {
    cut_dim = cd;         // cutting dimension
    cut_val = cv;         // cutting value
    cd_bnds[ANN_LO] = lv; // lower bound for rectangle
    cd_bnds[ANN_HI] = hv; // upper bound for rectangle
    child[ANN_LO] = lc;   // left child
    child[ANN_HI] = hc;   // right child
  }

  ~ANNkd_split() override // destructor
  {
    if (child[ANN_LO] != NULL && child[ANN_LO] != KD_TRIVIAL)
      delete child[ANN_LO];
    if (child[ANN_HI] != NULL && child[ANN_HI] != KD_TRIVIAL)
      delete child[ANN_HI];
  }

  void
  getStats(                          // get tree statistics
    int           dim,               // dimension of space
    ANNkdStats &  st,                // statistics
    ANNorthRect & bnd_box) override; // bounding box
  void
  print(int level, ostream & out) override; // print node
  void
  dump(ostream & out) override; // dump node

  void ann_search(ANNdist) override;     // standard search
  void ann_pri_search(ANNdist) override; // priority search
  void ann_FR_search(ANNdist) override;  // fixed-radius search
};

//----------------------------------------------------------------------
//		External entry points
//----------------------------------------------------------------------

ANNkd_ptr
rkd_tree(                   // recursive construction of kd-tree
  ANNpointArray  pa,        // point array (unaltered)
  ANNidxArray    pidx,      // point indices to store in subtree
  int            n,         // number of points
  int            dim,       // dimension of space
  int            bsp,       // bucket space
  ANNorthRect &  bnd_box,   // bounding box for current node
  ANNkd_splitter splitter); // splitting routine

#endif
