//TITLE
//
//    R-TREES: A DYNAMIC INDEX STRUCTURE FOR SPATIAL SEARCHING
//
//DESCRIPTION
//
//    A C++ templated version of the RTree algorithm.
//    For more information please read the comments in RTree.h
//
//AUTHORS
//
//    * 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely
//    * 1994 ANCI C ported from original test code by Melinda Green - melinda@superliminal.com
//    * 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook
//    * 2004 Templated C++ port by Greg Douglas
//    * 2013 CERN (www.cern.ch)
//    * 2020 KiCad Developers - Add std::iterator support for searching
//    * 2020 KiCad Developers - Add container nearest neighbor based on Hjaltason & Samet
//

/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
 * Copyright (C) 2013 CERN
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-3.0.html
 * or you may search the http://www.gnu.org website for the version 3 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#ifndef RTREE_H
#define RTREE_H

// NOTE This file compiles under MSVC 6 SP5 and MSVC .Net 2003 it may not work on other compilers without modification.

// NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>

#include <algorithm>
#include <array>
#include <functional>
#include <iterator>
#include <limits>
#include <queue>
#include <vector>

#ifdef DEBUG
#define ASSERT assert    // RTree uses ASSERT( condition )
#else
#define ASSERT( _x )
#endif

//
// RTree.h
//

#define RTREE_TEMPLATE          template <class DATATYPE, class ELEMTYPE, int NUMDIMS, \
    class ELEMTYPEREAL, int TMAXNODES, int TMINNODES>
#define RTREE_SEARCH_TEMPLATE   template <class DATATYPE, class ELEMTYPE, int NUMDIMS, \
    class ELEMTYPEREAL, int TMAXNODES, int TMINNODES, class VISITOR>
#define RTREE_QUAL              RTree<DATATYPE, ELEMTYPE, NUMDIMS, ELEMTYPEREAL, TMAXNODES, \
    TMINNODES>
#define RTREE_SEARCH_QUAL       RTree<DATATYPE, ELEMTYPE, NUMDIMS, ELEMTYPEREAL, TMAXNODES, \
    TMINNODES, VISITOR>

#define RTREE_DONT_USE_MEMPOOLS     // This version does not contain a fixed memory allocator, fill in lines with EXAMPLE to implement one.
#define RTREE_USE_SPHERICAL_VOLUME  // Better split classification, may be slower on some systems

// Fwd decl
class RTFileStream;    // File I/O helper class, look below for implementation and notes.


/// \class RTree
/// Implementation of RTree, a multidimensional bounding rectangle tree.
/// Example usage: For a 3-dimensional tree use RTree<Object*, float, 3> myTree;
///
/// This modified, templated C++ version by Greg Douglas at Auran (http://www.auran.com)
///
/// DATATYPE Referenced data, should be int, void*, obj* etc. no larger than sizeof<void*> and simple type
/// ELEMTYPE Type of element such as int or float
/// NUMDIMS Number of dimensions such as 2 or 3
/// ELEMTYPEREAL Type of element that allows fractional and large values such as float or double, for use in volume calcs
///
/// NOTES: Inserting and removing data requires the knowledge of its constant Minimal Bounding Rectangle.
///        This version uses new/delete for nodes, I recommend using a fixed size allocator for efficiency.
///        Instead of using a callback function for returned results, I recommend and efficient pre-sized, grow-only memory
///        array similar to MFC CArray or STL Vector for returning search query result.
///
template <class DATATYPE, class ELEMTYPE, int NUMDIMS,
          class ELEMTYPEREAL = ELEMTYPE, int TMAXNODES = 8, int TMINNODES = TMAXNODES / 2>
class RTree
{
protected:

    struct Node; // Fwd decl.  Used by other internal structs and iterator

public:
    /// Minimal bounding rectangle (n-dimensional)
    struct Rect
    {
        ELEMTYPE m_min[NUMDIMS]; ///< Min dimensions of bounding box
        ELEMTYPE m_max[NUMDIMS]; ///< Max dimensions of bounding box
    };

    // These constant must be declared after Branch and before Node struct
    // Stuck up here for MSVC 6 compiler.  NSVC .NET 2003 is much happier.
    enum {
        MAXNODES = TMAXNODES,                       ///< Max elements in node
        MINNODES = TMINNODES                        ///< Min elements in node
    };

    struct Statistics {
        int maxDepth;
        int avgDepth;

        int maxNodeLoad;
        int avgNodeLoad;
        int totalItems;
    };

public:

    RTree();
    virtual ~RTree();

    /// Insert entry
    /// \param a_min Min of bounding rect
    /// \param a_max Max of bounding rect
    /// \param a_dataId Positive Id of data.  Maybe zero, but negative numbers not allowed.
    void Insert( const ELEMTYPE     a_min[NUMDIMS],
                 const ELEMTYPE     a_max[NUMDIMS],
                 const DATATYPE&    a_dataId );

    /// Remove entry
    /// \param a_min Min of bounding rect
    /// \param a_max Max of bounding rect
    /// \param a_dataId Positive Id of data.  Maybe zero, but negative numbers not allowed.
    /// \return  1 if record not found, 0 if success.
    bool Remove( const ELEMTYPE     a_min[NUMDIMS],
                 const ELEMTYPE     a_max[NUMDIMS],
                 const DATATYPE&    a_dataId );

    /// Find all within search rectangle
    /// \param a_min Min of search bounding rect
    /// \param a_max Max of search bounding rect
    /// \param a_callback Callback function to return result.  Callback should return 'true' to continue searching
    /// \return Returns the number of entries found
    int Search( const ELEMTYPE a_min[NUMDIMS],
                const ELEMTYPE a_max[NUMDIMS],
                std::function<bool (const DATATYPE&)> a_callback ) const;

    /// Find all within search rectangle
    /// \param a_min Min of search bounding rect
    /// \param a_max Max of search bounding rect
    /// \param a_callback Callback function to return result.  Callback should return 'true' to continue searching
    /// \param aFinished This is set to true if the search completed and false if it was interupted
    /// \return Returns the number of entries found
    int Search( const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS],
            std::function<bool( const DATATYPE& )> a_callback, bool& aFinished ) const;

    template <class VISITOR>
    int Search( const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], VISITOR& a_visitor ) const
    {
  #ifdef _DEBUG

        for( int index = 0; index < NUMDIMS; ++index )
        {
            ASSERT( a_min[index] <= a_max[index] );
        }

  #endif    // _DEBUG

        Rect rect;

        for( int axis = 0; axis < NUMDIMS; ++axis )
        {
            rect.m_min[axis] = a_min[axis];
            rect.m_max[axis] = a_max[axis];
        }


        // NOTE: May want to return search result another way, perhaps returning the number of found elements here.
        int cnt = 0;

        Search( m_root, &rect, a_visitor, cnt );

        return cnt;
    }

    /// Calculate Statistics

    Statistics CalcStats();

    /// Remove all entries from tree
    void    RemoveAll();

    /// Count the data elements in this container.  This is slow as no internal counter is maintained.
    int     Count() const;

    /// Load tree contents from file
    bool    Load( const char* a_fileName );

    /// Load tree contents from stream
    bool    Load( RTFileStream& a_stream ) const;


    /// Save tree contents to file
    bool    Save( const char* a_fileName );

    /// Save tree contents to stream
    bool    Save( RTFileStream& a_stream ) const;

    /**
     * Gets an ordered vector of the nearest data elements to a specified point
     * @param aPoint coordinate to measure against
     * @param aTerminate Callback routine to check when we have gathered sufficient elements
     * @param aFilter Callback routine to remove specific elements from the query results
     * @param aSquaredDist Callback routine to measure the distance from the point to the data element
     * @return vector of matching elements and their distance to the point
     */
    std::vector<std::pair<ELEMTYPE, DATATYPE>> NearestNeighbors(
            const ELEMTYPE aPoint[NUMDIMS],
            std::function<bool( const std::size_t aNumResults, const ELEMTYPE aMinDist )> aTerminate,
            std::function<bool( const DATATYPE aElement )> aFilter,
            std::function<ELEMTYPE( const ELEMTYPE a_point[NUMDIMS], const DATATYPE a_data )> aSquaredDist ) const;

