/*
 * GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/.
 */

/*
 * MT safe
 */

#include "qemu/osdep.h"
#include "qemu/qtree.h"

/**
 * SECTION:trees-binary
 * @title: Balanced Binary Trees
 * @short_description: a sorted collection of key/value pairs optimized
 *                     for searching and traversing in order
 *
 * The #QTree structure and its associated functions provide a sorted
 * collection of key/value pairs optimized for searching and traversing
 * in order. This means that most of the operations  (access, search,
 * insertion, deletion, ...) on #QTree are O(log(n)) in average and O(n)
 * in worst case for time complexity. But, note that maintaining a
 * balanced sorted #QTree of n elements is done in time O(n log(n)).
 *
 * To create a new #QTree use q_tree_new().
 *
 * To insert a key/value pair into a #QTree use q_tree_insert()
 * (O(n log(n))).
 *
 * To remove a key/value pair use q_tree_remove() (O(n log(n))).
 *
 * To look up the value corresponding to a given key, use
 * q_tree_lookup() and q_tree_lookup_extended().
 *
 * To find out the number of nodes in a #QTree, use q_tree_nnodes(). To
 * get the height of a #QTree, use q_tree_height().
 *
 * To traverse a #QTree, calling a function for each node visited in
 * the traversal, use q_tree_foreach().
 *
 * To destroy a #QTree, use q_tree_destroy().
 **/

#define MAX_GTREE_HEIGHT 40

/**
 * QTree:
 *
 * The QTree struct is an opaque data structure representing a
 * [balanced binary tree][glib-Balanced-Binary-Trees]. It should be
 * accessed only by using the following functions.
 */
struct _QTree {
    QTreeNode        *root;
    GCompareDataFunc  key_compare;
    GDestroyNotify    key_destroy_func;
    GDestroyNotify    value_destroy_func;
    gpointer          key_compare_data;
    guint             nnodes;
    gint              ref_count;
};

struct _QTreeNode {
    gpointer   key;         /* key for this node */
    gpointer   value;       /* value stored at this node */
    QTreeNode *left;        /* left subtree */
    QTreeNode *right;       /* right subtree */
    gint8      balance;     /* height (right) - height (left) */
    guint8     left_child;
    guint8     right_child;
};


static QTreeNode *q_tree_node_new(gpointer       key,
                                  gpointer       value);
static QTreeNode *q_tree_insert_internal(QTree *tree,
                                         gpointer key,
                                         gpointer value,
                                         gboolean replace);
static gboolean   q_tree_remove_internal(QTree         *tree,
                                         gconstpointer  key,
                                         gboolean       steal);
static QTreeNode *q_tree_node_balance(QTreeNode     *node);
static QTreeNode *q_tree_find_node(QTree         *tree,
                                   gconstpointer  key);
static QTreeNode *q_tree_node_search(QTreeNode *node,
                                     GCompareFunc search_func,
                                     gconstpointer data);
static QTreeNode *q_tree_node_rotate_left(QTreeNode     *node);
static QTreeNode *q_tree_node_rotate_right(QTreeNode     *node);
#ifdef Q_TREE_DEBUG
static void       q_tree_node_check(QTreeNode     *node);
#endif

static QTreeNode*
q_tree_node_new(gpointer key,
                gpointer value)
{
    QTreeNode *node = g_new(QTreeNode, 1);

    node->balance = 0;
    node->left = NULL;
    node->right = NULL;
    node->left_child = FALSE;
    node->right_child = FALSE;
    node->key = key;
    node->value = value;

    return node;
}

/**
 * q_tree_new:
 * @key_compare_func: the function used to order the nodes in the #QTree.
 *   It should return values similar to the standard strcmp() function -
 *   0 if the two arguments are equal, a negative value if the first argument
 *   comes before the second, or a positive value if the first argument comes
 *   after the second.
 *
 * Creates a new #QTree.
 *
 * Returns: a newly allocated #QTree
 */
QTree *
q_tree_new(GCompareFunc key_compare_func)
{
    g_return_val_if_fail(key_compare_func != NULL, NULL);

    return q_tree_new_full((GCompareDataFunc) key_compare_func, NULL,
                           NULL, NULL);
}

