aboutsummaryrefslogblamecommitdiffstats
path: root/litmus/binheap.c
blob: 8d42403ad52c4a62160cfe95c1f6dcbb29ff8121 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                           

                                                                                            






                                                      
 


                                     
 








                                                                              
 






                                                       



                                                                 
 













                                                                        
 





                                                             
 







                                                     
 







                                                     
 

                                           
 
                           
 

                                    
 



                                             
 

                                    
 



                                             
 

                                 

 
                                            
 





                                    
 





                                    
 















                                                                   
 



                                                             
 



                                                       
 



                                                  
 



                                                                              
 
                                                               
 









                                                                        
 



                                                              
 



                                                       
 



                                                 
 









                                      





                                              
 




                                                                                                    
 




                                                                







                                                                
 



























                                                                             




                                                  
 


                                             
 






                                                        
 
                                                
 







                                                              
 
                                                
 





                                                              
 


                                        
 


















                                                                            
 




                                                   
 




                                                             
 

                                                                     
 
                                                            
 

                                                       
 







                                                                          
 





                                                             
 
                                               
 









                                                                     
 










                                                                                   
 
                                       
 








                                                
 













                                                                   
 



                                                        

         




                                                
 

                                                                                 
 

                                                      
 
                                                                          
                                                                        








                                                         
 



                                                   

         




                                                
  

                                            
#include <litmus/binheap.h>

//extern void dump_node_data(struct binheap_node* parent, struct binheap_node* child);
//extern void dump_node_data2(struct binheap_handle *handle, struct binheap_node* bad_node);

int binheap_is_in_this_heap(struct binheap_node *node,
	struct binheap_handle* heap)
{
	if(!binheap_is_in_heap(node)) {
		return 0;
	}

	while(node->parent != NULL) {
		node = node->parent;
	}

	return (node == heap->root);
}

/* Update the node reference pointers.  Same logic as Litmus binomial heap. */
static void __update_ref(struct binheap_node *parent,
	struct binheap_node *child)
{
	*(parent->ref_ptr) = child;
	*(child->ref_ptr) = parent;

	swap(parent->ref_ptr, child->ref_ptr);
}

/* Swaps data between two nodes. */
static void __binheap_swap(struct binheap_node *parent,
	struct binheap_node *child)
{
//	if(parent == BINHEAP_POISON || child == BINHEAP_POISON) {
//		dump_node_data(parent, child);
//		BUG();
//	}

	swap(parent->data, child->data);
	__update_ref(parent, child);
}


/* Swaps memory and data between two nodes. Actual nodes swap instead of
 * just data.  Needed when we delete nodes from the heap.
 */
static void __binheap_swap_safe(struct binheap_handle *handle,
	struct binheap_node *a,
	struct binheap_node *b)
{
	swap(a->data, b->data);
	__update_ref(a, b);

	if((a->parent != NULL) && (a->parent == b->parent)) {
		/* special case: shared parent */
		swap(a->parent->left, a->parent->right);
	}
	else {
		/* Update pointers to swap parents. */

		if(a->parent) {
			if(a == a->parent->left) {
				a->parent->left = b;
			}
			else {
				a->parent->right = b;
			}
		}

		if(b->parent) {
			if(b == b->parent->left) {
				b->parent->left = a;
			}
			else {
				b->parent->right = a;
			}
		}

		swap(a->parent, b->parent);
	}

	/* swap children */

	if(a->left) {
		a->left->parent = b;

		if(a->right) {
			a->right->parent = b;
		}
	}

	if(b->left) {
		b->left->parent = a;

		if(b->right) {
			b->right->parent = a;
		}
	}

	swap(a->left, b->left);
	swap(a->right, b->right);


	/* update next/last/root pointers */

	if(a == handle->next) {
		handle->next = b;
	}
	else if(b == handle->next) {
		handle->next = a;
	}

