// Copyright (c) 2005  Tel-Aviv University (Israel).  All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL: https://github.com/CGAL/cgal/blob/v6.1.1/STL_Extension/include/CGAL/Multiset.h $
// $Id: include/CGAL/Multiset.h 08b27d3db14 $
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Ron Wein <wein@post.tau.ac.il>

#ifndef CGAL_MULTISET_H
#define CGAL_MULTISET_H

#include <CGAL/config.h>
#include <CGAL/assertions.h>
#include <CGAL/enum.h>
#include <CGAL/memory.h>
#include <CGAL/number_utils_classes.h>
#include <CGAL/Compact_container.h>
#include <iterator>

namespace CGAL {

/*!
 * Container class representing a red-black tree, which is a balanced binary
 * tree that has the following invariants:
 * 1. Each node has a color, which is either red or black.
 * 2. Each red node has two black children (if a child is missing, it is
 *    considered as a black node).
 * 3. The number of black nodes from every path from the tree root to a leaf
 *    is the same for all tree leaves (it is called the 'black height' of the
 *    tree).
 * Due to properties 2-3, the height of a red-black tree containing n nodes
 * is bounded by 2*log_2(n).
 *
 * The Multiset template requires three template parameters:
 * - The contained Type class represents the objects stored in the tree.
 *   It has to support the default constructor, the copy constructor and
 *   the assignment operator (operator=).
 * - Compare is a three-valued functor used to define the order of objects of
 *   class Type: It has to support an operator() that receives two objects from
 *   the Type class and returns SMALLER, EQUAL or LARGER, depending on the
 *   comparison result.
 *   In case the default parameter is supplied, the Type class has to support
 *   the less-than (<) and the equal (==) operators.
 * - The Allocator represents an allocator class. By default, it is the CGAL
 *   allocator.
 */

template <typename Type_,
          class Compare_ = CGAL::Compare<Type_>,
          class Allocator_ = CGAL_ALLOCATOR(int),
#ifdef CGAL_MULTISET_USE_COMPACT_CONTAINER_AS_DEFAULT
          typename UseCompactContainer = Tag_true>
#else
          typename UseCompactContainer = Tag_false>
#endif
class Multiset
{
public:

  // General type definitions:
  typedef Type_                                     Type;
  typedef Compare_                                  Compare;
  typedef Allocator_                                Allocator;
  typedef Multiset<Type, Compare, Allocator, UseCompactContainer>
                                                    Self;

  // Type definitions for STL compatibility.
  typedef Type                                      value_type;
  typedef Type                                      key_type;
  typedef value_type&                               reference;
  typedef const value_type&                         const_reference;
  typedef Compare                                   key_compare;
  typedef Compare                                   value_compare;
  typedef size_t                                    size_type;
  typedef std::ptrdiff_t                  difference_type;

protected:

  /*! \struct
   * Representation of a node in a red-black tree.
   */
  struct Node
  {

    enum Color
    {
      RED,                        // Regular red node in the tree.
      BLACK,                      // Regular black node in the tree.
      DUMMY_BEGIN,                // The dummy before-the-begin tree node.
      DUMMY_END                   // The dummy past-the-end tree node.
    };

    typedef char Node_color;

    Type         object;           // The stored object.
    Node_color   color;            // The color of the node.
    Node        *parentP;          // Points on the parent node.
    Node        *rightP;           // Points on the right child of the node.
    Node        *leftP;            // Points on the left child of the node.

    /*! Default constructor. */
    Node() :
      parentP(nullptr),
      rightP(nullptr),
      leftP(nullptr)
    {}

    /*!
     * Constructor of a red-black tree node.
     * \param _object The object stored in the node.
     * \param _color The color of the node.
     */
    Node (const Type& _object,  Node_color _color) :
      object(_object),
      color(_color),
      parentP(nullptr),
      rightP(nullptr),
      leftP(nullptr)
    {}

    /*!
     * Initialize the node.
     * \param _object The object stored in the node.
     * \param _color The color of the node.
     */
    void init (const Type& _object, Node_color _color)
    {
      object = _object;
      color = _color;
    }

    /*! Destructor. */
    ~Node ()
    {}

    /*!
     * Check if the node is valid (not a dummy node).
     */
    inline bool is_valid () const
    {
      return (color == BLACK || color == RED);
    }

    /*!
     * Check if the node is red.
     */
    inline bool is_red () const
    {
      return (color == RED);
    }

    /*!
     * Check if the node is black.
     * Note that dummy nodes are also considered to be black.
     */
    inline bool is_black () const
    {
      return (color != RED);
    }

    /*!
     * Get the previous node in the tree (according to the tree order).
     */
    Node* predecessor () const
    {
      // The DUMMY_BEGIN node has no predecessor.
      CGAL_assertion (color != DUMMY_BEGIN);

      Node        *predP;

      if (leftP != nullptr)
      {
        // If there is a left child, the predecessor is the maximal object in
        // the sub-tree spanned by this child.
        predP = leftP;
        while (predP->rightP != nullptr)
          predP = predP->rightP;
      }
      else
      {
        // Otherwise, go up the tree until reaching the parent from the right
        // direction (this also covers the case of the DUMMY_END node).
        const Node    *prevP = this;

        predP = parentP;
        while (predP != nullptr && prevP == predP->leftP)
        {
          prevP = predP;
          predP = predP->parentP;
        }
      }

      return (predP);
    }

    /*!
     * Get the next node in the tree (according to the tree order).
     */
    Node* successor () const
    {
      // The DUMMY_END node has no successor.
      CGAL_assertion (color != DUMMY_END);

      Node        *succP;

      if (rightP != nullptr)
      {
        // If there is a right child, the successor is the minimal object in
        // the sub-tree spanned by this child.
        succP = rightP;
        while (succP->leftP != nullptr)
          succP = succP->leftP;
      }
      else
      {
        // Otherwise, go up the tree until reaching the parent from the left
        // direction (this also covers the case of the DUMMY_BEGIN node).
        const Node    *prevP = this;

        succP = parentP;
        while (succP != nullptr && prevP == succP->rightP)
        {
          prevP = succP;
          succP = succP->parentP;
        }
      }

      return (succP);
    }

    void* for_compact_container() const
    {
      return parentP;
    }

    void for_compact_container (void * p)
    {
      reinterpret_cast<void*&>(parentP) = p;
    }

  };

  // Rebind the allocator to the Node type:
  class Default_node_allocator
  {
    typedef std::allocator_traits<Allocator> Allocator_traits;
    typedef typename Allocator_traits::template rebind_alloc<Node> Base;
    Base base;

  public:

    Node* allocate (const Node& n)
    {
      Node* new_node = base.allocate(1);
      std::allocator_traits<Base>::construct(base, new_node, n);
      return new_node;
    }

    void deallocate (Node* n)
    {
      std::allocator_traits<Base>::destroy(base, n);
      base.deallocate (n, 1);
    }
  };

  class CC_node_allocator
  {
    typedef Compact_container<Node> Base;
    Base base;

  public:

    Node* allocate (const Node& n)
    {
      Node* new_node = &*base.emplace(n);
      return new_node;
    }

    void deallocate (Node* n)
    {
      base.erase (base.iterator_to(*n));
    }
  };

  typedef typename std::conditional<UseCompactContainer::value,
                                    CC_node_allocator,
                                    Default_node_allocator>::type Node_allocator;

public:

  // Forward declaration:
  class const_iterator;

  /*! \class
   * An iterator for the red black tree.
   * The iterator also servers as a handle to a tree nodes.
   */
  class iterator
  {
    // Give the red-black tree class template access to the iterator's members.
    friend class Multiset<Type, Compare, Allocator, UseCompactContainer>;
    friend class const_iterator;

  public:

    // Type definitions:
    typedef std::bidirectional_iterator_tag  iterator_category;
    typedef Type                             value_type;
    typedef std::ptrdiff_t         difference_type;
    typedef size_t                           size_type;
    typedef value_type&                      reference;
    typedef value_type*                      pointer;

  private:

    Node    *nodeP;              // Points to a tree node.

    /*! Private constructor. */
    iterator (Node *_nodeP) :
      nodeP (_nodeP)
    {}

  public:

    /*! Default constructor. */
    iterator () :
      nodeP (nullptr)
    {}

    /*! Equality operator. */
    bool operator== (const iterator& iter) const
    {
      return (nodeP == iter.nodeP);
    }

    /*! Inequality operator. */
    bool operator!= (const iterator& iter) const
    {
      return (nodeP != iter.nodeP);
    }

    /*! Increment operator (prefix notation). */
    iterator& operator++ ()
    {
      CGAL_precondition (nodeP != nullptr);

      nodeP = nodeP->successor();
      return (*this);
    }

    /*! Increment operator (postfix notation). */
    iterator operator++ (int )
    {
      CGAL_precondition (nodeP != nullptr);

      iterator temp = *this;

      nodeP = nodeP->successor();
      return (temp);
    }

    /*! Decrement operator (prefix notation). */
    iterator& operator-- ()
    {
      CGAL_precondition (nodeP != nullptr);

      nodeP = nodeP->predecessor();
      return (*this);
    }

    /*! Decrement operator (postfix notation). */
    iterator operator-- (int )
    {
      CGAL_precondition (nodeP != nullptr);

      iterator temp = *this;

      nodeP = nodeP->predecessor();
      return (temp);
    }

    /*!
     * Get the object pointed by the iterator.
     */
    reference operator* () const
    {
      CGAL_precondition (nodeP != nullptr && nodeP->is_valid());

      return (nodeP->object);
    }

    /*!
     * Get the a pointer object pointed by the iterator.
     */
    pointer operator-> () const
    {
      CGAL_precondition (nodeP != nullptr && nodeP->is_valid());

      return (&(nodeP->object));
    }

  };
  friend class iterator;

  /*! \class
   * An const iterator for the red black tree.
   * The iterator also servers as a const handle to a tree nodes.
   */
  class const_iterator
  {
    // Give the red-black tree class template access to the iterator's members.
    friend class Multiset<Type, Compare, Allocator, UseCompactContainer>;

  public:

    // Type definitions:
    typedef std::bidirectional_iterator_tag  iterator_category;
    typedef Type                             value_type;
    typedef std::ptrdiff_t         difference_type;
    typedef size_t                           size_type;
    typedef const value_type&                reference;
    typedef const value_type*                pointer;

  private:

    const Node    *nodeP;     // Points to a tree node.

    /*! Private constructor. */
    const_iterator (const Node *_nodeP) :
      nodeP (_nodeP)
    {}

  public:

    /*! Default constructor. */
    const_iterator () :
      nodeP (nullptr)
    {}

    /*! Constructor from a mutable iterator. */
    const_iterator (const iterator& iter) :
      nodeP (iter.nodeP)
    {}

    /*! Equality operator. */
    bool operator== (const const_iterator& iter) const
    {
      return (nodeP == iter.nodeP);
    }

    /*! Inequality operator. */
    bool operator!= (const const_iterator& iter) const
    {
      return (nodeP != iter.nodeP);
    }

    /*! Increment operator (prefix notation). */
    const_iterator& operator++ ()
    {
      CGAL_precondition (nodeP != nullptr);

      nodeP = nodeP->successor();
      return (*this);
    }

    /*! Increment operator (postfix notation). */
    const_iterator operator++ (int )
    {
      CGAL_precondition (nodeP != nullptr);

      const_iterator temp = *this;

      nodeP = nodeP->successor();
      return (temp);
    }

    /*! Decrement operator (prefix notation). */
    const_iterator& operator-- ()
    {
      CGAL_precondition (nodeP != nullptr);

      nodeP = nodeP->predecessor();
      return (*this);
    }

    /*! Decrement operator (postfix notation). */
    const_iterator operator-- (int )
    {
      CGAL_precondition (nodeP != nullptr);

      const_iterator temp = *this;

      nodeP = nodeP->predecessor();
      return (temp);
    }

    /*!
     * Get the object pointed by the iterator.
     */
    reference operator* () const
    {
      CGAL_precondition (nodeP != nullptr && nodeP->is_valid());

      return (nodeP->object);
    }

    /*!
     * Get the a pointer object pointed by the iterator.
     */
    pointer operator-> () const
    {
      CGAL_precondition (nodeP != nullptr && nodeP->is_valid());

      return (&(nodeP->object));
    }

  };
  friend class const_iterator;

  // Define the reverse iterators:
  typedef std::reverse_iterator<iterator>         reverse_iterator;
  typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;

protected:

  // Data members:
  Node            *rootP;        // Pointer to the tree root.
  size_t           iSize;        // Number of objects stored in the tree.
  size_t           iBlackHeight; // The black-height of the tree (the number
                                 // of black nodes from the root to each leaf).
  Compare          comp_f;       // A comparison functor.
  Node_allocator   node_alloc;   // Allocator for the tree nodes.
  Node             beginNode;    // A fictitious before-the-minimum node.
                                 // Its parent is the leftmost tree node.
  Node             endNode;      // A fictitious past-the-maximum node.
                                 // Its parent is the rightmost tree node.

public:

  /// \name Construction and destruction functions.
  //@{

  /*!
   * Default constructor. [takes \cgalBigO{1} operations]
   */
  Multiset ();

  /*!
   * Constructor with a comparison object. [takes \cgalBigO{1} operations]
   * \param comp A comparison object to be used by the tree.
   */
  Multiset (const Compare& comp);