public:
    /// Iterator is not remove safe.
    class Iterator
    {
    private:
        enum
        {
            MAX_STACK = 32
        }; // Max stack size. Allows almost n^32 where n is number of branches in node

        struct StackElement
        {
            Node* m_node;
            int   m_branchIndex;
        };

    public:
        typedef std::forward_iterator_tag iterator_category;
        typedef DATATYPE                  value_type;
        typedef ptrdiff_t                 difference_type;
        typedef DATATYPE*                 pointer;
        typedef DATATYPE&                 reference;

    public:
        Iterator() : m_stack( {} ), m_tos( 0 )
        {
            for( int i = 0; i < NUMDIMS; ++i )
            {
                m_rect.m_min[i] = std::numeric_limits<ELEMTYPE>::min();
                m_rect.m_max[i] = std::numeric_limits<ELEMTYPE>::max();
            }
        }

        Iterator( const Rect& aRect ) : m_stack( {} ), m_tos( 0 ), m_rect( aRect )
        {
        }

        ~Iterator()
        {
        }

        /// Is iterator pointing to valid data
        constexpr bool IsNotNull() const
        {
            return m_tos > 0;
        }

        /// Access the current data element. Caller must be sure iterator is not NULL first.
        DATATYPE& operator*()
        {
            ASSERT( IsNotNull() );
            StackElement& curTos = m_stack[m_tos - 1];
            return curTos.m_node->m_branch[curTos.m_branchIndex].m_data;
        }

        /// Access the current data element. Caller must be sure iterator is not NULL first.
        const DATATYPE& operator*() const
        {
            ASSERT( IsNotNull() );
            StackElement& curTos = m_stack[m_tos - 1];
            return curTos.m_node->m_branch[curTos.m_branchIndex].m_data;
        }

        DATATYPE* operator->()
        {
            ASSERT( IsNotNull() );
            StackElement& curTos = m_stack[m_tos - 1];
            return &( curTos.m_node->m_branch[curTos.m_branchIndex].m_data );
        }

        /// Prefix ++ operator
        Iterator& operator++()
        {
            FindNextData();
            return *this;
        }

        /// Postfix ++ operator
        Iterator operator++( int )
        {
            Iterator retval = *this;
            FindNextData();
            return retval;
        }

        bool operator==( const Iterator& rhs ) const
        {
            return ( ( m_tos <= 0 && rhs.m_tos <= 0 )
                     || ( m_tos == rhs.m_tos && m_stack[m_tos].m_node == rhs.m_stack[m_tos].m_node
                                && m_stack[m_tos].m_branchIndex
                                           == rhs.m_stack[m_tos].m_branchIndex ) );
        }

        bool operator!=( const Iterator& rhs ) const
        {
            return ( ( m_tos > 0 || rhs.m_tos > 0 )
                     && ( m_tos != rhs.m_tos || m_stack[m_tos].m_node != rhs.m_stack[m_tos].m_node
                                || m_stack[m_tos].m_branchIndex
                                           != rhs.m_stack[m_tos].m_branchIndex ) );
        }

    private:
        /// Find the next data element in the tree (For internal use only)
        void FindNextData()
        {
            while( m_tos > 0 )
            {
                StackElement curTos     = Pop();
                int          nextBranch = curTos.m_branchIndex + 1;

                if( curTos.m_node->IsLeaf() )
                {
                    // Keep walking through siblings until we find an overlapping leaf
                    for( int i = nextBranch; i < curTos.m_node->m_count; i++ )
                    {
                        if( RTree::Overlap( &m_rect, &curTos.m_node->m_branch[i].m_rect ) )
                        {
                            Push( curTos.m_node, i );
                            return;
                        }
                    }
                    // No more data, so it will fall back to previous level
                }
                else
                {
                    // Look for an overlapping sibling that we can use as the fall-back node
                    // when we've iterated down the current branch
                    for( int i = nextBranch; i < curTos.m_node->m_count; i++ )
                    {
                        if( RTree::Overlap( &m_rect, &curTos.m_node->m_branch[i].m_rect ) )
                        {
                            Push( curTos.m_node, i );
                            break;
                        }
                    }

                    Node* nextLevelnode = curTos.m_node->m_branch[curTos.m_branchIndex].m_child;

                    // Since cur node is not a leaf, push first of next level,
                    // zero-th branch to get deeper into the tree
                    Push( nextLevelnode, 0 );

                    // If the branch is a leaf, and it overlaps, then break with the current data
                    // Otherwise, we allow it to seed our next iteration as it may have siblings that
                    // do overlap
                    if( nextLevelnode->IsLeaf()
                            && RTree::Overlap( &m_rect, &nextLevelnode->m_branch[0].m_rect ) )
                        return;
                }
            }
        }

        /// Push node and branch onto iteration stack (For internal use only)
        void Push( Node* a_node, int a_branchIndex )
        {
            m_stack[m_tos].m_node = a_node;
            m_stack[m_tos].m_branchIndex = a_branchIndex;
            ++m_tos;
            ASSERT( m_tos <= MAX_STACK );
        }

        /// Pop element off iteration stack (For internal use only)
        StackElement& Pop()
        {
            ASSERT( m_tos > 0 );
            --m_tos;
            return m_stack[m_tos];
        }

        std::array<StackElement, MAX_STACK> m_stack; ///< Stack for iteration
        int                                 m_tos;   ///< Top Of Stack index
        Rect                                m_rect;  ///< Search rectangle

        friend class RTree;
        // Allow hiding of non-public functions while allowing manipulation by logical owner
    };

    using iterator       = Iterator;
    using const_iterator = const Iterator;

    iterator begin( const Rect& aRect ) const
    {
        iterator retval( aRect );

        if( !m_root->m_count )
            return retval;

        retval.Push( m_root, 0 );

        // If the first leaf matches, return the root pointer, otherwise,
        // increment to the first match or empty if none.
        if( m_root->IsLeaf() && Overlap( &aRect, &m_root->m_branch[0].m_rect ) )
            return retval;

        ++retval;
        return retval;
    }

    iterator begin() const
    {
        Rect full_rect;

        std::fill_n( full_rect.m_min, NUMDIMS, INT_MIN );
        std::fill_n( full_rect.m_max, NUMDIMS, INT_MAX );

        return begin( full_rect );
    }

    iterator end() const
    {
        iterator retval;
        return retval;
    }

    iterator end( const Rect& aRect ) const
    {
        return end();
    }


