/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
 *  This file is part of clib library
 *  Copyright (C) 2011 Avinash Dongre ( dongre.avinash@gmail.com )
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 * 
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 * 
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/

#include "c_lib.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>

#define rb_sentinel &pTree->sentinel

static void debug_verify_properties(struct clib_rb*);
static void debug_verify_property_1(struct clib_rb*,struct clib_rb_node*);
static void debug_verify_property_2(struct clib_rb*,struct clib_rb_node*);
static int debug_node_color(struct clib_rb*,struct clib_rb_node* n);
static void debug_verify_property_4(struct clib_rb*,struct clib_rb_node*);
static void debug_verify_property_5(struct clib_rb*,struct clib_rb_node*);
static void debug_verify_property_5_helper(struct clib_rb*,struct clib_rb_node*,int,int*);


static void
pvt_left_rotate(struct clib_rb *pTree, struct clib_rb_node *x){
    struct clib_rb_node *y;
    y = x->right;
    x->right = y->left;
    if (y->left != rb_sentinel)
        y->left->parent = x;
    if (y != rb_sentinel)
        y->parent = x->parent;
    if (x->parent){
        if (x == x->parent->left)
            x->parent->left = y;
        else
            x->parent->right = y;
    }
    else
        pTree->root = y;
    y->left = x;
    if (x != rb_sentinel)
        x->parent = y;
}
static void
pvt_right_rotate(struct clib_rb *pTree, struct clib_rb_node *x) {
    struct clib_rb_node *y = x->left;
    x->left = y->right;
    if (y->right != rb_sentinel)
        y->right->parent = x;
    if (y != rb_sentinel)
        y->parent = x->parent;
    if (x->parent) {
        if (x == x->parent->right)
            x->parent->right = y;
        else
            x->parent->left = y;
    }
    else
        pTree->root = y;
    y->right = x;
    if (x != rb_sentinel)
        x->parent = y;
}

struct clib_rb*
new_clib_rb(clib_compare fn_c,clib_destroy fn_ed, clib_destroy fn_vd ){

    struct clib_rb *pTree = (struct clib_rb*)malloc(sizeof(struct clib_rb));
    if ( pTree == (struct clib_rb*)0 )
        return (struct clib_rb*)0;

    pTree->compare_fn           = fn_c;
    pTree->destruct_k_fn        = fn_ed;
    pTree->destruct_v_fn        = fn_vd;
    pTree->root                 = rb_sentinel;
    pTree->sentinel.left        = rb_sentinel;
    pTree->sentinel.right       = rb_sentinel;
    pTree->sentinel.parent      = (struct clib_rb_node*)0 ;
    pTree->sentinel.color       = clib_black;

    return pTree;
}
static void
pvt_rb_insert_fixup( struct clib_rb *pTree, struct clib_rb_node *x ) {
    while (x != pTree->root && x->parent->color == clib_red) {
        if (x->parent == x->parent->parent->left) {
            struct clib_rb_node *y = x->parent->parent->right;
            if (y->color == clib_red) {
                x->parent->color         = clib_black;
                y->color                 = clib_black;
                x->parent->parent->color = clib_red;
                x = x->parent->parent;
            } else {
                if (x == x->parent->right){
                    x = x->parent;
                    pvt_left_rotate (pTree, x);
                }
                x->parent->color         = clib_black;
                x->parent->parent->color = clib_red;
                pvt_right_rotate (pTree, x->parent->parent);
            }
        } else {
            struct clib_rb_node *y = x->parent->parent->left;
            if (y->color == clib_red) {
                x->parent->color         = clib_black;
                y->color                 = clib_black;
                x->parent->parent->color = clib_red;
                x = x->parent->parent;
            } else {
                if (x == x->parent->left) {
                    x = x->parent;
                    pvt_right_rotate (pTree, x);
                }
                x->parent->color         = clib_black;
                x->parent->parent->color = clib_red;
                pvt_left_rotate (pTree, x->parent->parent);
            }
        }
    }
    pTree->root->color = clib_black;
}
struct clib_rb_node*   
find_clib_rb (struct clib_rb *pTree, void *key) {
    struct clib_rb_node *x = pTree->root;

    while (x != rb_sentinel) {
        int c = 0;
        void *cur_key ;
        get_raw_clib_object ( x->key, &cur_key );
        c = pTree->compare_fn (key, cur_key);
        free ( cur_key );
        if (c == 0) {
            break;
        } else {
            x = c < 0 ? x->left : x->right;
        }
    }
    if ( x == rb_sentinel )
        return (struct clib_rb_node*)0 ;

    return x;
}

