/*
 * Copyright (c) 2010, 2011 Richard Braun.
 * All rights reserved.
 *
 * 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.
 *
 * 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.
 *
 *
 * Red-black tree.
 */

#ifndef _KERN_RBTREE_H
#define _KERN_RBTREE_H

/** @file */

#include <stddef.h>
#include <assert.h>
#include <stdint.h>
#include <sys/types.h>

#include <starpu_util.h>

#define MACRO_BEGIN ({
#define MACRO_END })
/*
 * Indexes of the left and right nodes in the children array of a node.
 */
#define STARPU_RBTREE_LEFT     0
#define STARPU_RBTREE_RIGHT    1

/**
 * Red-black node.
 */
struct starpu_rbtree_node;

/**
 * Red-black tree.
 */
struct starpu_rbtree;

/**
 * Static tree initializer.
 */
#define STARPU_RBTREE_INITIALIZER { NULL }

#include "rbtree_i.h"

/**
 * Initialize a tree.
 */
static inline void starpu_rbtree_init(struct starpu_rbtree *tree)
{
    tree->root = NULL;
}

/**
 * This version assumes that the content of tree was already zeroed
 */
static inline void starpu_rbtree_init0(struct starpu_rbtree *tree STARPU_ATTRIBUTE_UNUSED)
{
}

/**
 * Initialize a node.
 *
 * A node is in no tree when its parent points to itself.
 */
static inline void starpu_rbtree_node_init(struct starpu_rbtree_node *node)
{
    assert(starpu_rbtree_check_alignment(node));

    node->parent = (uintptr_t)node | STARPU_RBTREE_COLOR_RED;
    node->children[STARPU_RBTREE_LEFT] = NULL;
    node->children[STARPU_RBTREE_RIGHT] = NULL;
}

/**
 * This version assumes that the content of node was already zeroed
 */
static inline void starpu_rbtree_node_init0(struct starpu_rbtree_node *node)
{
    assert(starpu_rbtree_check_alignment(node));

    node->parent = (uintptr_t)node | STARPU_RBTREE_COLOR_RED;
    //node->children[STARPU_RBTREE_LEFT] = NULL;
    //node->children[STARPU_RBTREE_RIGHT] = NULL;
}

/**
 * Return true if node is in no tree.
 */
static inline int starpu_rbtree_node_unlinked(const struct starpu_rbtree_node *node)
{
    return starpu_rbtree_parent(node) == node;
}

/**
 * Macro that evaluates to the address of the structure containing the
 * given node based on the given type and member.
 */
#define starpu_rbtree_entry(node, type, member) structof(node, type, member)

/**
 * Return true if tree is empty.
 */
static inline int starpu_rbtree_empty(const struct starpu_rbtree *tree)
{
    return tree->root == NULL;
}

/**
 * Look up a node in a tree.
 *
 * Note that implementing the lookup algorithm as a macro gives two benefits:
 * First, it avoids the overhead of a callback function. Next, the type of the
 * cmp_fn parameter isn't rigid. The only guarantee offered by this
 * implementation is that the key parameter is the first parameter given to
 * cmp_fn. This way, users can pass only the value they need for comparison
 * instead of e.g. allocating a full structure on the stack.
 *
 * See starpu_rbtree_insert().
 */
#define starpu_rbtree_lookup(tree, key, cmp_fn)                \
MACRO_BEGIN                                             \
    struct starpu_rbtree_node *___cur;                         \
    int ___diff;                                        \
                                                        \
    ___cur = (tree)->root;                              \
                                                        \
    while (___cur != NULL) {                            \
        ___diff = cmp_fn(key, ___cur);                  \
                                                        \
        if (___diff == 0)                               \
            break;                                      \
                                                        \
        ___cur = ___cur->children[starpu_rbtree_d2i(___diff)]; \
    }                                                   \
                                                        \
    ___cur;                                             \
MACRO_END

/**
 * Look up a node or one of its nearest nodes in a tree.
 *
 * This macro essentially acts as starpu_rbtree_lookup() but if no entry matched
 * the key, an additional step is performed to obtain the next or previous
 * node, depending on the direction (left or right).
 *
 * The constraints that apply to the key parameter are the same as for
 * starpu_rbtree_lookup().
 */
#define starpu_rbtree_lookup_nearest(tree, key, cmp_fn, dir)       \
MACRO_BEGIN                                                 \
    struct starpu_rbtree_node *___cur, *___prev;                   \
    int ___diff, ___index;                                  \
                                                            \
    ___prev = NULL;                                         \
    ___index = -1;                                          \
    ___cur = (tree)->root;                                  \
                                                            \
    while (___cur != NULL) {                                \
        ___diff = cmp_fn(key, ___cur);                      \
                                                            \
        if (___diff == 0)                                   \
            break;                                          \
                                                            \
        ___prev = ___cur;                                   \
        ___index = starpu_rbtree_d2i(___diff);                     \
        ___cur = ___cur->children[___index];                \
    }                                                       \
                                                            \
    if (___cur == NULL)                                     \
        ___cur = starpu_rbtree_nearest(___prev, ___index, dir);    \
                                                            \
    ___cur;                                                 \
MACRO_END