protected:
    /// May be data or may be another subtree
    /// The parents level determines this.
    /// If the parents level is 0, then this is data
    struct Branch
    {
        Rect m_rect;                              ///< Bounds
        union
        {
            Node*       m_child;                    ///< Child node
            DATATYPE    m_data;                     ///< Data Id or Ptr
        };
    };

    /// Node for each branch level
    struct Node
    {
        constexpr bool IsInternalNode() const { return m_level > 0; }   // Not a leaf, but a internal node
        constexpr bool IsLeaf()         const { return m_level == 0; }  // A leaf, contains data

        int     m_count;                            ///< Count
        int     m_level;                            ///< Leaf is zero, others positive
        Branch  m_branch[MAXNODES];                 ///< Branch
    };

    /// A link list of nodes for reinsertion after a delete operation
    struct ListNode
    {
        ListNode*   m_next;                         ///< Next in list
        Node*       m_node;                         ///< Node
    };

    /// Variables for finding a split partition
    struct PartitionVars
    {
        int             m_partition[MAXNODES + 1];
        int             m_total;
        int             m_minFill;
        bool            m_taken[MAXNODES + 1];
        int             m_count[2];
        Rect            m_cover[2];
        ELEMTYPEREAL    m_area[2];

        Branch          m_branchBuf[MAXNODES + 1];
        int             m_branchCount;
        Rect            m_coverSplit;
        ELEMTYPEREAL    m_coverSplitArea;
    };

    /// Data structure used for Nearest Neighbor search implementation
    struct NNNode
    {
        Branch m_branch;
        ELEMTYPE minDist;
        bool isLeaf;

        inline bool operator<(const NNNode &other) const
        {
            /// This is reversed on purpose to use std::priority_queue
            return other.minDist < minDist;
        }
    };

    Node*           AllocNode() const;
    void            FreeNode( Node* a_node ) const;
    void            InitNode( Node* a_node ) const;
    void            InitRect( Rect* a_rect ) const;
    bool            InsertRectRec( const Rect*      a_rect,
                                   const DATATYPE&  a_id,
                                   Node*            a_node,
                                   Node**           a_newNode,
                                   int              a_level ) const;
    bool            InsertRect( const Rect* a_rect, const DATATYPE& a_id, Node** a_root, int a_level ) const;
    Rect            NodeCover( Node* a_node ) const;
    bool            AddBranch( const Branch* a_branch, Node* a_node, Node** a_newNode ) const;
    void            DisconnectBranch( Node* a_node, int a_index ) const;
    int             PickBranch( const Rect* a_rect, Node* a_node ) const;
    Rect            CombineRect( const Rect* a_rectA, const Rect* a_rectB ) const;
    void            SplitNode( Node* a_node, const Branch* a_branch, Node** a_newNode ) const;
    ELEMTYPEREAL    RectSphericalVolume( const Rect* a_rect ) const;
    ELEMTYPEREAL    RectVolume( const Rect* a_rect ) const;
    ELEMTYPEREAL    CalcRectVolume( const Rect* a_rect ) const;
    void            GetBranches( Node* a_node, const Branch* a_branch, PartitionVars* a_parVars ) const;
    void            ChoosePartition( PartitionVars* a_parVars, int a_minFill ) const;
    void            LoadNodes( Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars ) const;
    void            InitParVars( PartitionVars* a_parVars, int a_maxRects, int a_minFill ) const;
    void            PickSeeds( PartitionVars* a_parVars ) const;
    void            Classify( int a_index, int a_group, PartitionVars* a_parVars ) const;
    bool            RemoveRect( const Rect* a_rect, const DATATYPE& a_id, Node** a_root ) const;
    bool            RemoveRectRec( const Rect*      a_rect,
                                   const DATATYPE&  a_id,
                                   Node*            a_node,
                                   ListNode**       a_listNode ) const;
    ListNode*       AllocListNode() const;
    void            FreeListNode( ListNode* a_listNode ) const;
    static bool     Overlap( const Rect* a_rectA, const Rect* a_rectB );
    void            ReInsert( Node* a_node, ListNode** a_listNode ) const;
    ELEMTYPE        MinDist( const ELEMTYPE a_point[NUMDIMS], const Rect& a_rect ) const;

    bool Search( const Node* a_node, const Rect* a_rect, int& a_foundCount,
                 std::function<bool (const DATATYPE&)> a_callback ) const;

    template <class VISITOR>
    bool Search( const Node* a_node, const Rect* a_rect, VISITOR& a_visitor, int& a_foundCount ) const
    {
        ASSERT( a_node );
        ASSERT( a_node->m_level >= 0 );
        ASSERT( a_rect );

        if( a_node->IsInternalNode() ) // This is an internal node in the tree
        {
            for( int index = 0; index < a_node->m_count; ++index )
            {
                if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) )
                {
                    if( !Search( a_node->m_branch[index].m_child, a_rect, a_visitor, a_foundCount ) )
                    {
                        return false; // Don't continue searching
                    }
                }
            }
        }
        else // This is a leaf node
        {
            for( int index = 0; index < a_node->m_count; ++index )
            {
                if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) )
                {
                    const DATATYPE& id = a_node->m_branch[index].m_data;

                    if( !a_visitor( id ) )
                        return false;

                    a_foundCount++;
                }
            }
        }

        return true; // Continue searching
    }

    void    RemoveAllRec( Node* a_node ) const;
    void    Reset() const;
    void    CountRec( const Node* a_node, int& a_count ) const;

    bool    SaveRec( const Node* a_node, RTFileStream& a_stream ) const;
    bool    LoadRec( const Node* a_node, RTFileStream& a_stream ) const;

    Node*           m_root;                         ///< Root of tree
    ELEMTYPEREAL    m_unitSphereVolume;             ///< Unit sphere constant for required number of dimensions
};


// Because there is not stream support, this is a quick and dirty file I/O helper.
// Users will likely replace its usage with a Stream implementation from their favorite API.
class RTFileStream
{
    FILE* m_file;
public:


    RTFileStream()
    {
        m_file = NULL;
    }

    ~RTFileStream()
    {
        Close();
    }

    bool OpenRead( const char* a_fileName )
    {
        m_file = std::fopen( a_fileName, "rb" );

        if( !m_file )
        {
            return false;
        }

        return true;
    }

    bool OpenWrite( const char* a_fileName )
    {
        m_file = std::fopen( a_fileName, "wb" );

        if( !m_file )
        {
            return false;
        }

        return true;
    }

    void Close()
    {
        if( m_file )
        {
            std::fclose( m_file );
            m_file = NULL;
        }
    }

    template <typename TYPE>
    size_t Write( const TYPE& a_value )
    {
        ASSERT( m_file );
        return std::fwrite( (void*) &a_value, sizeof(a_value), 1, m_file );
    }

    template <typename TYPE>
    size_t WriteArray( const TYPE* a_array, int a_count )
    {
        ASSERT( m_file );
        return std::fwrite( (void*) a_array, sizeof(TYPE) * a_count, 1, m_file );
    }

    template <typename TYPE>
    size_t Read( TYPE& a_value )
    {
        ASSERT( m_file );
        return std::fread( (void*) &a_value, sizeof(a_value), 1, m_file );
    }

    template <typename TYPE>
    size_t ReadArray( TYPE* a_array, int a_count )
    {
        ASSERT( m_file );
        return std::fread( (void*) a_array, sizeof(TYPE) * a_count, 1, m_file );
    }
};


RTREE_TEMPLATE RTREE_QUAL::RTree()
{
    ASSERT( MAXNODES > MINNODES );
    ASSERT( MINNODES > 0 );


    // We only support machine word size simple data type eg. integer index or object pointer.
    // Since we are storing as union with non data branch
    ASSERT( sizeof(DATATYPE) == sizeof(void*) || sizeof(DATATYPE) == sizeof(int) );

    // Precomputed volumes of the unit spheres for the first few dimensions
    const float UNIT_SPHERE_VOLUMES[] =
    {
        0.000000f, 2.000000f, 3.141593f,    // Dimension  0,1,2
        4.188790f, 4.934802f, 5.263789f,    // Dimension  3,4,5
        5.167713f, 4.724766f, 4.058712f,    // Dimension  6,7,8
        3.298509f, 2.550164f, 1.884104f,    // Dimension  9,10,11
        1.335263f, 0.910629f, 0.599265f,    // Dimension  12,13,14
        0.381443f, 0.235331f, 0.140981f,    // Dimension  15,16,17
        0.082146f, 0.046622f, 0.025807f,    // Dimension  18,19,20
    };

    m_root = AllocNode();
    m_root->m_level     = 0;
    m_unitSphereVolume  = (ELEMTYPEREAL) UNIT_SPHERE_VOLUMES[NUMDIMS];
}


