/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2013 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2015-2018 Los Alamos National Security, LLC. All rights
 *                         reserved.
 * Copyright (c) 2020      Google, LLC. All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */
/*
 * @file
 */

#include "opal_config.h"

#include "opal/class/opal_interval_tree.h"
#include <limits.h>

/* Private functions */
static void opal_interval_tree_insert_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node);

/* tree rebalancing functions */
static void opal_interval_tree_delete_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *node,
                                             opal_interval_tree_node_t *parent);
static void opal_interval_tree_insert_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *x);

static opal_interval_tree_node_t *opal_interval_tree_next (opal_interval_tree_t *tree,
                                                           opal_interval_tree_node_t *node);
static opal_interval_tree_node_t * opal_interval_tree_find_node(opal_interval_tree_t *tree,
                                                                uint64_t low, uint64_t high,
                                                                void *data);

static opal_interval_tree_node_t *left_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x);
static opal_interval_tree_node_t *right_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x);

static void inorder_destroy(opal_interval_tree_t *tree, opal_interval_tree_node_t * node);

#define max(x,y) (((x) > (y)) ? (x) : (y))

/**
 * the constructor function. creates the free list to get the nodes from
 *
 * @param object the tree that is to be used
 *
 * @retval NONE
 */
static void opal_interval_tree_construct (opal_interval_tree_t *tree)
{
    OBJ_CONSTRUCT(&tree->root, opal_interval_tree_node_t);
    OBJ_CONSTRUCT(&tree->nill, opal_interval_tree_node_t);
    OBJ_CONSTRUCT(&tree->free_list, opal_free_list_t);
    OBJ_CONSTRUCT(&tree->gc_list, opal_list_t);

    /* initialize sentinel */
    tree->nill.color = OPAL_INTERVAL_TREE_COLOR_BLACK;
    tree->nill.left = tree->nill.right = tree->nill.parent = &tree->nill;
    tree->nill.max = 0;
    tree->nill.data = NULL;

    /* initialize root sentinel */
    tree->root.color = OPAL_INTERVAL_TREE_COLOR_BLACK;
    tree->root.left = tree->root.right = tree->root.parent = &tree->nill;
    /* this simplifies inserting at the root as we only have to check the
     * low value. */
    tree->root.low = (uint64_t) -1;
    tree->root.data = NULL;

    /* set the tree size to zero */
    tree->tree_size = 0;
    tree->lock = 0;
    tree->reader_count = 0;
    tree->epoch = 0;

    /* set all reader epochs to UINT_MAX. this value is used to simplfy
     * checks against the current epoch. */
    for (int i = 0 ; i < OPAL_INTERVAL_TREE_MAX_READERS ; ++i) {
        tree->reader_epochs[i] = UINT_MAX;
    }
}

/**
 * the destructor function. Free the tree and destroys the free list.
 *
 * @param object the tree object
 */
static void opal_interval_tree_destruct (opal_interval_tree_t *tree)
{
    opal_interval_tree_destroy (tree);

    OBJ_DESTRUCT(&tree->free_list);
    OBJ_DESTRUCT(&tree->root);
    OBJ_DESTRUCT(&tree->nill);
}

/* declare the instance of the classes  */
OBJ_CLASS_INSTANCE(opal_interval_tree_node_t, opal_free_list_item_t, NULL, NULL);
OBJ_CLASS_INSTANCE(opal_interval_tree_t, opal_object_t, opal_interval_tree_construct,
                   opal_interval_tree_destruct);

typedef int32_t opal_interval_tree_token_t;

/**
 * @brief pick and return a reader slot
 */
static opal_interval_tree_token_t opal_interval_tree_reader_get_token (opal_interval_tree_t *tree)
{
    opal_interval_tree_token_t token = -1;

    if (token < 0) {
        int32_t reader_count = tree->reader_count;
        /* NTH: could have used an atomic here but all we are after is some distribution of threads
         * across the reader slots. with high thread counts i see no real performance difference
         * using atomics. */
        token = tree->reader_id++ % OPAL_INTERVAL_TREE_MAX_READERS;
        while (OPAL_UNLIKELY(reader_count <= token)) {
            if (opal_atomic_compare_exchange_strong_32 (&tree->reader_count, &reader_count, token + 1)) {
                break;
            }
        }
    }

    while (!OPAL_ATOMIC_COMPARE_EXCHANGE_STRONG_32((volatile int32_t *) &tree->reader_epochs[token],
                                                   &(int32_t) {UINT_MAX}, tree->epoch));

    return token;
}

