/*
 * SquareLists Data Structure
 *
 * $Id: squarelists.c,v 1.7 2003/09/27 13:06:52 fcs Exp $
 *
 * $Log: squarelists.c,v $
 * Revision 1.7  2003/09/27 13:06:52  fcs
 * Version 1.1.3
 * - SqList_deleteNode is now allowed to delete the head node
 * 	without causing segmentation faults.
 * - prog2.c updated to test for this condition.
 *
 * Revision 1.6  2003/09/16 00:28:16  fcs
 * Version 1.1.1 -- fix the head with no data bug in external routines
 *
 * Revision 1.5  2003/09/15 19:13:35  fcs
 * Update to version 1.1.0
 * SqList_printList takes a FILE pointer so you can control where to print
 * the list.
 *
 * Revision 1.4  2003/05/22 15:21:25  fcs
 * Version 1.0.1
 * 	- prefix all routines with SqList_
 * 	- Add SqList_getNext_r routine
 *
 * Revision 1.3  2003/05/22 00:09:46  fcs
 * Add get* routines
 *
 * Revision 1.2  2003/05/21 20:04:13  fcs
 * Fix bugs in shiftLeft and shiftRight
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <squarelists.h>

#if DEBUG
static SquareList *myHEAD = NULL;
#endif

/*
 * SqList_newNode(void *value): allocate memory for a new node and return it
 *
 * success: returns the address of the new node
 * failure: returns NULL
 *
 */
static SquareList *
SqList_newNode(void *value)
{
	SquareList *n;
	n = (SquareList *)malloc(sizeof(SquareList));
	if ( n != NULL ) {
		n->left = n->right = n->up = n->down = NULL;
		n->vertNodes = n->Nodes = n->maxSize = 0;
		n->cmp = NULL;
		n->data = value;
	}
	return n;
}

/*
 * SqList_findInVertList: find the node in the vertical list
 *
 * top: top node of vertical list to search
 * toAdd: value to search for
 *
 * success: returns the pointer to the correct node
 * failure: returns NULL
 */
static SquareList *
SqList_findInVertList(SquareList *top, SquareList *toAdd)
{
	SquareList *p;
	int cmp_rc;

	p = top;
	while(p != NULL && (cmp_rc=p->cmp(p->data, toAdd->data)) != 0) {
		p = p->down;
	}
	if (p != NULL && cmp_rc == 0) {
		return p;
	} else {
		return NULL;
	}
}

/*
 * SqList_findVertList: find the vertical list to put the value into
 *
 * head: head of structure
 * toAdd: value to search for
 *
 * success: returns the pointer to the correct top node
 * failure: returns NULL
 */
static SquareList *
SqList_findVertList(SquareList **head, SquareList *toAdd)
{
	SquareList *p;

	p = *head;
	while (p->right != *head && p->cmp(p->right->data, toAdd->data) != -1) {
		p = p->right;
	}
	return p;
}

/*
 * SqList_putInVertList: Insert the node into the correct location
 * 		in the vertical list
 *
 * head: the head of the square list structure
 * temp: the top node of the vertical list to insert in
 * toAdd: the node to be added
 *
 * success: return 0
 * failure: return -1
 */
static int
SqList_putInVertList(SquareList **head,SquareList *temp,SquareList *toAdd)
{
	SquareList *p;

	p=temp;
	if (p->cmp(p->data, toAdd->data) == -1) {
		/* Is this a new head node? */
		if ( p == *head ) {
			toAdd->Nodes = p->Nodes;
			toAdd->maxSize = p->maxSize;
			*head = toAdd;
			p->Nodes = p->maxSize = 0;
		}
		/* General top node stuff */
		if (p->left == p) {
			/* Only top node in list */
			toAdd->left = toAdd->right = toAdd;
		} else {
			toAdd->left = p->left;
			toAdd->right = p->right;
			toAdd->left->right = toAdd; 
			toAdd->right->left = toAdd;
		}
		/* Handle the vertical node portion of top node */
		toAdd->up = p->up;	/* point to bottom node */
		toAdd->down = p;	/* p is now below toAdd */
		toAdd->vertNodes = p->vertNodes;

		/* Now update p */
		p->up = toAdd;
		p->left = p->right = NULL;
		p->vertNodes = 0;

		/* Now point at top node */
		p = toAdd;
	} else {
		while (p != NULL && p->cmp(p->data, toAdd->data) >= 0) {
			p = p->down;
		}
		if (p == NULL) {
			/* Oops... fell off the bottom */
			/* hang it off the bottom */
			p = temp->up;		/* Bottom node */
			p->down = toAdd;	/* toAdd is now bottom */
			toAdd->up = p;		/* point back up to list */
			temp->up = toAdd;	/* new bottom node */
		} else {
			/* Insert in the middle (above current node) */
			toAdd->up = p->up;
			toAdd->down = p;
			toAdd->up->down = toAdd;
			p->up = toAdd;
		}

		/* Now point at top node */
		p = temp;
	}

	/* p points at the top node here */
	p->vertNodes++; /* Increase the number of nodes */

	/* Ok.. we're done */
	return 0;
}