  /*!
   * Copy constructor. [takes \cgalBigO{n} operations]
   * \param tree The copied tree.
   */
  Multiset (const Self& tree);

  /*!
   * Construct a tree that contains all objects in the given range.
   * [takes \cgalBigO{n log n} operations]
   * \param first An iterator for the first object in the range.
   * \param last A past-the-end iterator for the range.
   */
  template <class InputIterator>
  Multiset (InputIterator first, InputIterator last,
            const Compare& comp = Compare()) :
    rootP (nullptr),
    iSize (0),
    iBlackHeight (0),
    comp_f (comp)
  {
    // Mark the two fictitious nodes as dummies.
    beginNode.color = Node::DUMMY_BEGIN;
    endNode.color = Node::DUMMY_END;

    // Insert all objects to the tree.
    while (first != last)
    {
      insert (*first);
      ++first;
    }
  }

  /*!
   * Destructor. [takes \cgalBigO{n} operations]
   */
  virtual ~Multiset () noexcept(!CGAL_ASSERTIONS_ENABLED);

  /*!
   * Assignment operator. [takes \cgalBigO{n} operations]
   * \param tree The copied tree.
   */
  Self& operator= (const Self& tree);

  /*!
   * Swap two trees. [takes \cgalBigO{1} operations]
   * \param tree The copied tree.
   */
  void swap (Self& tree);
  //@}

  /// \name Comparison operations.
  //@{

  /*!
   * Test two trees for equality. [takes \cgalBigO{n} operations]
   * \param tree The compared tree.
   */
  bool operator== (const Self& tree) const;

  /*!
   * Check if our tree is lexicographically smaller. [takes \cgalBigO{n} operations]
   * \param tree The compared tree.
   */
  bool operator< (const Self& tree) const;
  //@}

  /// \name Access functions.
  //@{

  /*!
   * Get the comparison object used by the tree (non-const version).
   */
  inline Compare& key_comp ()
  {
    return (comp_f);
  }

  /*!
   * Get the comparison object used by the tree (non-const version).
   */
  inline Compare& value_comp ()
  {
    return (comp_f);
  }


  /*!
   * Get the comparison object used by the tree (const version).
   */
  inline const Compare& key_comp () const
  {
    return (comp_f);
  }

  /*!
   * Get the comparison object used by the tree (const version).
   */
  inline const Compare& value_comp () const
  {
    return (comp_f);
  }

  /*!
   * Get an iterator for the minimum object in the tree (non-const version).
   */
  inline iterator begin ();

  /*!
   * Get a past-the-end iterator for the tree objects (non-const version).
   */
  inline iterator end ();

  /*!
   * Get an iterator for the minimum object in the tree (const version).
   */
  inline const_iterator begin () const;

  /*!
   * Get a past-the-end iterator for the tree objects (const version).
   */
  inline const_iterator end () const;

  /*!
   * Get a reverse iterator for the maxnimum object in the tree
   * (non-const version).
   */
  inline reverse_iterator rbegin ();

  /*!
   * Get a pre-the-begin reverse iterator for the tree objects
   * (non-const version).
   */
  inline reverse_iterator rend ();

  /*!
   * Get a reverse iterator for the maximum object in the tree (const version).
   */
  inline const_reverse_iterator rbegin () const;

  /*!
   * Get a pre-the-begin reverse iterator for the tree objects (const version).
   */
  inline const_reverse_iterator rend () const;

  /*!
   * Check whether the tree is empty.
   */
  inline bool empty () const
  {
    return (rootP == nullptr);
  }

  /*!
   * Get the size of the tree. [takes \cgalBigO{1} operations, unless the tree
   * was involved in a split operation, then it may take \cgalBigO{n} time.]
   * \return The number of objects stored in the tree.
   */
  size_t size () const;

  /*!
   * Get the maximal possible size (equivalent to size()).
   */
  size_t max_size () const
  {
    return (size());
  }
  //@}

  /// \name Insertion functions.

  /*!
   * Insert an object into the tree. [takes \cgalBigO{log n} operations]
   * \param object The object to be inserted.
   * \return An iterator pointing to the inserted object.
   */
  iterator insert (const Type& object);

  /*!
   * Insert a range of k objects into the tree. [takes \cgalBigO{k log n} operations]
   * \param first An iterator for the first object in the range.
   * \param last A past-the-end iterator for the range.
   */
  template <class InputIterator>
  void insert (InputIterator first, InputIterator last)
  {
    // Insert all objects to the tree.
    while (first != last)
    {
      insert (*first);
      ++first;
    }

    return;
  }

  /*!
   * Insert an object to the tree, with a given hint to its position.
   * [takes \cgalBigO{log n} operations at worst-case, but only \cgalBigO{1} amortized]
   * \param position A hint for the position of the object.
   * \param object The object to be inserted.
   * \return An iterator pointing to the inserted object.
   */
  iterator insert (iterator position,
                   const Type& object);

  /*!
   * Insert an object to the tree, as the successor the given object.
   * [takes \cgalBigO{log n} operations at worst-case, but only \cgalBigO{1} amortized]
   * \param position Points to the object after which the new object should
   *                 be inserted (or an invalid iterator to insert the object
   *                 as the tree minimum).
   * \param object The object to be inserted.
   * \pre The operation does not violate the tree properties.
   * \return An iterator pointing to the inserted object.
   */
  iterator insert_after (iterator position,
                         const Type& object);

  /*!
   * Insert an object to the tree, as the predecessor the given object.
   * [takes \cgalBigO{log n} operations at worst-case, but only \cgalBigO{1} amortized]
   * \param position Points to the object before which the new object should
   *                 be inserted (or an invalid iterator to insert the object
   *                 as the tree maximum).
   * \param object The object to be inserted.
   * \pre The operation does not violate the tree properties.
   * \return An iterator pointing to the inserted object.
   */
  iterator insert_before (iterator position,
                          const Type& object);

  /// \name Erasing functions.
  //@{

  /*!
   * Erase objects from the tree. [takes \cgalBigO{log n} operations]
   * \param object The object to be removed.
   * \return The number of objects removed from the tree.
   *          Note that all iterators to the erased objects become invalid.
   */
  size_t erase (const Type& object);

  /*!
   * Remove the object pointed by the given iterator.
   * [takes \cgalBigO{log n} operations at worst-case, but only \cgalBigO{1} amortized]
   * \param position An iterator pointing the object to be erased.
   * \pre The iterator must be a valid.
   *      Note that all iterators to the erased object become invalid.
   */
  void erase (iterator position);

  /*!
   * Clear the contents of the tree. [takes \cgalBigO{n} operations]
   */
  void clear ();

  //@}

  /// \name Search functions.
  //@{

  /*!
   * Search the tree for the given key (non-const version).
   * [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return A iterator pointing to the first equivalent object in the tree,
   *         or end() if no such object is found in the tree.
   */
  iterator find (const Type& object)
  {
    return (find (object, comp_f));
  }

  template <class Key, class CompareKey>
  iterator find (const Key& key,
                 const CompareKey& comp_key)
  {
    bool    is_equal;
    Node   *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (_is_valid(nodeP) && is_equal)
      return (iterator (nodeP));
    else
      return (iterator (&endNode));
  }

  /*!
   * Search the tree for the given key (const version).
   * [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return A iterator pointing to the first equivalent object in the tree,
   *         or end() if no such object is found in the tree.
   */
  const_iterator find (const Type& object) const
  {
    return (find (object, comp_f));
  }

  template <class Key, class CompareKey>
  const_iterator find (const Key& key,
                       const CompareKey& comp_key) const
  {
    bool          is_equal;
    const Node   *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP) && is_equal)
      return (const_iterator (nodeP));
    else
      return (const_iterator (&endNode));
  }

  /*!
   * Count the number of object in the tree equivalent to a given key.
   * [takes \cgalBigO{log n + d} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return The number of equivalent objects.
   */
  size_type count (const Type& object) const
  {
    return (count (object, comp_f));
  }

  template <class Key, class CompareKey>
  size_type count (const Key& key,
                   const CompareKey& comp_key) const
  {
    // Get the first equivalent object (if any).
    size_t      n_objects = 0;
    bool        is_equal;
    const Node *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (! is_equal)
      return (0);

    while (_is_valid (nodeP) &&
           comp_key (key, nodeP->object) == EQUAL)
    {
      n_objects++;

      // Proceed to the successor.
      nodeP = nodeP->successor();
    }

    return (n_objects);
  }

  /*!
   * Get the first element whose key is not less than a given key
   * (non-const version). [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return The lower bound of the key, or end() if the key is not found
   *         in the tree.
   */
  iterator lower_bound (const Type& object)
  {
    return (lower_bound (object, comp_f));
  }

  template <class Key, class CompareKey>
  iterator lower_bound (const Key& key,
                        const CompareKey& comp_key)
  {
    bool    is_equal;
    Node   *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP))
      return (iterator (nodeP));
    else
      return (iterator (&endNode));
  }

  /*!
   * Get the first element whose key is not less than a given key
   * (non-const version). [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return The lower bound of the key, along with a flag indicating whether
   *         the bound equals the given key.
   */
  std::pair<iterator, bool> find_lower (const Type& object)
  {
    return (find_lower (object, comp_f));
  }

  template <class Key, class CompareKey>
  std::pair<iterator, bool> find_lower (const Key& key,
                                        const CompareKey& comp_key)
  {
    bool    is_equal;
    Node   *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP))
      return (std::make_pair (iterator (nodeP), is_equal));
    else
      return (std::make_pair (iterator (&endNode), false));
  }

  /*!
   * Get the first element whose key is greater than a given key
   * (non-const version). [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return The upper bound of the key, or end() if the key is not found
   *         in the tree.
   */
  iterator upper_bound (const Type& object)
  {
    return (upper_bound (object, comp_f));
  }

  template <class Key, class CompareKey>
  iterator upper_bound (const Key& key,
                        const CompareKey& comp_key)
  {
    bool    is_equal;
    Node   *nodeP = _bound (UPPER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP))
      return (iterator (nodeP));
    else
      return (iterator (&endNode));
  }

  /*!
   * Get the first element whose key is not less than a given key
   * (const version). [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return The lower bound of the key, or end() if the key is not found
   *         in the tree.
   */
  const_iterator lower_bound (const Type& object) const
  {
    return (lower_bound (object, comp_f));
  }

  template <class Key, class CompareKey>
  const_iterator lower_bound (const Key& key,
                              const CompareKey& comp_key) const
  {
    bool          is_equal;
    const Node   *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP))
      return (const_iterator (nodeP));
    else
      return (const_iterator (&endNode));
  }

  /*!
   * Get the first element whose key is not less than a given key
   * (const version). [takes \cgalBigO{log n} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return The lower bound of the key, along with a flag indicating whether
   *         the bound equals the given key.
   */
  std::pair<const_iterator, bool> find_lower (const Type& object) const
  {
    return (find_lower (object, comp_f));
  }

  template <class Key, class CompareKey>
  std::pair<const_iterator, bool> find_lower (const Key& key,
                                              const CompareKey& comp_key) const
  {
    bool          is_equal;
    const Node   *nodeP = _bound (LOWER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP))
      return (std::make_pair (const_iterator (nodeP), is_equal));
    else
      return (std::make_pair (const_iterator (&endNode), false));
  }

  /*!
   * Get the first element whose key is greater than a given key
   * (const version). [takes \cgalBigO{log n} operations]
   * \param object The query object.
   * \return The upper bound of the key, or end() if the key is not found
   *         in the tree.
   */
  const_iterator upper_bound (const Type& object) const
  {
    return (upper_bound (object, comp_f));
  }

  template <class Key, class CompareKey>
  const_iterator upper_bound (const Key& key,
                              const CompareKey& comp_key) const
  {
    bool          is_equal;
    const Node   *nodeP = _bound (UPPER_BOUND, key, comp_key, is_equal);

    if (_is_valid (nodeP))
      return (const_iterator (nodeP));
    else
      return (const_iterator (&endNode));
  }

  /*!
   * Get the range of objects in the tree that are equivalent to a given key
   * (non-const version). [takes \cgalBigO{log n + d} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return A pair of (lower_bound(key), upper_bound(key)).
   */
  std::pair<iterator, iterator> equal_range (const Type& object)
  {
    return (equal_range (object, comp_f));
  }

  template <class Key, class CompareKey>
  std::pair<iterator, iterator>
  equal_range (const Key& key,
               const CompareKey& comp_key)
  {
    // Get the first equivalent object (if any).
    const size_t   max_steps = static_cast<size_t> (1.5 * iBlackHeight);
    bool           is_equal;
    Node          *lowerP = _bound (LOWER_BOUND, key, comp_key, is_equal);
    Node          *upperP = lowerP;
    size_t         n_objects = 0;

    if (is_equal)
    {
      while (_is_valid (upperP) &&
             comp_key (key, upperP->object) == EQUAL)
      {
        n_objects++;
        if (n_objects >= max_steps)
        {
          // If we have more than log(n) objects in the range, locate the
          // upper bound directly.
          upperP = _bound (UPPER_BOUND, key, comp_key, is_equal);
          break;
        }

        // Proceed to the successor.
        upperP = upperP->successor();
      }
    }

    return (std::pair<iterator, iterator>
      ((_is_valid (lowerP)) ? iterator (lowerP) : iterator (&endNode),
       (_is_valid (upperP)) ? iterator (upperP) : iterator (&endNode)));
  }

  /*!
   * Get the range of objects in the tree that are equivalent to a given key
   * (const version). [takes \cgalBigO{log n + d} operations]
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \return A pair of (lower_bound(key), upper_bound(key)).
   */
  std::pair<const_iterator, const_iterator>
  equal_range (const Type& object) const
  {
    return (equal_range (object, comp_f));
  }

  template <class Key, class CompareKey>
  std::pair<const_iterator, const_iterator>
  equal_range (const Key& key,
               const CompareKey& comp_key) const
  {
    // Get the first equivalent object (if any).
    const size_t   max_steps = static_cast<size_t> (1.5 * iBlackHeight);
    bool           is_equal;
    const Node    *lowerP = _bound (LOWER_BOUND, key, comp_key, is_equal);
    const Node    *upperP = lowerP;
    size_t         n_objects = 0;

    if (is_equal)
    {
      while (_is_valid (upperP) &&
             comp_key (key, upperP->object) == EQUAL)
      {
        n_objects++;
        if (n_objects >= max_steps)
        {
          // If we have more than log(n) objects in the range, locate the
          // upper bound directly.
          upperP = _bound (UPPER_BOUND, key, comp_key, is_equal);
          break;
        }

        // Proceed to the successor.
        upperP = upperP->successor();
      }
    }

    return (std::pair<const_iterator, const_iterator>
      ((_is_valid (lowerP)) ? const_iterator(lowerP) :
                              const_iterator(&endNode),
       (_is_valid (upperP)) ? const_iterator(upperP) :
                              const_iterator(&endNode)));
  }
  //@}

  /// \name Special functions.
  //@{

  /*!
   * Replace the object pointed by a given iterator with another object.
   * [takes \cgalBigO{1} operations]
   * \param position An iterator pointing the object to be replaced.
   * \param object The new object.
   * \pre The given iterator is valid.
   *      The operation does not violate the tree properties.
   */
  void replace (iterator position,
                const Type& object);

  /*!
   * Swap the location two objects in the tree, given by their positions.
   * [takes \cgalBigO{1} operations]
   * \param pos1 An iterator pointing to the first object.
   * \param pos1 An iterator pointing to the second object.
   * \pre The two iterators are valid.
   *      The operation does not violate the tree properties.
   */
  void swap (iterator pos1, iterator pos2);

  /*!
   * Catenate the tree with a given tree, whose minimal object is not less
   * than the maximal object of this tree. [takes \cgalBigO{log n} operations]
   * The function clears the other given tree, but all its iterators remain
   * valid and can be used with the catenated tree.
   * \param tree The tree to catenate to out tree.
   * \pre The minimal object in the given tree is not less than the maximal
   *      objects.
   */
  void catenate (Self& tree);

  /*!
   * Split the tree such that all remaining objects are less than a given
   * key, and all objects greater than (or equal to) this key form
   * a new output tree. [takes \cgalBigO{log n} operations]
   * \param key The split key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \param tree Output: The tree that will eventually contain all objects
   *                     greater than the split object.
   * \pre The output tree is initially empty.
   */
  void split (const Type& object, Self& tree)
  {
    split (object, comp_f, tree);
    return;
  }

  template <class Key, class CompareKey>
  void split (const Key& key, const CompareKey& comp_key,
              Self& tree)
  {
    split (lower_bound (key, comp_key), tree);
    return;
  }

  /*!
   * Split the tree at a given position, such that it contains all objects
   * in the range [begin, position) and all objects in the range
   * [position, end) form a new output tree. [takes \cgalBigO{log n} operations]
   * \param position An iterator pointing at the split position.
   * \param tree Output: The output tree.
   * \pre The output tree is initially empty.
   */
  void split (iterator position, Self& tree);

  //@}

  /// \name Debugging utilities.
  //@{

  /*!
   * Check validation of the tree. [takes o(n) operations]
   * \return true iff the tree is valid.
   *
   */
  bool is_valid() const;

  /*!
   * Get the height of the tree. [takes \cgalBigO{n} operations]
   * \return The length of the longest path from the root to a leaf node.
   */
  size_t height () const;

  /*!
   * Get the black-height of the tree. [takes \cgalBigO{1} operations]
   * \return The number of black nodes from the root to each leaf node.
   */
  inline size_t black_height () const
  {
    return (iBlackHeight);
  }
  //@}