RTREE_TEMPLATE
RTREE_QUAL::~RTree() {
    Reset(); // Free, or reset node memory
}


RTREE_TEMPLATE
void RTREE_QUAL::Insert( const ELEMTYPE     a_min[NUMDIMS],
                         const ELEMTYPE     a_max[NUMDIMS],
                         const DATATYPE&    a_dataId )
{
#ifdef _DEBUG

    for( int index = 0; index<NUMDIMS; ++index )
    {
        ASSERT( a_min[index] <= a_max[index] );
    }

#endif    // _DEBUG

    Rect rect;

    for( int axis = 0; axis < NUMDIMS; ++axis )
    {
        rect.m_min[axis] = a_min[axis];
        rect.m_max[axis] = a_max[axis];
    }

    InsertRect( &rect, a_dataId, &m_root, 0 );
}


RTREE_TEMPLATE
bool RTREE_QUAL::Remove( const ELEMTYPE     a_min[NUMDIMS],
                         const ELEMTYPE     a_max[NUMDIMS],
                         const DATATYPE&    a_dataId )
{
#ifdef _DEBUG

    for( int index = 0; index<NUMDIMS; ++index )
    {
        ASSERT( a_min[index] <= a_max[index] );
    }

#endif    // _DEBUG

    Rect rect;

    for( int axis = 0; axis < NUMDIMS; ++axis )
    {
        rect.m_min[axis] = a_min[axis];
        rect.m_max[axis] = a_max[axis];
    }

    return RemoveRect( &rect, a_dataId, &m_root );
}


RTREE_TEMPLATE
int RTREE_QUAL::Search( const ELEMTYPE a_min[NUMDIMS],
                        const ELEMTYPE a_max[NUMDIMS],
                        std::function<bool (const DATATYPE&)> a_callback ) const
{
#ifdef _DEBUG

    for( int index = 0; index < NUMDIMS; ++index )
    {
        ASSERT( a_min[index] <= a_max[index] );
    }

#endif    // _DEBUG

    Rect rect;

    for( int axis = 0; axis < NUMDIMS; ++axis )
    {
        rect.m_min[axis] = a_min[axis];
        rect.m_max[axis] = a_max[axis];
    }

    // NOTE: May want to return search result another way, perhaps returning the number of found elements here.

    int foundCount = 0;
    Search( m_root, &rect, foundCount, a_callback );
    return foundCount;
}


RTREE_TEMPLATE
int RTREE_QUAL::Search( const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS],
        std::function<bool( const DATATYPE& )> a_callback, bool& aFinished ) const
{
#ifdef _DEBUG

    for( int index = 0; index < NUMDIMS; ++index )
    {
        ASSERT( a_min[index] <= a_max[index] );
    }

#endif // _DEBUG

    Rect rect;

    for( int axis = 0; axis < NUMDIMS; ++axis )
    {
        rect.m_min[axis] = a_min[axis];
        rect.m_max[axis] = a_max[axis];
    }

    // NOTE: May want to return search result another way, perhaps returning the number of found elements here.

    int foundCount = 0;
    aFinished      = Search( m_root, &rect, foundCount, a_callback );
    return foundCount;
}


RTREE_TEMPLATE
std::vector<std::pair<ELEMTYPE, DATATYPE>> RTREE_QUAL::NearestNeighbors(
        const ELEMTYPE a_point[NUMDIMS],
        std::function<bool( const std::size_t aNumResults, const ELEMTYPE aMinDist )> aTerminate,
        std::function<bool( const DATATYPE aElement )> aFilter,
        std::function<ELEMTYPE( const ELEMTYPE a_point[NUMDIMS], const DATATYPE a_data )> aSquaredDist ) const
{
    std::vector<std::pair<ELEMTYPE, DATATYPE>> result;
    std::priority_queue<NNNode> search_q;

    for( int i = 0; i < m_root->m_count; ++i )
    {
        if( m_root->IsLeaf() )
        {
            search_q.push( NNNode{ m_root->m_branch[i],
                               aSquaredDist( a_point, m_root->m_branch[i].m_data ),
                               m_root->IsLeaf() });
        }
        else
        {
            search_q.push( NNNode{ m_root->m_branch[i],
                               MinDist(a_point, m_root->m_branch[i].m_rect),
                               m_root->IsLeaf() });
        }
    }

    while( !search_q.empty() )
    {
        const NNNode curNode = search_q.top();

        if( aTerminate( result.size(), curNode.minDist ) )
            break;

        search_q.pop();

        if( curNode.isLeaf )
        {
            if( aFilter( curNode.m_branch.m_data ) )
                result.emplace_back( curNode.minDist, curNode.m_branch.m_data );
        }
        else
        {
            Node* node = curNode.m_branch.m_child;

            for( int i = 0; i < node->m_count; ++i )
            {
                NNNode newNode;
                newNode.isLeaf = node->IsLeaf();
                newNode.m_branch = node->m_branch[i];
                if( newNode.isLeaf )
                    newNode.minDist = aSquaredDist( a_point, newNode.m_branch.m_data );
                else
                    newNode.minDist = this->MinDist( a_point, node->m_branch[i].m_rect );

                search_q.push( newNode );
            }
        }
    }

    return result;
}

RTREE_TEMPLATE
int RTREE_QUAL::Count() const
{
    int count = 0;

    CountRec( m_root, count );

    return count;
}


RTREE_TEMPLATE
void RTREE_QUAL::CountRec( const Node* a_node, int& a_count ) const
{
    if( a_node->IsInternalNode() ) // not a leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            CountRec( a_node->m_branch[index].m_child, a_count );
        }
    }
    else // A leaf node
    {
        a_count += a_node->m_count;
    }
}


RTREE_TEMPLATE
bool RTREE_QUAL::Load( const char* a_fileName )
{
    RemoveAll(); // Clear existing tree

    RTFileStream stream;

    if( !stream.OpenRead( a_fileName ) )
    {
        return false;
    }

    bool result = Load( stream );

    stream.Close();

    return result;
}


RTREE_TEMPLATE
bool RTREE_QUAL::Load( RTFileStream& a_stream ) const
{
    // Write some kind of header
    int _dataFileId         = ('R' << 0) | ('T' << 8) | ('R' << 16) | ('E' << 24);
    int _dataSize           = sizeof(DATATYPE);
    int _dataNumDims        = NUMDIMS;
    int _dataElemSize       = sizeof(ELEMTYPE);
    int _dataElemRealSize   = sizeof(ELEMTYPEREAL);
    int _dataMaxNodes       = TMAXNODES;
    int _dataMinNodes       = TMINNODES;

    int dataFileId          = 0;
    int dataSize            = 0;
    int dataNumDims         = 0;
    int dataElemSize        = 0;
    int dataElemRealSize    = 0;
    int dataMaxNodes        = 0;
    int dataMinNodes        = 0;

    a_stream.Read( dataFileId );
    a_stream.Read( dataSize );
    a_stream.Read( dataNumDims );
    a_stream.Read( dataElemSize );
    a_stream.Read( dataElemRealSize );
    a_stream.Read( dataMaxNodes );
    a_stream.Read( dataMinNodes );

    bool result = false;

    // Test if header was valid and compatible
    if( (dataFileId == _dataFileId)
        && (dataSize == _dataSize)
        && (dataNumDims == _dataNumDims)
        && (dataElemSize == _dataElemSize)
        && (dataElemRealSize == _dataElemRealSize)
        && (dataMaxNodes == _dataMaxNodes)
        && (dataMinNodes == _dataMinNodes)
         )
    {
        // Recursively load tree
        result = LoadRec( m_root, a_stream );
    }

    return result;
}