/**
 * q_tree_new_with_data:
 * @key_compare_func: qsort()-style comparison function
 * @key_compare_data: data to pass to comparison function
 *
 * Creates a new #QTree with a comparison function that accepts user data.
 * See q_tree_new() for more details.
 *
 * Returns: a newly allocated #QTree
 */
QTree *
q_tree_new_with_data(GCompareDataFunc key_compare_func,
                     gpointer         key_compare_data)
{
    g_return_val_if_fail(key_compare_func != NULL, NULL);

    return q_tree_new_full(key_compare_func, key_compare_data,
                           NULL, NULL);
}

/**
 * q_tree_new_full:
 * @key_compare_func: qsort()-style comparison function
 * @key_compare_data: data to pass to comparison function
 * @key_destroy_func: a function to free the memory allocated for the key
 *   used when removing the entry from the #QTree or %NULL if you don't
 *   want to supply such a function
 * @value_destroy_func: a function to free the memory allocated for the
 *   value used when removing the entry from the #QTree or %NULL if you
 *   don't want to supply such a function
 *
 * Creates a new #QTree like q_tree_new() and allows to specify functions
 * to free the memory allocated for the key and value that get called when
 * removing the entry from the #QTree.
 *
 * Returns: a newly allocated #QTree
 */
QTree *
q_tree_new_full(GCompareDataFunc key_compare_func,
                gpointer         key_compare_data,
                GDestroyNotify   key_destroy_func,
                GDestroyNotify   value_destroy_func)
{
    QTree *tree;

    g_return_val_if_fail(key_compare_func != NULL, NULL);

    tree = g_new(QTree, 1);
    tree->root               = NULL;
    tree->key_compare        = key_compare_func;
    tree->key_destroy_func   = key_destroy_func;
    tree->value_destroy_func = value_destroy_func;
    tree->key_compare_data   = key_compare_data;
    tree->nnodes             = 0;
    tree->ref_count          = 1;

    return tree;
}

/**
 * q_tree_node_first:
 * @tree: a #QTree
 *
 * Returns the first in-order node of the tree, or %NULL
 * for an empty tree.
 *
 * Returns: (nullable) (transfer none): the first node in the tree
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_node_first(QTree *tree)
{
    QTreeNode *tmp;

    g_return_val_if_fail(tree != NULL, NULL);

    if (!tree->root) {
        return NULL;
    }

    tmp = tree->root;

    while (tmp->left_child) {
        tmp = tmp->left;
    }

    return tmp;
}

/**
 * q_tree_node_previous
 * @node: a #QTree node
 *
 * Returns the previous in-order node of the tree, or %NULL
 * if the passed node was already the first one.
 *
 * Returns: (nullable) (transfer none): the previous node in the tree
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_node_previous(QTreeNode *node)
{
    QTreeNode *tmp;

    g_return_val_if_fail(node != NULL, NULL);

    tmp = node->left;

    if (node->left_child) {
        while (tmp->right_child) {
            tmp = tmp->right;
        }
    }

    return tmp;
}

/**
 * q_tree_node_next
 * @node: a #QTree node
 *
 * Returns the next in-order node of the tree, or %NULL
 * if the passed node was already the last one.
 *
 * Returns: (nullable) (transfer none): the next node in the tree
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_node_next(QTreeNode *node)
{
    QTreeNode *tmp;

    g_return_val_if_fail(node != NULL, NULL);

    tmp = node->right;

    if (node->right_child) {
        while (tmp->left_child) {
            tmp = tmp->left;
        }
    }

    return tmp;
}

/**
 * q_tree_remove_all:
 * @tree: a #QTree
 *
 * Removes all nodes from a #QTree and destroys their keys and values,
 * then resets the #QTree’s root to %NULL.
 *
 * Since: 2.70 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static void
q_tree_remove_all(QTree *tree)
{
    QTreeNode *node;
    QTreeNode *next;

    g_return_if_fail(tree != NULL);

    node = q_tree_node_first(tree);

    while (node) {
        next = q_tree_node_next(node);

        if (tree->key_destroy_func) {
            tree->key_destroy_func(node->key);
        }
        if (tree->value_destroy_func) {
            tree->value_destroy_func(node->value);
        }
        g_free(node);

#ifdef Q_TREE_DEBUG
        g_assert(tree->nnodes > 0);
        tree->nnodes--;
#endif

        node = next;
    }

#ifdef Q_TREE_DEBUG
    g_assert(tree->nnodes == 0);
#endif

    tree->root = NULL;
#ifndef Q_TREE_DEBUG
    tree->nnodes = 0;
#endif
}

/**
 * q_tree_ref:
 * @tree: a #QTree
 *
 * Increments the reference count of @tree by one.
 *
 * It is safe to call this function from any thread.
 *
 * Returns: the passed in #QTree
 *
 * Since: 2.22
 */