protected:

  /// \name Auxiliary predicates.
  //@{

  /*! Check whether a node is valid. */
  inline bool _is_valid (const Node *nodeP) const
  {
    return (nodeP != nullptr && nodeP->is_valid());
  }

  /*! Check whether a node is red. */
  inline bool _is_red (const Node *nodeP) const
  {
    return (nodeP != nullptr && nodeP->color == Node::RED);
  }

  /*! Check whether a node is black. */
  inline bool _is_black (const Node *nodeP) const
  {
    // Note that invalid nodes are considered to be black as well.
    return (nodeP == nullptr || nodeP->color != Node::RED);
  }
  //@}

  /// \name Auxiliary operations.
  //@{

  /*!
   * Move the contents of one tree to another without actually duplicating
   * the nodes. This operation also clears the copied tree.
   * \param tree The tree to be copied (and eventuallu cleared).
   */
  void _shallow_assign (Self& tree);

  /*!
   * Clear the properties of the tree, without actually deallocating its
   * nodes.
   */
  void _shallow_clear ();

  /*!
   * Return the pointer to the node containing the first object that is not
   * less than (or greater than) the given key.
   * \param type The bound type (lower or upper).
   * \param key The query key.
   * \param comp_key A comparison functor for comparing keys and objects.
   * \param is_equal Output: In case of a lower bound, indicates whether the
   *                         returned node contains an object equivalent to
   *                         the given key.
   * \return A node that contains the first object that is not less than the
   *         given key (if type is LOWER_BOUND), or anode that contains the
   *         first object that is greater than the given key (if type is
   *         UPPER_BOUND) - or a nullptr node if no such nodes exist.
   */
  enum Bound_type {LOWER_BOUND, UPPER_BOUND};

  template <class Key, class CompareKey>
  Node* _bound (Bound_type type,
                const Key& key,
                const CompareKey& comp_key,
                bool& is_equal) const
  {
    // Initially mark that the key is not found in the tree.
    is_equal = false;

    if (rootP == nullptr)
      // The tree is empty:
      return (nullptr);

    Node                      *currentP = rootP;
    Node                      *prevP = currentP;
    Comparison_result          comp_res = EQUAL;

    while (_is_valid (currentP))
    {
      comp_res = comp_key (key, currentP->object);

      if (comp_res == EQUAL)
      {
        // The key is found in the tree:
        if (type == LOWER_BOUND)
        {
          is_equal = true;

          // Lower bound computation:
          // Go backward as long as we find equivalent objects.
          prevP = currentP->predecessor();
          while (_is_valid (prevP))
          {
            if (comp_key (key, prevP->object) != EQUAL)
              break;
            currentP = prevP;
            prevP = currentP->predecessor();
          }
        }
        else
        {
          // Upper bound computation:
          // Go backward until we encounter a non-equivalent objects.
          do
          {
            currentP = currentP->successor();
          } while (_is_valid (currentP) &&
                   comp_key (key, currentP->object) == EQUAL);
        }

        return (currentP);
      }
      else if (comp_res == SMALLER)
      {
        prevP = currentP;

        // Go down to the left child.
        currentP = currentP->leftP;
      }
      else // comp_res == LARGER
      {
        prevP = currentP;

        // Go down to the right child.
        currentP = currentP->rightP;
      }
    }

    // If we reached here, the object is not found in the tree. We check if the
    // last node we visited (given by prevP) contains an object greater than
    // the query object. If not, we return its successor.
    if (comp_res == SMALLER)
      return (prevP);
    else
      return (prevP->successor());
  }

  /*!
   * Remove the object stored in the given tree node.
   * \param nodeP The node storing the object to be removed from the tree.
   */
  void _remove_at (Node* nodeP);

  /*!
   * Swap the location two nodes in the tree.
   * \param node1_P The first node.
   * \param node2_P The second node.
   */
  void _swap (Node* node1_P, Node* node2_P);

  /*!
   * Swap the location two sibling nodes in the tree.
   * \param node1_P The first node.
   * \param node2_P The second node.
   * \pre The two nodes have a common parent.
   */
  void _swap_siblings (Node* node1_P, Node* node2_P);

  /*!
   * Calculate the height of the sub-tree spanned by the given node.
   * \param nodeP The sub-tree root.
   * \return The height of the sub-tree.
   */
  size_t _sub_height (const Node* nodeP) const;

  /*!
   * Check whether a sub-tree is valid.
   * \param nodeP The sub-tree root.
   * \param sub_size Output: The size of the sub-tree.
   * \param sub_bh Output: The black height of the sub-tree.
   */
  bool _sub_is_valid (const Node* nodeP,
                      size_t& sub_size,
                      size_t& sub_bh) const;

  /*!
   * Get the leftmost node in the sub-tree spanned by the given node.
   * \param nodeP The sub-tree root.
   * \return The sub-tree minimum.
   */
  Node* _sub_minimum (Node* nodeP) const;

  /*!
   * Get the rightmost node in the sub-tree spanned by the given node.
   * \param nodeP The sub-tree root.
   * \return The sub-tree maximum.
   */
  Node* _sub_maximum (Node* nodeP) const;

  /*!
   * Left-rotate the sub-tree spanned by the given node.
   * \param nodeP The sub-tree root.
   */
  void _rotate_left (Node* nodeP);

  /*!
   * Right-rotate the sub-tree spanned by the given node.
   * \param nodeP The sub-tree root.
   */
  void _rotate_right (Node* nodeP);

  /*!
   * Duplicate the entire sub-tree rooted at the given node.
   * \param nodeP The sub-tree root.
   * \return A pointer to the duplicated sub-tree root.
   */
  Node* _duplicate (const Node* nodeP);

  /*!
   * Destroy the entire sub-tree rooted at the given node.
   * \param nodeP The sub-tree root.
   */
  void _destroy (Node* nodeP);

  /*!
   * Fix-up the red-black tree properties after an insertion operation.
   * \param nodeP The node that has just been inserted to the tree.
   */
  void _insert_fixup (Node* nodeP);

  /*!
   * Fix-up the red-black tree properties after a removal operation.
   * \param nodeP The child of the node that has just been removed from
   *              the tree (may be a nullptr node).
   * \param parentP The parent node of nodeP (as nodeP may be a nullptr node,
   *                we have to specify its parent explicitly).
   */
  void _remove_fixup (Node* nodeP, Node* parentP);

  /*!
   * Allocate and initialize new tree node.
   * \param object The object stored in the node.
   * \param color The node color.
   * \return A pointer to the newly created node.
   */
  Node* _allocate_node (const Type& object,
                        typename Node::Node_color color);

  /*!
   * De-allocate a tree node.
   * \param nodeP The object node to be deallocated.
   */
  void _deallocate_node (Node* nodeP);
  //@}
};

//---------------------------------------------------------
// Default constructor.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
Multiset<Type, Compare, Allocator, UseCompactContainer>::Multiset () :
  rootP (nullptr),
  iSize (0),
  iBlackHeight (0),
  comp_f ()
{
  // Mark the two fictitious nodes as dummies.
  beginNode.color = Node::DUMMY_BEGIN;
  endNode.color = Node::DUMMY_END;
}

//---------------------------------------------------------
// Constructor with a pointer to comparison object.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
Multiset<Type, Compare, Allocator, UseCompactContainer>::Multiset (const Compare& comp) :
  rootP (nullptr),
  iSize (0),
  iBlackHeight (0),
  comp_f (comp)
{
  // Mark the two fictitious nodes as dummies.
  beginNode.color = Node::DUMMY_BEGIN;
  endNode.color = Node::DUMMY_END;
}

//---------------------------------------------------------
// Copy constructor.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
Multiset<Type, Compare, Allocator, UseCompactContainer>::Multiset (const Self& tree) :
  rootP (nullptr),
  iSize (tree.iSize),
  iBlackHeight (tree.iBlackHeight),
  comp_f (tree.comp_f)
{
  // Mark the two fictitious nodes as dummies.
  beginNode.color = Node::DUMMY_BEGIN;
  endNode.color = Node::DUMMY_END;

  // Copy all the copied tree's nodes recursively.
  if (tree.rootP != nullptr)
  {
    rootP = _duplicate (tree.rootP);

    // Set the dummy nodes.
    beginNode.parentP = _sub_minimum (rootP);
    beginNode.parentP->leftP = &beginNode;
    endNode.parentP = _sub_maximum (rootP);
    endNode.parentP->rightP = &endNode;
  }
  else
  {
    beginNode.parentP = nullptr;
    endNode.parentP = nullptr;
  }
}

//---------------------------------------------------------
// Destructor.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
Multiset<Type, Compare, Allocator, UseCompactContainer>::~Multiset () noexcept(!CGAL_ASSERTIONS_ENABLED)
{
  if (UseCompactContainer::value)
    return;

  // Delete the entire tree recursively.
  CGAL_destructor_assertion_catch(
    if (rootP != nullptr)
      _destroy (rootP);
  );

  rootP = nullptr;
  beginNode.parentP = nullptr;
  endNode.parentP = nullptr;
}