RTREE_TEMPLATE
bool RTREE_QUAL::LoadRec( const Node* a_node, RTFileStream& a_stream ) const
{
    a_stream.Read( a_node->m_level );
    a_stream.Read( a_node->m_count );

    if( a_node->IsInternalNode() ) // not a leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            const Branch* curBranch = &a_node->m_branch[index];

            a_stream.ReadArray( curBranch->m_rect.m_min, NUMDIMS );
            a_stream.ReadArray( curBranch->m_rect.m_max, NUMDIMS );

            curBranch->m_child = AllocNode();
            LoadRec( curBranch->m_child, a_stream );
        }
    }
    else // A leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            const Branch* curBranch = &a_node->m_branch[index];

            a_stream.ReadArray( curBranch->m_rect.m_min, NUMDIMS );
            a_stream.ReadArray( curBranch->m_rect.m_max, NUMDIMS );

            a_stream.Read( curBranch->m_data );
        }
    }

    return true; // Should do more error checking on I/O operations
}


RTREE_TEMPLATE
bool RTREE_QUAL::Save( const char* a_fileName )
{
    RTFileStream stream;

    if( !stream.OpenWrite( a_fileName ) )
    {
        return false;
    }

    bool result = Save( stream );

    stream.Close();

    return result;
}


RTREE_TEMPLATE
bool RTREE_QUAL::Save( RTFileStream& a_stream ) const
{
    // Write some kind of header
    int dataFileId          = ('R' << 0) | ('T' << 8) | ('R' << 16) | ('E' << 24);
    int dataSize            = sizeof(DATATYPE);
    int dataNumDims         = NUMDIMS;
    int dataElemSize        = sizeof(ELEMTYPE);
    int dataElemRealSize    = sizeof(ELEMTYPEREAL);
    int dataMaxNodes        = TMAXNODES;
    int dataMinNodes        = TMINNODES;

    a_stream.Write( dataFileId );
    a_stream.Write( dataSize );
    a_stream.Write( dataNumDims );
    a_stream.Write( dataElemSize );
    a_stream.Write( dataElemRealSize );
    a_stream.Write( dataMaxNodes );
    a_stream.Write( dataMinNodes );

    // Recursively save tree
    bool result = SaveRec( m_root, a_stream );

    return result;
}


RTREE_TEMPLATE
bool RTREE_QUAL::SaveRec( const Node* a_node, RTFileStream& a_stream ) const
{
    a_stream.Write( a_node->m_level );
    a_stream.Write( a_node->m_count );

    if( a_node->IsInternalNode() ) // not a leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            const Branch* curBranch = &a_node->m_branch[index];

            a_stream.WriteArray( curBranch->m_rect.m_min, NUMDIMS );
            a_stream.WriteArray( curBranch->m_rect.m_max, NUMDIMS );

            SaveRec( curBranch->m_child, a_stream );
        }
    }
    else // A leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            const Branch* curBranch = &a_node->m_branch[index];

            a_stream.WriteArray( curBranch->m_rect.m_min, NUMDIMS );
            a_stream.WriteArray( curBranch->m_rect.m_max, NUMDIMS );

            a_stream.Write( curBranch->m_data );
        }
    }

    return true; // Should do more error checking on I/O operations
}


RTREE_TEMPLATE
void RTREE_QUAL::RemoveAll()
{
    // Delete all existing nodes
    Reset();

    m_root = AllocNode();
    m_root->m_level = 0;
}


RTREE_TEMPLATE
void RTREE_QUAL::Reset() const
{
#ifdef RTREE_DONT_USE_MEMPOOLS
    // Delete all existing nodes
    RemoveAllRec( m_root );
#else    // RTREE_DONT_USE_MEMPOOLS
    // Just reset memory pools.  We are not using complex types
    // EXAMPLE
#endif    // RTREE_DONT_USE_MEMPOOLS
}


RTREE_TEMPLATE
void RTREE_QUAL::RemoveAllRec( Node* a_node ) const
{
    ASSERT( a_node );
    ASSERT( a_node->m_level >= 0 );

    if( a_node->IsInternalNode() ) // This is an internal node in the tree
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            RemoveAllRec( a_node->m_branch[index].m_child );
        }
    }

    FreeNode( a_node );
}


RTREE_TEMPLATE
typename RTREE_QUAL::Node* RTREE_QUAL::AllocNode() const
{
    Node* newNode;

#ifdef RTREE_DONT_USE_MEMPOOLS
    newNode = new Node;
#else       // RTREE_DONT_USE_MEMPOOLS
    // EXAMPLE
#endif      // RTREE_DONT_USE_MEMPOOLS
    InitNode( newNode );
    return newNode;
}


RTREE_TEMPLATE
void RTREE_QUAL::FreeNode( Node* a_node ) const
{
    ASSERT( a_node );

#ifdef RTREE_DONT_USE_MEMPOOLS
    delete a_node;
#else       // RTREE_DONT_USE_MEMPOOLS
    // EXAMPLE
#endif      // RTREE_DONT_USE_MEMPOOLS
}


// Allocate space for a node in the list used in DeletRect to
// store Nodes that are too empty.
RTREE_TEMPLATE
typename RTREE_QUAL::ListNode* RTREE_QUAL::AllocListNode() const
{
#ifdef RTREE_DONT_USE_MEMPOOLS
    return new ListNode;
#else       // RTREE_DONT_USE_MEMPOOLS
    // EXAMPLE
#endif      // RTREE_DONT_USE_MEMPOOLS
}


RTREE_TEMPLATE
void RTREE_QUAL::FreeListNode( ListNode* a_listNode ) const
{
#ifdef RTREE_DONT_USE_MEMPOOLS
    delete a_listNode;
#else       // RTREE_DONT_USE_MEMPOOLS
    // EXAMPLE
#endif      // RTREE_DONT_USE_MEMPOOLS
}


RTREE_TEMPLATE
void RTREE_QUAL::InitNode( Node* a_node ) const
{
    a_node->m_count = 0;
    a_node->m_level = -1;
}


RTREE_TEMPLATE
void RTREE_QUAL::InitRect( Rect* a_rect ) const
{
    for( int index = 0; index < NUMDIMS; ++index )
    {
        a_rect->m_min[index] = (ELEMTYPE) 0;
        a_rect->m_max[index] = (ELEMTYPE) 0;
    }
}


// Inserts a new data rectangle into the index structure.
// Recursively descends tree, propagates splits back up.
// Returns 0 if node was not split.  Old node updated.
// If node was split, returns 1 and sets the pointer pointed to by
// new_node to point to the new node.  Old node updated to become one of two.
// The level argument specifies the number of steps up from the leaf
// level to insert; e.g. a data rectangle goes in at level = 0.
RTREE_TEMPLATE
bool RTREE_QUAL::InsertRectRec( const Rect*     a_rect,
                                const DATATYPE& a_id,
                                Node*           a_node,
                                Node**          a_newNode,
                                int             a_level ) const
{
    ASSERT( a_rect && a_node && a_newNode );
    ASSERT( a_level >= 0 && a_level <= a_node->m_level );

    int     index;
    Branch  branch;
    Node*   otherNode;

    // Still above level for insertion, go down tree recursively
    if( a_node->m_level > a_level )
    {
        index = PickBranch( a_rect, a_node );

        if( !InsertRectRec( a_rect, a_id, a_node->m_branch[index].m_child, &otherNode, a_level ) )
        {
            // Child was not split
            a_node->m_branch[index].m_rect =
                CombineRect( a_rect, &(a_node->m_branch[index].m_rect) );
            return false;
        }
        else // Child was split
        {
            a_node->m_branch[index].m_rect = NodeCover( a_node->m_branch[index].m_child );
            branch.m_child  = otherNode;
            branch.m_rect   = NodeCover( otherNode );
            return AddBranch( &branch, a_node, a_newNode );
        }
    }
    else if( a_node->m_level == a_level ) // Have reached level for insertion. Add rect, split if necessary
    {
        branch.m_rect   = *a_rect;
        branch.m_child  = (Node*) a_id;
        // Child field of leaves contains id of data record
        return AddBranch( &branch, a_node, a_newNode );
    }
    else
    {
        // Should never occur
        ASSERT( 0 );
        return false;
    }
}