/*
 * SqList_shiftLeft:
 * 	Shift the top node to the right to the bottom of this vertical list
 *
 * head: head node of structure
 * n: top node of vertical list to shift bottom node onto
 */
static void
SqList_shiftLeft(SquareList **head, SquareList *n)
{
	SquareList *p, *t;

	n->vertNodes++;
	p = n->right;			/* new bottom node */
	if (p->vertNodes == 1) {	/* Simple case... just move it */
		n->right = p->right;	/* remove p from top nodes */
		p->right->left = n;	/* make sure everyone knows of it */
	} else {			/* Hard case... shift and make a new top node */
		t = p->down;		/* new right top node */
		/*
		 * make the new top node
		 */
		t->right = p->right;
		p->right->left = t;
		t->left = n;
		t->vertNodes = p->vertNodes - 1;
		t->up = p->up;
		n->right = t;
	}

	/*
	 * make p the new bottom node for n's vertical list
	 */
	p->up = n->up;
	p->left = p->right = p->down = NULL;
	p->vertNodes = 0;
	n->up = p;
	p->up->down = p;
	return;
}

/*
 * SqList_shiftRight: 
 * 	Shift the bottom node of this list to the right.
 *
 * head: head node of structure
 * n: top node of vertical list to shift bottom node from
 */
static void
SqList_shiftRight(SquareList **head, SquareList *n)
{
	SquareList *p;

	if (n->vertNodes > 1) {
		p = n->up;		/* p is now the bottom node */

		/* Make the node above p the bottom node */
		p->up->down = NULL;
		n->up = p->up;
		n->vertNodes--;

		/* Make p a top node to the right of n */
		if (n->right == *head) {
			/* node is the right most top node */
			p->left = n;
			p->right = *head;
			(*head)->left = p;
			n->right = p;
			p->vertNodes = 1;
			p->up = p;
			p->down = NULL;
		} else {
			/* Replacing a top node */
			p->right = n->right->right;
			p->right->left = p;
			p->left = n;
			p->up = n->right->up;
			p->down = n->right;
			n->right = p;
			p->vertNodes = p->down->vertNodes + 1;

			p->down->up = p;
			p = p->down;
			p->left = NULL;
			p->right = NULL;
			p->vertNodes = 0;
		}
	}
	return;
}


/*
 * SqList_max:
 *   return the larger of two integers
 */
static int
SqList_max(int a, int b)
{
	return a > b ? a : b;
}

/*
 * SqList_updateMaxDepth: 
 * 	update the head->maxSize value and run through the list
 * 	making sure none of the vertical lists are too long
 *
 * head: the head node
 */
static void
SqList_updateMaxDepth(SquareList **head)
{
	SquareList *p;

	/* Calculate the new maxSize */
	(*head)->maxSize = SqList_max((int)sqrt((*head)->Nodes),1);

#if DEBUG
	printf("updateMaxDepth: Here's the SquareList...\n\n");
	SqList_printList(head, stdout, NULL);
	printf("****************************************\n\n");
#endif

	p = *head;
	while (p->vertNodes > p->maxSize) {
		SqList_shiftRight(head,p);
	} 
	while (p->vertNodes < p->maxSize && p->right != (*head)) {
		SqList_shiftLeft(head,p);
	}
	p = p->right;
	while (p != *head) {
		while (p->vertNodes > (*head)->maxSize) {
			SqList_shiftRight(head,p);
		} 
		while (p->vertNodes < (*head)->maxSize && p->right != (*head)) {
			SqList_shiftLeft(head,p);
		}
		p = p->right;
	}
}