	if(a == handle->last) {
		handle->last = b;
	}
	else if(b == handle->last) {
		handle->last = a;
	}

	if(a == handle->root) {
		handle->root = b;
	}
	else if(b == handle->root) {
		handle->root = a;
	}
}


/**
 * Update the pointer to the last node in the complete binary tree.
 * Called internally after the root node has been deleted.
 */
static void __binheap_update_last(struct binheap_handle *handle)
{
	struct binheap_node *temp = handle->last;

	/* find a "bend" in the tree. */
	while(temp->parent && (temp == temp->parent->left)) {
		temp = temp->parent;
	}

	/* step over to sibling if we're not at root */
	if(temp->parent != NULL) {
		temp = temp->parent->left;
	}

	/* now travel right as far as possible. */
	while(temp->right != NULL) {
		temp = temp->right;
	}

	/* take one step to the left if we're not at the bottom-most level. */
	if(temp->left != NULL) {
		temp = temp->left;
	}

	//BUG_ON(!(temp->left == NULL && temp->right == NULL));

	handle->last = temp;
}

/**
 * Update the pointer to the node that will take the next inserted node.
 * Called internally after a node has been inserted.
 */
static void __binheap_update_next(struct binheap_handle *handle)
{
	struct binheap_node *temp = handle->next;

	/* find a "bend" in the tree. */
	while(temp->parent && (temp == temp->parent->right)) {
		temp = temp->parent;
	}

	/* step over to sibling if we're not at root */
	if(temp->parent != NULL) {
		temp = temp->parent->right;
	}

	/* now travel left as far as possible. */
	while(temp->left != NULL) {
		temp = temp->left;
	}

	handle->next = temp;
}



/* bubble node up towards root */
static void __binheap_bubble_up(
	struct binheap_handle *handle,
	struct binheap_node *node)
{
	//BUG_ON(!binheap_is_in_heap(node));
//	if(!binheap_is_in_heap(node))
//	{
//		dump_node_data2(handle, node);
//		BUG();
//	}

	while((node->parent != NULL) &&
		  ((node->data == BINHEAP_POISON) /* let BINHEAP_POISON data bubble to the top */ ||
		   handle->compare(node, node->parent))) {
			  __binheap_swap(node->parent, node);
			  node = node->parent;

//			  if(!binheap_is_in_heap(node))
//			  {
//				  dump_node_data2(handle, node);
//				  BUG();
//			  }
	}
}


/* bubble node down, swapping with min-child */
static void __binheap_bubble_down(struct binheap_handle *handle)
{
	struct binheap_node *node = handle->root;

	while(node->left != NULL) {
		if(node->right && handle->compare(node->right, node->left)) {
			if(handle->compare(node->right, node)) {
				__binheap_swap(node, node->right);
				node = node->right;
			}
			else {
				break;
			}
		}
		else {
			if(handle->compare(node->left, node)) {
				__binheap_swap(node, node->left);
				node = node->left;
			}
			else {
				break;
			}
		}
	}
}



void __binheap_add(struct binheap_node *new_node,
	struct binheap_handle *handle,
	void *data)
{
//	if(binheap_is_in_heap(new_node))
//	{
//		dump_node_data2(handle, new_node);
//		BUG();
//	}

	new_node->data = data;
	new_node->ref = new_node;
	new_node->ref_ptr = &(new_node->ref);

	if(!binheap_empty(handle)) {
		/* insert left side first */
		if(handle->next->left == NULL) {
			handle->next->left = new_node;
			new_node->parent = handle->next;
			new_node->left = NULL;
			new_node->right = NULL;

			handle->last = new_node;

			__binheap_bubble_up(handle, new_node);
		}
		else {
			/* left occupied. insert right. */
			handle->next->right = new_node;
			new_node->parent = handle->next;
			new_node->left = NULL;
			new_node->right = NULL;

			handle->last = new_node;

			__binheap_update_next(handle);
			__binheap_bubble_up(handle, new_node);
		}
	}
	else {
		/* first node in heap */

		new_node->parent = NULL;
		new_node->left = NULL;
		new_node->right = NULL;

		handle->root = new_node;
		handle->next = new_node;
		handle->last = new_node;
	}
}