static void opal_interval_tree_reader_return_token (opal_interval_tree_t *tree, opal_interval_tree_token_t token)
{
    tree->reader_epochs[token] = UINT_MAX;
}

/* Create the tree */
int opal_interval_tree_init (opal_interval_tree_t *tree)
{
    return opal_free_list_init (&tree->free_list, sizeof(opal_interval_tree_node_t),
                                opal_cache_line_size, OBJ_CLASS(opal_interval_tree_node_t),
                                0, opal_cache_line_size, 0, -1 , 128, NULL, 0, NULL, NULL, NULL);
}

static bool opal_interval_tree_write_trylock (opal_interval_tree_t *tree)
{
    opal_atomic_rmb ();
    return !(tree->lock || opal_atomic_swap_32 (&tree->lock, 1));
}

static void opal_interval_tree_write_lock (opal_interval_tree_t *tree)
{
    while (!opal_interval_tree_write_trylock (tree));
}

static void opal_interval_tree_write_unlock (opal_interval_tree_t *tree)
{
    opal_atomic_wmb ();
    tree->lock = 0;
}

static void opal_interval_tree_insert_fixup_helper (opal_interval_tree_t *tree, opal_interval_tree_node_t *node) {
    opal_interval_tree_node_t *y, *parent = node->parent;
    bool rotate_right = false;

    if (parent->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
        return;
    }

    if (parent == parent->parent->left) {
        y = parent->parent->right;
        rotate_right = true;
    } else {
        y = parent->parent->left;
    }

    if (y->color == OPAL_INTERVAL_TREE_COLOR_RED) {
        parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        y->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        parent->parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
        opal_interval_tree_insert_fixup_helper (tree, parent->parent);
        return;
    }

    if (rotate_right) {
        if (node == parent->right) {
            node = left_rotate (tree, parent);
            parent = node->parent;
        }

        parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        parent->parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
        (void) right_rotate(tree, parent->parent);
    } else {
        if (node == parent->left) {
            node = right_rotate(tree, parent);
            parent = node->parent;
        }
        parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        parent->parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
        (void) left_rotate(tree, parent->parent);
    }

    opal_interval_tree_insert_fixup_helper (tree, node);
}

static void opal_interval_tree_insert_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *node) {
    /* do the rotations */
    /* usually one would have to check for NULL, but because of the sentinal,
     * we don't have to   */
    opal_interval_tree_insert_fixup_helper (tree, node);

    /* after the rotations the root is black */
    tree->root.left->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
}

/**
 * @brief Guts of the delete fixup
 *
 * @param[in] tree    opal interval tree
 * @param[in] node    node to fixup
 * @param[in] left    true if the node is a left child of its parent
 *
 * @returns the next node to fixup or root if done
 */
static inline opal_interval_tree_node_t *
opal_interval_tree_delete_fixup_helper (opal_interval_tree_t *tree, opal_interval_tree_node_t *node,
                                        opal_interval_tree_node_t *parent, const bool left)
{
    opal_interval_tree_node_t *w;

    /* get sibling */
    w = left ? parent->right : parent->left;
    if (w->color == OPAL_INTERVAL_TREE_COLOR_RED) {
        w->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        parent->color = OPAL_INTERVAL_TREE_COLOR_RED;
        if (left) {
            (void) left_rotate(tree, parent);
            w = parent->right;
        } else {
            (void) right_rotate(tree, parent);
            w = parent->left;
        }
    }

    if ((w->left->color == OPAL_INTERVAL_TREE_COLOR_BLACK) && (w->right->color == OPAL_INTERVAL_TREE_COLOR_BLACK)) {
        w->color = OPAL_INTERVAL_TREE_COLOR_RED;
        return parent;
    }

    if (left) {
        if (w->right->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
            w->left->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
            w->color = OPAL_INTERVAL_TREE_COLOR_RED;
            (void) right_rotate(tree, w);
            w = parent->right;
        }
        w->color = parent->color;
        parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        w->right->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        (void) left_rotate(tree, parent);
    } else {
        if (w->left->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
            w->right->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
            w->color = OPAL_INTERVAL_TREE_COLOR_RED;
            (void) left_rotate(tree, w);
            w = parent->left;
        }
        w->color = parent->color;
        parent->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        w->left->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        (void) right_rotate(tree, parent);
    }

    /* return the root */
    return tree->root.left;
 }