QTree *
q_tree_ref(QTree *tree)
{
    g_return_val_if_fail(tree != NULL, NULL);

    g_atomic_int_inc(&tree->ref_count);

    return tree;
}

/**
 * q_tree_unref:
 * @tree: a #QTree
 *
 * Decrements the reference count of @tree by one.
 * If the reference count drops to 0, all keys and values will
 * be destroyed (if destroy functions were specified) and all
 * memory allocated by @tree will be released.
 *
 * It is safe to call this function from any thread.
 *
 * Since: 2.22
 */
void
q_tree_unref(QTree *tree)
{
    g_return_if_fail(tree != NULL);

    if (g_atomic_int_dec_and_test(&tree->ref_count)) {
        q_tree_remove_all(tree);
        g_free(tree);
    }
}

/**
 * q_tree_destroy:
 * @tree: a #QTree
 *
 * Removes all keys and values from the #QTree and decreases its
 * reference count by one. If keys and/or values are dynamically
 * allocated, you should either free them first or create the #QTree
 * using q_tree_new_full(). In the latter case the destroy functions
 * you supplied will be called on all keys and values before destroying
 * the #QTree.
 */
void
q_tree_destroy(QTree *tree)
{
    g_return_if_fail(tree != NULL);

    q_tree_remove_all(tree);
    q_tree_unref(tree);
}

/**
 * q_tree_insert_node:
 * @tree: a #QTree
 * @key: the key to insert
 * @value: the value corresponding to the key
 *
 * Inserts a key/value pair into a #QTree.
 *
 * If the given key already exists in the #QTree its corresponding value
 * is set to the new value. If you supplied a @value_destroy_func when
 * creating the #QTree, the old value is freed using that function. If
 * you supplied a @key_destroy_func when creating the #QTree, the passed
 * key is freed using that function.
 *
 * The tree is automatically 'balanced' as new key/value pairs are added,
 * so that the distance from the root to every leaf is as small as possible.
 * The cost of maintaining a balanced tree while inserting new key/value
 * result in a O(n log(n)) operation where most of the other operations
 * are O(log(n)).
 *
 * Returns: (transfer none): the inserted (or set) node.
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_insert_node(QTree    *tree,
                   gpointer  key,
                   gpointer  value)
{
    QTreeNode *node;

    g_return_val_if_fail(tree != NULL, NULL);

    node = q_tree_insert_internal(tree, key, value, FALSE);

#ifdef Q_TREE_DEBUG
    q_tree_node_check(tree->root);
#endif

    return node;
}

/**
 * q_tree_insert:
 * @tree: a #QTree
 * @key: the key to insert
 * @value: the value corresponding to the key
 *
 * Inserts a key/value pair into a #QTree.
 *
 * Inserts a new key and value into a #QTree as q_tree_insert_node() does,
 * only this function does not return the inserted or set node.
 */
void
q_tree_insert(QTree    *tree,
              gpointer  key,
              gpointer  value)
{
    q_tree_insert_node(tree, key, value);
}