//---------------------------------------------------------
// Assignment operator.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
Multiset<Type, Compare, Allocator, UseCompactContainer>&
Multiset<Type, Compare, Allocator, UseCompactContainer>::operator= (const Self& tree)
{
  // Avoid self-assignment.
  if (this == &tree)
    return (*this);

  // Free all objects currently stored in the tree.
  clear();

  // Update the number of objects stored in the tree.
  iSize = tree.iSize;
  iBlackHeight = tree.iBlackHeight;

  // Copy all the copied tree's nodes recursively.
  if (tree.rootP != nullptr)
  {
    rootP = _duplicate (tree.rootP);

    // Set the dummy nodes.
    beginNode.parentP = _sub_minimum (rootP);
    beginNode.parentP->leftP = &beginNode;
    endNode.parentP = _sub_maximum (rootP);
    endNode.parentP->rightP = &endNode;
  }
  else
  {
    beginNode.parentP = nullptr;
    endNode.parentP = nullptr;
  }

  return (*this);
}

//---------------------------------------------------------
// Swap two trees (replace their contents).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::swap (Self& tree)
{
  // Avoid self-swapping.
  if (this == &tree)
    return;

  // Replace the contents of the trees.
  Node            *tempP = rootP;
  rootP = tree.rootP;
  tree.rootP = tempP;

  size_t           iTemp = iSize;
  iSize = tree.iSize;
  tree.iSize = iTemp;

  iTemp = iBlackHeight;
  iBlackHeight = tree.iBlackHeight;
  tree.iBlackHeight = iTemp;

  // Update the fictitious begin and end nodes.
  tempP = beginNode.parentP;
  beginNode.parentP = tree.beginNode.parentP;
  if (beginNode.parentP != nullptr)
    beginNode.parentP->leftP = &beginNode;
  tree.beginNode.parentP = tempP;
  if (tree.beginNode.parentP != nullptr)
    tree.beginNode.parentP->leftP = &(tree.beginNode);

  tempP = endNode.parentP;
  endNode.parentP = tree.endNode.parentP;
  if (endNode.parentP != nullptr)
    endNode.parentP->rightP = &endNode;
  tree.endNode.parentP = tempP;
  if (tree.endNode.parentP != nullptr)
    tree.endNode.parentP->rightP = &(tree.endNode);

  return;
}

//---------------------------------------------------------
// Test two trees for equality.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
bool Multiset<Type,Compare,Allocator,UseCompactContainer>::operator== (const Self& tree) const
{
  // The sizes of the two trees must be the same.
  if (size() != tree.size())
    return (false);

  // Go over all elements in both tree and compare them pairwise.
  const_iterator   it1 = this->begin();
  const_iterator   it2 = tree.begin();

  while (it1 != this->end() && it2 != tree.end())
  {
    if (comp_f (*it1, *it2) != EQUAL)
      return (false);

    ++it1;
    ++it2;
  }

  // If we reached here, the two trees are equal.
  return (true);
}

//---------------------------------------------------------
// Check if our tree is lexicographically smaller that a given tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
bool Multiset<Type,Compare,Allocator,UseCompactContainer>::operator< (const Self& tree) const
{
  // Go over all elements in both tree and compare them pairwise.
  const_iterator   it1 = this->begin();
  const_iterator   it2 = tree.begin();

  while (it1 != this->end() && it2 != tree.end())
  {
    const Comparison_result   res = comp_f (*it1, *it2);

    if (res == SMALLER)
      return (true);
    if (res == LARGER)
      return (false);

    ++it1;
    ++it2;
  }

  // If we reached here, one tree is the prefix of the other tree. We now
  // check which tree contains more elements.
  if (it1 != this->end())
  {
    // Our tree contains the other tree as a prefix, so it is not smaller.
    return (false);
  }
  else if (it2 != tree.end())
  {
    // The other tree contains our tree as a prefix, so our tree is smaller.
    return (true);
  }

  // The two trees are equal:
  return (false);
}

//---------------------------------------------------------
// Get an iterator for the minimum object in the tree (non-const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::begin ()
{
  if (beginNode.parentP != nullptr)
    return (iterator (beginNode.parentP));
  else
    return (iterator (&endNode));
}

//---------------------------------------------------------
// Get a past-the-end iterator for the tree objects (non-const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type, Compare,Allocator, UseCompactContainer>::iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::end ()
{
  return (iterator (&endNode));
}

//---------------------------------------------------------
// Get an iterator for the minimum object in the tree (const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::const_iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::begin () const
{
  if (beginNode.parentP != nullptr)
    return (const_iterator (beginNode.parentP));
  else
    return (const_iterator (&endNode));
}

//---------------------------------------------------------
// Get a past-the-end iterator for the tree objects (const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::const_iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::end () const
{
  return (const_iterator (&endNode));
}

//---------------------------------------------------------
// Get a reverse iterator for the maxnimum object in the tree
// (non-const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::reverse_iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::rbegin ()
{
  return (reverse_iterator (end()));
}

//---------------------------------------------------------
// Get a pre-the-begin reverse iterator for the tree objects
// (non-const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::reverse_iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::rend ()
{
  return (reverse_iterator (begin()));
}

//---------------------------------------------------------
// Get a reverse iterator for the maximum object in the tree (const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::const_reverse_iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::rbegin () const
{
  return (const_reverse_iterator (end()));
}

//---------------------------------------------------------
// Get a pre-the-begin reverse iterator for the tree objects (const version).
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
inline typename Multiset<Type,Compare,Allocator,UseCompactContainer>::const_reverse_iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::rend () const
{
  return (const_reverse_iterator (begin()));
}

//---------------------------------------------------------
// Get the size of the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
size_t Multiset<Type, Compare, Allocator, UseCompactContainer>::size () const
{
  if (rootP == nullptr)
    // The tree is empty:
    return (0);
  else if (iSize > 0)
    return (iSize);

  // If we reached here, the tree is the result of a split operation and its
  // size is not known - compute it now.
  const Node    *nodeP = beginNode.parentP;
  size_t         iComputedSize = 0;

  while (nodeP != &endNode)
  {
    nodeP = nodeP->successor();
    iComputedSize++;
  }

  // Assign the computed size.
  Self  *myThis = const_cast<Self*> (this);
  myThis->iSize = iComputedSize;

  return (iComputedSize);
}

//---------------------------------------------------------
// Insert a new object to the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::insert (const Type& object)
{
  if (rootP == nullptr)
  {
    // In case the tree is empty, assign a new rootP.
    // Notice that the root is always black.
    rootP = _allocate_node (object, Node::BLACK);

    iSize = 1;
    iBlackHeight = 1;

    // As the tree now contains a single node, it is both the tree
    // maximum and minimum.
    beginNode.parentP = rootP;
    rootP->leftP = &beginNode;
    endNode.parentP = rootP;
    rootP->rightP = &endNode;

    return (iterator (rootP));
  }

  // Find a place for the new object, and insert it as a red leaf.
  Node                      *currentP = rootP;
  Node                      *newNodeP = _allocate_node (object, Node::RED);

  Comparison_result         comp_res;
  bool                      is_leftmost = true;
  bool                      is_rightmost = true;

  while (_is_valid (currentP))
  {
    // Compare the inserted object with the object stored in the current node.
    comp_res = comp_f (object, currentP->object);

    if (comp_res == SMALLER)
    {
      is_rightmost = false;

      if (! _is_valid (currentP->leftP))
      {
        // Insert the new leaf as the left child of the current node.
        currentP->leftP = newNodeP;
        newNodeP->parentP = currentP;
        currentP = nullptr;        // In order to terminate the while loop.

        if (is_leftmost)
        {
          // Assign a new tree minimum.
          beginNode.parentP = newNodeP;
          newNodeP->leftP = &beginNode;
        }
      }
      else
      {
        // Go to the left sub-tree.
        currentP = currentP->leftP;
      }
    }
    else
    {
      is_leftmost = false;

      if (! _is_valid (currentP->rightP))
      {
        // Insert the new leaf as the right child of the current node.
        currentP->rightP = newNodeP;
        newNodeP->parentP = currentP;
        currentP = nullptr;        // In order to terminate the while loop.

        if (is_rightmost)
        {
          // Assign a new tree maximum.
          endNode.parentP = newNodeP;
          newNodeP->rightP = &endNode;
        }
      }
      else
      {
        // Go to the right sub-tree.
        currentP = currentP->rightP;
      }
    }
  }

  // Mark that a new node was added.
  if (iSize > 0)
    iSize++;

  // Fix up the tree properties.
  _insert_fixup (newNodeP);

  return (iterator(newNodeP));
}

//---------------------------------------------------------
// Insert an object to the tree, with a given hint to its position.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::insert (iterator position,
                                            const Type& object)
{
  Node  *nodeP = position.nodeP;

  CGAL_precondition (_is_valid (nodeP));

  // Compare the object to the one stored at the given node in order to decide
  // in which direction to proceed.
  const size_t       max_steps = static_cast<size_t> (1.5 * iBlackHeight);
  Comparison_result  res = comp_f(object, nodeP->object);
  bool               found_pos = true;
  size_t             k = 0;

  if (res == EQUAL)
    return (insert_after (position, object));

  if (res == SMALLER)
  {
    // Go back until locating an object not greater than the inserted one.
    Node  *predP = nodeP->predecessor();

    while (_is_valid (predP) &&
           comp_f (object, predP->object) == SMALLER)
    {
      k++;
      if (k > max_steps)
      {
        // In case the given position is too far away (more than log(n) steps)
        // from the true position of the object, break the loop.
        found_pos = false;
        break;
      }

      nodeP = predP;
      predP = nodeP->predecessor();
    }

    if (found_pos)
      return (insert_before (iterator (nodeP), object));
  }
  else
  {
    // Go forward until locating an object not less than the inserted one.
    Node  *succP = nodeP->successor();

    while (_is_valid (succP) &&
           comp_f (object, succP->object) == LARGER)
    {
      k++;
      if (k > max_steps)
      {
        // In case the given position is too far away (more than log(n) steps)
        // from the true position of the object, break the loop.
        found_pos = false;
        break;
      }

      nodeP = succP;
      succP = nodeP->successor();
    }

    if (found_pos)
      return (insert_after (iterator (nodeP), object));
  }

  // If the hint was too far than the actual position, perform a normal
  // insertion:
  return (insert (object));
}

//---------------------------------------------------------
// Insert a new object to the tree as the a successor of a given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::insert_after (iterator position,
                                                  const Type& object)
{
  Node  *nodeP = position.nodeP;

  // In case we are given a nullptr node, object should be the tree minimum.
  CGAL_assertion (nodeP != &endNode);

  if (nodeP == &beginNode)
    nodeP = nullptr;

  if (rootP == nullptr)
  {
    // In case the tree is empty, make sure that we did not receive a valid
    // iterator.
    CGAL_precondition (nodeP == nullptr);

    // Assign a new root node. Notice that the root is always black.
    rootP = _allocate_node (object, Node::BLACK);

    iSize = 1;
    iBlackHeight = 1;

    // As the tree now contains a single node, it is both the tree
    // maximum and minimum:
    beginNode.parentP = rootP;
    rootP->leftP = &beginNode;
    endNode.parentP = rootP;
    rootP->rightP = &endNode;

    return (iterator (rootP));
  }

  // Insert the new object as a red leaf, being the successor of nodeP.
  Node        *parentP;
  Node        *newNodeP = _allocate_node (object, Node::RED);

  if (nodeP == nullptr)
  {
    // The new node should become the tree minimum: Place is as the left
    // child of the current minimal leaf.
    parentP = beginNode.parentP;

    CGAL_precondition (comp_f(object, parentP->object) != LARGER);

    parentP->leftP = newNodeP;

    // As we inserted a new tree minimum:
    beginNode.parentP = newNodeP;
    newNodeP->leftP = &beginNode;
  }
  else
  {
    // Make sure the insertion does not violate the tree order.
    CGAL_precondition_code (Node *_succP = nodeP->successor());
    CGAL_precondition (comp_f(object, nodeP->object) != SMALLER);
    CGAL_precondition (! _succP->is_valid() ||
                                comp_f(object, _succP->object) != LARGER);

    // In case given node has no right child, place the new node as its
    // right child. Otherwise, place it at the leftmost position at the
    // sub-tree rooted at its right side.
    if (! _is_valid (nodeP->rightP))
    {
      parentP = nodeP;
      parentP->rightP = newNodeP;
    }
    else
    {
      parentP = _sub_minimum (nodeP->rightP);
      parentP->leftP = newNodeP;
    }

    if (nodeP == endNode.parentP)
    {
      // As we inserted a new tree maximum:
      endNode.parentP = newNodeP;
      newNodeP->rightP = &endNode;
    }
  }

  newNodeP->parentP = parentP;

  // Mark that a new node was added.
  if (iSize > 0)
    iSize++;

  // Fix up the tree properties.
  _insert_fixup (newNodeP);

  return (iterator (newNodeP));
}