/* Fixup the balance of the btree after deletion    */
static void opal_interval_tree_delete_fixup (opal_interval_tree_t *tree, opal_interval_tree_node_t *node,
                                             opal_interval_tree_node_t *parent)
{
    while ((node != tree->root.left) && (node->color == OPAL_INTERVAL_TREE_COLOR_BLACK)) {
        node = opal_interval_tree_delete_fixup_helper (tree, node, parent, node == parent->left);
        parent = node->parent;
    }

    node->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
    tree->nill.color = OPAL_INTERVAL_TREE_COLOR_BLACK;
}

/* traverse the garbage-collection list and return any nodes that can not have any
 * references. this function MUST be called with the writer lock held. */
static void opal_interval_tree_gc_clean (opal_interval_tree_t *tree)
{
    opal_interval_tree_node_t *node, *next;
    uint32_t oldest_epoch = UINT_MAX;

    if (0 == opal_list_get_size (&tree->gc_list)) {
        return;
    }

    for (int i = 0 ; i < tree->reader_count ; ++i) {
        oldest_epoch = (oldest_epoch < tree->reader_epochs[i]) ? oldest_epoch : tree->reader_epochs[i];
    }

    OPAL_LIST_FOREACH_SAFE(node, next, &tree->gc_list, opal_interval_tree_node_t) {
        if (node->epoch < oldest_epoch) {
            opal_list_remove_item (&tree->gc_list, &node->super.super);
            opal_free_list_return_st (&tree->free_list, &node->super);
        }
    }
}

/* This inserts a node into the tree based on the passed values. */
int opal_interval_tree_insert (opal_interval_tree_t *tree, void *value, uint64_t low, uint64_t high)
{
    opal_interval_tree_node_t * node;

    if (low > high) {
        return OPAL_ERR_BAD_PARAM;
    }

    opal_interval_tree_write_lock (tree);

    opal_interval_tree_gc_clean (tree);

    /* get the memory for a node */
    node = (opal_interval_tree_node_t *) opal_free_list_get (&tree->free_list);
    if (OPAL_UNLIKELY(NULL == node)) {
        opal_interval_tree_write_unlock (tree);
        return OPAL_ERR_OUT_OF_RESOURCE;
    }

    /* insert the data into the node */
    node->data = value;
    node->low = low;
    node->high = high;
    node->max = high;
    node->epoch = tree->epoch;

    /* insert the node into the tree */
    opal_interval_tree_insert_node (tree, node);

    opal_interval_tree_insert_fixup (tree, node);
    opal_interval_tree_write_unlock (tree);

    return OPAL_SUCCESS;
}

static int opal_interval_tree_compare_node(opal_interval_tree_node_t *node, uint64_t low, uint64_t high, void *data) {
    if ((data && node->low == low && node->high == high && node->data == data) ||
        (!data && node->low <= low && node->high >= high)) {
        return 0;
    }
    if (node->low > low) {
        return -1;
    }
    if (node->low < low) {
        return 1;
    }
    if (node->high < high) {
        return -1;
    }
    if (node->high > high) {
        return 1;
    }
    if (node->data > data) {
        return -1;
    }
    return 1;
}

static opal_interval_tree_node_t *opal_interval_tree_find_interval(opal_interval_tree_t *tree, opal_interval_tree_node_t *node, uint64_t low,
                                                                   uint64_t high, void *data)
{
    if (node == &tree->nill) {
        return NULL;
    }

    int check = opal_interval_tree_compare_node(node, low, high, data);
    if (0 == check) {
        return node;
    }

    if (-1 == check) {
        return opal_interval_tree_find_interval (tree, node->left, low, high, data);
    }

    return opal_interval_tree_find_interval (tree, node->right, low, high, data);
}

/* Finds the node in the tree based on the key and returns a pointer
 * to the node. This is a bit a code duplication, but this has to be fast
 * so we go ahead with the duplication */