/**
 * q_tree_replace_node:
 * @tree: a #QTree
 * @key: the key to insert
 * @value: the value corresponding to the key
 *
 * Inserts a new key and value into a #QTree similar to q_tree_insert_node().
 * The difference is that if the key already exists in the #QTree, it gets
 * replaced by the new key. If you supplied a @value_destroy_func when
 * creating the #QTree, the old value is freed using that function. If you
 * supplied a @key_destroy_func when creating the #QTree, the old key is
 * freed using that function.
 *
 * The tree is automatically 'balanced' as new key/value pairs are added,
 * so that the distance from the root to every leaf is as small as possible.
 *
 * Returns: (transfer none): the inserted (or set) node.
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_replace_node(QTree    *tree,
                    gpointer  key,
                    gpointer  value)
{
    QTreeNode *node;

    g_return_val_if_fail(tree != NULL, NULL);

    node = q_tree_insert_internal(tree, key, value, TRUE);

#ifdef Q_TREE_DEBUG
    q_tree_node_check(tree->root);
#endif

    return node;
}

/**
 * q_tree_replace:
 * @tree: a #QTree
 * @key: the key to insert
 * @value: the value corresponding to the key
 *
 * Inserts a new key and value into a #QTree as q_tree_replace_node() does,
 * only this function does not return the inserted or set node.
 */
void
q_tree_replace(QTree    *tree,
               gpointer  key,
               gpointer  value)
{
    q_tree_replace_node(tree, key, value);
}

/* internal insert routine */
static QTreeNode *
q_tree_insert_internal(QTree    *tree,
                       gpointer  key,
                       gpointer  value,
                       gboolean  replace)
{
    QTreeNode *node, *retnode;
    QTreeNode *path[MAX_GTREE_HEIGHT];
    int idx;

    g_return_val_if_fail(tree != NULL, NULL);

    if (!tree->root) {
        tree->root = q_tree_node_new(key, value);
        tree->nnodes++;
        return tree->root;
    }

    idx = 0;
    path[idx++] = NULL;
    node = tree->root;

    while (1) {
        int cmp = tree->key_compare(key, node->key, tree->key_compare_data);

        if (cmp == 0) {
            if (tree->value_destroy_func) {
                tree->value_destroy_func(node->value);
            }

            node->value = value;

            if (replace) {
                if (tree->key_destroy_func) {
                    tree->key_destroy_func(node->key);
                }

                node->key = key;
            } else {
                /* free the passed key */
                if (tree->key_destroy_func) {
                    tree->key_destroy_func(key);
                }
            }

            return node;
        } else if (cmp < 0) {
            if (node->left_child) {
                path[idx++] = node;
                node = node->left;
            } else {
                QTreeNode *child = q_tree_node_new(key, value);

                child->left = node->left;
                child->right = node;
                node->left = child;
                node->left_child = TRUE;
                node->balance -= 1;

                tree->nnodes++;

                retnode = child;
                break;
            }
        } else {
            if (node->right_child) {
                path[idx++] = node;
                node = node->right;
            } else {
                QTreeNode *child = q_tree_node_new(key, value);

                child->right = node->right;
                child->left = node;
                node->right = child;
                node->right_child = TRUE;
                node->balance += 1;

                tree->nnodes++;

                retnode = child;
                break;
            }
        }
    }

    /*
     * Restore balance. This is the goodness of a non-recursive
     * implementation, when we are done with balancing we 'break'
     * the loop and we are done.
     */
    while (1) {
        QTreeNode *bparent = path[--idx];
        gboolean left_node = (bparent && node == bparent->left);
        g_assert(!bparent || bparent->left == node || bparent->right == node);

        if (node->balance < -1 || node->balance > 1) {
            node = q_tree_node_balance(node);
            if (bparent == NULL) {
                tree->root = node;
            } else if (left_node) {
                bparent->left = node;
            } else {
                bparent->right = node;
            }
        }

        if (node->balance == 0 || bparent == NULL) {
            break;
        }

        if (left_node) {
            bparent->balance -= 1;
        } else {
            bparent->balance += 1;
        }

        node = bparent;
    }

    return retnode;
}

/**
 * q_tree_remove:
 * @tree: a #QTree
 * @key: the key to remove
 *
 * Removes a key/value pair from a #QTree.
 *
 * If the #QTree was created using q_tree_new_full(), the key and value
 * are freed using the supplied destroy functions, otherwise you have to
 * make sure that any dynamically allocated values are freed yourself.
 * If the key does not exist in the #QTree, the function does nothing.
 *
 * The cost of maintaining a balanced tree while removing a key/value
 * result in a O(n log(n)) operation where most of the other operations
 * are O(log(n)).
 *
 * Returns: %TRUE if the key was found (prior to 2.8, this function
 *     returned nothing)
 */
