#ifndef ROOT_TKDTree
#define ROOT_TKDTree
#ifndef ROOT_TObject
#include "TObject.h"
#endif
#include "TMath.h"
#include <vector>
template <typename Index, typename Value> class TKDTree : public TObject
{
public:
TKDTree();
TKDTree(Index npoints, Index ndim, UInt_t bsize);
TKDTree(Index npoints, Index ndim, UInt_t bsize, Value **data);
~TKDTree();
void Build();
Double_t Distance(const Value *point, Index ind, Int_t type=2) const;
void DistanceToNode(const Value *point, Index inode, Value &min, Value &max, Int_t type=2);
Int_t GetLeft(Int_t inode) const {return inode*2+1;}
Int_t GetRight(Int_t inode) const {return (inode+1)*2;}
Int_t GetParent(Int_t inode) const {return (inode-1)/2;}
Index* GetPointsIndexes(Int_t node) const;
void GetNodePointsIndexes(Int_t node, Int_t &first1, Int_t &last1, Int_t &first2, Int_t &last2) const;
UChar_t GetNodeAxis(Int_t id) const {return (id < 0 || id >= fNNodes) ? 0 : fAxis[id];}
Value GetNodeValue(Int_t id) const {return (id < 0 || id >= fNNodes) ? 0 : fValue[id];}
Int_t GetNNodes() const {return fNNodes;}
Int_t GetTotalNodes() const {return fTotalNodes;}
Value* GetBoundaries();
Value* GetBoundariesExact();
Value* GetBoundary(const Int_t node);
Value* GetBoundaryExact(const Int_t node);
Index GetNPoints() { return fNPoints; }
Index GetNDim() { return fNDim; }
Index GetNPointsNode(Int_t node) const;
Int_t GetRowT0() {return fRowT0;}
Int_t GetCrossNode() {return fCrossNode;}
Int_t GetOffset() {return fOffset;}
Index* GetIndPoints() {return fIndPoints;}
Index GetBucketSize() {return fBucketSize;}
void FindNearestNeighbors(const Value *point, Int_t k, Index *ind, Value *dist);
Index FindNode(const Value * point) const;
void FindPoint(Value * point, Index &index, Int_t &iter);
void FindInRange(Value *point, Value range, std::vector<Index> &res);
void FindBNodeA(Value * point, Value * delta, Int_t &inode);
Bool_t IsTerminal(Index inode) const {return (inode>=fNNodes);}
Int_t IsOwner() { return fDataOwner; }
Value KOrdStat(Index ntotal, Value *a, Index k, Index *index) const;
void MakeBoundaries(Value *range = 0x0);
void MakeBoundariesExact();
void SetData(Index npoints, Index ndim, UInt_t bsize, Value **data);
Int_t SetData(Index idim, Value *data);
void SetOwner(Int_t owner) { fDataOwner = owner; }
void Spread(Index ntotal, Value *a, Index *index, Value &min, Value &max) const;
private:
TKDTree(const TKDTree &);
TKDTree<Index, Value>& operator=(const TKDTree<Index, Value>&);
void CookBoundaries(const Int_t node, Bool_t left);
void UpdateNearestNeighbors(Index inode, const Value *point, Int_t kNN, Index *ind, Value *dist);
void UpdateRange(Index inode, Value *point, Value range, std::vector<Index> &res);
protected:
Int_t fDataOwner;
Int_t fNNodes;
Int_t fTotalNodes;
Index fNDim;
Index fNDimm;
Index fNPoints;
Index fBucketSize;
UChar_t *fAxis;
Value *fValue;
Value *fRange;
Value **fData;
Value *fBoundaries;
Index *fIndPoints;
Int_t fRowT0;
Int_t fCrossNode;
Int_t fOffset;
ClassDef(TKDTree, 1)
};
typedef TKDTree<Int_t, Double_t> TKDTreeID;
typedef TKDTree<Int_t, Float_t> TKDTreeIF;
TKDTreeIF * TKDTreeTestBuild();
#endif