static opal_interval_tree_node_t *opal_interval_tree_find_node(opal_interval_tree_t *tree, uint64_t low, uint64_t high, void *data)
{
    return opal_interval_tree_find_interval (tree, tree->root.left, low, high, data);
}

void *opal_interval_tree_find_overlapping (opal_interval_tree_t *tree, uint64_t low, uint64_t high)
{
    opal_interval_tree_token_t token;
    opal_interval_tree_node_t *node;

    token = opal_interval_tree_reader_get_token (tree);
    node = opal_interval_tree_find_node (tree, low, high, NULL);
    opal_interval_tree_reader_return_token (tree, token);

    return node ? node->data : NULL;
}

static size_t opal_interval_tree_depth_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    if (&tree->nill == node) {
        return 0;
    }

    return 1 + max (opal_interval_tree_depth_node (tree, node->right), opal_interval_tree_depth_node (tree, node->left));
}

size_t opal_interval_tree_depth (opal_interval_tree_t *tree)
{
    opal_interval_tree_token_t token;
    size_t depth;

    token = opal_interval_tree_reader_get_token (tree);
    depth = opal_interval_tree_depth_node (tree, &tree->root);
    opal_interval_tree_reader_return_token (tree, token);

    return depth;
}

/* update the value of a tree pointer */
static inline void rp_publish (opal_interval_tree_node_t **ptr, opal_interval_tree_node_t *node)
{
    /* ensure all writes complete before continuing */
    opal_atomic_wmb ();
    /* just set the value */
    *ptr = node;
}


static inline void rp_wait_for_readers (opal_interval_tree_t *tree)
{
    uint32_t epoch_id = ++tree->epoch;

    /* wait for all readers to see the new tree version */
    for (int i = 0 ; i < tree->reader_count ; ++i) {
        while (tree->reader_epochs[i] < epoch_id);
    }
}

/* waits for all writers to finish with the node then releases the last reference */
static inline void rp_free_wait (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    rp_wait_for_readers (tree);
    /* no other threads are working on this node so go ahead and return it */
    opal_free_list_return_st (&tree->free_list, &node->super);
}

/* schedules the node for releasing */
static inline void rp_free (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    opal_list_append (&tree->gc_list, &node->super.super);
}

static opal_interval_tree_node_t *opal_interval_tree_node_copy (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    opal_interval_tree_node_t *copy = (opal_interval_tree_node_t *) opal_free_list_wait_st (&tree->free_list);
    size_t color_offset = offsetof(opal_interval_tree_node_t, color);
    assert (NULL != copy);
    memcpy ((unsigned char *) copy + color_offset, (unsigned char *) node + color_offset,
            sizeof (*node) - color_offset);
    return copy;
}

/* this function deletes a node that is either a left or right leaf (or both) */
static void opal_interval_tree_delete_leaf (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    const opal_interval_tree_node_t *nill = &tree->nill;
    opal_interval_tree_node_t **parent_ptr, *next, *parent = node->parent;
    opal_interval_tree_nodecolor_t color = node->color;

    assert (node->left == nill || node->right == nill);

    parent_ptr = (parent->right == node) ? &parent->right : &parent->left;

    next = (node->right == nill) ? node->left : node->right;

    next->parent = node->parent;
    rp_publish (parent_ptr, next);

    rp_free (tree, node);

    if (OPAL_INTERVAL_TREE_COLOR_BLACK == color) {
        if (OPAL_INTERVAL_TREE_COLOR_RED == next->color) {
            next->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
        } else {
            opal_interval_tree_delete_fixup (tree, next, parent);
        }
    }
}

static void opal_interval_tree_delete_interior (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    opal_interval_tree_node_t **parent_ptr, *next, *next_copy, *parent = node->parent;
    opal_interval_tree_nodecolor_t color = node->color, next_color;

    parent_ptr = (parent->right == node) ? &parent->right : &parent->left;
    next = opal_interval_tree_next (tree, node);
    next_color = next->color;

    if (next != node->right) {
        /* case 3 */
        next_copy = opal_interval_tree_node_copy (tree, next);
        next_copy->color = node->color;
        next_copy->left = node->left;
        next_copy->left->parent = next_copy;
        next_copy->right = node->right;
        next_copy->right->parent = next_copy;
        next_copy->parent = node->parent;

        rp_publish (parent_ptr, next_copy);
        rp_free_wait (tree, node);

        opal_interval_tree_delete_leaf (tree, next);
    } else {
        /* case 2. no copies are needed */
        next->color = color;
        next->left = node->left;
        next->left->parent = next;
        next->parent = node->parent;
        rp_publish (parent_ptr, next);
        rp_free (tree, node);

	/* since we are actually "deleting" the next node the fixup needs to happen on the
	 * right child of next (by definition next was a left child) */
        if (OPAL_INTERVAL_TREE_COLOR_BLACK == next_color) {
	    if (OPAL_INTERVAL_TREE_COLOR_RED == next->right->color) {
		next->right->color = OPAL_INTERVAL_TREE_COLOR_BLACK;
            } else {
		opal_interval_tree_delete_fixup (tree, next->right, next);
	    }
	}
    }
}