// Insert a data rectangle into an index structure.
// InsertRect provides for splitting the root;
// returns 1 if root was split, 0 if it was not.
// The level argument specifies the number of steps up from the leaf
// level to insert; e.g. a data rectangle goes in at level = 0.
// InsertRect2 does the recursion.
//
RTREE_TEMPLATE
bool RTREE_QUAL::InsertRect( const Rect* a_rect, const DATATYPE& a_id, Node** a_root, int a_level ) const
{
    ASSERT( a_rect && a_root );
    ASSERT( a_level >= 0 && a_level <= (*a_root)->m_level );
#ifdef _DEBUG

    for( int index = 0; index < NUMDIMS; ++index )
    {
        ASSERT( a_rect->m_min[index] <= a_rect->m_max[index] );
    }

#endif    // _DEBUG

    Node*   newRoot;
    Node*   newNode;
    Branch  branch;

    if( InsertRectRec( a_rect, a_id, *a_root, &newNode, a_level ) ) // Root split
    {
        newRoot = AllocNode();                                      // Grow tree taller and new root
        newRoot->m_level    = (*a_root)->m_level + 1;
        branch.m_rect       = NodeCover( *a_root );
        branch.m_child      = *a_root;
        AddBranch( &branch, newRoot, NULL );
        branch.m_rect   = NodeCover( newNode );
        branch.m_child  = newNode;
        AddBranch( &branch, newRoot, NULL );
        *a_root = newRoot;
        return true;
    }

    return false;
}


// Find the smallest rectangle that includes all rectangles in branches of a node.
RTREE_TEMPLATE
typename RTREE_QUAL::Rect RTREE_QUAL::NodeCover( Node* a_node ) const
{
    ASSERT( a_node );

    bool     firstTime = true;
    Rect    rect;
    InitRect( &rect );

    for( int index = 0; index < a_node->m_count; ++index )
    {
        if( firstTime )
        {
            rect = a_node->m_branch[index].m_rect;
            firstTime = false;
        }
        else
        {
            rect = CombineRect( &rect, &(a_node->m_branch[index].m_rect) );
        }
    }

    return rect;
}


// Add a branch to a node.  Split the node if necessary.
// Returns 0 if node not split.  Old node updated.
// Returns 1 if node split, sets *new_node to address of new node.
// Old node updated, becomes one of two.
RTREE_TEMPLATE
bool RTREE_QUAL::AddBranch( const Branch* a_branch, Node* a_node, Node** a_newNode ) const
{
    ASSERT( a_branch );
    ASSERT( a_node );

    if( a_node->m_count < MAXNODES ) // Split won't be necessary
    {
        a_node->m_branch[a_node->m_count] = *a_branch;
        ++a_node->m_count;

        return false;
    }
    else
    {
        ASSERT( a_newNode );

        SplitNode( a_node, a_branch, a_newNode );
        return true;
    }
}


// Disconnect a dependent node.
// Caller must return (or stop using iteration index) after this as count has changed
RTREE_TEMPLATE
void RTREE_QUAL::DisconnectBranch( Node* a_node, int a_index ) const
{
    ASSERT( a_node && (a_index >= 0) && (a_index < MAXNODES) );
    ASSERT( a_node->m_count > 0 );

    // Remove element by swapping with the last element to prevent gaps in array
    a_node->m_branch[a_index] = a_node->m_branch[a_node->m_count - 1];

    --a_node->m_count;
}


// Pick a branch.  Pick the one that will need the smallest increase
// in area to accomodate the new rectangle.  This will result in the
// least total area for the covering rectangles in the current node.
// In case of a tie, pick the one which was smaller before, to get
// the best resolution when searching.
RTREE_TEMPLATE
int RTREE_QUAL::PickBranch( const Rect* a_rect, Node* a_node ) const
{
    ASSERT( a_rect && a_node );

    bool            firstTime = true;
    ELEMTYPEREAL    increase;
    ELEMTYPEREAL    bestIncr = (ELEMTYPEREAL) -1;
    ELEMTYPEREAL    area;
    ELEMTYPEREAL    bestArea = 0;
    int             best = 0;
    Rect            tempRect;

    for( int index = 0; index < a_node->m_count; ++index )
    {
        Rect* curRect = &a_node->m_branch[index].m_rect;
        area        = CalcRectVolume( curRect );
        tempRect    = CombineRect( a_rect, curRect );
        increase    = CalcRectVolume( &tempRect ) - area;

        if( (increase < bestIncr) || firstTime )
        {
            best        = index;
            bestArea    = area;
            bestIncr    = increase;
            firstTime   = false;
        }
        else if( (increase == bestIncr) && (area < bestArea) )
        {
            best        = index;
            bestArea    = area;
            bestIncr    = increase;
        }
    }

    return best;
}


// Combine two rectangles into larger one containing both
RTREE_TEMPLATE
typename RTREE_QUAL::Rect RTREE_QUAL::CombineRect( const Rect* a_rectA, const Rect* a_rectB ) const
{
    ASSERT( a_rectA && a_rectB );

    Rect newRect;

    for( int index = 0; index < NUMDIMS; ++index )
    {
        newRect.m_min[index] = std::min( a_rectA->m_min[index], a_rectB->m_min[index] );
        newRect.m_max[index] = std::max( a_rectA->m_max[index], a_rectB->m_max[index] );
    }

    return newRect;
}


// Split a node.
// Divides the nodes branches and the extra one between two nodes.
// Old node is one of the new ones, and one really new one is created.
// Tries more than one method for choosing a partition, uses best result.
RTREE_TEMPLATE
void RTREE_QUAL::SplitNode( Node* a_node, const Branch* a_branch, Node** a_newNode ) const
{
    ASSERT( a_node );
    ASSERT( a_branch );

    // Could just use local here, but member or external is faster since it is reused
    PartitionVars   localVars;
    PartitionVars*  parVars = &localVars;
    int             level;

    // Load all the branches into a buffer, initialize old node
    level = a_node->m_level;
    GetBranches( a_node, a_branch, parVars );

    // Find partition
    ChoosePartition( parVars, MINNODES );

    // Put branches from buffer into 2 nodes according to chosen partition
    *a_newNode = AllocNode();
    (*a_newNode)->m_level = a_node->m_level = level;
    LoadNodes( a_node, *a_newNode, parVars );

    ASSERT( (a_node->m_count + (*a_newNode)->m_count) == parVars->m_total );
}


// Calculate the n-dimensional volume of a rectangle
RTREE_TEMPLATE
ELEMTYPEREAL RTREE_QUAL::RectVolume( const Rect* a_rect ) const
{
    ASSERT( a_rect );

    ELEMTYPEREAL volume = (ELEMTYPEREAL) 1;

    for( int index = 0; index<NUMDIMS; ++index )
    {
        volume *= a_rect->m_max[index] - a_rect->m_min[index];
    }

    ASSERT( volume >= (ELEMTYPEREAL) 0 );

    return volume;
}