/*
 * SqList_printNode: prints the entire structure
 *
 * node: The node to print
 * fp: the FILE * to print to
 * printData: A routine to print the data value
 */
static void
SqList_printNode(SquareList *node, FILE *fp, void (*printData)())
{
#if DEBUG
	if (node == myHEAD) {
		printf("Head Node: %08x\n", node);
	} else if (node->vertNodes > 0) {
		printf("\nTop Node: %08x\n", node);
	} else {
		printf("Node: %08x\n", node);
	}
	printf("     Nodes: %d\n", node->Nodes);
	printf(" vertNodes: %d\n", node->vertNodes);
	printf("   maxSize: %d\n", node->maxSize);
	printf("  left: %08x\n right: %08x\n", node->left, node->right);
	printf("    up: %08x\n  down: %08x\n", node->up, node->down);
	printf("  data: %08x\n", node->data);
#endif
	if (printData != NULL) {
		printData(fp, node->data);
	}
}

/*
 * SqList_printVertList: prints the entire structure
 *
 * top: The top node of this vertical list
 * fp: the FILE * to print to
 * printData: A routine to print the data value
 */
static void
SqList_printVertList(SquareList *top, FILE *fp, void (*printData)())
{
	SquareList *p;

	for(p = top; p != NULL; p = p->down) {
		SqList_printNode(p, fp, printData);
	}
	return;
}

/*
 * SqList_freeVertList:
 *	free the nodes in the vertical list
 *
 * top: pointer to top node of vertical list
 * freeData: routine to free data pointed to
 */
static void
SqList_freeVertList(SquareList *top, void (*freeData)())
{
	SquareList *p, *t;

	/* Second, free the top nodes */
	for(p = top->up; p != top; /* blank on purpose */) {
		freeData(p->data);
		t = p->up;
		free(p);
		p = t;
	}

	top->down = NULL;	/* Make sure we don't go crazy */
	top->up = top;
	top->vertNodes = 1;
	return;
}

/*
 * SqList_makeHead: create the head node (empty)
 *
 * success: returns the address of the head node.
 * failure: returns NULL
 *
 */
SquareList *
SqList_makeHead( int (*compare)() )
{
	SquareList *p;

	p = SqList_newNode(NULL);
	if ( p != NULL ) {
		p->cmp = compare;
		p->left = p;
		p->right = p;
		p->up = p;
		p->maxSize = 1; /* Arbitrary starting point */
	}
	return p;
}

/*
 * SqList_insertNode: insert the data into the structure
 *
 * head: the pointer to the structure head
 * value: the data element to insert
 *
 * success: returns 0
 * failure: returns -1
 */
int
SqList_insertNode(SquareList **head, void *value)
{
	SquareList *toAdd, *temp;

	if ( *head == NULL ) {
		/* Basic failure... must have a head */
		return -1;
	} else {
		(*head)->Nodes++;
		if ((*head)->Nodes == 1) {
			(*head)->data = value;
			(*head)->vertNodes++;
		} else {
			toAdd = SqList_newNode(value);
			if (toAdd != NULL) {
				toAdd->cmp = (*head)->cmp;
				temp = SqList_findVertList(head,toAdd);
				SqList_putInVertList(head,temp,toAdd);
			} else {
				return -1;
			}
		}
		/* Run through the list and square it up */
		SqList_updateMaxDepth(head);
	}
	return 0;
}

/*
 * SqList_deleteNode: delete the data from the structure
 *
 * head: the pointer to the structure head
 * value: the data element to delete
 *
 * success: returns the data structure from the deleted node
 * failure: returns NULL
 */