//---------------------------------------------------------
// Insert a new object to the tree as the a predecessor of a given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::iterator
Multiset<Type, Compare, Allocator, UseCompactContainer>::insert_before (iterator position,
                                                   const Type& object)
{
  Node  *nodeP = position.nodeP;

  // In case we are given a nullptr node, object should be the tree maximum.
  CGAL_assertion (nodeP != &beginNode);

  if (nodeP == &endNode)
    nodeP = nullptr;

  if (rootP == nullptr)
  {
    // In case the tree is empty, make sure that we did not receive a valid
    // iterator.
    CGAL_precondition (nodeP == nullptr);

    // Assign a new root node. Notice that the root is always black.
    rootP = _allocate_node(object, Node::BLACK);

    iSize = 1;
    iBlackHeight = 1;

    // As the tree now contains a single node, it is both the tree
    // maximum and minimum:
    beginNode.parentP = rootP;
    rootP->leftP = &beginNode;
    endNode.parentP = rootP;
    rootP->rightP = &endNode;

    return (iterator (rootP));
  }

  // Insert the new object as a red leaf, being the predecessor of nodeP.
  Node        *parentP;
  Node        *newNodeP = _allocate_node (object, Node::RED);

  if (nodeP == nullptr)
  {
    // The new node should become the tree maximum: Place is as the right
    // child of the current maximal leaf.
    parentP = endNode.parentP;

    CGAL_precondition (comp_f(object, parentP->object) != SMALLER);

    parentP->rightP = newNodeP;

    // As we inserted a new tree maximum:
    endNode.parentP = newNodeP;
    newNodeP->rightP = &endNode;
  }
  else
  {
    // Make sure the insertion does not violate the tree order.
    CGAL_precondition_code (Node *_predP = nodeP->predecessor());
    CGAL_precondition (comp_f(object, nodeP->object) != LARGER);
    CGAL_precondition (! _predP->is_valid() ||
                                comp_f(object, _predP->object) != SMALLER);

    // In case given node has no left child, place the new node as its
    // left child. Otherwise, place it at the rightmost position at the
    // sub-tree rooted at its left side.
    if (! _is_valid (nodeP->leftP))
    {
      parentP = nodeP;
      parentP->leftP = newNodeP;
    }
    else
    {
      parentP = _sub_maximum (nodeP->leftP);
      parentP->rightP = newNodeP;
    }

    if (nodeP == beginNode.parentP)
    {
      // As we inserted a new tree minimum:
      beginNode.parentP = newNodeP;
      newNodeP->leftP = &beginNode;
    }
  }

  newNodeP->parentP = parentP;

  // Mark that a new node was added.
  if (iSize > 0)
    iSize++;

  // Fix up the tree properties.
  _insert_fixup (newNodeP);

  return (iterator (newNodeP));
}

//---------------------------------------------------------
// Remove an object from the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
size_t Multiset<Type, Compare, Allocator, UseCompactContainer>::erase (const Type& object)
{
  // Find the first node containing an object not less than the object to
  // be erased and from there look for objects equivalent to the given object.
  size_t      n_removed = 0;
  bool        is_equal;
  Node       *nodeP = _bound (LOWER_BOUND, object, comp_f, is_equal);
  Node       *succP;

  if (! is_equal)
    return (n_removed);

  while (_is_valid (nodeP) && comp_f (object, nodeP->object) == EQUAL)
  {
    // Keep a pointer to the successor node.
    succP = nodeP->successor();

    // Remove the current node.
    _remove_at (nodeP);
    n_removed++;

    // Proceed to the successor.
    nodeP = succP;
  }

  return (n_removed);
}

//---------------------------------------------------------
// Remove the object pointed by the given iterator.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::erase (iterator position)
{
  Node  *nodeP = position.nodeP;

  CGAL_precondition (_is_valid (nodeP));

  _remove_at (nodeP);
  return;
}

//---------------------------------------------------------
// Remove all objects from the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::clear ()
{
  // Delete all the tree nodes recursively.
  if (rootP != nullptr)
    _destroy (rootP);

  rootP = nullptr;
  beginNode.parentP = nullptr;
  endNode.parentP = nullptr;

  // Mark that there are no more objects in the tree.
  iSize = 0;
  iBlackHeight = 0;

  return;
}

//---------------------------------------------------------
// Replace the object pointed by a given iterator with another object.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::replace (iterator position,
                                                  const Type& object)
{
  Node  *nodeP = position.nodeP;

  CGAL_precondition (_is_valid (nodeP));

  // Make sure the replacement does not violate the tree order.
  CGAL_precondition_code (Node *_succP = nodeP->successor());
  CGAL_precondition (_succP == nullptr ||
                              _succP->color == Node::DUMMY_END ||
                              comp_f(object, _succP->object) != LARGER);

  CGAL_precondition_code (Node *_predP = nodeP->predecessor());
  CGAL_precondition (_predP == nullptr ||
                              _predP->color == Node::DUMMY_BEGIN ||
                              comp_f(object, _predP->object) != SMALLER);

  // Replace the object at nodeP.
  nodeP->object = object;

  return;
}

//---------------------------------------------------------
// Swap the location two objects in the tree, given by their positions.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::swap (iterator pos1,
                                               iterator pos2)
{
  Node        *node1_P = pos1.nodeP;
  Node        *node2_P = pos2.nodeP;

  CGAL_precondition (_is_valid (node1_P));
  CGAL_precondition (_is_valid (node2_P));

  if (node1_P == node2_P)
    return;

  // Make sure the swap does not violate the tree order.
  CGAL_precondition_code (Node *_succ1_P = node1_P->successor());
  CGAL_precondition (! _is_valid (_succ1_P) ||
                              comp_f (node2_P->object,
                                      _succ1_P->object) != LARGER);

  CGAL_precondition_code (Node *_pred1_P = node1_P->predecessor());
  CGAL_precondition (! _is_valid (_pred1_P) ||
                              comp_f (node2_P->object,
                                      _pred1_P->object) != SMALLER);

  CGAL_precondition_code (Node *_succ2_P = node2_P->successor());
  CGAL_precondition (! _is_valid (_succ2_P) ||
                              comp_f (node1_P->object,
                                      _succ2_P->object) != LARGER);

  CGAL_precondition_code (Node *_pred2_P = node2_P->predecessor());
  CGAL_precondition (! _is_valid (_pred2_P) ||
                              comp_f (node1_P->object,
                                      _pred2_P->object) != SMALLER);

  // Perform the swap.
  if (node1_P->parentP == node2_P->parentP)
    _swap_siblings (node1_P, node2_P);
  else
    _swap (node1_P, node2_P);

  return;
}

//---------------------------------------------------------
// Check if the tree is a valid one.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
bool Multiset<Type, Compare, Allocator, UseCompactContainer>::is_valid () const
{
  if (rootP == nullptr)
  {
    // If there is no root, make sure that the tree is empty.
    if (iSize != 0 || iBlackHeight != 0)
      return (false);

    if (beginNode.parentP != nullptr || endNode.parentP != nullptr)
      return (false);

    return (true);
  }

  // Check the validity of the fictitious nodes.
  if (beginNode.parentP == nullptr || beginNode.parentP->leftP != &beginNode)
    return (false);

  if (endNode.parentP == nullptr || endNode.parentP->rightP != &endNode)
    return (false);

  // Check recursively whether the tree is valid.
  size_t           iComputedSize;
  size_t           iComputedBHeight;

  if (! _sub_is_valid (rootP, iComputedSize, iComputedBHeight))
    return (false);

  // Make sure the tree properties are correct.
  if (iSize != 0)
  {
    if (iSize != iComputedSize)
      return (false);
  }
  else
  {
    // Assign the computed size.
    Self  *myThis = const_cast<Self*> (this);
    myThis->iSize = iComputedSize;
  }

  if (iBlackHeight != iComputedBHeight)
    return (false);

  // If we reached here, the entire tree is valid.
  return (true);
}

//---------------------------------------------------------
// Get the height of the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
size_t Multiset<Type, Compare, Allocator, UseCompactContainer>::height () const
{
  if (rootP == nullptr)
    // Empty tree.
    return (0);

  // Return the height of the root's sub-tree (the entire tree).
  return (_sub_height (rootP));
}

//---------------------------------------------------------
// Catenate the tree with another given tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::catenate (Self& tree)
{
  // Get the maximal node in our tree and the minimal node in the other tree.
  Node    *max1_P = endNode.parentP;
  Node    *min2_P = tree.beginNode.parentP;

  if (min2_P == nullptr)
  {
    // The other tree is empty - nothing to do.
    CGAL_assertion (tree.rootP == nullptr);
    return;
  }
  else if (max1_P == nullptr)
  {
    // Our tree is empty: Copy all other tree properties to our tree.
    CGAL_assertion (rootP == nullptr);

    _shallow_assign (tree);
    return;
  }

  // Make sure that the minimal object in the other tree is not less than the
  // maximal object in our tree.
  CGAL_precondition (comp_f (max1_P->object,
                                      min2_P->object) != LARGER);

  // Make sure both tree roots black.
  CGAL_assertion (_is_black (rootP));
  CGAL_assertion (_is_black (tree.rootP));

  // Splice max1_P (or min2_P) from its tree, but without deleting it.
  Node*   auxP = nullptr;

  if (max1_P != rootP)
  {
    // Splice max1_P from its current position in our tree.
    // We know it is has no right child, so we just have to connect its
    // left child with its parent.
    max1_P->parentP->rightP = max1_P->leftP;

    if (_is_valid (max1_P->leftP))
      max1_P->leftP->parentP = max1_P->parentP;

    // If max1_P is a black node, we have to fixup our tree.
    if (max1_P->color == Node::BLACK)
      _remove_fixup (max1_P->leftP, max1_P->parentP);

    auxP = max1_P;
  }
  else if (min2_P != tree.rootP)
  {
    // Splice min2_P from its current position in the other tree.
    // We know it is has no left child, so we just have to connect its
    // right child with its parent.
    if (min2_P->parentP != nullptr)
    {
      min2_P->parentP->leftP = min2_P->rightP;

      if (_is_valid (min2_P->rightP))
        min2_P->rightP->parentP = min2_P->parentP;

      // If min2_P is a black node, we have to fixup the other tree.
      if (min2_P->color == Node::BLACK)
        tree._remove_fixup (min2_P->rightP, min2_P->parentP);
    }

    auxP = min2_P;
  }
  else
  {
    // Both nodes are root nodes: Assign min2_P as the right child of the
    // root and color it red.
    rootP->rightP = min2_P;
    min2_P->parentP = rootP;
    min2_P->color = Node::RED;
    min2_P->leftP = nullptr;

    if (! _is_valid (min2_P->rightP))
    {
      // The other tree is of size 1 - set min2_P as the tree maximum.
      min2_P->rightP = &endNode;
      endNode.parentP = min2_P;
    }
    else
    {
      // The other tree is of size 2 - fixup from min2_P's right child and set
      // it as the tree maximum.
      _insert_fixup (min2_P->rightP);
      min2_P->rightP->rightP = &endNode;
      endNode.parentP = min2_P->rightP;
    }

    if (iSize > 0 && tree.iSize > 0)
      iSize += tree.iSize;
    else
      iSize = 0;

    // Clear the other tree (without actually deallocating the nodes).
    tree._shallow_clear();

    return;
  }

  // Mark that the maximal node in our tree is no longer the maximum.
  if (endNode.parentP != nullptr)
    endNode.parentP->rightP = nullptr;

  // Mark that the minimal node in the other tree is no longer the minimum.
  if (tree.beginNode.parentP != nullptr)
    tree.beginNode.parentP->leftP = nullptr;

  // Locate node1_P along the rightmost path in our tree and node2_P along the
  // leftmost path in the other tree, both having the same black-height.
  Node    *node1_P = rootP;
  Node    *node2_P = tree.rootP;
  size_t   iCurrBHeight;

  if (iBlackHeight <= tree.iBlackHeight)
  {
    // The other tree is taller (or both trees have the same height):
    // Go down the leftmost path of the other tree and locate node2_P.
    node2_P = tree.rootP;
    iCurrBHeight = tree.iBlackHeight;
    while (iCurrBHeight > iBlackHeight)
    {
      if (node2_P->color == Node::BLACK)
        iCurrBHeight--;
      node2_P = node2_P->leftP;
    }

    if (_is_red (node2_P))
      node2_P = node2_P->leftP;
    CGAL_assertion (_is_valid (node2_P));
  }
  else
  {
    // Our tree is taller:
    // Go down the rightmost path of our tree and locate node1_P.
    node1_P = rootP;
    iCurrBHeight = iBlackHeight;
    while (iCurrBHeight > tree.iBlackHeight)
    {
      if (node1_P->color == Node::BLACK)
        iCurrBHeight--;
      node1_P = node1_P->rightP;
    }

    if (_is_red (node1_P))
      node1_P = node1_P->rightP;
    CGAL_assertion (_is_valid (node2_P));
  }

  // Check which one of the tree roots have we reached.
  Node    *newRootP = nullptr;
  Node    *parentP;

  if (node1_P == rootP)
  {
    // We know that node2_P has the same number of black roots from it to
    // the minimal tree node as the number of black nodes from our tree root
    // to any of its leaves. We make rootP and node2_P siblings by moving
    // auxP to be their parent.
    parentP = node2_P->parentP;

    if (parentP == nullptr)
    {
      // Make auxP the root of the catenated tree.
      newRootP = auxP;
    }
    else
    {
      // The catenated tree will be rooted at the other tree's root.
      newRootP = tree.rootP;

      // Move auxP as the left child of parentP.
      parentP->leftP = auxP;
    }
  }
  else
  {
    // We know that node1_P has the same number of black roots from it to
    // the maximal tree node as the number of black nodes from the other tree
    // root to any of its leaves. We make tree.rootP and node1_P siblings by
    // moving auxP to be their parent.
    parentP = node1_P->parentP;

    CGAL_assertion (parentP != nullptr);

    // The catenated tree will be rooted at the current root of our tree.
    newRootP = rootP;

    // Move auxP as the right child of parentP.
    parentP->rightP = auxP;
  }

  // Move node1_P to be the left child of auxP, and node2_P to be its
  // right child. We also color this node red.
  auxP->parentP = parentP;
  auxP->color = Node::RED;
  auxP->leftP = node1_P;
  auxP->rightP = node2_P;

  node1_P->parentP = auxP;
  node2_P->parentP = auxP;

  // Set the catenated tree properties.
  if (rootP != newRootP)
  {
    // Take the black-height of the other tree.
    iBlackHeight = tree.iBlackHeight;
    rootP = newRootP;
  }

  if (iSize > 0 && tree.iSize > 0)
    iSize += tree.iSize;
  else
    iSize = 0;

  // Set the new maximal node in the tree (the minimal node remains unchanged).
  endNode.parentP = tree.endNode.parentP;
  endNode.parentP->rightP = &endNode;

  // Clear the other tree (without actually deallocating the nodes).
  tree._shallow_clear();

  // Perform a fixup of the tree invariants. This fixup will also take care
  // of updating the black-height of our catenated tree.
  _insert_fixup (auxP);

  return;
}