// The exact volume of the bounding sphere for the given Rect
RTREE_TEMPLATE
ELEMTYPEREAL RTREE_QUAL::RectSphericalVolume( const Rect* a_rect ) const
{
    ASSERT( a_rect );

    ELEMTYPEREAL    sumOfSquares = (ELEMTYPEREAL) 0;
    ELEMTYPEREAL    radius;

    for( int index = 0; index < NUMDIMS; ++index )
    {
        ELEMTYPEREAL halfExtent =
            ( (ELEMTYPEREAL) a_rect->m_max[index] - (ELEMTYPEREAL) a_rect->m_min[index] ) * 0.5f;
        sumOfSquares += halfExtent * halfExtent;
    }

    radius = (ELEMTYPEREAL) std::sqrt( sumOfSquares );

    // Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x.
    if( NUMDIMS == 3 )
    {
        return radius * radius * radius * m_unitSphereVolume;
    }
    else if( NUMDIMS == 2 )
    {
        return radius * radius * m_unitSphereVolume;
    }
    else
    {
        return (ELEMTYPEREAL) (std::pow( radius, NUMDIMS ) * m_unitSphereVolume);
    }
}


// Use one of the methods to calculate retangle volume
RTREE_TEMPLATE
ELEMTYPEREAL RTREE_QUAL::CalcRectVolume( const Rect* a_rect ) const
{
#ifdef RTREE_USE_SPHERICAL_VOLUME
    return RectSphericalVolume( a_rect );   // Slower but helps certain merge cases
#else                                       // RTREE_USE_SPHERICAL_VOLUME
    return RectVolume( a_rect );            // Faster but can cause poor merges
#endif                                      // RTREE_USE_SPHERICAL_VOLUME
}


// Load branch buffer with branches from full node plus the extra branch.
RTREE_TEMPLATE
void RTREE_QUAL::GetBranches( Node* a_node, const Branch* a_branch, PartitionVars* a_parVars ) const
{
    ASSERT( a_node );
    ASSERT( a_branch );

    ASSERT( a_node->m_count == MAXNODES );

    // Load the branch buffer
    for( int index = 0; index < MAXNODES; ++index )
    {
        a_parVars->m_branchBuf[index] = a_node->m_branch[index];
    }

    a_parVars->m_branchBuf[MAXNODES] = *a_branch;
    a_parVars->m_branchCount = MAXNODES + 1;

    // Calculate rect containing all in the set
    a_parVars->m_coverSplit = a_parVars->m_branchBuf[0].m_rect;

    for( int index = 1; index < MAXNODES + 1; ++index )
    {
        a_parVars->m_coverSplit =
            CombineRect( &a_parVars->m_coverSplit, &a_parVars->m_branchBuf[index].m_rect );
    }

    a_parVars->m_coverSplitArea = CalcRectVolume( &a_parVars->m_coverSplit );

    InitNode( a_node );
}


// Method #0 for choosing a partition:
// As the seeds for the two groups, pick the two rects that would waste the
// most area if covered by a single rectangle, i.e. evidently the worst pair
// to have in the same group.
// Of the remaining, one at a time is chosen to be put in one of the two groups.
// The one chosen is the one with the greatest difference in area expansion
// depending on which group - the rect most strongly attracted to one group
// and repelled from the other.
// If one group gets too full (more would force other group to violate min
// fill requirement) then other group gets the rest.
// These last are the ones that can go in either group most easily.
RTREE_TEMPLATE
void RTREE_QUAL::ChoosePartition( PartitionVars* a_parVars, int a_minFill ) const
{
    ASSERT( a_parVars );

    ELEMTYPEREAL    biggestDiff;
    int             group, chosen = 0, betterGroup = 0;

    InitParVars( a_parVars, a_parVars->m_branchCount, a_minFill );
    PickSeeds( a_parVars );

    while( ( (a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total )
           && ( a_parVars->m_count[0] < (a_parVars->m_total - a_parVars->m_minFill) )
           && ( a_parVars->m_count[1] < (a_parVars->m_total - a_parVars->m_minFill) ) )
    {
        biggestDiff = (ELEMTYPEREAL) -1;

        for( int index = 0; index<a_parVars->m_total; ++index )
        {
            if( !a_parVars->m_taken[index] )
            {
                const Rect*     curRect = &a_parVars->m_branchBuf[index].m_rect;
                const Rect      rect0   = CombineRect( curRect, &a_parVars->m_cover[0] );
                const Rect      rect1   = CombineRect( curRect, &a_parVars->m_cover[1] );
                ELEMTYPEREAL    growth0 = CalcRectVolume( &rect0 ) - a_parVars->m_area[0];
                ELEMTYPEREAL    growth1 = CalcRectVolume( &rect1 ) - a_parVars->m_area[1];
                ELEMTYPEREAL    diff    = growth1 - growth0;

                if( diff >= 0 )
                {
                    group = 0;
                }
                else
                {
                    group = 1;
                    diff  = -diff;
                }

                if( diff > biggestDiff )
                {
                    biggestDiff = diff;
                    chosen = index;
                    betterGroup = group;
                }
                else if( (diff == biggestDiff)
                         && (a_parVars->m_count[group] < a_parVars->m_count[betterGroup]) )
                {
                    chosen = index;
                    betterGroup = group;
                }
            }
        }

        Classify( chosen, betterGroup, a_parVars );
    }

    // If one group too full, put remaining rects in the other
    if( (a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total )
    {
        if( a_parVars->m_count[0] >= a_parVars->m_total - a_parVars->m_minFill )
        {
            group = 1;
        }
        else
        {
            group = 0;
        }

        for( int index = 0; index<a_parVars->m_total; ++index )
        {
            if( !a_parVars->m_taken[index] )
            {
                Classify( index, group, a_parVars );
            }
        }
    }

    ASSERT( (a_parVars->m_count[0] + a_parVars->m_count[1]) == a_parVars->m_total );
    ASSERT( (a_parVars->m_count[0] >= a_parVars->m_minFill)
            && (a_parVars->m_count[1] >= a_parVars->m_minFill) );
}


// Copy branches from the buffer into two nodes according to the partition.
RTREE_TEMPLATE
void RTREE_QUAL::LoadNodes( Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars ) const
{
    ASSERT( a_nodeA );
    ASSERT( a_nodeB );
    ASSERT( a_parVars );

    for( int index = 0; index < a_parVars->m_total; ++index )
    {
        ASSERT( a_parVars->m_partition[index] == 0 || a_parVars->m_partition[index] == 1 );

        if( a_parVars->m_partition[index] == 0 )
        {
            AddBranch( &a_parVars->m_branchBuf[index], a_nodeA, NULL );
        }
        else if( a_parVars->m_partition[index] == 1 )
        {
            AddBranch( &a_parVars->m_branchBuf[index], a_nodeB, NULL );
        }
    }
}


// Initialize a PartitionVars structure.
RTREE_TEMPLATE
void RTREE_QUAL::InitParVars( PartitionVars* a_parVars, int a_maxRects, int a_minFill ) const
{
    ASSERT( a_parVars );

    a_parVars->m_count[0]   = a_parVars->m_count[1] = 0;
    a_parVars->m_area[0]    = a_parVars->m_area[1] = (ELEMTYPEREAL) 0;
    a_parVars->m_total      = a_maxRects;
    a_parVars->m_minFill    = a_minFill;

    for( int index = 0; index < a_maxRects; ++index )
    {
        a_parVars->m_taken[index]       = false;
        a_parVars->m_partition[index]   = -1;
    }
}


RTREE_TEMPLATE
void RTREE_QUAL::PickSeeds( PartitionVars* a_parVars ) const
{
    int             seed0 = 0, seed1 = 0;
    ELEMTYPEREAL    worst, waste;
    ELEMTYPEREAL    area[MAXNODES + 1];

    for( int index = 0; index<a_parVars->m_total; ++index )
    {
        area[index] = CalcRectVolume( &a_parVars->m_branchBuf[index].m_rect );
    }

    worst = -a_parVars->m_coverSplitArea - 1;

    for( int indexA = 0; indexA < a_parVars->m_total - 1; ++indexA )
    {
        for( int indexB = indexA + 1; indexB < a_parVars->m_total; ++indexB )
        {
            Rect oneRect = CombineRect( &a_parVars->m_branchBuf[indexA].m_rect,
                                        &a_parVars->m_branchBuf[indexB].m_rect );
            waste = CalcRectVolume( &oneRect ) - area[indexA] - area[indexB];

            if( waste >= worst )
            {
                worst   = waste;
                seed0   = indexA;
                seed1   = indexB;
            }
        }
    }

    Classify( seed0, 0, a_parVars );
    Classify( seed1, 1, a_parVars );
}


// Put a branch in one of the groups.
RTREE_TEMPLATE
void RTREE_QUAL::Classify( int a_index, int a_group, PartitionVars* a_parVars ) const
{
    ASSERT( a_parVars );
    ASSERT( !a_parVars->m_taken[a_index] );

    a_parVars->m_partition[a_index] = a_group;
    a_parVars->m_taken[a_index]     = true;

    if( a_parVars->m_count[a_group] == 0 )
    {
        a_parVars->m_cover[a_group] = a_parVars->m_branchBuf[a_index].m_rect;
    }
    else
    {
        a_parVars->m_cover[a_group] = CombineRect( &a_parVars->m_branchBuf[a_index].m_rect,
                                                   &a_parVars->m_cover[a_group] );
    }

    a_parVars->m_area[a_group] = CalcRectVolume( &a_parVars->m_cover[a_group] );
    ++a_parVars->m_count[a_group];
}


// Delete a data rectangle from an index structure.
// Pass in a pointer to a Rect, the tid of the record, ptr to ptr to root node.
// Returns 1 if record not found, 0 if success.
// RemoveRect provides for eliminating the root.
RTREE_TEMPLATE
bool RTREE_QUAL::RemoveRect( const Rect* a_rect, const DATATYPE& a_id, Node** a_root ) const
{
    ASSERT( a_rect && a_root );
    ASSERT( *a_root );

    Node*       tempNode;
    ListNode*   reInsertList = NULL;

    if( !RemoveRectRec( a_rect, a_id, *a_root, &reInsertList ) )
    {
        // Found and deleted a data item
        // Reinsert any branches from eliminated nodes
        while( reInsertList )
        {
            tempNode = reInsertList->m_node;

            for( int index = 0; index < tempNode->m_count; ++index )
            {
                InsertRect( &(tempNode->m_branch[index].m_rect),
                            tempNode->m_branch[index].m_data,
                            a_root,
                            tempNode->m_level );
            }

            ListNode* remLNode = reInsertList;
            reInsertList = reInsertList->m_next;

            FreeNode( remLNode->m_node );
            FreeListNode( remLNode );
        }

        // Check for redundant root (not leaf, 1 child) and eliminate
        if( (*a_root)->m_count == 1 && (*a_root)->IsInternalNode() )
        {
            tempNode = (*a_root)->m_branch[0].m_child;

            ASSERT( tempNode );
            FreeNode( *a_root );
            *a_root = tempNode;
        }

        return false;
    }
    else
    {
        return true;
    }
}


// Delete a rectangle from non-root part of an index structure.
// Called by RemoveRect.  Descends tree recursively,
// merges branches on the way back up.
// Returns 1 if record not found, 0 if success.
RTREE_TEMPLATE
bool RTREE_QUAL::RemoveRectRec( const Rect*     a_rect,
                                const DATATYPE& a_id,
                                Node*           a_node,
                                ListNode**      a_listNode ) const
{
    ASSERT( a_rect && a_node && a_listNode );
    ASSERT( a_node->m_level >= 0 );

    if( a_node->IsInternalNode() ) // not a leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            if( Overlap( a_rect, &(a_node->m_branch[index].m_rect) ) )
            {
                if( !RemoveRectRec( a_rect, a_id, a_node->m_branch[index].m_child, a_listNode ) )
                {
                    if( a_node->m_branch[index].m_child->m_count >= MINNODES )
                    {
                        // child removed, just resize parent rect
                        a_node->m_branch[index].m_rect =
                            NodeCover( a_node->m_branch[index].m_child );
                    }
                    else
                    {
                        // child removed, not enough entries in node, eliminate node
                        ReInsert( a_node->m_branch[index].m_child, a_listNode );
                        DisconnectBranch( a_node, index ); // Must return after this call as count has changed
                    }

                    return false;
                }
            }
        }

        return true;
    }
    else // A leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            if( a_node->m_branch[index].m_child == (Node*) a_id )
            {
                DisconnectBranch( a_node, index ); // Must return after this call as count has changed
                return false;
            }
        }

        return true;
    }
}