void *
SqList_deleteNode(SquareList **head, void *value)
{
	SquareList *top, *toDel, *temp;
	void *ret = NULL;

	if (*head == NULL) {	/* safety blanket */
		return NULL;
	}

	if ( (*head)->Nodes == 0 ) {
		return NULL;	/* Nothing to search... */
	}

	temp = SqList_newNode(value);
	if (temp != NULL) {
		top = SqList_findVertList(head,temp);
		toDel = SqList_findInVertList(top,temp);
		free(temp);
		if (toDel != NULL) {
			ret = toDel->data;	/* save the value to return */
			(*head)->Nodes--;	/* one less node in structure */
			top->vertNodes--;	/* one less node in vert list */
			if (toDel == top) {	/* it's the top node (head??) */
				if (top->vertNodes == 0) { /* No body nodes */
					top->left->right = top->right;
					top->right->left = top->left;
					/* Head node? */
					if (toDel == *head) {
						if (top->Nodes > 0) {
							top->right->Nodes = toDel->Nodes;
							top->right->maxSize = toDel->maxSize;
							*head = top->right;
						} else {
							/* Nothing left */
							*head = NULL;
						}
					}
				} else { /* We have one or more body nodes */
					top->left->right = top->down;
					top->right->left = top->down;
					top->down->up = top->up;
					top->down->left = top->left;
					top->down->right = top->right;
					top->down->vertNodes = top->vertNodes;
					/* Head node? */
					if (toDel == *head) {
						top->down->Nodes = toDel->Nodes;
						top->down->maxSize = toDel->maxSize;
						*head = top->down;
					}
				}
			} else if (toDel->down == NULL) { /* Bottom node */
				top->up = toDel->up;
				toDel->up->down = NULL;
			} else {	/* middle node */
				toDel->up->down = toDel->down;
				toDel->down->up = toDel->up;
			}
			free(toDel);		/* free node */
			if (*head) {
				/* balance structure, if any left */
			       	SqList_updateMaxDepth(head);
			}
		}
	}
	return ret;
}

/*
 * SqList_findNode:
 * 	Find a node in the structure and return the data to caller
 *
 * head: head pointer of square list structure
 * value: data to find
 *
 * success: pointer to data returned
 * failure: NULL pointer returned
 */
void *
SqList_findNode(SquareList **head, void *value)
{
	SquareList *top, *temp, *node;

	if (*head == NULL) {	/* safety blanket */
		return NULL;
	}

	if ( (*head)->Nodes == 0 ) {
		return NULL;	/* Nothing to search... */
	}

	temp = SqList_newNode(value);
	if (temp != NULL) {
		top = SqList_findVertList(head,temp);
		node = SqList_findInVertList(top,temp);
		free(temp);
		if (node != NULL) {
			return node->data;
		}
	}
	return (void *)NULL;
}


/*
 * SqList_freeList:
 *	walk the entire list and free it
 *
 * head: head pointer of structure
 * freeData: routine to free data pointed to
 */
void
SqList_freeList(SquareList **head, void (*freeData)())
{
	SquareList *p, *t;

	if (*head) {
		/* First, walk the top nodes, freeing the body nodes */
		p = *head;
		do {
			SqList_freeVertList(p, freeData);
			p = p->right;
		} while (p != *head);

		/* Second, free the top nodes */
		p = *head;
		do {
			freeData(p->data);
			t = p->right;
			free(p);
			p = t;
		} while (p != *head);

		*head = NULL;	/* Make sure we don't go crazy */
	}
	return;
}


/*
 * SqList_printList: prints the entire structure
 *
 * head: The head of the structure
 * fp: the FILE * to print to
 * printData: A routine to print the data value
 */
void
SqList_printList(SquareList **head, FILE *fp, void (*printData)())
{
	SquareList *p;

	if (*head == NULL) {	/* Safety blanket */
		return;
	}

	if ( (*head)->Nodes == 0 ) {
		return;		/* Nothing to print... */
	}

#if DEBUG
	myHEAD = *head;
#endif
	p = *head;
	SqList_printVertList(p, fp, printData);
	p = p->right;
	while (p != *head) {
		SqList_printVertList(p, fp, printData);
		p = p->right;
	}
	return;
}

/*
 * SqList_DataNodePtr: Used to remember what node we were pointing at so
 * SqList_getNext can be used to move to the next one.
 *
 * It is initialized by SqList_getMin and SqList_getMax.
 */
static SquareList * SqList_DataNodePtr = NULL;

/*
 * SqList_getMin: returns the smallest data element
 *
 * head: the head of the structure
 */
void *
SqList_getMin(SquareList **head)
{
	if ( *head == NULL ) {
		return NULL;	/* dummy!  no nodes: can't give min */
	}
	if ( (*head)->Nodes == 0 ) {
		return NULL;	/* dummy!  no nodes: can't give min */
	}
	SqList_DataNodePtr = (*head);
	return (*head)->data;
}

/*
 * SqList_getMax: returns the largest data element
 *
 * head: the head of the structure
 */