/**
 * Removes the root node from the heap. The node is removed after coalescing
 * the binheap_node with its original data pointer at the root of the tree.
 *
 * The 'last' node in the tree is then swapped up to the root and bubbled
 * down.
 */
void __binheap_delete_root(struct binheap_handle *handle,
	struct binheap_node *container)
{
	struct binheap_node *root = handle->root;

//	if(!binheap_is_in_heap(container))
//	{
//		dump_node_data2(handle, container);
//		BUG();
//	}

	if(root != container) {
		/* coalesce */
		__binheap_swap_safe(handle, root, container);
		root = container;
	}

	if(handle->last != root) {
		/* swap 'last' node up to root and bubble it down. */

		struct binheap_node *to_move = handle->last;

		if(to_move->parent != root) {
			handle->next = to_move->parent;

			if(handle->next->right == to_move) {
				/* disconnect from parent */
				to_move->parent->right = NULL;
				handle->last = handle->next->left;
			}
			else {
				/* find new 'last' before we disconnect */
				__binheap_update_last(handle);

				/* disconnect from parent */
				to_move->parent->left = NULL;
			}
		}
		else {
			/* 'last' is direct child of root */

			handle->next = to_move;

			if(to_move == to_move->parent->right) {
				to_move->parent->right = NULL;
				handle->last = to_move->parent->left;
			}
			else {
				to_move->parent->left = NULL;
				handle->last = to_move;
			}
		}
		to_move->parent = NULL;

		/* reconnect as root.  We can't just swap data ptrs since root node
		 * may be freed after this function returns.
		 */
		to_move->left = root->left;
		to_move->right = root->right;
		if(to_move->left != NULL) {
			to_move->left->parent = to_move;
		}
		if(to_move->right != NULL) {
			to_move->right->parent = to_move;
		}

		handle->root = to_move;

		/* bubble down */
		__binheap_bubble_down(handle);
	}
	else {
		/* removing last node in tree */
		handle->root = NULL;
		handle->next = NULL;
		handle->last = NULL;
	}

	/* mark as removed */
	container->parent = BINHEAP_POISON;
}


/**
 * Delete an arbitrary node.  Bubble node to delete up to the root,
 * and then delete to root.
 */
void __binheap_delete(struct binheap_node *node_to_delete,
	struct binheap_handle *handle)
{
	struct binheap_node *target = node_to_delete->ref;
	void *temp_data = target->data;

//	if(!binheap_is_in_heap(node_to_delete))
//	{
//		dump_node_data2(handle, node_to_delete);
//		BUG();
//	}
//
//	if(!binheap_is_in_heap(target))
//	{
//		dump_node_data2(handle, target);
//		BUG();
//	}

	/* temporarily set data to null to allow node to bubble up to the top. */
	target->data = BINHEAP_POISON;

	__binheap_bubble_up(handle, target);
	__binheap_delete_root(handle, node_to_delete);

	node_to_delete->data = temp_data;  /* restore node data pointer */
	//node_to_delete->parent = BINHEAP_POISON; /* poison the node */
}

/**
 * Bubble up a node whose pointer has decreased in value.
 */
void __binheap_decrease(struct binheap_node *orig_node,
	struct binheap_handle *handle)
{
	struct binheap_node *target = orig_node->ref;

//	if(!binheap_is_in_heap(orig_node))
//	{
//		dump_node_data2(handle, orig_node);
//		BUG();
//	}
//
//	if(!binheap_is_in_heap(target))
//	{
//		dump_node_data2(handle, target);
//		BUG();
//	}
//
	__binheap_bubble_up(handle, target);
}