clib_error  
insert_clib_rb(struct clib_rb *pTree, void *k, size_t key_size, void *v, size_t value_size) {

    clib_error rc = CLIB_ERROR_SUCCESS;
    struct clib_rb_node *x;
	struct clib_rb_node *y;
	struct clib_rb_node *z;

    x = (struct clib_rb_node*)malloc (sizeof(struct clib_rb_node));
    if ( x == (struct clib_rb_node*)0  ) 
        return CLIB_ERROR_MEMORY;

    x->left    = rb_sentinel;
    x->right   = rb_sentinel;
    x->color   = clib_red;

    x->key     = new_clib_object ( k, key_size );
    if ( v ) {
        x->value   = new_clib_object ( v, value_size );
    } else {
        x->value =  (struct clib_object*)0;
    }

    y = pTree->root;
    z = (struct clib_rb_node*)0 ;

    while (y != rb_sentinel) {
        int c = 0;
        void *cur_key;
		void* new_key;

        get_raw_clib_object ( y->key, &cur_key );
        get_raw_clib_object ( x->key, &new_key );

        c = (pTree->compare_fn) ( new_key , cur_key);
        free ( cur_key );
        free ( new_key );
        if (c == 0) {
            /* TODO : Delete node here */
            return CLIB_RBTREE_KEY_DUPLICATE;
        }
        z = y;
        if ( c < 0 )
            y = y->left;
        else
            y = y->right;
    }    
    x->parent = z;
    if (z) {
        int c = 0;
        void *cur_key;
		void* new_key;
        get_raw_clib_object ( z->key, &cur_key );
        get_raw_clib_object ( x->key, &new_key );

        c = pTree->compare_fn( new_key, cur_key);
        free ( cur_key );
        free ( new_key );
        if (c < 0) {
            z->left = x;
        } else {
            z->right = x;
        }
    }
    else
        pTree->root = x;

    pvt_rb_insert_fixup (pTree, x);

    debug_verify_properties ( pTree);
    return rc;
}
static void
pvt_rb_remove_fixup( struct clib_rb *pTree, struct clib_rb_node *x ) {
    while (x != pTree->root && x->color == clib_black) {
        if (x == x->parent->left) {
            struct clib_rb_node *w = x->parent->right;
            if (w->color == clib_red) {
                w->color         = clib_black;
                x->parent->color = clib_red;
                pvt_left_rotate (pTree, x->parent);
                w = x->parent->right;
            }
            if (w->left->color == clib_black && w->right->color == clib_black) {
                w->color = clib_red;
                x = x->parent;
            } else {
                if (w->right->color == clib_black)  {
                    w->left->color = clib_black;
                    w->color       = clib_red;
                    pvt_right_rotate (pTree, w);
                    w = x->parent->right;
                }
                w->color = x->parent->color;
                x->parent->color = clib_black;
                w->right->color = clib_black;
                pvt_left_rotate (pTree, x->parent);
                x = pTree->root;
            }
        } else {
            struct clib_rb_node *w = x->parent->left;
            if (w->color == clib_red) {
                w->color = clib_black;
                x->parent->color = clib_red;
                pvt_right_rotate (pTree, x->parent);
                w = x->parent->left;
            }
            if (w->right->color == clib_black && w->left->color == clib_black) {
                w->color = clib_red;
                x = x->parent;
            } else {
                if (w->left->color == clib_black) {
                    w->right->color = clib_black;
                    w->color = clib_red;
                    pvt_left_rotate (pTree, w);
                    w = x->parent->left;
                }
                w->color = x->parent->color;
                x->parent->color = clib_black;
                w->left->color = clib_black;
                pvt_right_rotate (pTree, x->parent);
                x = pTree->root;
            }
        }
    }
    x->color = clib_black;
}

static struct clib_rb_node*  
pvt_remove_clib_rb(struct clib_rb *pTree, struct clib_rb_node *z ) {
    struct clib_rb_node *x = (struct clib_rb_node*)0 ;
    struct clib_rb_node *y = (struct clib_rb_node*)0 ;

    if (z->left == rb_sentinel || z->right == rb_sentinel)
        y = z;
    else {
        y = z->right;
        while (y->left != rb_sentinel)
            y = y->left;
    }
    if (y->left != rb_sentinel)
        x = y->left;
    else
        x = y->right;

    x->parent = y->parent;
    if (y->parent)
    {
        if (y == y->parent->left)
            y->parent->left = x;
        else
            y->parent->right = x;
    }
    else
        pTree->root = x;
    if (y != z) {
        struct clib_object* tmp;
        tmp    = z->key;
        z->key = y->key;
        y->key = tmp;

        tmp      = z->value;
        z->value = y->value;
        y->value = tmp;
    }
    if (y->color == clib_black)
        pvt_rb_remove_fixup (pTree, x);

    debug_verify_properties ( pTree);
    return y;
}