gboolean
q_tree_remove(QTree         *tree,
              gconstpointer  key)
{
    gboolean removed;

    g_return_val_if_fail(tree != NULL, FALSE);

    removed = q_tree_remove_internal(tree, key, FALSE);

#ifdef Q_TREE_DEBUG
    q_tree_node_check(tree->root);
#endif

    return removed;
}

/**
 * q_tree_steal:
 * @tree: a #QTree
 * @key: the key to remove
 *
 * Removes a key and its associated value from a #QTree without calling
 * the key and value destroy functions.
 *
 * If the key does not exist in the #QTree, the function does nothing.
 *
 * Returns: %TRUE if the key was found (prior to 2.8, this function
 *     returned nothing)
 */
gboolean
q_tree_steal(QTree         *tree,
             gconstpointer  key)
{
    gboolean removed;

    g_return_val_if_fail(tree != NULL, FALSE);

    removed = q_tree_remove_internal(tree, key, TRUE);

#ifdef Q_TREE_DEBUG
    q_tree_node_check(tree->root);
#endif

    return removed;
}

/* internal remove routine */
static gboolean
q_tree_remove_internal(QTree         *tree,
                       gconstpointer  key,
                       gboolean       steal)
{
    QTreeNode *node, *parent, *balance;
    QTreeNode *path[MAX_GTREE_HEIGHT];
    int idx;
    gboolean left_node;

    g_return_val_if_fail(tree != NULL, FALSE);

    if (!tree->root) {
        return FALSE;
    }

    idx = 0;
    path[idx++] = NULL;
    node = tree->root;

    while (1) {
        int cmp = tree->key_compare(key, node->key, tree->key_compare_data);

        if (cmp == 0) {
            break;
        } else if (cmp < 0) {
            if (!node->left_child) {
                return FALSE;
            }

            path[idx++] = node;
            node = node->left;
        } else {
            if (!node->right_child) {
                return FALSE;
            }

            path[idx++] = node;
            node = node->right;
        }
    }

    /*
     * The following code is almost equal to q_tree_remove_node,
     * except that we do not have to call q_tree_node_parent.
     */
    balance = parent = path[--idx];
    g_assert(!parent || parent->left == node || parent->right == node);
    left_node = (parent && node == parent->left);

    if (!node->left_child) {
        if (!node->right_child) {
            if (!parent) {
                tree->root = NULL;
            } else if (left_node) {
                parent->left_child = FALSE;
                parent->left = node->left;
                parent->balance += 1;
            } else {
                parent->right_child = FALSE;
                parent->right = node->right;
                parent->balance -= 1;
            }
        } else {
            /* node has a right child */
            QTreeNode *tmp = q_tree_node_next(node);
            tmp->left = node->left;

            if (!parent) {
                tree->root = node->right;
            } else if (left_node) {
                parent->left = node->right;
                parent->balance += 1;
            } else {
                parent->right = node->right;
                parent->balance -= 1;
            }
        }
    } else {
        /* node has a left child */
        if (!node->right_child) {
            QTreeNode *tmp = q_tree_node_previous(node);
            tmp->right = node->right;

            if (parent == NULL) {
                tree->root = node->left;
            } else if (left_node) {
                parent->left = node->left;
                parent->balance += 1;
            } else {
                parent->right = node->left;
                parent->balance -= 1;
            }
        } else {
            /* node has a both children (pant, pant!) */
            QTreeNode *prev = node->left;
            QTreeNode *next = node->right;
            QTreeNode *nextp = node;
            int old_idx = idx + 1;
            idx++;

            /* path[idx] == parent */
            /* find the immediately next node (and its parent) */
            while (next->left_child) {
                path[++idx] = nextp = next;
                next = next->left;
            }

            path[old_idx] = next;
            balance = path[idx];

            /* remove 'next' from the tree */
            if (nextp != node) {
                if (next->right_child) {
                    nextp->left = next->right;
                } else {
                    nextp->left_child = FALSE;
                }
                nextp->balance += 1;

                next->right_child = TRUE;
                next->right = node->right;
            } else {
                node->balance -= 1;
            }

            /* set the prev to point to the right place */
            while (prev->right_child) {
                prev = prev->right;
            }
            prev->right = next;

            /* prepare 'next' to replace 'node' */
            next->left_child = TRUE;
            next->left = node->left;
            next->balance = node->balance;

            if (!parent) {
                tree->root = next;
            } else if (left_node) {
                parent->left = next;
            } else {
                parent->right = next;
            }
        }
    }

    /* restore balance */
    if (balance) {
        while (1) {
            QTreeNode *bparent = path[--idx];
            g_assert(!bparent ||
                     bparent->left == balance ||
                     bparent->right == balance);
            left_node = (bparent && balance == bparent->left);

            if (balance->balance < -1 || balance->balance > 1) {
                balance = q_tree_node_balance(balance);
                if (!bparent) {
                    tree->root = balance;
                } else if (left_node) {
                    bparent->left = balance;
                } else {
                    bparent->right = balance;
                }
            }

            if (balance->balance != 0 || !bparent) {
                break;
            }

            if (left_node) {
                bparent->balance += 1;
            } else {
                bparent->balance -= 1;
            }

            balance = bparent;
        }
    }

    if (!steal) {
        if (tree->key_destroy_func) {
            tree->key_destroy_func(node->key);
        }
        if (tree->value_destroy_func) {
            tree->value_destroy_func(node->value);
        }
    }

    g_free(node);

    tree->nnodes--;

    return TRUE;
}

