    // Copyright (C) 2003  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_BINARY_SEARCH_TREE_KERNEl_2_
    #define DLIB_BINARY_SEARCH_TREE_KERNEl_2_

    #include "binary_search_tree_kernel_abstract.h"
    #include "../algs.h"
    #include "../interfaces/map_pair.h"
    #include "../interfaces/enumerable.h"
    #include "../interfaces/remover.h"
    #include "../serialize.h"
    #include <functional>

    namespace dlib
    {

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare = std::less<domain>
            >
        class binary_search_tree_kernel_2 : public enumerable<map_pair<domain,range> >,
                                            public asc_pair_remover<domain,range,compare>
        {

            /*!
                INITIAL VALUE
                    NIL == pointer to a node that represents a leaf
                    tree_size == 0
                    tree_root == NIL
                    at_start == true
                    current_element == 0

                CONVENTION
                    current_element_valid() == (current_element != 0)
                    if (current_element_valid()) then
                        element() == current_element->d and current_element->r
                    at_start_ == at_start()

                    tree_size   == size()

                    NIL == pointer to a node that represents a leaf

                    if (tree_size != 0)
                        tree_root == pointer to the root node of the binary search tree
                    else
                        tree_root == NIL

                    tree_root->color == black
                    Every leaf is black and all leafs are the NIL node.
                    The number of black nodes in any path from the root to a leaf is the
                    same.

                    for all nodes:
                    {
                        - left points to the left subtree or NIL if there is no left subtree
                        - right points to the right subtree or NIL if there is no right
                          subtree
                        - parent points to the parent node or NIL if the node is the root
                        - ordering of nodes is determined by comparing each node's d member
                        - all elements in a left subtree are <= the node
                        - all elements in a right subtree are >= the node
                        - color == red or black
                        - if (color == red)
                            - the node's children are black
                    }

            !*/

            class node
            {
            public:
                node* left;
                node* right;
                node* parent;
                domain d;
                range r;
                char color;
            };

            class mpair : public map_pair<domain,range>
            {
            public:
                const domain* d;
                range* r;

                const domain& key(
                ) const { return *d; }

                const range& value(
                ) const { return *r; }

                range& value(
                ) { return *r; }
            };

            const static char red = 0;
            const static char black = 1;

            public:

                typedef domain domain_type;
                typedef range range_type;
                typedef compare compare_type;
                typedef mem_manager mem_manager_type;

                binary_search_tree_kernel_2(
                ) :
                    NIL(pool.allocate()),
                    tree_size(0),
                    tree_root(NIL),
                    current_element(0),
                    at_start_(true)
                {
                    NIL->color = black;
                    NIL->left = 0;
                    NIL->right = 0;
                    NIL->parent = 0;
                }

                virtual ~binary_search_tree_kernel_2(
                );

                inline void clear(
                );

                inline short height (
                ) const;

                inline unsigned long count (
                    const domain& d
                ) const;

                inline void add (
                    domain& d,
                    range& r
                );

                void remove (
                    const domain& d,
                    domain& d_copy,
                    range& r
                );

                void destroy (
                    const domain& d
                );

                void remove_any (
                    domain& d,
                    range& r
                );

                inline const range* operator[] (
                    const domain& item
                ) const;

                inline range* operator[] (
                    const domain& item
                );

                inline void swap (
                    binary_search_tree_kernel_2& item
                );

                // functions from the enumerable interface
                inline size_t size (
                ) const;

                bool at_start (
                ) const;

                inline void reset (
                ) const;

                bool current_element_valid (
                ) const;

                const map_pair<domain,range>& element (
                ) const;

                map_pair<domain,range>& element (
                );

                bool move_next (
                ) const;

                void remove_last_in_order (
                    domain& d,
                    range& r
                );

                void remove_current_element (
                    domain& d,
                    range& r
                );

                void position_enumerator (
                    const domain& d
                ) const;

            private:

                inline void rotate_left (
                    node* t
                );
                /*!
                    requires
                        - t != NIL
                        - t->right != NIL
                    ensures
                        - performs a left rotation around t and its right child
                !*/

                inline void rotate_right (
                    node* t
                );
                /*!
                    requires
                        - t != NIL
                        - t->left != NIL
                    ensures
                        - performs a right rotation around t and its left child
                !*/

                inline void double_rotate_right (
                    node* t
                );
                /*!
                    requires
                        - t != NIL
                        - t->left != NIL
                        - t->left->right != NIL
                        - double_rotate_right() is only called in fix_after_add()
                    ensures
                        - performs a left rotation around t->left
                        - then performs a right rotation around t
                !*/

                inline void double_rotate_left (
                    node* t
                );
                /*!
                    requires
                        - t != NIL
                        - t->right != NIL
                        - t->right->left != NIL
                        - double_rotate_left() is only called in fix_after_add()
                    ensures
                        - performs a right rotation around t->right
                        - then performs a left rotation around t
                !*/

                void remove_biggest_element_in_tree (
                    node* t,
                    domain& d,
                    range& r
                );
                /*!
                    requires
                        - t != NIL  (i.e. there must be something in the tree to remove)
                    ensures
                        - the biggest node in t has been removed
                        - the biggest node element in t has been put into #d and #r
                        - #t is still a binary search tree
                !*/

                bool remove_least_element_in_tree (
                    node* t,
                    domain& d,
                    range& r
                );
                /*!
                    requires
                        - t != NIL  (i.e. there must be something in the tree to remove)
                    ensures
                        - the least node in t has been removed
                        - the least node element in t has been put into #d and #r
                        - #t is still a binary search tree
                        - if (the node that was removed was the one pointed to by current_element) then
                            - returns true
                        - else
                            - returns false
                !*/

                void add_to_tree (
                    node* t,
                    domain& d,
                    range& r
                );
                /*!
                    requires
                        - t != NIL
                    ensures
                        - d and r are now in #t
                        - there is a mapping from d to r in #t
                        - #d and #r have initial values for their types
                        - #t is still a binary search tree
                !*/

                void remove_from_tree (
                    node* t,
                    const domain& d,
                    domain& d_copy,
                    range& r
                );
                /*!
                    requires
                        - return_reference(t,d) != 0
                    ensures
                        - #d_copy is equivalent to d
                        - the first element in t equivalent to d that is encountered when searching down the tree
                          from t has been removed and swapped into #d_copy.  Also, the associated range element
                          has been removed and swapped into #r.
                        - if (the node that got removed wasn't current_element) then
                            - adjusts the current_element pointer if the data in the node that it points to gets moved.
                        - else
                            - the value of current_element is now invalid
                        - #t is still a binary search tree
                !*/

                void remove_from_tree (
                    node* t,
                    const domain& d
                );
                /*!
                    requires
                        - return_reference(t,d) != 0
                    ensures
                        - an element in t equivalent to d has been removed
                        - #t is still a binary search tree
                !*/

                const range* return_reference (
                    const node* t,
                    const domain& d
                ) const;
                /*!
                    ensures
                        - if (there is a domain element equivalent to d in t) then
                            - returns a pointer to the element in the range equivalent to d
                        - else
                            - returns 0
                !*/

                range* return_reference (
                    node* t,
                    const domain& d
                );
                /*!
                    ensures
                        - if (there is a domain element equivalent to d in t) then
                            - returns a pointer to the element in the range equivalent to d
                        - else
                            - returns 0
                !*/

                void fix_after_add (
                    node* t
                );
                /*!
                    requires
                        - t == pointer to the node just added
                        - t->color == red
                        - t->parent != NIL (t must not be the root)
                        - fix_after_add() is only called after a new node has been added
                          to t
                    ensures
                        - fixes any deviations from the CONVENTION caused by adding a node
                !*/

                void fix_after_remove (
                    node* t
                );
                /*!
                    requires
                        - t == pointer to the only child of the node that was spliced out
                        - fix_after_remove() is only called after a node has been removed
                          from t
                        - the color of the spliced out node was black
                    ensures
                        - fixes any deviations from the CONVENTION causes by removing a node
                !*/

                short tree_height (
                    node* t
                ) const;
                /*!
                    ensures
                        - returns the number of nodes in the longest path from the root of the
                          tree to a leaf
                !*/

                void delete_tree (
                    node* t
                );
                /*!
                    requires
                        - t == root of binary search tree
                        - t != NIL
                    ensures
                        - deletes all nodes in t except for NIL
                !*/

                unsigned long get_count (
                    const domain& item,
                    node* tree_root
                ) const;
                /*!
                    requires
                        - tree_root == the root of a binary search tree or NIL
                    ensures
                        - if (tree_root == NIL) then
                            - returns 0
                        - else
                            - returns the number of elements in tree_root that are
                              equivalent to item
                !*/

                // data members
                typename mem_manager::template rebind<node>::other pool;
                node* NIL;
                unsigned long tree_size;
                node* tree_root;
                mutable node* current_element;
                mutable bool at_start_;
                mutable mpair p;
                compare comp;

                // restricted functions
                binary_search_tree_kernel_2(binary_search_tree_kernel_2&);
                binary_search_tree_kernel_2& operator=(binary_search_tree_kernel_2&);

        };

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        inline void swap (
            binary_search_tree_kernel_2<domain,range,mem_manager,compare>& a,
            binary_search_tree_kernel_2<domain,range,mem_manager,compare>& b
        ) { a.swap(b); }

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void deserialize (
            binary_search_tree_kernel_2<domain,range,mem_manager,compare>& item,
            std::istream& in
        )
        {
            try
            {
                item.clear();
                unsigned long size;
                deserialize(size,in);
                domain d;
                range r;
                for (unsigned long i = 0; i < size; ++i)
                {
                    deserialize(d,in);
                    deserialize(r,in);
                    item.add(d,r);
                }
            }
            catch (serialization_error& e)
            {
                item.clear();
                throw serialization_error(e.info + "\n   while deserializing object of type binary_search_tree_kernel_2");
            }
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        ~binary_search_tree_kernel_2 (
        )
        {
            if (tree_root != NIL)
                delete_tree(tree_root);
            pool.deallocate(NIL);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        clear (
        )
        {
            if (tree_size > 0)
            {
                delete_tree(tree_root);
                tree_root = NIL;
                tree_size = 0;
            }
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        size_t binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        size (
        ) const
        {
            return tree_size;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        short binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        height (
        ) const
        {
            return tree_height(tree_root);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        unsigned long binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        count (
            const domain& item
        ) const
        {
            return get_count(item,tree_root);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        add (
            domain& d,
            range& r
        )
        {
            if (tree_size == 0)
            {
                tree_root = pool.allocate();
                tree_root->color = black;
                tree_root->left = NIL;
                tree_root->right = NIL;
                tree_root->parent = NIL;
                exchange(tree_root->d,d);
                exchange(tree_root->r,r);
            }
            else
            {
                add_to_tree(tree_root,d,r);
            }
            ++tree_size;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove (
            const domain& d,
            domain& d_copy,
            range& r
        )
        {
            remove_from_tree(tree_root,d,d_copy,r);
            --tree_size;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        destroy (
            const domain& item
        )
        {
            remove_from_tree(tree_root,item);
            --tree_size;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_any (
            domain& d,
            range& r
        )
        {
            remove_least_element_in_tree(tree_root,d,r);
            --tree_size;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        range* binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        operator[] (
            const domain& d
        )
        {
            return return_reference(tree_root,d);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        const range* binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        operator[] (
            const domain& d
        ) const
        {
            return return_reference(tree_root,d);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        swap (
            binary_search_tree_kernel_2<domain,range,mem_manager,compare>& item
        )
        {
            pool.swap(item.pool);

            exchange(p,item.p);
            exchange(comp,item.comp);

            node* tree_root_temp            = item.tree_root;
            unsigned long tree_size_temp    = item.tree_size;
            node* const NIL_temp            = item.NIL;
            node* current_element_temp      = item.current_element;
            bool at_start_temp              = item.at_start_;

            item.tree_root                  = tree_root;
            item.tree_size                  = tree_size;
            item.NIL                        = NIL;
            item.current_element            = current_element;
            item.at_start_                  = at_start_;

            tree_root                       = tree_root_temp;
            tree_size                       = tree_size_temp;
            NIL                             = NIL_temp;
            current_element                 = current_element_temp;
            at_start_                       = at_start_temp;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_last_in_order (
            domain& d,
            range& r
        )
        {
            remove_biggest_element_in_tree(tree_root,d,r);
            --tree_size;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_current_element (
            domain& d,
            range& r
        )
        {
            node* t = current_element;
            move_next();
            remove_from_tree(t,t->d,d,r);
            --tree_size;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        position_enumerator (
            const domain& d
        ) const
        {
            // clear the enumerator state and make sure the stack is empty
            reset();
            at_start_ = false;
            node* t = tree_root;
            node* parent = NIL;
            bool went_left = false;
            while (t != NIL)
            {
                if ( comp(d , t->d ))
                {
                    // if item is on the left then look in left
                    parent = t;
                    t = t->left;
                    went_left = true;
                }
                else if (comp(t->d , d))
                {
                    // if item is on the right then look in right
                    parent = t;
                    t = t->right;
                    went_left = false;
                }
                else
                {
                    current_element = t;
                    return;
                }
            }

            // if we didn't find any matches but there might be something after the
            // d in this tree.
            if (parent != NIL)
            {
                current_element = parent;
                // if we went left from this node then this node is the next
                // biggest.
                if (went_left)
                {
                    return;
                }
                else
                {
                    move_next();
                }
            }
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // enumerable function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        bool binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        at_start (
        ) const
        {
            return at_start_;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        reset (
        ) const
        {
            at_start_ = true;
            current_element = 0;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        bool binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        current_element_valid (
        ) const
        {
            return (current_element != 0);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        const map_pair<domain,range>& binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        element (
        ) const
        {
            p.d = &(current_element->d);
            p.r = &(current_element->r);
            return p;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        map_pair<domain,range>& binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        element (
        )
        {
            p.d = &(current_element->d);
            p.r = &(current_element->r);
            return p;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        bool binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        move_next (
        ) const
        {
            // if we haven't started iterating yet
            if (at_start_)
            {
                at_start_ = false;
                if (tree_size == 0)
                {
                    return false;
                }
                else
                {
                    // find the first element in the tree
                    current_element = tree_root;
                    node* temp = current_element->left;
                    while (temp != NIL)
                    {
                        current_element = temp;
                        temp = current_element->left;
                    }
                    return true;
                }
            }
            else
            {
                if (current_element == 0)
                {
                    return false;
                }
                else
                {
                    bool went_up;  // true if we went up the tree from a child node to parent
                    bool from_left = false; // true if we went up and were coming from a left child node
                    // find the next element in the tree
                    if (current_element->right != NIL)
                    {
                        // go right and down
                        current_element = current_element->right;
                        went_up = false;
                    }
                    else
                    {
                        went_up = true;
                        node* parent = current_element->parent;
                        if (parent == NIL)
                        {
                            // in this case we have iterated over all the element of the tree
                            current_element = 0;
                            return false;
                        }

                        from_left = (parent->left == current_element);
                        // go up to parent
                        current_element = parent;
                    }

                    while (true)
                    {
                        if (went_up)
                        {
                            if (from_left)
                            {
                                // in this case we have found the next node
                                break;
                            }
                            else
                            {
                                // we should go up
                                node* parent = current_element->parent;
                                from_left = (parent->left == current_element);
                                current_element = parent;
                                if (current_element == NIL)
                                {
                                    // in this case we have iterated over all the elements
                                    // in the tree
                                    current_element = 0;
                                    return false;
                                }
                            }
                        }
                        else
                        {
                            // we just went down to a child node
                            if (current_element->left != NIL)
                            {
                                // go left
                                went_up = false;
                                current_element = current_element->left;
                            }
                            else
                            {
                                // if there is no left child then we have found the next node
                                break;
                            }
                        }
                    }

                    return true;
                }
            }
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // private member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        delete_tree (
            node* t
        )
        {
            if (t->left != NIL)
                delete_tree(t->left);
            if (t->right != NIL)
                delete_tree(t->right);
            pool.deallocate(t);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        rotate_left (
            node* t
        )
        {

            // perform the rotation
            node* temp = t->right;
            t->right = temp->left;
            if (temp->left != NIL)
                temp->left->parent = t;
            temp->left = t;
            temp->parent = t->parent;

            if (t == tree_root)
                tree_root = temp;
            else
            {
                // if t was on the left
                if (t->parent->left == t)
                    t->parent->left = temp;
                else
                    t->parent->right = temp;
            }

            t->parent = temp;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        rotate_right (
            node* t
        )
        {
            // perform the rotation
            node* temp = t->left;
            t->left = temp->right;
            if (temp->right != NIL)
                temp->right->parent = t;
            temp->right = t;
            temp->parent = t->parent;

            if (t == tree_root)
                tree_root = temp;
            else
            {
                // if t is a left child
                if (t->parent->left == t)
                    t->parent->left = temp;
                else
                    t->parent->right = temp;
            }

            t->parent = temp;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        double_rotate_right (
            node* t
        )
        {

            // preform the rotation
            node& temp = *(t->left->right);
            t->left = temp.right;
            temp.right->parent = t;
            temp.left->parent = temp.parent;
            temp.parent->right = temp.left;
            temp.parent->parent = &temp;
            temp.right = t;
            temp.left = temp.parent;
            temp.parent = t->parent;

            if (tree_root == t)
                tree_root = &temp;
            else
            {
                // t is a left child
                if (t->parent->left == t)
                    t->parent->left = &temp;
                else
                    t->parent->right = &temp;
            }
            t->parent = &temp;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        double_rotate_left (
            node* t
        )
        {

            // preform the rotation
            node& temp = *(t->right->left);
            t->right = temp.left;
            temp.left->parent = t;
            temp.right->parent = temp.parent;
            temp.parent->left = temp.right;
            temp.parent->parent = &temp;
            temp.left = t;
            temp.right = temp.parent;
            temp.parent = t->parent;

            if (tree_root == t)
                tree_root = &temp;
            else
            {
                // t is a left child
                if (t->parent->left == t)
                    t->parent->left = &temp;
                else
                    t->parent->right = &temp;
            }
            t->parent = &temp;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_biggest_element_in_tree (
            node* t,
            domain& d,
            range& r
        )
        {

            node* next = t->right;
            node* child;  // the child node of the one we will slice out

            if (next == NIL)
            {
                // need to determine if t is a right or left child
                if (t->parent->right == t)
                    child = t->parent->right = t->left;
                else
                    child = t->parent->left = t->left;

                // update tree_root if necessary
                if (t == tree_root)
                    tree_root = child;
            }
            else
            {
                // find the least node
                do
                {
                    t = next;
                    next = next->right;
                } while (next != NIL);
                // t is a right child
                child = t->parent->right = t->left;

            }

            // swap the item from this node into d and r
            exchange(d,t->d);
            exchange(r,t->r);

            // plug hole right by removing this node
            child->parent = t->parent;

            // keep the red-black properties true
            if (t->color == black)
                fix_after_remove(child);

            // free the memory for this removed node
            pool.deallocate(t);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        bool binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_least_element_in_tree (
            node* t,
            domain& d,
            range& r
        )
        {

            node* next = t->left;
            node* child;  // the child node of the one we will slice out

            if (next == NIL)
            {
                // need to determine if t is a left or right child
                if (t->parent->left == t)
                    child = t->parent->left = t->right;
                else
                    child = t->parent->right = t->right;

                // update tree_root if necessary
                if (t == tree_root)
                    tree_root = child;
            }
            else
            {
                // find the least node
                do
                {
                    t = next;
                    next = next->left;
                } while (next != NIL);
                // t is a left child
                child = t->parent->left = t->right;

            }

            // swap the item from this node into d and r
            exchange(d,t->d);
            exchange(r,t->r);

            // plug hole left by removing this node
            child->parent = t->parent;

            // keep the red-black properties true
            if (t->color == black)
                fix_after_remove(child);

            bool rvalue = (t == current_element);
            // free the memory for this removed node
            pool.deallocate(t);
            return rvalue;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        add_to_tree (
            node* t,
            domain& d,
            range& r
        )
        {
            // parent of the current node
            node* parent;

            // find a place to add node
            while (true)
            {
                parent = t;
                // if item should be put on the left then go left
                if (comp(d , t->d))
                {
                    t = t->left;
                    if (t == NIL)
                    {
                        t = parent->left = pool.allocate();
                        break;
                    }
                }
                // if item should be put on the right then go right
                else
                {
                    t = t->right;
                    if (t == NIL)
                    {
                        t = parent->right = pool.allocate();
                        break;
                    }
                }
            }

            // t is now the node where we will add item and
            // parent is the parent of t

            t->parent = parent;
            t->left = NIL;
            t->right = NIL;
            t->color = red;
            exchange(t->d,d);
            exchange(t->r,r);

            // keep the red-black properties true
            fix_after_add(t);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_from_tree (
            node* t,
            const domain& d,
            domain& d_copy,
            range& r
        )
        {
            while (true)
            {
                if ( comp(d , t->d) )
                {
                    // if item is on the left then look in left
                    t = t->left;
                }
                else if (comp(t->d , d))
                {
                    // if item is on the right then look in right
                    t = t->right;
                }
                else
                {
                    // found the node we want to remove

                    // swap out the item into d_copy and r
                    exchange(d_copy,t->d);
                    exchange(r,t->r);

                    if (t->left == NIL)
                    {
                        // if there is no left subtree

                        node* parent = t->parent;

                        // plug hole with right subtree

                        // if t is on the left
                        if (parent->left == t)
                            parent->left = t->right;
                        else
                            parent->right = t->right;
                        t->right->parent = parent;

                        // update tree_root if necessary
                        if (t == tree_root)
                            tree_root = t->right;

                        if (t->color == black)
                            fix_after_remove(t->right);

                        // delete old node
                        pool.deallocate(t);
                    }
                    else if (t->right == NIL)
                    {
                        // if there is no right subtree

                        node* parent = t->parent;

                        // plug hole with left subtree
                        if (parent->left == t)
                            parent->left = t->left;
                        else
                            parent->right = t->left;
                        t->left->parent = parent;

                        // update tree_root if necessary
                        if (t == tree_root)
                            tree_root = t->left;

                        if (t->color == black)
                            fix_after_remove(t->left);

                        // delete old node
                        pool.deallocate(t);
                    }
                    else
                    {
                        // if there is both a left and right subtree
                        // get an element to fill this node now that its been swapped into
                        // item_copy
                        if (remove_least_element_in_tree(t->right,t->d,t->r))
                        {
                            // the node removed was the one pointed to by current_element so we
                            // need to update it so that it points to the right spot.
                            current_element = t;
                        }
                    }

                    // quit loop
                    break;
                }
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        remove_from_tree (
            node* t,
            const domain& d
        )
        {
            while (true)
            {
                if ( comp(d , t->d) )
                {
                    // if item is on the left then look in left
                    t = t->left;
                }
                else if (comp(t->d , d))
                {
                    // if item is on the right then look in right
                    t = t->right;
                }
                else
                {
                    // found the node we want to remove

                    if (t->left == NIL)
                    {
                        // if there is no left subtree

                        node* parent = t->parent;

                        // plug hole with right subtree

                        if (parent->left == t)
                            parent->left = t->right;
                        else
                            parent->right = t->right;
                        t->right->parent = parent;

                        // update tree_root if necessary
                        if (t == tree_root)
                            tree_root = t->right;

                        if (t->color == black)
                            fix_after_remove(t->right);

                        // delete old node
                        pool.deallocate(t);
                    }
                    else if (t->right == NIL)
                    {
                        // if there is no right subtree

                        node* parent = t->parent;

                        // plug hole with left subtree
                        if (parent->left == t)
                            parent->left = t->left;
                        else
                            parent->right = t->left;
                        t->left->parent = parent;

                        // update tree_root if necessary
                        if (t == tree_root)
                            tree_root = t->left;

                        if (t->color == black)
                            fix_after_remove(t->left);

                        // delete old node
                        pool.deallocate(t);
                    }
                    else
                    {
                        // if there is both a left and right subtree
                        // get an element to fill this node now that its been swapped into
                        // item_copy
                        remove_least_element_in_tree(t->right,t->d,t->r);

                    }

                    // quit loop
                    break;
                }
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        range* binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        return_reference (
            node* t,
            const domain& d
        )
        {
            while (t != NIL)
            {
                if ( comp(d , t->d ))
                {
                    // if item is on the left then look in left
                    t = t->left;
                }
                else if (comp(t->d , d))
                {
                    // if item is on the right then look in right
                    t = t->right;
                }
                else
                {
                    // if it's found then return a reference to it
                    return &(t->r);
                }
            }
            return 0;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        const range* binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        return_reference (
            const node* t,
            const domain& d
        ) const
        {
            while (t != NIL)
            {
                if ( comp(d , t->d) )
                {
                    // if item is on the left then look in left
                    t = t->left;
                }
                else if (comp(t->d , d))
                {
                    // if item is on the right then look in right
                    t = t->right;
                }
                else
                {
                    // if it's found then return a reference to it
                    return &(t->r);
                }
            }
            return 0;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        fix_after_add (
            node* t
        )
        {

            while (t->parent->color == red)
            {
                node& grandparent = *(t->parent->parent);

                // if both t's parent and its sibling are red
                if (grandparent.left->color == grandparent.right->color)
                {
                    grandparent.color = red;
                    grandparent.left->color = black;
                    grandparent.right->color = black;
                    t = &grandparent;
                }
                else
                {
                    // if t is a left child
                    if (t == t->parent->left)
                    {
                        // if t's parent is a left child
                        if (t->parent == grandparent.left)
                        {
                            grandparent.color = red;
                            grandparent.left->color = black;
                            rotate_right(&grandparent);
                        }
                        // if t's parent is a right child
                        else
                        {
                            t->color = black;
                            grandparent.color = red;
                            double_rotate_left(&grandparent);
                        }
                    }
                    // if t is a right child
                    else
                    {
                        // if t's parent is a left child
                        if (t->parent == grandparent.left)
                        {
                            t->color = black;
                            grandparent.color = red;
                            double_rotate_right(&grandparent);
                        }
                        // if t's parent is a right child
                        else
                        {
                            grandparent.color = red;
                            grandparent.right->color = black;
                            rotate_left(&grandparent);
                        }
                    }
                    break;
                }
            }
            tree_root->color = black;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        void binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        fix_after_remove (
            node* t
        )
        {

            while (t != tree_root && t->color == black)
            {
                if (t->parent->left == t)
                {
                    node* sibling = t->parent->right;
                    if (sibling->color == red)
                    {
                        sibling->color = black;
                        t->parent->color = red;
                        rotate_left(t->parent);
                        sibling = t->parent->right;
                    }

                    if (sibling->left->color == black && sibling->right->color == black)
                    {
                        sibling->color = red;
                        t = t->parent;
                    }
                    else
                    {
                        if (sibling->right->color == black)
                        {
                            sibling->left->color = black;
                            sibling->color = red;
                            rotate_right(sibling);
                            sibling = t->parent->right;
                        }

                        sibling->color = t->parent->color;
                        t->parent->color = black;
                        sibling->right->color = black;
                        rotate_left(t->parent);
                        t = tree_root;

                    }

                }
                else
                {

                    node* sibling = t->parent->left;
                    if (sibling->color == red)
                    {
                        sibling->color = black;
                        t->parent->color = red;
                        rotate_right(t->parent);
                        sibling = t->parent->left;
                    }

                    if (sibling->left->color == black && sibling->right->color == black)
                    {
                        sibling->color = red;
                        t = t->parent;
                    }
                    else
                    {
                        if (sibling->left->color == black)
                        {
                            sibling->right->color = black;
                            sibling->color = red;
                            rotate_left(sibling);
                            sibling = t->parent->left;
                        }

                        sibling->color = t->parent->color;
                        t->parent->color = black;
                        sibling->left->color = black;
                        rotate_right(t->parent);
                        t = tree_root;

                    }

                }

            }
            t->color = black;

        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        short binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        tree_height (
            node* t
        ) const
        {
            if (t == NIL)
                return 0;

            short height1 = tree_height(t->left);
            short height2 = tree_height(t->right);
            if (height1 > height2)
                return height1 + 1;
            else
                return height2 + 1;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename domain,
            typename range,
            typename mem_manager,
            typename compare
            >
        unsigned long binary_search_tree_kernel_2<domain,range,mem_manager,compare>::
        get_count (
            const domain& d,
            node* tree_root
        ) const
        {
            if (tree_root != NIL)
            {
                if (comp(d , tree_root->d))
                {
                    // go left
                    return get_count(d,tree_root->left);
                }
                else if (comp(tree_root->d , d))
                {
                    // go right
                    return get_count(d,tree_root->right);
                }
                else
                {
                    // go left and right to look for more matches
                    return   get_count(d,tree_root->left)
                           + get_count(d,tree_root->right)
                           + 1;
                }
            }
            return 0;
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_BINARY_SEARCH_TREE_KERNEl_2_