void *
SqList_getMax(SquareList **head)
{
	if ( *head == NULL ) {
		return NULL;	/* dummy!  no nodes: can't give max */
	}
	if ( (*head)->Nodes == 0 ) {
		return NULL;	/* dummy!  no nodes: can't give max */
	}
	SqList_DataNodePtr = (*head)->left->up;
	return (*head)->left->up->data;
}

/*
 * SqList_getNext: returns the next data element
 *
 * head: the head of the structure
 *
 * returns: pointer to next data element or NULL if
 * 	it has fallen off the end of the SquareList structure.
 *
 * WARNING: THIS IS NOT THREADSAFE IT USES STATIC MEMORY
 *
 * Suggested use is "SqList_getMin" followed by getNext until NULL is returned.
 */
void *
SqList_getNext(SquareList **head)
{
	SquareList *p;

#if DEBUG
	printf("SqList_getNext: @Entry - SqList_DataNodePtr (%08X)\n",
			SqList_DataNodePtr);
#endif
	if (*head == NULL) {
		return NULL;	/* Dummy!  No nodes: Can't give you squat */
	}
	if ( (*head)->Nodes == 0 ) {
		return NULL;	/* Dummy!  No nodes: Can't give you squat */
	}
	if (SqList_DataNodePtr == NULL) {
		/* Well, they violated the rules, but we'll cheat */
		SqList_DataNodePtr = *head;
		return (*head)->data;
	}
	if (SqList_DataNodePtr->down != NULL) {
		/* Great, down the list we go */
		SqList_DataNodePtr = SqList_DataNodePtr->down;
		return SqList_DataNodePtr->data;
	}
	/* Drat! end of a vertical list! */
	if (SqList_DataNodePtr == (*head)->left->up) {
		/* Max Node ... end of list */
		SqList_DataNodePtr = NULL; /* cheat: next call will give min */
		return (void *)NULL;
	}
	/* Ok, find myself so I can return the next topnode */
	p = *head;
	while (p->up != SqList_DataNodePtr && p->right != *head) {
		p = p->right;
	}
	if (p->up != SqList_DataNodePtr) {
		/* This cannot be... unless they did something stupid */
#if DEBUG
		fprintf(stderr, "SqList_DataNodePtr (%08X) NOT IN STRUCTURE!\n",
				SqList_DataNodePtr);
#endif
		SqList_DataNodePtr = NULL;
		return NULL;	/* fake it... */
	}
	SqList_DataNodePtr = p->right; /* move to next vertical list */
	return SqList_DataNodePtr->data;
}

/*
 * SqList_getNext_r: returns the next data element
 *
 * head: the head of the structure
 * remember_where: pointer to keep track of where we are
 *
 * returns: pointer to next data element or NULL if
 * 	it has fallen off the end of the SquareList structure.
 *
 */
void *
SqList_getNext_r(SquareList **head, SquareList **remember_where)
{
	SquareList *p;

#if DEBUG
	printf("SqList_getNext_r: @Entry - remember_where (%08X)\n",
			*remember_where);
#endif
	if (*head == NULL) {
		*remember_where = NULL;
		return NULL;	/* Dummy!  No nodes: Can't give you squat */
	}
	if ( (*head)->Nodes == 0 ) {
		*remember_where = NULL;
		return NULL;	/* Dummy!  No nodes: Can't give you squat */
	}
	if (*remember_where == NULL) {
		/* Well, they violated the rules, but we'll cheat */
		*remember_where = *head;
		return (*head)->data;
	}
	if ((*remember_where)->down != NULL) {
		/* Great, down the list we go */
		(*remember_where) = (*remember_where)->down;
		return (*remember_where)->data;
	}
	/* Drat! end of a vertical list! */
	if ((*remember_where) == (*head)->left->up) {
		/* Max Node ... end of list */
		(*remember_where) = NULL; /* cheat: next call will give min */
		return (void *)NULL;
	}
	/* Ok, find myself so I can return the next topnode */
	p = *head;
	while (p->up != (*remember_where) && p->right != *head) {
		p = p->right;
	}
	if (p->up != (*remember_where)) {
		/* This cannot be... unless they did something stupid */
#if DEBUG
		fprintf(stderr, "remember_where (%08X) NOT IN STRUCTURE!\n",
				(*remember_where));
#endif
		(*remember_where) = NULL;
		return NULL;	/* fake it... */
	}
	(*remember_where) = p->right; /* move to next vertical list */
	return (*remember_where)->data;
}