/**
 * q_tree_lookup_node:
 * @tree: a #QTree
 * @key: the key to look up
 *
 * Gets the tree node corresponding to the given key. Since a #QTree is
 * automatically balanced as key/value pairs are added, key lookup
 * is O(log n) (where n is the number of key/value pairs in the tree).
 *
 * Returns: (nullable) (transfer none): the tree node corresponding to
 *          the key, or %NULL if the key was not found
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_lookup_node(QTree         *tree,
                   gconstpointer  key)
{
    g_return_val_if_fail(tree != NULL, NULL);

    return q_tree_find_node(tree, key);
}

/**
 * q_tree_lookup:
 * @tree: a #QTree
 * @key: the key to look up
 *
 * Gets the value corresponding to the given key. Since a #QTree is
 * automatically balanced as key/value pairs are added, key lookup
 * is O(log n) (where n is the number of key/value pairs in the tree).
 *
 * Returns: the value corresponding to the key, or %NULL
 *     if the key was not found
 */
gpointer
q_tree_lookup(QTree         *tree,
              gconstpointer  key)
{
    QTreeNode *node;

    node = q_tree_lookup_node(tree, key);

    return node ? node->value : NULL;
}

/**
 * q_tree_lookup_extended:
 * @tree: a #QTree
 * @lookup_key: the key to look up
 * @orig_key: (out) (optional) (nullable): returns the original key
 * @value: (out) (optional) (nullable): returns the value associated with
 *         the key
 *
 * Looks up a key in the #QTree, returning the original key and the
 * associated value. This is useful if you need to free the memory
 * allocated for the original key, for example before calling
 * q_tree_remove().
 *
 * Returns: %TRUE if the key was found in the #QTree
 */
gboolean
q_tree_lookup_extended(QTree         *tree,
                       gconstpointer  lookup_key,
                       gpointer      *orig_key,
                       gpointer      *value)
{
    QTreeNode *node;

    g_return_val_if_fail(tree != NULL, FALSE);

    node = q_tree_find_node(tree, lookup_key);

    if (node) {
        if (orig_key) {
            *orig_key = node->key;
        }
        if (value) {
            *value = node->value;
        }
        return TRUE;
    } else {
        return FALSE;
    }
}

/**
 * q_tree_foreach:
 * @tree: a #QTree
 * @func: the function to call for each node visited.
 *     If this function returns %TRUE, the traversal is stopped.
 * @user_data: user data to pass to the function
 *
 * Calls the given function for each of the key/value pairs in the #QTree.
 * The function is passed the key and value of each pair, and the given
 * @data parameter. The tree is traversed in sorted order.
 *
 * The tree may not be modified while iterating over it (you can't
 * add/remove items). To remove all items matching a predicate, you need
 * to add each item to a list in your #GTraverseFunc as you walk over
 * the tree, then walk the list and remove each item.
 */