/**
 * Insert a node in a tree.
 *
 * This macro performs a standard lookup to obtain the insertion point of
 * the given node in the tree (it is assumed that the inserted node never
 * compares equal to any other entry in the tree) and links the node. It
 * then checks red-black rules violations, and rebalances the tree if
 * necessary.
 *
 * Unlike starpu_rbtree_lookup(), the cmp_fn parameter must compare two complete
 * entries, so it is suggested to use two different comparison inline
 * functions, such as myobj_cmp_lookup() and myobj_cmp_insert(). There is no
 * guarantee about the order of the nodes given to the comparison function.
 *
 * See starpu_rbtree_lookup().
 */
#define starpu_rbtree_insert(tree, node, cmp_fn)                   \
MACRO_BEGIN                                                 \
    struct starpu_rbtree_node *___cur, *___prev;                   \
    int ___diff, ___index;                                  \
                                                            \
    ___prev = NULL;                                         \
    ___index = -1;                                          \
    ___cur = (tree)->root;                                  \
                                                            \
    while (___cur != NULL) {                                \
        ___diff = cmp_fn(node, ___cur);                     \
        assert(___diff != 0);                               \
        ___prev = ___cur;                                   \
        ___index = starpu_rbtree_d2i(___diff);                     \
        ___cur = ___cur->children[___index];                \
    }                                                       \
                                                            \
    starpu_rbtree_insert_rebalance(tree, ___prev, ___index, node); \
MACRO_END

/**
 * Look up a node/slot pair in a tree.
 *
 * This macro essentially acts as starpu_rbtree_lookup() but in addition to a node,
 * it also returns a slot, which identifies an insertion point in the tree.
 * If the returned node is null, the slot can be used by starpu_rbtree_insert_slot()
 * to insert without the overhead of an additional lookup. The slot is a
 * simple uintptr_t integer.
 *
 * The constraints that apply to the key parameter are the same as for
 * starpu_rbtree_lookup().
 */
#define starpu_rbtree_lookup_slot(tree, key, cmp_fn, slot) \
MACRO_BEGIN                                         \
    struct starpu_rbtree_node *___cur, *___prev;           \
    int ___diff, ___index;                          \
                                                    \
    ___prev = NULL;                                 \
    ___index = 0;                                   \
    ___cur = (tree)->root;                          \
                                                    \
    while (___cur != NULL) {                        \
        ___diff = cmp_fn(key, ___cur);              \
                                                    \
        if (___diff == 0)                           \
            break;                                  \
                                                    \
        ___prev = ___cur;                           \
        ___index = starpu_rbtree_d2i(___diff);             \
        ___cur = ___cur->children[___index];        \
    }                                               \
                                                    \
    (slot) = starpu_rbtree_slot(___prev, ___index);        \
    ___cur;                                         \
MACRO_END

/**
 * Insert a node at an insertion point in a tree.
 *
 * This macro essentially acts as starpu_rbtree_insert() except that it doesn't
 * obtain the insertion point with a standard lookup. The insertion point
 * is obtained by calling starpu_rbtree_lookup_slot(). In addition, the new node
 * must not compare equal to an existing node in the tree (i.e. the slot
 * must denote a null node).
 */
static inline void starpu_rbtree_insert_slot(struct starpu_rbtree *tree, uintptr_t slot,
                   struct starpu_rbtree_node *node)
{
    struct starpu_rbtree_node *parent;
    int index;

    parent = starpu_rbtree_slot_parent(slot);
    index = starpu_rbtree_slot_index(slot);
    starpu_rbtree_insert_rebalance(tree, parent, index, node);
}

/**
 * Remove a node from a tree.
 *
 * After completion, the node is stale.
 */
void starpu_rbtree_remove(struct starpu_rbtree *tree, struct starpu_rbtree_node *node);

/**
 * Return the first node of a tree.
 */
/* TODO: optimize by maintaining the first node of the tree */
#define starpu_rbtree_first(tree) starpu_rbtree_firstlast(tree, STARPU_RBTREE_LEFT)

/**
 * Return the last node of a tree.
 */
/* TODO: optimize by maintaining the first node of the tree */
/* TODO: could be useful to optimize the case when the key being inserted is
 * bigger that the biggest node */
#define starpu_rbtree_last(tree) starpu_rbtree_firstlast(tree, STARPU_RBTREE_RIGHT)

/**
 * Return the node previous to the given node.
 */
#define starpu_rbtree_prev(node) starpu_rbtree_walk(node, STARPU_RBTREE_LEFT)

/**
 * Return the node next to the given node.
 */
#define starpu_rbtree_next(node) starpu_rbtree_walk(node, STARPU_RBTREE_RIGHT)

/**
 * Forge a loop to process all nodes of a tree, removing them when visited.
 *
 * This macro can only be used to destroy a tree, so that the resources used
 * by the entries can be released by the user. It basically removes all nodes
 * without doing any color checking.
 *
 * After completion, all nodes and the tree root member are stale.
 */
#define starpu_rbtree_for_each_remove(tree, node, tmp)         \
for (node = starpu_rbtree_postwalk_deepest(tree),              \
     tmp = starpu_rbtree_postwalk_unlink(node);                \
     node != NULL;                                      \
     node = tmp, tmp = starpu_rbtree_postwalk_unlink(node))    \

#endif /* _KERN_RBTREE_H */