/* Delete a node from the tree based on the key */
int opal_interval_tree_delete (opal_interval_tree_t *tree, uint64_t low, uint64_t high, void *data)
{
    opal_interval_tree_node_t *node;

    opal_interval_tree_write_lock (tree);
    node = opal_interval_tree_find_node (tree, low, high, data);
    if (NULL == node) {
        opal_interval_tree_write_unlock (tree);
        return OPAL_ERR_NOT_FOUND;
    }

    /* there are three cases that have to be handled:
     * 1) the node p is a left leaf or a right left (one of p's children is nill)
     *    in this case we can delete p and we can replace it with one of it's children
     *    or nill (if both children are nill).
     * 2) the right child of p is a left leaf (node->right->left == nill)
     *    in this case we can set node->right->left = node->left and replace node with node->right
     * 3) p is a interior node
     *    we replace node with next(node)
     */
    
    if ((node->left == &tree->nill) || (node->right == &tree->nill)) {
        /* handle case 1 */
        opal_interval_tree_delete_leaf (tree, node);
    } else {
        /* handle case 2 and 3 */
        opal_interval_tree_delete_interior (tree, node);
    }

    --tree->tree_size;

    opal_interval_tree_write_unlock (tree);

    return OPAL_SUCCESS;
}

int opal_interval_tree_destroy (opal_interval_tree_t *tree)
{
    /* Recursive inorder traversal for delete */
    inorder_destroy(tree, &tree->root);
    tree->tree_size = 0;
    return OPAL_SUCCESS;
}


/* Find the next inorder successor of a node    */
static opal_interval_tree_node_t *opal_interval_tree_next (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    opal_interval_tree_node_t *p = node->right;

    if (p == &tree->nill) {
        p = node->parent;
        while (node == p->right) {
            node = p;
            p = p->parent;
        }

        if (p == &tree->root) {
            return &tree->nill;
        }

        return p;
    }

    while (p->left != &tree->nill) {
        p = p->left;
    }

    return p;
}

/* Insert an element in the normal binary search tree fashion    */
/* this function goes through the tree and finds the leaf where
 * the node will be inserted   */
static void opal_interval_tree_insert_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    opal_interval_tree_node_t *parent = &tree->root;
    opal_interval_tree_node_t *n = parent->left; /* the real root of the tree */
    opal_interval_tree_node_t *nill = &tree->nill;

    /* set up initial values for the node */
    node->color = OPAL_INTERVAL_TREE_COLOR_RED;
    node->parent = NULL;
    node->left = nill;
    node->right = nill;

    /* find the leaf where we will insert the node */
    int check = -1;
    while (n != nill) {
        check = opal_interval_tree_compare_node(n, node->low, node->high, node->data);
        /* node already exists */
        assert (0 != check);

        if (n->max < node->high) {
            n->max = node->high;
        }

        parent = n;
        n = (-1 == check) ? n->left : n->right;
        assert (nill == n || n->parent == parent);
    }

    /* place it on either the left or the right */
    if (-1 == check) {
        parent->left = node;
    } else {
        parent->right = node;
    }

    /* set its parent and children */
    node->parent = parent;

    ++tree->tree_size;
}

