/**
 * Quick balanced binary search tree
 *
 * @version 2016-03-21_001
 * @author  Robert Altnoeder (r.altnoeder@gmx.net)
 *
 * Copyright (C) 2012 - 2016 Robert ALTNOEDER
 *
 * Redistribution and use in source and binary forms,
 * with or without modification, are permitted provided that
 * the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef QTREE_H
#define	QTREE_H

#include <cstddef>
#include <new>

#include <dsaext.h>

template<typename K, typename V>
class QTree : public dsaext::Map<K, V>
{
  private:
    enum qtree_dir
    {
        DIR_LESS,
        DIR_GREATER
    };

  public:
    typedef int (*compare_func)(const K* key, const K* other);

    class Node : dsaext::Map<K, V>::Node
    {
        friend class QTree;

      private:
        K*    key     {nullptr};
        V*    value   {nullptr};
        Node* less    {nullptr};
        Node* greater {nullptr};
        Node* parent  {nullptr};
        int   balance {0};

      public:
        Node(K* key_ptr, V* value_ptr)
        {
            key = key_ptr;
            value = value_ptr;
        }

        Node(const Node& orig):
            key(orig.key),
            value(orig.value)
        {
        }

        Node& operator=(const Node& orig)
        {
            if (this != &orig)
            {
                key = orig.key;
                value = orig.value;
            }
            return *this;
        }

        Node(Node&& orig) = default;
        Node& operator=(Node&& orig) = default;

        virtual ~Node() = default;

        virtual K* get_key() const
        {
            return key;
        }

        virtual V* get_value() const
        {
            return value;
        }

        virtual void reuse()
        {
            key     = nullptr;
            value   = nullptr;
            less    = nullptr;
            greater = nullptr;
            parent  = nullptr;
            balance = 0;
        }
    };

  protected:
    template<typename T>
    class BaseIterator : public dsaext::QIterator<T>
    {
      public:
        BaseIterator(QTree<K, V>& qtree_ref):
            qtree_obj(qtree_ref)
        {
            if (qtree_obj.root != nullptr)
            {
                for (iter_node = qtree_obj.root;
                     iter_node->less != nullptr;
                     iter_node = iter_node->less)
                {
                    // intentional no-op block
                }
            }
            else
            {
                iter_node = nullptr;
            }
        }

        BaseIterator(const BaseIterator& orig) = default;
        BaseIterator& operator=(const BaseIterator& orig) = default;
        BaseIterator(BaseIterator&& orig) = default;
        BaseIterator& operator=(BaseIterator&& orig) = default;

        virtual ~BaseIterator()
        {
        }

        virtual T* next() = 0;

        virtual bool has_next() const
        {
            return iter_node != nullptr;
        }

        virtual size_t get_size() const
        {
            return qtree_obj.size;
        }

      protected:
        virtual Node* next_node()
        {
            Node* ret_node = iter_node;

            if (ret_node != nullptr)
            {
                if (iter_node->greater != nullptr)
                {
                    for (iter_node = iter_node->greater;
                         iter_node->less != nullptr;
                         iter_node = iter_node->less)
                    {
                        // intentional no-op block
                    }
                }
                else
                {
                    do
                    {
                        if (iter_node->parent != nullptr)
                        {
                            if (iter_node->parent->less == iter_node)
                            {
                                iter_node = iter_node->parent;
                                break;
                            }
                        }
                        iter_node = iter_node->parent;
                    }
                    while (iter_node != nullptr);
                }
            }

            return ret_node;
        }
      private:
        QTree& qtree_obj;
        Node*  iter_node {nullptr};
    };

  public:
    class KeysIterator : public BaseIterator<K>
    {
      public:
        KeysIterator(QTree<K, V>& qtree_ref):
            BaseIterator<K>::BaseIterator(qtree_ref)
        {
        }

        KeysIterator(const KeysIterator& orig) = default;
        KeysIterator& operator=(const KeysIterator& orig) = default;
        KeysIterator(KeysIterator&& orig) = default;
        KeysIterator& operator=(KeysIterator&& orig) = default;

        virtual ~KeysIterator()
        {
        }

        virtual K* next()
        {
            K* iter_key {nullptr};
            Node* node = BaseIterator<K>::next_node();
            if (node != nullptr)
            {
                iter_key = node->key;
            }
            return iter_key;
        }
    };

    class ValuesIterator : public BaseIterator<V>
    {
      public:
        ValuesIterator(QTree<K, V>& qtree_ref):
            BaseIterator<V>::BaseIterator(qtree_ref)
        {
        }

        ValuesIterator(const ValuesIterator& orig) = default;
        ValuesIterator& operator=(const ValuesIterator& orig) = default;
        ValuesIterator(ValuesIterator&& orig) = default;
        ValuesIterator& operator=(ValuesIterator&& orig) = default;

        virtual ~ValuesIterator()
        {
        }

        virtual V* next()
        {
            V* iter_value {nullptr};
            Node* node = BaseIterator<V>::next_node();
            if (node != nullptr)
            {
                iter_value = node->value;
            }
            return iter_value;
        }
    };

    class NodesIterator : public BaseIterator<Node>
    {
      public:
        NodesIterator(QTree<K, V>& qtree_ref):
            BaseIterator<Node>::BaseIterator(qtree_ref)
        {
        }

        NodesIterator(const NodesIterator& orig) = default;
        NodesIterator& operator=(const NodesIterator& orig) = default;
        NodesIterator(NodesIterator&& orig) = default;
        NodesIterator& operator=(NodesIterator&& orig) = default;

        virtual ~NodesIterator()
        {
        }

        virtual Node* next()
        {
            return BaseIterator<Node>::next_node();
        }
    };

  public:
    QTree(compare_func compare_fn):
        compare(compare_fn)
    {
    }

    QTree(const QTree& orig) = delete;
    QTree& operator=(const QTree& orig) = delete;
    QTree(QTree&& orig) = default;
    QTree& operator=(QTree&& orig) = default;

    virtual ~QTree()
    {
        clear_impl();
    }

    virtual V* get(const K* key) const
    {
        V* value {nullptr};
        Node* node = find_node(key);
        if (node != nullptr)
        {
            value = node->value;
        }
        return value;
    }

    virtual typename dsaext::Map<K, V>::entry get_entry(const K* key) const
    {
        typename dsaext::Map<K, V>::entry qtree_entry {nullptr, nullptr};
        Node* node = find_node(key);
        if (node != nullptr)
        {
            qtree_entry.key = node->key;
            qtree_entry.value = node->value;
        }
        return qtree_entry;
    }

    virtual Node* get_node(const K* key) const
    {
        return find_node(key);
    }

    // @throws std::bad_alloc, dsaext::DuplicateInsertionException
    virtual void insert(K* key, V* value)
    {
        Node* ins_node = new Node(key, value);
        if (!insert_node_impl(ins_node))
        {
            throw dsaext::DuplicateInsertException();
        }
    }

    // @throws std::bad_alloc, dsaext::DuplicateInsertionException
    virtual void insert_node(Node* node)
    {
        if (!insert_node_impl(node))
        {
            throw dsaext::DuplicateInsertException();
        }
    }

    virtual void remove(const K* key)
    {
        Node* node = find_node(key);
        if (node != nullptr)
        {
            remove_node_impl(node);
        }
    }

    virtual void remove_node(Node* node)
    {
        remove_node_impl(node);
    }

    virtual void unlink_node(Node* node)
    {
        unlink_node_impl(node);
    }

    virtual void clear()
    {
        clear_impl();
        root = nullptr;
        size = 0;
    }

    size_t get_size() const
    {
        return size;
    }

  private:
    inline void clear_impl()
    {
        Node* node = root;
        while (node != nullptr)
        {
            if (node->less != nullptr)
            {
                node = node->less;
            }
            else
            if (node->greater != nullptr)
            {
                node = node->greater;
            }
            else
            {
                Node* leaf = node;
                node = node->parent;
                if (node != nullptr)
                {
                    if (leaf == node->less)
                    {
                        node->less = nullptr;
                    }
                    else
                    {
                        node->greater = nullptr;
                    }
                }
                delete leaf;
            }
        }
    }

    inline Node* find_node(const K* key) const
    {
        Node* node = root;
        while (node != nullptr)
        {
            int cmp_rc = compare(key, node->key);
            if (cmp_rc < 0)
            {
                node = node->less;
            }
            else
            if (cmp_rc > 0)
            {
                node = node->greater;
            }
            else
            {
                break;
            }
        }

        return node;
    }

    inline bool insert_node_impl(Node* ins_node)
    {
        bool inserted = true;

        if (root == nullptr)
        {
            root = ins_node;
            ++size;
        }
        else
        {
            Node* parent_node = root;
            while (true)
            {
                int cmp_rc = compare(ins_node->key, parent_node->key);
                if (cmp_rc < 0)
                {
                    if (parent_node->less == nullptr)
                    {
                        parent_node->less = ins_node;
                        ins_node->parent  = parent_node;
                        ++size;
                        rebalance_insert(ins_node, parent_node);
                        break;
                    }
                    else
                    {
                        parent_node = parent_node->less;
                    }
                }
                else
                if (cmp_rc > 0)
                {
                    if (parent_node->greater == nullptr)
                    {
                        parent_node->greater = ins_node;
                        ins_node->parent     = parent_node;
                        ++size;
                        rebalance_insert(ins_node, parent_node);
                        break;
                    }
                    else
                    {
                        parent_node = parent_node->greater;
                    }
                }
                else
                {
                    inserted = false;
                    break;
                }
            }
        }

        return inserted;
    }

    inline void remove_node_impl(Node* rm_node)
    {
        unlink_node_impl(rm_node);
        delete rm_node;
    }

    inline void unlink_node_impl(Node* rm_node)
    {
        --size;

        if (rm_node->less == nullptr && rm_node->greater == nullptr)
        {
            // leaf node - removal without replacement
            if (root == rm_node)
            {
                // root node leaf
                root = nullptr;
            }
            else
            {
                // non-root node leaf
                Node* rot_node = rm_node->parent;

                QTree::qtree_dir dir;
                if (rot_node->less == rm_node)
                {
                    // node to remove is in the left subtree
                    // of its parent

                    // save direction
                    dir = QTree::qtree_dir::DIR_LESS;
                    rot_node->less = nullptr;
                }
                else
                {
                    // node to remove is in the right subtree
                    // of its parent

                    // save direction
                    dir = QTree::qtree_dir::DIR_GREATER;
                    rot_node->greater = nullptr;
                }
                rebalance_remove(dir, rot_node);
            }
        }
        else
        {
            Node* replace_node {nullptr};
            // not a leaf node, removal by replacement
            // at least one child, or a child and a subtree, or two subtrees
            // find replacement node
            if (rm_node->balance == -1)
            {
                for (replace_node = rm_node->less;
                     replace_node->greater != nullptr;
                     replace_node = replace_node->greater)
                {
                    // intentional no-op block
                }
            }
            else
            {
                for (replace_node = rm_node->greater;
                     replace_node->less != nullptr;
                     replace_node = replace_node->less)
                {
                    // intentional no-op block
                }
            }
            Node* rot_node = replace_node->parent;

            QTree::qtree_dir dir;
            if (rot_node->less == replace_node)
            {
                // node to remove is in the left subtree
                // of its parent

                // save direction
                dir = QTree::qtree_dir::DIR_LESS;

                if (replace_node->less != nullptr)
                {
                    // replace node by its left child
                    rot_node->less = replace_node->less;
                    replace_node->less->parent = rot_node;
                }
                else
                if (replace_node->greater != nullptr)
                {
                    // replace node by its right child
                    rot_node->less = replace_node->greater;
                    replace_node->greater->parent = rot_node;
                }
                else
                {
                    // non-root leaf node
                    rot_node->less = nullptr;
                }
            }
            else
            {
                // node to remove is in the right subtree
                // of its parent

                // save direction
                dir = QTree::qtree_dir::DIR_GREATER;

                if (replace_node->less != nullptr)
                {
                    // replace node by its left child
                    rot_node->greater = replace_node->less;
                    replace_node->less->parent = rot_node;
                }
                else
                if (replace_node->greater != nullptr)
                {
                    // replace node by its right child
                    rot_node->greater = replace_node->greater;
                    replace_node->greater->parent = rot_node;
                }
                else
                {
                    // non-root leaf node
                    rot_node->greater = nullptr;
                }
            }

            // replace rm_node with replace_node
            if (rm_node->parent == nullptr)
            {
                // Node to be removed is the root node
                root = replace_node;
            }
            else
            {
                if (rm_node->parent->less == rm_node)
                {
                    rm_node->parent->less = replace_node;
                }
                else
                {
                    rm_node->parent->greater = replace_node;
                }
            }
            if (rm_node->less != nullptr)
            {
                rm_node->less->parent = replace_node;
            }
            if (rm_node->greater != nullptr)
            {
                rm_node->greater->parent = replace_node;
            }
            replace_node->parent  = rm_node->parent;
            replace_node->less    = rm_node->less;
            replace_node->greater = rm_node->greater;
            replace_node->balance = rm_node->balance;

            if (rot_node == rm_node)
            {
                rot_node = replace_node;
            }

            rebalance_remove(dir, rot_node);
        }
    }

    inline void rebalance_insert(
        Node* sub_node,
        Node* rot_node
    )
    {
        // update balance and perform rotations
        do
        {
            if (rot_node->less == sub_node)
            {
                --(rot_node->balance);
            }
            else
            {
                ++(rot_node->balance);
            }

            if (rot_node->balance == 0)
            {
                break;
            }
            else
            if (rot_node->balance == -2)
            {
                if (sub_node->balance == -1)
                {
                    // rotate R
                    rot_node->balance = 0;
                    sub_node->balance = 0;

                    sub_node->parent = rot_node->parent;
                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node;
                        }
                    }
                    else
                    {
                        root = sub_node;
                    }

                    rot_node->less = sub_node->greater;
                    if (sub_node->greater != nullptr)
                    {
                        sub_node->greater->parent = rot_node;
                    }

                    sub_node->greater = rot_node;
                    rot_node->parent = sub_node;
                }
                else
                {
                    // rotate LR
                    if (sub_node->greater->balance == -1)
                    {
                        sub_node->balance = 0;
                        rot_node->balance = 1;
                    }
                    else
                    if (sub_node->greater->balance == 1)
                    {
                        sub_node->balance = -1;
                        rot_node->balance = 0;
                    }
                    else
                    {
                        sub_node->balance = 0;
                        rot_node->balance = 0;
                    }
                    sub_node->greater->balance = 0;

                    sub_node->parent         = sub_node->greater;
                    sub_node->greater        = sub_node->greater->less;
                    sub_node->parent->less   = sub_node;
                    rot_node->less           = sub_node->parent->greater;
                    sub_node->parent->parent = rot_node->parent;
                    if (sub_node->greater != nullptr)
                    {
                        sub_node->greater->parent = sub_node;
                    }
                    if (rot_node->less != nullptr)
                    {
                        rot_node->less->parent = rot_node;
                    }

                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node->parent;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node->parent;
                        }
                    }
                    else
                    {
                        root = sub_node->parent;
                    }

                    rot_node->parent          = sub_node->parent;
                    sub_node->parent->greater = rot_node;
                }
                break;
            }
            else
            if (rot_node->balance == 2)
            {
                if (sub_node->balance == 1)
                {
                    // rotate L
                    rot_node->balance = 0;
                    sub_node->balance = 0;

                    sub_node->parent = rot_node->parent;
                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node;
                        }
                    }
                    else
                    {
                        root = sub_node;
                    }

                    rot_node->greater = sub_node->less;
                    if (sub_node->less != nullptr)
                    {
                        sub_node->less->parent = rot_node;
                    }

                    sub_node->less = rot_node;
                    rot_node->parent = sub_node;
                }
                else
                {
                    // rotate RL
                    if (sub_node->less->balance == -1)
                    {
                        sub_node->balance = 1;
                        rot_node->balance = 0;
                    }
                    else
                    if (sub_node->less->balance == 1)
                    {
                        sub_node->balance = 0;
                        rot_node->balance = -1;
                    }
                    else
                    {
                        sub_node->balance = 0;
                        rot_node->balance = 0;
                    }
                    sub_node->less->balance = 0;

                    sub_node->parent          = sub_node->less;
                    sub_node->less            = sub_node->less->greater;
                    sub_node->parent->greater = sub_node;
                    rot_node->greater         = sub_node->parent->less;
                    sub_node->parent->parent  = rot_node->parent;
                    if (sub_node->less != nullptr)
                    {
                        sub_node->less->parent = sub_node;
                    }
                    if (rot_node->greater != nullptr)
                    {
                        rot_node->greater->parent = rot_node;
                    }

                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node->parent;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node->parent;
                        }
                    }
                    else
                    {
                        root = sub_node->parent;
                    }

                    rot_node->parent       = sub_node->parent;
                    sub_node->parent->less = rot_node;
                }
                break;
            }

            sub_node = rot_node;
            rot_node = rot_node->parent;
        }
        while (rot_node != nullptr);
    }

    inline void rebalance_remove(qtree_dir dir, Node* rot_node)
    {
        // update balance and perform rotations
        while (rot_node != nullptr)
        {
            if (dir == QTree::qtree_dir::DIR_LESS)
            {
                // node was removed from left subtree
                ++rot_node->balance;
                if (rot_node->balance == 1)
                {
                    break;
                }
            }
            else
            {
                // node was removed from right subtree
                --rot_node->balance;
                if (rot_node->balance == -1)
                {
                    break;
                }
            }

            if (rot_node->parent != nullptr)
            {
                if (rot_node->parent->less == rot_node)
                {
                    dir = QTree::qtree_dir::DIR_LESS;
                }
                else
                {
                    dir = QTree::qtree_dir::DIR_GREATER;
                }
            }

            // update balance and perform rotations
            if (rot_node->balance == -2)
            {
                Node* sub_node = rot_node->less;
                // 0 or -1
                if (sub_node->balance <= 0)
                {
                    // rotate R
                    sub_node->parent = rot_node->parent;
                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node;
                        }
                    }
                    else
                    {
                        root = sub_node;
                    }

                    rot_node->less = sub_node->greater;
                    if (sub_node->greater != nullptr)
                    {
                        sub_node->greater->parent = rot_node;
                    }

                    sub_node->greater = rot_node;
                    rot_node->parent  = sub_node;

                    if (sub_node->balance == 0)
                    {
                        rot_node->balance = -1;
                        sub_node->balance = 1;
                        break;
                    }
                    else
                    {
                        rot_node->balance = 0;
                        sub_node->balance = 0;
                    }
                }
                else
                {
                    // rotate LR
                    if (sub_node->greater->balance == -1)
                    {
                        sub_node->balance = 0;
                        rot_node->balance = 1;
                    }
                    else
                    if (sub_node->greater->balance == 1)
                    {
                        sub_node->balance = -1;
                        rot_node->balance = 0;
                    }
                    else
                    {
                        sub_node->balance = 0;
                        rot_node->balance = 0;
                    }
                    sub_node->greater->balance = 0;

                    sub_node->parent         = sub_node->greater;
                    sub_node->greater        = sub_node->greater->less;
                    sub_node->parent->less   = sub_node;
                    rot_node->less           = sub_node->parent->greater;
                    sub_node->parent->parent = rot_node->parent;
                    if (sub_node->greater != nullptr)
                    {
                        sub_node->greater->parent = sub_node;
                    }
                    if (rot_node->less != nullptr)
                    {
                        rot_node->less->parent = rot_node;
                    }

                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node->parent;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node->parent;
                        }
                    }
                    else
                    {
                        root = sub_node->parent;
                    }

                    rot_node->parent          = sub_node->parent;
                    sub_node->parent->greater = rot_node;
                }
                rot_node = rot_node->parent;
                // end of R / LR rotations
            }
            else
            if (rot_node->balance == 2)
            {
                Node* sub_node = rot_node->greater;
                // 0 or 1
                if (sub_node->balance >= 0)
                {
                    // rotate L
                    sub_node->parent = rot_node->parent;
                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node;
                        }
                    }
                    else
                    {
                        root = sub_node;
                    }

                    rot_node->greater = sub_node->less;
                    if (sub_node->less != nullptr)
                    {
                        sub_node->less->parent = rot_node;
                    }

                    sub_node->less   = rot_node;
                    rot_node->parent = sub_node;
                    if (sub_node->balance == 0)
                    {
                        rot_node->balance = 1;
                        sub_node->balance = -1;
                        break;
                    }
                    else
                    {
                        rot_node->balance = 0;
                        sub_node->balance = 0;
                    }
                }
                else
                {
                    // rotate RL
                    if (sub_node->less->balance == -1)
                    {
                        sub_node->balance = 1;
                        rot_node->balance = 0;
                    }
                    else
                    if (sub_node->less->balance == 1)
                    {
                        sub_node->balance = 0;
                        rot_node->balance = -1;
                    }
                    else
                    {
                        sub_node->balance = 0;
                        rot_node->balance = 0;
                    }
                    sub_node->less->balance = 0;

                    sub_node->parent          = sub_node->less;
                    sub_node->less            = sub_node->less->greater;
                    sub_node->parent->greater = sub_node;
                    rot_node->greater         = sub_node->parent->less;
                    sub_node->parent->parent  = rot_node->parent;
                    if (sub_node->less != nullptr)
                    {
                        sub_node->less->parent = sub_node;
                    }
                    if (rot_node->greater != nullptr)
                    {
                        rot_node->greater->parent = rot_node;
                    }

                    if (rot_node->parent != nullptr)
                    {
                        if (rot_node->parent->less == rot_node)
                        {
                            rot_node->parent->less = sub_node->parent;
                        }
                        else
                        {
                            rot_node->parent->greater = sub_node->parent;
                        }
                    }
                    else
                    {
                        root = sub_node->parent;
                    }

                    rot_node->parent       = sub_node->parent;
                    sub_node->parent->less = rot_node;
                }
                rot_node = rot_node->parent;
                // end of L / RL rotations
            }
            rot_node = rot_node->parent;
        }
    }

  private:
    Node*  root {nullptr};
    size_t size {0};

    compare_func compare;
};

#endif	/* QTREE_H */