void
q_tree_foreach(QTree         *tree,
               GTraverseFunc  func,
               gpointer       user_data)
{
    QTreeNode *node;

    g_return_if_fail(tree != NULL);

    if (!tree->root) {
        return;
    }

    node = q_tree_node_first(tree);

    while (node) {
        if ((*func)(node->key, node->value, user_data)) {
            break;
        }

        node = q_tree_node_next(node);
    }
}

/**
 * q_tree_search_node:
 * @tree: a #QTree
 * @search_func: a function used to search the #QTree
 * @user_data: the data passed as the second argument to @search_func
 *
 * Searches a #QTree using @search_func.
 *
 * The @search_func is called with a pointer to the key of a key/value
 * pair in the tree, and the passed in @user_data. If @search_func returns
 * 0 for a key/value pair, then the corresponding node is returned as
 * the result of q_tree_search(). If @search_func returns -1, searching
 * will proceed among the key/value pairs that have a smaller key; if
 * @search_func returns 1, searching will proceed among the key/value
 * pairs that have a larger key.
 *
 * Returns: (nullable) (transfer none): the node corresponding to the
 *          found key, or %NULL if the key was not found
 *
 * Since: 2.68 in GLib. Internal in Qtree, i.e. not in the public API.
 */
static QTreeNode *
q_tree_search_node(QTree         *tree,
                   GCompareFunc   search_func,
                   gconstpointer  user_data)
{
    g_return_val_if_fail(tree != NULL, NULL);

    if (!tree->root) {
        return NULL;
    }

    return q_tree_node_search(tree->root, search_func, user_data);
}

/**
 * q_tree_search:
 * @tree: a #QTree
 * @search_func: a function used to search the #QTree
 * @user_data: the data passed as the second argument to @search_func
 *
 * Searches a #QTree using @search_func.
 *
 * The @search_func is called with a pointer to the key of a key/value
 * pair in the tree, and the passed in @user_data. If @search_func returns
 * 0 for a key/value pair, then the corresponding value is returned as
 * the result of q_tree_search(). If @search_func returns -1, searching
 * will proceed among the key/value pairs that have a smaller key; if
 * @search_func returns 1, searching will proceed among the key/value
 * pairs that have a larger key.
 *
 * Returns: the value corresponding to the found key, or %NULL
 *     if the key was not found
 */
gpointer
q_tree_search(QTree         *tree,
              GCompareFunc   search_func,
              gconstpointer  user_data)
{
    QTreeNode *node;

    node = q_tree_search_node(tree, search_func, user_data);

    return node ? node->value : NULL;
}

/**
 * q_tree_height:
 * @tree: a #QTree
 *
 * Gets the height of a #QTree.
 *
 * If the #QTree contains no nodes, the height is 0.
 * If the #QTree contains only one root node the height is 1.
 * If the root node has children the height is 2, etc.
 *
 * Returns: the height of @tree
 */
gint
q_tree_height(QTree *tree)
{
    QTreeNode *node;
    gint height;

    g_return_val_if_fail(tree != NULL, 0);

    if (!tree->root) {
        return 0;
    }

    height = 0;
    node = tree->root;

    while (1) {
        height += 1 + MAX(node->balance, 0);

        if (!node->left_child) {
            return height;
        }

        node = node->left;
    }
}

/**
 * q_tree_nnodes:
 * @tree: a #QTree
 *
 * Gets the number of nodes in a #QTree.
 *
 * Returns: the number of nodes in @tree
 */
gint
q_tree_nnodes(QTree *tree)
{
    g_return_val_if_fail(tree != NULL, 0);

    return tree->nnodes;
}

static QTreeNode *
q_tree_node_balance(QTreeNode *node)
{
    if (node->balance < -1) {
        if (node->left->balance > 0) {
            node->left = q_tree_node_rotate_left(node->left);
        }
        node = q_tree_node_rotate_right(node);
    } else if (node->balance > 1) {
        if (node->right->balance < 0) {
            node->right = q_tree_node_rotate_right(node->right);
        }
        node = q_tree_node_rotate_left(node);
    }

    return node;
}