// Decide whether two rectangles overlap.
RTREE_TEMPLATE
bool RTREE_QUAL::Overlap( const Rect* a_rectA, const Rect* a_rectB )
{
    ASSERT( a_rectA && a_rectB );

    for( int index = 0; index < NUMDIMS; ++index )
    {
        if( a_rectA->m_min[index] > a_rectB->m_max[index]
            || a_rectB->m_min[index] > a_rectA->m_max[index] )
        {
            return false;
        }
    }

    return true;
}


// Add a node to the reinsertion list.  All its branches will later
// be reinserted into the index structure.
RTREE_TEMPLATE
void RTREE_QUAL::ReInsert( Node* a_node, ListNode** a_listNode ) const
{
    ListNode* newListNode;

    newListNode = AllocListNode();
    newListNode->m_node = a_node;
    newListNode->m_next = *a_listNode;
    *a_listNode = newListNode;
}


// Search in an index tree or subtree for all data rectangles that overlap the argument rectangle.
RTREE_TEMPLATE
bool RTREE_QUAL::Search( const Node* a_node, const Rect* a_rect, int& a_foundCount,
        std::function<bool (const DATATYPE&)> a_callback ) const
{
    ASSERT( a_node );
    ASSERT( a_node->m_level >= 0 );
    ASSERT( a_rect );

    if( a_node->IsInternalNode() ) // This is an internal node in the tree
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) )
            {
                if( !Search( a_node->m_branch[index].m_child, a_rect, a_foundCount, a_callback ) )
                {
                    return false; // Don't continue searching
                }
            }
        }
    }
    else // This is a leaf node
    {
        for( int index = 0; index < a_node->m_count; ++index )
        {
            if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) )
            {
                DATATYPE& id = a_node->m_branch[index].m_data;
                ++a_foundCount;

                if( a_callback && !a_callback( id ) )
                {
                    return false; // Don't continue searching
                }
            }
        }
    }

    return true; // Continue searching
}


//calculate the minimum distance between a point and a rectangle as defined by Manolopoulos et al.
// returns Euclidean norm to ensure value fits in ELEMTYPE
RTREE_TEMPLATE
ELEMTYPE RTREE_QUAL::MinDist( const ELEMTYPE a_point[NUMDIMS], const Rect& a_rect ) const
{
    const ELEMTYPE *q, *s, *t;
    q = a_point;
    s = a_rect.m_min;
    t = a_rect.m_max;
    double minDist = 0.0;

    for( int index = 0; index < NUMDIMS; index++ )
    {
        int r = q[index];

        if( q[index] < s[index] )
        {
            r = s[index];
        }
        else if( q[index] > t[index] )
        {
            r = t[index];
        }

        double addend = q[index] - r;
        minDist += addend * addend;
    }

    return std::lround( std::sqrt( minDist ) );
}


#undef RTREE_TEMPLATE
#undef RTREE_QUAL
#undef RTREE_SEARCH_TEMPLATE
#undef RTREE_SEARCH_QUAL

#endif    // RTREE_H