static int inorder_traversal (opal_interval_tree_t *tree, uint64_t low, uint64_t high,
			      bool partial_ok, opal_interval_tree_action_fn_t action,
			      opal_interval_tree_node_t * node, void *ctx)
{
    int rc;

    if (node == &tree->nill) {
        return OPAL_SUCCESS;
    }

    rc = inorder_traversal(tree, low, high, partial_ok, action, node->left, ctx);
    if (OPAL_SUCCESS != rc) {
        return rc;
    }

    if ((!partial_ok && (node->low <= low && node->high >= high)) ||
        (partial_ok && ((low >= node->low && low <= node->high) ||
                        (high >= node->low && high <= node->high) ||
                        (node->low >= low && node->low <= high) ||
                        (node->high >= high && node->high <= high)))) {
        rc = action (node->low, node->high, node->data, ctx);
        if (OPAL_SUCCESS != rc) {
            return rc;
        }
    }

    return inorder_traversal(tree, low, high, partial_ok, action, node->right, ctx);
}

/* Free the nodes in inorder fashion    */

static void inorder_destroy (opal_interval_tree_t *tree, opal_interval_tree_node_t *node)
{
    if (node == &tree->nill) {
        return;
    }

    inorder_destroy(tree, node->left);
    inorder_destroy(tree, node->right);

    if (node->left != &tree->nill) {
        opal_free_list_return_st (&tree->free_list, &node->left->super);
    }

    if (node->right != &tree->nill) {
        opal_free_list_return_st (&tree->free_list, &node->right->super);
    }
}

/* Try to access all the elements of the hashmap conditionally */

int opal_interval_tree_traverse (opal_interval_tree_t *tree, uint64_t low, uint64_t high,
                                 bool partial_ok, opal_interval_tree_action_fn_t action, void *ctx)
{
    opal_interval_tree_token_t token;
    int rc;

    if (action == NULL) {
        return OPAL_ERR_BAD_PARAM;
    }

    token = opal_interval_tree_reader_get_token (tree);
    rc = inorder_traversal (tree, low, high, partial_ok, action, tree->root.left, ctx);
    opal_interval_tree_reader_return_token (tree, token);
    return rc;
}

/* Left rotate the tree    */
/* basically what we want to do is to make x be the left child
 * of its right child    */
static opal_interval_tree_node_t *left_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x)
{
    opal_interval_tree_node_t *x_copy = x;
    opal_interval_tree_node_t *y = x->right;
    opal_interval_tree_node_t *parent = x->parent;

    /* make the left child of y's parent be x if it is not the sentinal node*/
    if (y->left != &tree->nill) {
        y->left->parent = x_copy;
    }

    /* x's parent is now y */
    x_copy->parent = y;
    x_copy->right = y->left;
    x_copy->max = max (x_copy->high, max (x_copy->left->max, x_copy->left->max));

    rp_publish (&y->left, x_copy);

    /* normlly we would have to check to see if we are at the root.
     * however, the root sentinal takes care of it for us */
    if (x == parent->left) {
        rp_publish (&parent->left, y);
    } else {
        rp_publish (&parent->right, y);
    }

    /* the old parent of x is now y's parent */
    y->parent = parent;

    return x_copy;
}


/* Right rotate the tree    */
/* basically what we want to do is to make x be the right child
 * of its left child */
static opal_interval_tree_node_t *right_rotate (opal_interval_tree_t *tree, opal_interval_tree_node_t *x)
{
    opal_interval_tree_node_t *x_copy = x;
    opal_interval_tree_node_t *y = x->left;
    opal_interval_tree_node_t *parent = x->parent;

    /* make the left child of y's parent be x if it is not the sentinal node*/
    if (y->right != &tree->nill) {
        y->right->parent = x_copy;
    }

    x_copy->left = y->right;
    x_copy->parent = y;

    rp_publish (&y->right, x_copy);

    /* the maximum value in the subtree rooted at y is now the value it
     * was at x */
    y->max = x->max;
    y->parent = parent;

    if (parent->left == x) {
        rp_publish (&parent->left, y);
    } else {
        rp_publish (&parent->right, y);
    }

    return x_copy;
}

/* returns the size of the tree */
size_t opal_interval_tree_size(opal_interval_tree_t *tree)
{
    return tree->tree_size;
}