//---------------------------------------------------------
// Split the tree at a given position, such that it contains all objects
// in the range [begin, position) and all objects in the range
// [position, end) form a new output tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::split (iterator position,
                                                Self& tree)
{
  CGAL_precondition (tree.empty());

  // Check the extremal cases.
  if (position == begin())
  {
    // The tree should be copied to the output tree.
    tree._shallow_assign (*this);
    return;
  }
  else if (position == end())
  {
    // Nothing to do - all objects should remain in our tree.
    return;
  }

  // Prepare a vector describing the path from the given node to the tree
  // root (where SMALLER designates a left turn and LARGER designates a
  // right turn). Note that the length of this path (the depth of nodeP)
  // is at most twice the black-height of the tree.
  Node    *nodeP = position.nodeP;

  CGAL_precondition (_is_valid (nodeP));

  Node              *currP = nodeP;
  Comparison_result *path = new Comparison_result [2 * iBlackHeight];
  int                depth = 0;

  path[depth] = EQUAL;
  while (currP->parentP != nullptr)
  {
    depth++;
    if (currP == currP->parentP->leftP)
      path[depth] = SMALLER;
    else
      path[depth] = LARGER;

    currP = currP->parentP;
  }
  CGAL_assertion (currP == rootP);

  // Now go down the path and split the tree accordingly. We also keep
  // track of the black-height of the current node.
  size_t  iCurrBHeight = iBlackHeight;
  Self    leftTree;
  size_t  iLeftBHeight = 0;
  Node   *spineLeftP = nullptr;
  Node   *auxLeftP = nullptr;
  Self    rightTree;
  size_t  iRightBHeight = 0;
  Node   *spineRightP = nullptr;
  Node   *auxRightP = nullptr;
  Node   *childP = nullptr;
  Node   *nextP = nullptr;

  while (depth >= 0)
  {
    CGAL_assertion (_is_valid (currP));

    // If we encounter a black node, the black-height of both its left and
    // right subtrees is decremented.
    if (_is_black (currP))
      iCurrBHeight--;

    // Check in which direction we have to go.
    if (path[depth] != LARGER)
    {
      // We go left, so currP and its entire right sub-tree (T_r) should be
      // included in the right split tree.
      //
      //              (.) currP           .
      //              / \                 .
      //             /   \                .
      //           (.)    T_r             .
      //
      // This also covers that case where currP is the split node (nodeP).
      childP = currP->rightP;
      nextP = currP->leftP;

      if (_is_valid (childP) && rightTree.rootP == nullptr)
      {
        // Assign T_r to rightTree.
        rightTree.rootP = childP;
        rightTree.iBlackHeight = iCurrBHeight;

        // Make sure the root of rightTree is black.
        rightTree.rootP->parentP = nullptr;
        if (_is_red (rightTree.rootP))
        {
          rightTree.rootP->color = Node::BLACK;
          rightTree.iBlackHeight++;
        }

        // We store a black node along the leftmost spine of rightTree whose
        // black-hieght is exactly iRightBHeight.
        iRightBHeight = rightTree.iBlackHeight;
        spineRightP = rightTree.rootP;
      }
      else if (_is_valid (childP))
      {
        // Catenate T_r with the current rightTree.
        CGAL_assertion (_is_valid (spineRightP) &&
                        _is_valid(auxRightP));

        // Make sure the root of T_r is black.
        size_t    iCurrRightBHeight = iCurrBHeight;

        if (_is_red (childP))
        {
          childP->color = Node::BLACK;
          iCurrRightBHeight++;
        }

        // Go down the leftmost path of rightTree until locating a black
        // node whose black height is exactly iCurrRightBHeight.
        CGAL_assertion (iRightBHeight >= iCurrRightBHeight);

        while (iRightBHeight > iCurrRightBHeight)
        {
          if (spineRightP->color == Node::BLACK)
            iRightBHeight--;
          spineRightP = spineRightP->leftP;
        }

        if (_is_red (spineRightP))
          spineRightP = spineRightP->leftP;
        CGAL_assertion (_is_valid (spineRightP));

        // Use the auxiliary node and make it the parent of T_r (which
        // becomes its left sub-tree) and spineRightP (which becomes its
        // right child). We color the auxiliary node red, as both its
        // children are black.
        auxRightP->parentP = spineRightP->parentP;
        auxRightP->color = Node::RED;
        auxRightP->leftP = childP;
        auxRightP->rightP = spineRightP;

        if (auxRightP->parentP != nullptr)
          auxRightP->parentP->leftP = auxRightP;
        else
          rightTree.rootP = auxRightP;

        childP->parentP = auxRightP;
        spineRightP->parentP = auxRightP;

        // Perform a fixup on the right tree.
        rightTree._insert_fixup (auxRightP);
        auxRightP = nullptr;

        // Note that childP is now located on the leftmost spine of
        // rightTree and its black-height is exactly iCurrRightBHeight.
        iRightBHeight = iCurrRightBHeight;
        spineRightP = childP;
      }

      // In case we have an auxiliary right node that has not been inserted
      // into the right tree, insert it now.
      if (auxRightP != nullptr)
      {
        if (rightTree.rootP != nullptr)
        {
          // The right tree is not empty. Traverse its leftmost spine to
          // locate the parent of auxRightP.
          while (_is_valid (spineRightP->leftP))
            spineRightP = spineRightP->leftP;

          auxRightP->parentP = spineRightP;
          auxRightP->color = Node::RED;
          auxRightP->rightP = nullptr;
          auxRightP->leftP = nullptr;

          spineRightP->leftP = auxRightP;

          // Perform a fixup on the right tree, following the insertion of
          // the auxiliary right node.
          rightTree._insert_fixup (auxRightP);
        }
        else
        {
          // auxRightP is the only node in the current right tree.
          rightTree.rootP = auxRightP;
          rightTree.iBlackHeight = 1;

          auxRightP->parentP = nullptr;
          auxRightP->color = Node::BLACK;
          auxRightP->rightP = nullptr;
          auxRightP->leftP = nullptr;
        }

        // Assign spineRightP to be the auxiliary node.
        spineRightP = auxRightP;
        iRightBHeight = (_is_black (spineRightP)) ? 1 : 0;
        auxRightP = nullptr;
      }

      // Mark currP as the auxiliary right node.
      auxRightP = currP;
    }

    if (path[depth] != SMALLER)
    {
      // We go right, so currP and its entire left sub-tree (T_l) should be
      // included in the left split tree.
      //
      //              (.) currP           .
      //              / \                 .
      //             /   \                .
      //          T_l    (.)              .
      //
      // An exception to this rule is when currP is the split node (nodeP),
      // so it should not be included in the left tree.
      childP = currP->leftP;
      nextP = currP->rightP;

      if (_is_valid (childP) && leftTree.rootP == nullptr)
      {
        // Assign T_l to leftTree.
        leftTree.rootP = childP;
        leftTree.iBlackHeight = iCurrBHeight;

        // Make sure the root of leftTree is black.
        leftTree.rootP->parentP = nullptr;
        if (_is_red (leftTree.rootP))
        {
          leftTree.rootP->color = Node::BLACK;
          leftTree.iBlackHeight++;
        }

        // We store a black node along the rightmost spine of leftTree whose
        // black-hieght is exactly iLeftBHeight.
        iLeftBHeight = leftTree.iBlackHeight;
        spineLeftP = leftTree.rootP;
      }
      else if (_is_valid (childP))
      {
        // Catenate T_l with the current leftTree.
        CGAL_assertion (_is_valid (spineLeftP) &&
                        _is_valid(auxLeftP));

        // Make sure the root of T_l is black.
        size_t    iCurrLeftBHeight = iCurrBHeight;

        if (_is_red (childP))
        {
          childP->color = Node::BLACK;
          iCurrLeftBHeight++;
        }

        // Go down the rightmost path of leftTree until locating a black
        // node whose black height is exactly iCurrLeftBHeight.
        CGAL_assertion (iLeftBHeight >= iCurrLeftBHeight);

        while (iLeftBHeight > iCurrLeftBHeight)
        {
          if (spineLeftP->color == Node::BLACK)
            iLeftBHeight--;
          spineLeftP = spineLeftP->rightP;
        }

        if (_is_red (spineLeftP))
          spineLeftP = spineLeftP->rightP;
        CGAL_assertion (_is_valid (spineLeftP));

        // Use the auxiliary node and make it the parent of T_l (which
        // becomes its right sub-tree) and spineLeftP (which becomes its
        // left child). We color the auxiliary node red, as both its
        // children are black.
        auxLeftP->parentP = spineLeftP->parentP;
        auxLeftP->color = Node::RED;
        auxLeftP->leftP = spineLeftP;
        auxLeftP->rightP = childP;

        if (auxLeftP->parentP != nullptr)
          auxLeftP->parentP->rightP = auxLeftP;
        else
          leftTree.rootP = auxLeftP;

        childP->parentP = auxLeftP;
        spineLeftP->parentP = auxLeftP;

        // Perform a fixup on the left tree.
        leftTree._insert_fixup (auxLeftP);
        auxLeftP = nullptr;

        // Note that childP is now located on the rightmost spine of
        // leftTree and its black-height is exactly iCurrLeftBHeight.
        iLeftBHeight = iCurrLeftBHeight;
        spineLeftP = childP;
      }

      // In case we have an auxiliary left node that has not been inserted
      // into the left tree, insert it now.
      if (auxLeftP != nullptr)
      {
        if (leftTree.rootP != nullptr)
        {
          // The left tree is not empty. Traverse its rightmost spine to
          // locate the parent of auxLeftP.
          while (_is_valid (spineLeftP->rightP))
            spineLeftP = spineLeftP->rightP;

          auxLeftP->parentP = spineLeftP;
          auxLeftP->color = Node::RED;
          auxLeftP->rightP = nullptr;
          auxLeftP->leftP = nullptr;

          spineLeftP->rightP = auxLeftP;

          // Perform a fixup on the left tree, following the insertion of
          // the auxiliary left node.
          leftTree._insert_fixup (auxLeftP);
        }
        else
        {
          // auxLeftP is the only node in the left tree.
          leftTree.rootP = auxLeftP;
          leftTree.iBlackHeight = 1;

          auxLeftP->parentP = nullptr;
          auxLeftP->color = Node::BLACK;
          auxLeftP->rightP = nullptr;
          auxLeftP->leftP = nullptr;
        }

        // Assign spineLeftP to be the auxiliary node.
        spineLeftP = auxLeftP;
        iLeftBHeight = (_is_black (spineLeftP)) ? 1 : 0;
        auxLeftP = nullptr;
      }

      // Mark currP as the auxiliary right node.
      if (depth > 0)
        auxLeftP = currP;
    }

    // Proceed to the next step in the path.
    currP = nextP;
    depth--;
  }

  // It is now possible to free the path.
  delete[] path;

  CGAL_assertion (auxLeftP == nullptr && auxRightP == nodeP);

  // Fix the properties of the left tree: We know its minimal node is the
  // same as the current minimum.
  leftTree.beginNode.parentP = beginNode.parentP;
  leftTree.beginNode.parentP->leftP = &(leftTree.beginNode);

  // Traverse the rightmost path of the left tree to find the its maximum.
  CGAL_assertion (_is_valid (spineLeftP));

  while (_is_valid (spineLeftP->rightP))
    spineLeftP = spineLeftP->rightP;

  leftTree.endNode.parentP = spineLeftP;
  spineLeftP->rightP = &(leftTree.endNode);

  // Fix the properties of the right tree: We know its maximal node is the
  // same as the current maximum.
  rightTree.endNode.parentP = endNode.parentP;
  rightTree.endNode.parentP->rightP = &(rightTree.endNode);

  // We still have to insert the split node as the minimum node of the right
  // tree (we can traverse its leftmost path to find its parent).
  if (rightTree.rootP != nullptr)
  {
    while (_is_valid (spineRightP->leftP))
      spineRightP = spineRightP->leftP;

    nodeP->parentP = spineRightP;
    nodeP->color = Node::RED;
    nodeP->rightP = nullptr;
    nodeP->leftP = nullptr;

    spineRightP->leftP = nodeP;

    // Perform a fixup on the right tree, following the insertion of the
    // leftmost node.
    rightTree._insert_fixup (nodeP);
  }
  else
  {
    // nodeP is the only node in the right tree.
    rightTree.rootP = nodeP;
    rightTree.iBlackHeight = 1;

    nodeP->parentP = nullptr;
    nodeP->color = Node::BLACK;
    nodeP->rightP = nullptr;
    nodeP->leftP = nullptr;

    // In this case we also know the tree sizes:
    leftTree.iSize = iSize - 1;
    rightTree.iSize = 1;
  }

  // Set nodeP as the minimal node is the right tree.
  rightTree.beginNode.parentP = nodeP;
  nodeP->leftP = &(rightTree.beginNode);

  // Assign leftTree to (*this) and rightTree to the output tree.
  _shallow_assign (leftTree);

  tree.clear();
  tree._shallow_assign (rightTree);

  return;
}