struct clib_rb_node*
remove_clib_rb (struct clib_rb *pTree, void *key) {
    struct clib_rb_node *z = (struct clib_rb_node*)0 ;

    z = pTree->root;
    while (z != rb_sentinel) {
        int c = 0;
        void *cur_key;
        get_raw_clib_object ( z->key, &cur_key );
        c = pTree->compare_fn (key, cur_key);
        free ( cur_key );
        if ( c == 0) {
            break;
        }
        else {
            z = ( c < 0) ? z->left : z->right;
        }
    }
    if (z == rb_sentinel)
        return (struct clib_rb_node*)0 ;
    return pvt_remove_clib_rb(pTree, z );
}
static void
pvt_delete_clib_rb_node (struct clib_rb *pTree, struct clib_rb_node *x ) {

    void *key;
	void *value;

    if ( pTree->destruct_k_fn ) {
        get_raw_clib_object ( x->key, &key );
        pTree->destruct_k_fn ( key );
    }
    delete_clib_object( x->key );

    if ( x->value ) {
        if ( pTree->destruct_v_fn ) {
            get_raw_clib_object ( x->value, &value);
            pTree->destruct_v_fn ( value );
        }
        delete_clib_object( x->value );
    }
}

clib_error  
delete_clib_rb(struct clib_rb *pTree) {

    clib_error rc = CLIB_ERROR_SUCCESS;
    struct clib_rb_node *z = pTree->root;

    while (z != rb_sentinel) {
        if (z->left != rb_sentinel)
            z = z->left;
        else if (z->right != rb_sentinel)
            z = z->right;
        else {
            pvt_delete_clib_rb_node ( pTree, z );
            if (z->parent) {
                z = z->parent;
                if (z->left != rb_sentinel){
                    free ( z->left );
                    z->left = rb_sentinel;
                }else if (z->right != rb_sentinel){
                    free ( z->right );
                    z->right = rb_sentinel;
                }
            } else {
                free ( z );
                z = rb_sentinel;
            }
        }
    }
    free ( pTree );
    return rc;
}
struct clib_rb_node *
minimum_clib_rb( struct clib_rb *pTree, struct clib_rb_node *x ) {
	while ( x->left != rb_sentinel)
		x = x->left;
	return x;
}

struct clib_rb_node *
maximum_clib_rb( struct clib_rb *pTree, struct clib_rb_node *x ) {
	while ( x->right != rb_sentinel)
		x = x->right;
	return x;
}


clib_bool 
empty_clib_rb(struct clib_rb *pTree) {
    if ( pTree->root != rb_sentinel )
        return clib_true;
    return clib_false;
}
struct clib_rb_node*
tree_successor(struct clib_rb *pTree, struct clib_rb_node *x) {
	struct clib_rb_node *y = (struct clib_rb_node*)0;
	if ( x->right != rb_sentinel)
		return minimum_clib_rb( pTree, x->right);
	
	if ( x  == maximum_clib_rb(pTree,pTree->root)) 
		return (struct clib_rb_node*)0;

	y = x->parent;
	while ( y != rb_sentinel && x == y->right ){
		x = y;
		y = y->parent;
	}
	return y;
}


void debug_verify_properties(struct clib_rb* t) {
    debug_verify_property_1(t,t->root);
    debug_verify_property_2(t,t->root);
    debug_verify_property_4(t,t->root);
    debug_verify_property_5(t,t->root);
}

void debug_verify_property_1(struct clib_rb *pTree,struct clib_rb_node* n) {
    assert(debug_node_color(pTree,n) == clib_red || debug_node_color(pTree,n) == clib_black);
    if (n == rb_sentinel) return;
    debug_verify_property_1(pTree,n->left);
    debug_verify_property_1(pTree,n->right);
}

void debug_verify_property_2(struct clib_rb *pTree,struct clib_rb_node* root) {
    assert(debug_node_color(pTree,root) == clib_black);
}

int debug_node_color(struct clib_rb *pTree,struct clib_rb_node* n) {
    return n == rb_sentinel ? clib_black : n->color;
}

void debug_verify_property_4(struct clib_rb *pTree,struct clib_rb_node* n) {
    if (debug_node_color(pTree,n) == clib_red) {
        assert (debug_node_color(pTree,n->left)   == clib_black);
        assert (debug_node_color(pTree,n->right)  == clib_black);
        assert (debug_node_color(pTree,n->parent) == clib_black);
    }
    if (n == rb_sentinel) return;
    debug_verify_property_4(pTree,n->left);
    debug_verify_property_4(pTree,n->right);
}

void debug_verify_property_5(struct clib_rb *pTree,struct clib_rb_node* root) {
    int black_count_path = -1;
    debug_verify_property_5_helper(pTree,root, 0, &black_count_path);
}

void debug_verify_property_5_helper(struct clib_rb *pTree,struct clib_rb_node* n, int black_count, int* path_black_count) {
    if (debug_node_color(pTree,n) == clib_black) {
        black_count++;
    }
    if (n == rb_sentinel) {
        if (*path_black_count == -1) {
            *path_black_count = black_count;
        } else {
            assert (black_count == *path_black_count);
        }
        return;
    }
    debug_verify_property_5_helper(pTree,n->left,  black_count, path_black_count);
    debug_verify_property_5_helper(pTree,n->right, black_count, path_black_count);
}