static bool opal_interval_tree_verify_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node, int black_depth,
                                            int current_black_depth)
{
    if (node == &tree->nill) {
        return true;
    }

    if (OPAL_INTERVAL_TREE_COLOR_RED == node->color &&
        (OPAL_INTERVAL_TREE_COLOR_BLACK != node->left->color ||
         OPAL_INTERVAL_TREE_COLOR_BLACK != node->right->color)) {
        fprintf (stderr, "Red node has a red child!\n");
        return false;
    }

    if (OPAL_INTERVAL_TREE_COLOR_BLACK == node->color) {
        current_black_depth++;
    }

    if (node->left == &tree->nill && node->right == &tree->nill) {
        if (black_depth != current_black_depth) {
            fprintf (stderr, "Found leaf with unexpected black depth: %d, expected: %d\n", current_black_depth, black_depth);
            return false;
        }

        return true;
    }

    return opal_interval_tree_verify_node (tree, node->left, black_depth, current_black_depth) ||
        opal_interval_tree_verify_node (tree, node->right, black_depth, current_black_depth);
}

static int opal_interval_tree_black_depth (opal_interval_tree_t *tree, opal_interval_tree_node_t *node, int depth)
{
    if (node == &tree->nill) {
        return depth;
    }

    /* suffices to always go left */
    if (OPAL_INTERVAL_TREE_COLOR_BLACK == node->color) {
        depth++;
    }

    return opal_interval_tree_black_depth (tree, node->left, depth);
}

bool opal_interval_tree_verify (opal_interval_tree_t *tree)
{
    int black_depth;

    if (OPAL_INTERVAL_TREE_COLOR_BLACK != tree->root.left->color) {
        fprintf (stderr, "Root node of tree is NOT black!\n");
        return false;
    }

    if (OPAL_INTERVAL_TREE_COLOR_BLACK != tree->nill.color) {
        fprintf (stderr, "Leaf node color is NOT black!\n");
        return false;
    }

    black_depth = opal_interval_tree_black_depth (tree, tree->root.left, 0);

    return opal_interval_tree_verify_node (tree, tree->root.left, black_depth, 0);
}

static void opal_interval_tree_dump_node (opal_interval_tree_t *tree, opal_interval_tree_node_t *node, int black_rank, FILE *fh)
{
    const char *color = (node->color == OPAL_INTERVAL_TREE_COLOR_BLACK) ? "black" : "red";
    uintptr_t left = (uintptr_t) node->left, right = (uintptr_t) node->right;
    opal_interval_tree_node_t *nill = &tree->nill;

    if (node->color == OPAL_INTERVAL_TREE_COLOR_BLACK) {
	++black_rank;
    }

    if (nill == node) {
        return;
    }

    /* print out nill nodes if any */
    if ((uintptr_t) nill == left) {
        left = (uintptr_t) node | 0x1;
        fprintf (fh, "  Node%lx [color=black,label=nill];\n\n", left);
    } else {
        left = (uintptr_t) node->left;
    }

    if ((uintptr_t) nill == right) {
        right = (uintptr_t) node | 0x2;
        fprintf (fh, "  Node%lx [color=black,label=nill];\n\n", right);
    } else {
        right = (uintptr_t) node->right;
    }

    /* print out this node and its edges */
    fprintf (fh, "  Node%lx [color=%s,shape=box,label=\"[0x%" PRIx64 ",0x%" PRIx64 "]\\nmax=0x%" PRIx64
             "\\ndata=0x%lx\\nblack rank=%d\"];\n", (uintptr_t) node, color, node->low, node->high, node->max,
             (uintptr_t) node->data, black_rank);
    fprintf (fh, "  Node%lx -> Node%lx;\n", (uintptr_t) node, left);
    fprintf (fh, "  Node%lx -> Node%lx;\n\n", (uintptr_t) node, right);
    if (node != tree->root.left) {
        fprintf (fh, "  Node%lx -> Node%lx;\n\n", (uintptr_t) node, (uintptr_t) node->parent);
    }
    opal_interval_tree_dump_node (tree, node->left, black_rank, fh);
    opal_interval_tree_dump_node (tree, node->right, black_rank, fh);
}

int opal_interval_tree_dump (opal_interval_tree_t *tree, const char *path)
{
    FILE *fh;

    fh = fopen (path, "w");
    if (NULL == fh) {
        return OPAL_ERR_BAD_PARAM;
    }

    fprintf (fh, "digraph {\n");
    fprintf (fh, "  graph [ordering=\"out\"];");
    opal_interval_tree_dump_node (tree, tree->root.left, 0, fh);
    fprintf (fh, "}\n");

    fclose (fh);

    return OPAL_SUCCESS;
}