//---------------------------------------------------------
// Move the contents of one tree to another without actually duplicating
// the nodes. This operation also clears the copied tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_shallow_assign (Self& tree)
{
  // Copy the assigned tree properties.
  rootP = tree.rootP;
  iSize = tree.iSize;
  iBlackHeight = tree.iBlackHeight;

  // Properly mark the minimal and maximal tree nodes.
  beginNode.parentP = tree.beginNode.parentP;

  if (beginNode.parentP != nullptr)
    beginNode.parentP->leftP = &beginNode;

  endNode.parentP = tree.endNode.parentP;

  if (endNode.parentP != nullptr)
    endNode.parentP->rightP = &endNode;

  // Clear the other tree (without actually deallocating the nodes).
  tree._shallow_clear();

  return;
}

//---------------------------------------------------------
// Clear the properties of the tree, without actually deallocating its nodes.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_shallow_clear ()
{
  rootP = nullptr;
  iSize = 0;
  iBlackHeight = 0;
  beginNode.parentP = nullptr;
  endNode.parentP = nullptr;

  return;
}

//---------------------------------------------------------
// Remove the given tree node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_remove_at (Node* nodeP)
{
  CGAL_precondition (_is_valid (nodeP));

  if (nodeP == rootP &&
      ! _is_valid (rootP->leftP) && ! _is_valid (rootP->rightP))
  {
    // In case of deleting the single object stored in the tree, free the root,
    // thus emptying the tree.
    _deallocate_node (rootP);

    rootP = nullptr;
    beginNode.parentP = nullptr;
    endNode.parentP = nullptr;
    iSize = 0;
    iBlackHeight = 0;

    return;
  }

  // Remove the given node from the tree.
  if (_is_valid (nodeP->leftP) && _is_valid (nodeP->rightP))
  {
    // If the node we want to remove has two children, find its successor,
    // which is the leftmost child in its right sub-tree and has at most
    // one child (it may have a right child).
    Node    *succP = _sub_minimum (nodeP->rightP);
    CGAL_assertion (_is_valid (succP));

    // Now physically swap nodeP and its successor. Notice this may temporarily
    // violate the tree properties, but we are going to remove nodeP anyway.
    // This way we have moved nodeP to a position were it is more convenient
    // to delete it.
    _swap (nodeP, succP);
  }

  // At this stage, the node we are going to remove has at most one child.
  Node        *childP = nullptr;

  if (_is_valid (nodeP->leftP))
  {
    CGAL_assertion (! _is_valid (nodeP->rightP));
    childP = nodeP->leftP;
  }
  else
  {
    childP = nodeP->rightP;
  }

  // Splice out the node to be removed, by linking its parent straight to the
  // removed node's single child.
  if (_is_valid (childP))
    childP->parentP = nodeP->parentP;

  if (nodeP->parentP == nullptr)
  {
    // If we are deleting the root, make the child the new tree node.
    rootP = childP;

    // If the deleted root is black, decrement the black height of the tree.
    if (nodeP->color == Node::BLACK)
      iBlackHeight--;
  }
  else
  {
    // Link the removed node parent to its child.
    if (nodeP == nodeP->parentP->leftP)
    {
      nodeP->parentP->leftP = childP;
    }
    else
    {
      nodeP->parentP->rightP = childP;
    }
  }

  // Fix-up the red-black properties that may have been damaged: If we have
  // just removed a black node, the black-height property is no longer valid.
  if (nodeP->color == Node::BLACK)
    _remove_fixup (childP, nodeP->parentP);

  // In case we delete the tree minimum of maximum, update the relevant
  // pointers.
  if (nodeP == beginNode.parentP)
  {
    beginNode.parentP = nodeP->successor();

    if (_is_valid (beginNode.parentP))
      beginNode.parentP->leftP = &beginNode;
    else
      beginNode.parentP = nullptr;
  }
  else if (nodeP == endNode.parentP)
  {
    endNode.parentP = nodeP->predecessor();

    if (_is_valid (endNode.parentP))
      endNode.parentP->rightP = &endNode;
    else
      endNode.parentP = nullptr;
  }

  // Delete the unnecessary node.
  _deallocate_node (nodeP);

  // Decrement the number of objects in the tree.
  if (iSize > 0)
    iSize--;

  return;
}

//---------------------------------------------------------
// Swap the location two nodes in the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_swap (Node* node1_P,
                                                Node* node2_P)
{
  CGAL_assertion (_is_valid (node1_P));
  CGAL_assertion (_is_valid (node2_P));

  // Store the properties of the first node.
  typename Node::Node_color   color1 = node1_P->color;
  Node        *parent1_P = node1_P->parentP;
  Node        *right1_P = node1_P->rightP;
  Node        *left1_P = node1_P->leftP;

  // Copy the properties of the second node to the first node.
  node1_P->color = node2_P->color;

  if (node1_P != node2_P->parentP)
  {
    if (node2_P->parentP == nullptr)
    {
      rootP = node1_P;
    }
    else
    {
      if (node2_P->parentP->leftP == node2_P)
        node2_P->parentP->leftP = node1_P;
      else
        node2_P->parentP->rightP = node1_P;
    }

    node1_P->parentP = node2_P->parentP;
  }
  else
  {
    node1_P->parentP = node2_P;
  }

  if (node1_P != node2_P->rightP)
  {
    if (_is_valid (node2_P->rightP))
      node2_P->rightP->parentP = node1_P;

    node1_P->rightP = node2_P->rightP;
  }
  else
  {
    node1_P->rightP = node2_P;
  }

  if (node1_P != node2_P->leftP)
  {
    if (_is_valid (node2_P->leftP))
      node2_P->leftP->parentP = node1_P;

    node1_P->leftP = node2_P->leftP;
  }
  else
  {
    node1_P->leftP = node2_P;
  }

  // Copy the stored properties of the first node to the second node.
  node2_P->color = color1;

  if (node2_P != parent1_P)
  {
    if (parent1_P == nullptr)
    {
      rootP = node2_P;
    }
    else
    {
      if (parent1_P->leftP == node1_P)
        parent1_P->leftP = node2_P;
      else
        parent1_P->rightP = node2_P;
    }

    node2_P->parentP = parent1_P;
  }
  else
  {
    node2_P->parentP = node1_P;
  }

  if (node2_P != right1_P)
  {
    if (_is_valid (right1_P))
      right1_P->parentP = node2_P;

    node2_P->rightP = right1_P;
  }
  else
  {
    node2_P->rightP = node1_P;
  }

  if (node2_P != left1_P)
  {
    if (_is_valid (left1_P))
      left1_P->parentP = node2_P;

    node2_P->leftP = left1_P;
  }
  else
  {
    node2_P->leftP = node1_P;
  }

  // If one of the swapped nodes used to be the tree minimum, update
  // the properties of the fictitious before-the-begin node.
  if (beginNode.parentP == node1_P)
  {
    beginNode.parentP = node2_P;
    node2_P->leftP = &beginNode;
  }
  else if (beginNode.parentP == node2_P)
  {
    beginNode.parentP = node1_P;
    node1_P->leftP = &beginNode;
  }

  // If one of the swapped nodes used to be the tree maximum, update
  // the properties of the fictitious past-the-end node.
  if (endNode.parentP == node1_P)
  {
    endNode.parentP = node2_P;
    node2_P->rightP = &endNode;
  }
  else if (endNode.parentP == node2_P)
  {
    endNode.parentP = node1_P;
    node1_P->rightP = &endNode;
  }

  return;
}

//---------------------------------------------------------
// Swap the location two sibling nodes in the tree.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_swap_siblings (Node* node1_P,
                                                         Node* node2_P)
{
  CGAL_assertion (_is_valid (node1_P));
  CGAL_assertion (_is_valid (node2_P));

  // Store the properties of the first node.
  typename Node::Node_color   color1 = node1_P->color;
  Node        *right1_P = node1_P->rightP;
  Node        *left1_P = node1_P->leftP;

  // Copy the properties of the second node to the first node.
  node1_P->color = node2_P->color;

  node1_P->rightP = node2_P->rightP;
  if (_is_valid (node1_P->rightP))
    node1_P->rightP->parentP = node1_P;

  node1_P->leftP = node2_P->leftP;
  if (_is_valid (node1_P->leftP))
    node1_P->leftP->parentP = node1_P;

  // Copy the stored properties of the first node to the second node.
  node2_P->color = color1;

  node2_P->rightP = right1_P;
  if (_is_valid (node2_P->rightP))
    node2_P->rightP->parentP = node2_P;

  node2_P->leftP = left1_P;
  if (_is_valid (node2_P->leftP))
    node2_P->leftP->parentP = node2_P;

  // Swap the children of the common parent node.
  Node        *parent_P = node1_P->parentP;
  Node        *temp;

  CGAL_assertion (parent_P == node2_P->parentP);

  temp = parent_P->leftP;
  parent_P->leftP = parent_P->rightP;
  parent_P->rightP = temp;

  // If one of the swapped nodes used to be the tree minimum, update
  // the properties of the fictitious before-the-begin node.
  if (beginNode.parentP == node1_P)
  {
    beginNode.parentP = node2_P;
    node2_P->leftP = &beginNode;
  }
  else if (beginNode.parentP == node2_P)
  {
    beginNode.parentP = node1_P;
    node1_P->leftP = &beginNode;
  }

  // If one of the swapped nodes used to be the tree maximum, update
  // the properties of the fictitious past-the-end node.
  if (endNode.parentP == node1_P)
  {
    endNode.parentP = node2_P;
    node2_P->rightP = &endNode;
  }
  else if (endNode.parentP == node2_P)
  {
    endNode.parentP = node1_P;
    node1_P->rightP = &endNode;
  }

  return;
}

//---------------------------------------------------------
// Calculate the height of the subtree spanned by a given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
size_t Multiset<Type, Compare, Allocator, UseCompactContainer>::_sub_height
    (const Node* nodeP) const
{
  CGAL_assertion (_is_valid (nodeP));

  // Recursively calculate the heights of the left and right sub-trees.
  size_t   iRightHeight = 0;
  size_t   iLeftHeight = 0;

  if (_is_valid (nodeP->rightP))
    iRightHeight = _sub_height (nodeP->rightP);

  if (_is_valid (nodeP->leftP))
    iLeftHeight = _sub_height (nodeP->leftP);

  // Return the maximal child sub-height + 1 (the current node).
  return ((iRightHeight > iLeftHeight) ? (iRightHeight + 1) :
                                         (iLeftHeight + 1));
}

//---------------------------------------------------------
// Calculate the height of the subtree spanned by a given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
bool Multiset<Type, Compare, Allocator, UseCompactContainer>::_sub_is_valid
    (const Node* nodeP,
     size_t& sub_size,
     size_t& sub_bh) const
{
  // Make sure that the node is valid.
  if (! _is_valid (nodeP))
    return (false);

  // If the node is red, make sure that both it children are black (note that
  // nullptr nodes are also considered to be black).
  if (_is_red (nodeP) &&
      (! _is_black (nodeP->rightP) || ! _is_black (nodeP->leftP)))
  {
    return (false);
  }

  // Recursively calculate the black heights of the left and right sub-trees.
  size_t   iBlack = ((nodeP->color == Node::BLACK) ? 1 : 0);
  size_t   iLeftSize = 0;
  size_t   iLeftBHeight = 0;
  size_t   iRightSize = 0;
  size_t   iRightBHeight = 0;

  if (_is_valid (nodeP->leftP))
  {
    // Make sure that the object stored at nodeP is not smaller than the one
    // stored at its left child.
    if (comp_f (nodeP->object, nodeP->leftP->object) == SMALLER)
      return (false);

    // Recursively check that the left sub-tree is valid.
    if (! _sub_is_valid (nodeP->leftP, iLeftSize, iLeftBHeight))
      return (false);
  }

  if (_is_valid (nodeP->rightP))
  {
    // Make sure that the object stored at nodeP is not larger than the one
    // stored at its right child.
    if (comp_f (nodeP->object, nodeP->rightP->object) == LARGER)
      return (false);

    // Recursively check that the right sub-tree is valid.
    if (! _sub_is_valid (nodeP->rightP, iRightSize, iRightBHeight))
      return (false);
  }

  // Compute the size of the entire sub-tree.
  sub_size = iRightSize + iLeftSize + 1;

  // Make sure that the black heights of both sub-trees are equal.
  if (iRightBHeight != iLeftBHeight)
    return (false);
  sub_bh = iRightBHeight + iBlack;

  // If we reached here, the subtree is valid.
  return (true);
}