static QTreeNode *
q_tree_find_node(QTree        *tree,
                 gconstpointer key)
{
    QTreeNode *node;
    gint cmp;

    node = tree->root;
    if (!node) {
        return NULL;
    }

    while (1) {
        cmp = tree->key_compare(key, node->key, tree->key_compare_data);
        if (cmp == 0) {
            return node;
        } else if (cmp < 0) {
            if (!node->left_child) {
                return NULL;
            }

            node = node->left;
        } else {
            if (!node->right_child) {
                return NULL;
            }

            node = node->right;
        }
    }
}

static QTreeNode *
q_tree_node_search(QTreeNode     *node,
                   GCompareFunc   search_func,
                   gconstpointer  data)
{
    gint dir;

    if (!node) {
        return NULL;
    }

    while (1) {
        dir = (*search_func)(node->key, data);
        if (dir == 0) {
            return node;
        } else if (dir < 0) {
            if (!node->left_child) {
                return NULL;
            }

            node = node->left;
        } else {
            if (!node->right_child) {
                return NULL;
            }

            node = node->right;
        }
    }
}

static QTreeNode *
q_tree_node_rotate_left(QTreeNode *node)
{
    QTreeNode *right;
    gint a_bal;
    gint b_bal;

    right = node->right;

    if (right->left_child) {
        node->right = right->left;
    } else {
        node->right_child = FALSE;
        right->left_child = TRUE;
    }
    right->left = node;

    a_bal = node->balance;
    b_bal = right->balance;

    if (b_bal <= 0) {
        if (a_bal >= 1) {
            right->balance = b_bal - 1;
        } else {
            right->balance = a_bal + b_bal - 2;
        }
        node->balance = a_bal - 1;
    } else {
        if (a_bal <= b_bal) {
            right->balance = a_bal - 2;
        } else {
            right->balance = b_bal - 1;
        }
        node->balance = a_bal - b_bal - 1;
    }

    return right;
}

static QTreeNode *
q_tree_node_rotate_right(QTreeNode *node)
{
    QTreeNode *left;
    gint a_bal;
    gint b_bal;

    left = node->left;

    if (left->right_child) {
        node->left = left->right;
    } else {
        node->left_child = FALSE;
        left->right_child = TRUE;
    }
    left->right = node;

    a_bal = node->balance;
    b_bal = left->balance;

    if (b_bal <= 0) {
        if (b_bal > a_bal) {
            left->balance = b_bal + 1;
        } else {
            left->balance = a_bal + 2;
        }
        node->balance = a_bal - b_bal + 1;
    } else {
        if (a_bal <= -1) {
            left->balance = b_bal + 1;
        } else {
            left->balance = a_bal + b_bal + 2;
        }
        node->balance = a_bal + 1;
    }

    return left;
}

#ifdef Q_TREE_DEBUG
static gint
q_tree_node_height(QTreeNode *node)
{
    gint left_height;
    gint right_height;

    if (node) {
        left_height = 0;
        right_height = 0;

        if (node->left_child) {
            left_height = q_tree_node_height(node->left);
        }

        if (node->right_child) {
            right_height = q_tree_node_height(node->right);
        }

        return MAX(left_height, right_height) + 1;
    }

    return 0;
}

static void q_tree_node_check(QTreeNode *node)
{
    gint left_height;
    gint right_height;
    gint balance;
    QTreeNode *tmp;

    if (node) {
        if (node->left_child) {
            tmp = q_tree_node_previous(node);
            g_assert(tmp->right == node);
        }

        if (node->right_child) {
            tmp = q_tree_node_next(node);
            g_assert(tmp->left == node);
        }

        left_height = 0;
        right_height = 0;

        if (node->left_child) {
            left_height = q_tree_node_height(node->left);
        }
        if (node->right_child) {
            right_height = q_tree_node_height(node->right);
        }

        balance = right_height - left_height;
        g_assert(balance == node->balance);

        if (node->left_child) {
            q_tree_node_check(node->left);
        }
        if (node->right_child) {
            q_tree_node_check(node->right);
        }
    }
}
#endif