//---------------------------------------------------------
// Get the leftmost node in the sub-tree spanned by the given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::Node*
Multiset<Type, Compare, Allocator, UseCompactContainer>::_sub_minimum (Node* nodeP) const
{
  CGAL_assertion (_is_valid (nodeP));

  Node    *minP = nodeP;

  while (_is_valid (minP->leftP))
    minP = minP->leftP;
  return (minP);
}

//---------------------------------------------------------
// Get the rightmost node in the sub-tree spanned by the given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::Node*
Multiset<Type, Compare, Allocator, UseCompactContainer>::_sub_maximum (Node* nodeP) const
{
  CGAL_assertion (_is_valid (nodeP));

  Node    *maxP = nodeP;

  while (_is_valid (maxP->rightP))
    maxP = maxP->rightP;
  return (maxP);
}

//---------------------------------------------------------
// Left-rotate the sub-tree spanned by the given node:
//
//          |          RoateRight(y)            |
//          y         -------------->           x
//        /   \                               /   \       .
//       x     T3       RoatateLeft(x)       T1    y      .
//     /   \          <--------------            /   \    .
//    T1    T2                                  T2    T3
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_rotate_left (Node* xNodeP)
{
  // Get the right child of the node.
  Node        *yNodeP = xNodeP->rightP;

  CGAL_assertion (_is_valid (yNodeP));

  // Change its left subtree (T2) to x's right subtree.
  xNodeP->rightP = yNodeP->leftP;

  // Link T2 to its new parent x.
  if (_is_valid (yNodeP->leftP))
    yNodeP->leftP->parentP = xNodeP;

  // Assign x's parent to be y's parent.
  yNodeP->parentP = xNodeP->parentP;

  if (xNodeP->parentP == nullptr)
  {
    // Make y the new tree root.
    rootP = yNodeP;
  }
  else
  {
    // Assign a pointer to y from x's parent.
    if (xNodeP == xNodeP->parentP->leftP)
    {
      xNodeP->parentP->leftP = yNodeP;
    }
    else
    {
      xNodeP->parentP->rightP = yNodeP;
    }
  }

  // Assign x to be y's left child.
  yNodeP->leftP = xNodeP;
  xNodeP->parentP = yNodeP;

  return;
}

//---------------------------------------------------------
// Right-rotate the sub-tree spanned by the given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_rotate_right (Node* yNodeP)
{
  // Get the left child of the node.
  Node        *xNodeP = yNodeP->leftP;

  CGAL_assertion (_is_valid (xNodeP));

  // Change its right subtree (T2) to y's left subtree.
  yNodeP->leftP = xNodeP->rightP;

  // Link T2 to its new parent y.
  if (_is_valid (xNodeP->rightP))
    xNodeP->rightP->parentP = yNodeP;

  // Assign y's parent to be x's parent.
  xNodeP->parentP = yNodeP->parentP;

  if (yNodeP->parentP == nullptr)
  {
    // Make x the new tree root.
    rootP = xNodeP;
  }
  else
  {
    // Assign a pointer to x from y's parent.
    if (yNodeP == yNodeP->parentP->leftP)
    {
      yNodeP->parentP->leftP = xNodeP;
    }
    else
    {
      yNodeP->parentP->rightP = xNodeP;
    }
  }

  // Assign y to be x's right child.
  xNodeP->rightP = yNodeP;
  yNodeP->parentP = xNodeP;

  return;
}

//---------------------------------------------------------
// Duplicate the entire sub-tree rooted at the given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::Node*
Multiset<Type, Compare, Allocator, UseCompactContainer>::_duplicate (const Node* nodeP)
{
  CGAL_assertion (_is_valid (nodeP));

  // Create a node of the same color, containing the same object.
  Node   *dupNodeP = _allocate_node(nodeP->object, nodeP->color);

  // Duplicate the children recursively.
  if (_is_valid (nodeP->rightP))
  {
    dupNodeP->rightP = _duplicate (nodeP->rightP);
    dupNodeP->rightP->parentP = dupNodeP;
  }

  if (_is_valid (nodeP->leftP))
  {
    dupNodeP->leftP = _duplicate (nodeP->leftP);
    dupNodeP->leftP->parentP = dupNodeP;
  }

  // Return the duplicated node.
  return (dupNodeP);
}

//---------------------------------------------------------
// Destroy the entire sub-tree rooted at the given node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_destroy (Node* nodeP)
{
  CGAL_assertion (_is_valid (nodeP));

  // Destroy the children recursively.
  if (_is_valid (nodeP->rightP))
    _destroy (nodeP->rightP);
  nodeP->rightP = nullptr;

  if (_is_valid (nodeP->leftP))
    _destroy (nodeP->leftP);
  nodeP->leftP = nullptr;

  // Free the subtree root node.
  _deallocate_node (nodeP);

  return;
}

//---------------------------------------------------------
// Fix-up the tree so it maintains the red-black properties after insertion.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_insert_fixup (Node* nodeP)
{
  CGAL_precondition (_is_red (nodeP));

  // Fix the red-black properties: we may have inserted a red leaf as the
  // child of a red parent - so we have to fix the coloring of the parent
  // recursively.
  Node        *currP = nodeP;
  Node        *grandparentP;
  Node        *uncleP;

  while (currP != rootP && _is_red (currP->parentP))
  {
    // Get a pointer to the current node's grandparent (notice the root is
    // always black, so the red parent must have a parent).
    grandparentP = currP->parentP->parentP;
    CGAL_precondition (grandparentP != nullptr);

    if (currP->parentP == grandparentP->leftP)
    {
      // If the red parent is a left child, the uncle is the right child of
      // the grandparent.
      uncleP = grandparentP->rightP;

      if (_is_red (uncleP))
      {
        // If both parent and uncle are red, color them black and color the
        // grandparent red.
        // In case of a nullptr uncle, we treat it as a black node.
        currP->parentP->color = Node::BLACK;
        uncleP->color = Node::BLACK;
        grandparentP->color = Node::RED;

        // Move to the grandparent.
        currP = grandparentP;
      }
      else
      {
        // Make sure the current node is a left child. If not, left-rotate
        // the parent's sub-tree so the parent becomes the left child of the
        // current node (see _rotate_left).
        if (currP == currP->parentP->rightP)
        {
          currP = currP->parentP;
          _rotate_left (currP);
        }

        // Color the parent black and the grandparent red.
        currP->parentP->color = Node::BLACK;
        CGAL_assertion (grandparentP == currP->parentP->parentP);
        grandparentP->color = Node::RED;

        // Right-rotate the grandparent's sub-tree
        _rotate_right (grandparentP);
      }
    }
    else
    {
      // If the red parent is a right child, the uncle is the left child of
      // the grandparent.
      uncleP = grandparentP->leftP;

      if (_is_red (uncleP))
      {
        // If both parent and uncle are red, color them black and color the
        // grandparent red.
        // In case of a nullptr uncle, we treat it as a black node.
        currP->parentP->color = Node::BLACK;
        uncleP->color = Node::BLACK;
        grandparentP->color = Node::RED;

        // Move to the grandparent.
        currP = grandparentP;
      }
      else
      {
        // Make sure the current node is a right child. If not, right-rotate
        // the parent's sub-tree so the parent becomes the right child of the
        // current node.
        if (currP == currP->parentP->leftP)
        {
          currP = currP->parentP;
          _rotate_right (currP);
        }

        // Color the parent black and the grandparent red.
        currP->parentP->color = Node::BLACK;
        CGAL_assertion(grandparentP == currP->parentP->parentP);
        grandparentP->color = Node::RED;

        // Left-rotate the grandparent's sub-tree
        _rotate_left (grandparentP);
      }
    }
  }

  // Make sure that the root is black.
  if (_is_red (rootP))
  {
    // In case we color a red root black, we should increment the black
    // height of the tree.
    rootP->color = Node::BLACK;
    iBlackHeight++;
  }

  return;
}

//---------------------------------------------------------
// Fix-up the tree so it maintains the red-black properties after removal.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_remove_fixup (Node* nodeP,
                                                        Node* parentP)
{
  Node        *currP = nodeP;
  Node        *currParentP = parentP;
  Node        *siblingP;

  while (currP != rootP && _is_black (currP))
  {
    // Get a pointer to the current node's sibling (notice that the node's
    // parent must exist, since the node is not the rootP).
    if (currP == currParentP->leftP)
    {
      // If the current node is a left child, its sibling is the right
      // child of the parent.
      siblingP = currParentP->rightP;

      // Check the sibling's color. Notice that nullptr nodes are treated
      // as if they are colored black.
      if (_is_red (siblingP))
      {
        // In case the sibling is red, color it black and rotate.
        // Then color the parent red (and the grandparent is now black).
        siblingP->color = Node::BLACK;
        currParentP->color = Node::RED;
        _rotate_left (currParentP);
        siblingP = currParentP->rightP;
      }

      CGAL_assertion (_is_valid (siblingP));

      if (_is_black (siblingP->leftP) && _is_black (siblingP->rightP))
      {
        // If the sibling has two black children, color it red.
        siblingP->color = Node::RED;

        // The black-height of the entire sub-tree rooted at the parent is
        // now too small - fix it up recursively.
        currP = currParentP;
        currParentP = currParentP->parentP;

        // In case the current node is the tree root, we have just decreased
        // the black height of the entire tree.
        if (currP == rootP)
        {
          CGAL_assertion (currParentP == nullptr);
          iBlackHeight--;
        }
      }
      else
      {
        // In this case, at least one of the sibling's children is red.
        // It is therefore obvious that the sibling itself is black.
        if (_is_black (siblingP->rightP))
        {
          // The left child is red: Color it black, and color the sibling red.
          siblingP->leftP->color = Node::BLACK;
          siblingP->color = Node::RED;

          _rotate_right (siblingP);
          siblingP = currParentP->rightP;
        }

        // Color the parent black (it is now safe to color the sibling with
        // the same color the parent used to have) and rotate left around it.
        siblingP->color = currParentP->color;
        currParentP->color = Node::BLACK;
        if (_is_valid (siblingP->rightP))
          siblingP->rightP->color = Node::BLACK;
        _rotate_left (currParentP);

        // We set currP to be the root node in order to terminate the loop.
        currP = rootP;
      }
    }
    else
    {
      // If the current node is a right child, its sibling is the left
      // child of the parent.
      siblingP = currParentP->leftP;

      // Check the sibling's color. Notice that nullptr nodes are treated
      // as if they are colored black.
      if (_is_red (siblingP))
      {
        // In case the sibling is red, color it black and rotate.
        // Then color the parent red (and the grandparent is now black).
        siblingP->color = Node::BLACK;
        currParentP->color = Node::RED;
        _rotate_right (currParentP);

        siblingP = currParentP->leftP;
      }

      CGAL_assertion (_is_valid (siblingP));

      if (_is_black (siblingP->leftP) && _is_black (siblingP->rightP))
      {
        // If the sibling has two black children, color it red.
        siblingP->color = Node::RED;

        // The black-height of the entire sub-tree rooted at the parent is
        // now too small - fix it up recursively.
        currP = currParentP;
        currParentP = currParentP->parentP;

        // In case the current node is the tree root, we have just decreased
        // the black height of the entire tree.
        if (currP == rootP)
        {
          CGAL_assertion (currParentP == nullptr);
          iBlackHeight--;
        }
      }
      else
      {
        // In this case, at least one of the sibling's children is red.
        // It is therefore obvious that the sibling itself is black.
        if (_is_black (siblingP->leftP))
        {
          // The right child is red: Color it black, and color the sibling red.
          siblingP->rightP->color = Node::BLACK;
          siblingP->color = Node::RED;

          _rotate_left (siblingP);
          siblingP = currParentP->leftP;
        }

        // Color the parent black (it is now safe to color the sibling with
        // the same color the parent used to have) and rotate right around it.
        siblingP->color = currParentP->color;
        currParentP->color = Node::BLACK;
        if (_is_valid (siblingP->leftP))
          siblingP->leftP->color = Node::BLACK;
        _rotate_right (currParentP);

        // We set currP to be the root node in order to terminate the loop.
        currP = rootP;
      }
    }
  }

  // Make sure the current node is black.
  if (_is_red (currP))
  {
    currP->color = Node::BLACK;

    if (currP == rootP)
    {
      // In case we color a red root black, we should increment the black
      // height of the tree.
      iBlackHeight++;
    }
  }

  return;
}

//---------------------------------------------------------
// Allocate and initialize new tree node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
typename Multiset<Type, Compare, Allocator, UseCompactContainer>::Node*
Multiset<Type, Compare, Allocator, UseCompactContainer>::_allocate_node
        (const Type& object,
         typename Node::Node_color color)
{
  CGAL_assertion (color != Node::DUMMY_BEGIN &&
                  color != Node::DUMMY_END);

  Node* new_node = node_alloc.allocate(beginNode);
  new_node->init(object, color);
  return (new_node);
}

//---------------------------------------------------------
// De-allocate a tree node.
//
template <class Type, class Compare, typename Allocator, typename UseCompactContainer>
void Multiset<Type, Compare, Allocator, UseCompactContainer>::_deallocate_node (Node* nodeP)
{
  node_alloc.deallocate (nodeP);
}

} //namespace CGAL

#endif
