/*-------------------------------------------------------------------------
 * 
 * 
 *                                AVLTREE 1.0
 *                       (c) Werner Van Belle aug 95
 *
 * 
 * NODE AddAvlNode(NODE root, NODE who)
 *   Voegt who toe aan root, geeft als resultaat de nieuwe root weer
 *   O(log2n)
 * 
 * NODE SearchAvlNode(NODE root, SEARCH_DATA watte)
 *   Zoekt watte op in root, geeft als resultaat de bijhorende node weer
 *   O(<log2n)
 * 
 * NODE DeleteAvlNode(NODE root, SEARCH_DATA wie)
 *   Verwijdert wie uit root, geeft als resultaat de nieuwe root weer
 *   O(log2n)
 * 
 * int ValidAvlTree(NODE root)
 *   Controleert of de AVL-tree wel aan de AVL-voorwaarde voldoet. Hier 
 *   wordt niet gecontroleerd of de linkerkinderen kleiner zijn dan root 
 *   en de rechterkinderen groter zijn dan root 
 *   O(n)
 * 
 * int AvlTreeCount(NODE root)
 *   geeft weer hoeveel elementen in de tree steken. 
 *   O(n)
 * 
 * int AvlDepth(NODE root)
 *   geeft de maximum diepte van een pad doorheen de tree weer. 
 *   O(n)
 *
 * void PrintAvlTree(NODE root, int diepte)
 *   Print de avltree. Deze functie is enkel aanwezig als PRINT_NODE
 *   gedefinieerd is. Het output formaat is 
 *     .rechterkind
 *     rootzelf
 *     .linkerkind
 */

#include <assert.h>
/* #define DEBUG_NODES */
static NODE RotateLeft(NODE root, int* depthchanged)
     {
	NODE sec;
	assert(!ISNULLNODE(root));
	assert(depthchanged);
	sec=RIGHT_NODE(root);
	assert(POSITIVE_FLAG(root));
	if (!(NEGATIVE_FLAG(sec)))
	  {
             #ifdef DEBUG_NODES
	     printf("single left rotation ");
	     PRINT_NODE(root);
	     printf("\n");
             #endif
	     SET_RIGHT_NODE(root,LEFT_NODE(sec));
	     SET_LEFT_NODE(sec,root);
	     if (POSITIVE_FLAG(sec))
	       {
		  SET_ZERO_FLAG(root);
		  SET_ZERO_FLAG(sec);
		  *depthchanged=1;
		  return sec;
	       }
	     SET_POSITIVE_FLAG(root);
	     SET_NEGATIVE_FLAG(sec);
	     return sec;
	  }
	  {
	     NODE third=LEFT_NODE(sec);
             #ifdef DEBUG_NODES
	     printf("double left rotation ");
	     PRINT_NODE(root);
	     printf("\n");
             #endif
	     SET_LEFT_NODE(sec,RIGHT_NODE(third));
	     SET_RIGHT_NODE(root,LEFT_NODE(third));
	     SET_RIGHT_NODE(third,sec);
	     SET_LEFT_NODE(third,root);
	     *depthchanged=1;
	     if (NEGATIVE_FLAG(third))
	       {
		  SET_POSITIVE_FLAG(sec);
		  SET_ZERO_FLAG(root);
		  SET_ZERO_FLAG(third);
		  return third;
	       }
	     if (ZERO_FLAG(third))
	       {
		  SET_ZERO_FLAG(sec);
		  SET_ZERO_FLAG(third);
		  SET_ZERO_FLAG(root);
		  return third;
	       }
	     SET_ZERO_FLAG(sec);
	     SET_NEGATIVE_FLAG(root);
	     SET_ZERO_FLAG(third);
	     return third;
	  }
     }

static NODE RotateRight(NODE root, int* depthchanged)
     {
	NODE sec;
	assert(!ISNULLNODE(root));
	assert(depthchanged);
	sec=LEFT_NODE(root);
	assert(NEGATIVE_FLAG(root));
	if (!(POSITIVE_FLAG(sec)))
	  {
             #ifdef DEBUG_NODES
	     printf("single right rotation ");
	     PRINT_NODE(root);
	     printf("\n");
             #endif
	     SET_LEFT_NODE(root,RIGHT_NODE(sec));
	     SET_RIGHT_NODE(sec,root);
	     if (NEGATIVE_FLAG(sec))
	       {
		  SET_ZERO_FLAG(root);
		  SET_ZERO_FLAG(sec);
		  *depthchanged=1;
		  return sec;
	       }
	     SET_NEGATIVE_FLAG(root);
	     SET_POSITIVE_FLAG(sec);
	     return sec;
	  }
        #ifdef DEBUG_NODES
	printf("double right rotation ");
	PRINT_NODE(root);
	printf("\n");
        #endif
	  {
	     NODE third=RIGHT_NODE(sec);
	     SET_RIGHT_NODE(sec,LEFT_NODE(third));
	     SET_LEFT_NODE(root,RIGHT_NODE(third));
	     SET_LEFT_NODE(third,sec);
	     SET_RIGHT_NODE(third,root);
	     *depthchanged=1;
	     if (NEGATIVE_FLAG(third))
	       {
		  SET_ZERO_FLAG(sec);
		  SET_POSITIVE_FLAG(root);
		  SET_ZERO_FLAG(third);
		  return third;
	       }
	     if (ZERO_FLAG(third))
	       {
		  SET_ZERO_FLAG(sec);
		  SET_ZERO_FLAG(third);
		  SET_ZERO_FLAG(root);
		  return third;
	       }
	     SET_NEGATIVE_FLAG(sec);
	     SET_ZERO_FLAG(third);
	     SET_ZERO_FLAG(root);
	     return third;
	  }
     }


static NODE AddAvlNode_aux(NODE root, NODE who, int* depthchanged)
/* geeft de nieuwe root weer */
{
  int CompareResult, heightchanged=0;
  assert(depthchanged);
#ifdef DEBUG_NODES
  printf("add ");
  PRINT_NODE(who);
  printf(" to root ");
  if (ISNULLNODE(root)) printf("(null)");
  else PRINT_NODE(root);
  printf("\n");
#endif
  if (ISNULLNODE(who)) return root;
	SET_LEFT_NODE(who,NULLNODE);
	SET_RIGHT_NODE(who,NULLNODE);
	SET_ZERO_FLAG(who);
	if (ISNULLNODE(root)) 
	  {
	     *depthchanged=1;
	     return who;
	  }
	CompareResult=COMPARE_ADD_DATA(root,who);
	/* 1. geen dubbels */
	assert(CompareResult);
	/* 2. hang links aan */
	if (CompareResult==1)
	  {
	     SET_LEFT_NODE(root,AddAvlNode_aux(LEFT_NODE(root),who,&heightchanged));
	     if (heightchanged) 
	       {
		  if (ZERO_FLAG(root))
		    {
		       SET_NEGATIVE_FLAG(root);
		       *depthchanged=1;
		       return root;
		    }
		  if (POSITIVE_FLAG(root))
		    {
		       SET_ZERO_FLAG(root);
		       return root;
		    }
		  heightchanged=0;
		  root=RotateRight(root,&heightchanged); 
		  /* als heightchanged verandert is; is root kleiner 
		   * geworden */
		  if (!heightchanged) *depthchanged=1;
		  return root;
	       }
	     return root;
	  }
	/* 3. hang rechts aan */
	SET_RIGHT_NODE(root,AddAvlNode_aux(RIGHT_NODE(root),who,&heightchanged));
	if (heightchanged) 
	  {
	     if (ZERO_FLAG(root))
	       {
		  SET_POSITIVE_FLAG(root);
		  *depthchanged=1;
		  return root;
	       }
	     if (NEGATIVE_FLAG(root))
	       {
		  SET_ZERO_FLAG(root);
		  return root;
	       }
	     heightchanged=0;
	     root=RotateLeft(root,&heightchanged);
	     if (!heightchanged) *depthchanged=1;
	  }
	return root;
     }

NODE AddAvlNode(NODE root, NODE who)
/* geeft de nieuwe root weer */
{
  int depthchanged=0;
  return AddAvlNode_aux(root,who,&depthchanged);
}

NODE SearchAvlNode(NODE root, SEARCH_DATA watte)
/* geeft het resultaat van het zoeken weer, NULLNODE indien niet gevonden */
     {
	int CompareResult;
	while(root)
	  {
	     CompareResult=COMPARE_SEARCH_DATA(root,watte);
	     if (CompareResult==0) return root;
	     if (CompareResult==1) root=LEFT_NODE(root);
	     else root=RIGHT_NODE(root);
	  }
	return NULLNODE;
     }

static NODE LeftFreedom_aux(NODE cur,NODE *placetostorefreedom,int*heightchanged)
     {
	assert(!ISNULLNODE(cur));
	assert(placetostorefreedom);
	if (ISNULLNODE(RIGHT_NODE(cur)))
	  {
	     if (ISNULLNODE(LEFT_NODE(cur)))
	       {
		  *placetostorefreedom=cur;
		  *heightchanged=1;
		  return NULLNODE;
	       }
	     *heightchanged=1;
	     *placetostorefreedom=cur;
	     return LEFT_NODE(cur);
	  }
	  {
	     int takverkleind=0;
	     SET_RIGHT_NODE(cur,LeftFreedom_aux(RIGHT_NODE(cur),placetostorefreedom,&takverkleind));
	     if (takverkleind) 
	       {
		  if (NEGATIVE_FLAG(cur)) return RotateRight(cur,heightchanged);
		  if (POSITIVE_FLAG(cur))
		    {
		       SET_ZERO_FLAG(cur);
		       *heightchanged=1;
		       return cur;
		    }
		  SET_NEGATIVE_FLAG(cur);
		  return cur;
	       }
	     return cur;
	  }
     }

static NODE GetLeftFreedom(NODE root, NODE*placetostorefreedom,int *heightchanged)
     {
	NODE left;
	assert(!ISNULLNODE(root));
	assert(!ISNULLNODE(LEFT_NODE(root)));
	assert(heightchanged);
	assert(placetostorefreedom);
	left=LEFT_NODE(root);
	if (!ISNULLNODE(RIGHT_NODE(left)))
	  return LeftFreedom_aux(left,placetostorefreedom,heightchanged);
	*heightchanged=1;
	*placetostorefreedom=left;
	return LEFT_NODE(left);
     }

static NODE RightFreedom_aux(NODE cur,NODE *placetostorefreedom,int*heightchanged)
     {
	assert(!ISNULLNODE(cur));
	assert(placetostorefreedom);
	if (ISNULLNODE(LEFT_NODE(cur)))
	  {
	     if (ISNULLNODE(RIGHT_NODE(cur)))
	       {
		  *placetostorefreedom=cur;
		  *heightchanged=1;
		  return NULLNODE;
	       }
	     *heightchanged=1;
	     *placetostorefreedom=cur;
	     return RIGHT_NODE(cur);
	  }
	  {
	     int takverkleind=0;
	     SET_LEFT_NODE(cur,RightFreedom_aux(LEFT_NODE(cur),placetostorefreedom,&takverkleind));
	     if (takverkleind) 
	       {
		  if (POSITIVE_FLAG(cur)) return RotateLeft(cur,heightchanged);
		  if (NEGATIVE_FLAG(cur))
		    {
		       SET_ZERO_FLAG(cur);
		       *heightchanged=1;
		       return cur;
		    }
		  SET_POSITIVE_FLAG(cur);
		  return cur;
	       }
	     return cur;
	  }
     }

static NODE GetRightFreedom(NODE root, NODE*placetostorefreedom,int *heightchanged)
     {
	NODE right;
	assert(!ISNULLNODE(root));
	assert(!ISNULLNODE(RIGHT_NODE(root)));
	assert(heightchanged);
	assert(placetostorefreedom);
	right=RIGHT_NODE(root);
	if (!ISNULLNODE(LEFT_NODE(right))) 
	  return RightFreedom_aux(right,placetostorefreedom,heightchanged);
	*heightchanged=1;
	*placetostorefreedom=right;
	return RIGHT_NODE(right);
     }

static NODE DeleteAvlNode_naardelete(NODE root, SEARCH_DATA wie, int* heightchanged)
     {
	int CompareResult,takkleiner=0;
	assert(heightchanged);
	if (ISNULLNODE(root)) return root;
	CompareResult=COMPARE_SEARCH_DATA(root,wie);
	switch (CompareResult)
	  {
	   case -1: /* root<wie */
	     SET_RIGHT_NODE(root,DeleteAvlNode_naardelete(RIGHT_NODE(root),wie,&takkleiner));
	     if (takkleiner)
	       {
		  if (POSITIVE_FLAG(root))
		    {
		       SET_ZERO_FLAG(root);
		       *heightchanged=1;
		       return root;
		    }
		  if (ZERO_FLAG(root))
		    {
		       SET_NEGATIVE_FLAG(root);
		       return root;
		    }
		  return RotateRight(root,heightchanged);
	       }
	     return root;
	   case 1: /* root>wie */
	     SET_LEFT_NODE(root,DeleteAvlNode_naardelete(LEFT_NODE(root),wie,&takkleiner));
	     if (takkleiner)
	       {
		  if (NEGATIVE_FLAG(root))
		    {
		       SET_ZERO_FLAG(root);
		       *heightchanged=1;
		       return root;
		    }
		  if (ZERO_FLAG(root))
		    {
		       SET_POSITIVE_FLAG(root);
		       return root;
		    }
		  return RotateLeft(root,heightchanged);
	       }
	     return root;
	   case 0: /* root==wie */
	     /* nu een opnderscheid maken voor welke kinderen de 
	      * root heeft */
	     if (ISNULLNODE(LEFT_NODE(root)) && ISNULLNODE(RIGHT_NODE(root)))
	       {
		  DELETE_NODE(root);
		  *heightchanged=1;
		  return NULLNODE;
	       }
	     if (ISNULLNODE(LEFT_NODE(root)))
	       {
		  NODE rechter;
		  rechter=RIGHT_NODE(root);
		  assert(POSITIVE_FLAG(root));
		  assert(!ISNULLNODE(rechter));
		  assert(ISNULLNODE(LEFT_NODE(rechter)));
		  assert(ISNULLNODE(RIGHT_NODE(rechter)));
		  *heightchanged=1;
		  DELETE_NODE(root);
		  return rechter;
	       }
	     if (ISNULLNODE(RIGHT_NODE(root)))
	       {
		  NODE linker;
		  linker=LEFT_NODE(root);
		  assert(NEGATIVE_FLAG(root));
		  assert(!ISNULLNODE(linker));
		  assert(ISNULLNODE(LEFT_NODE(linker)));
		  assert(ISNULLNODE(RIGHT_NODE(linker)));
		  DELETE_NODE(root);
		  *heightchanged=1;
		  return linker;
	       }
	     if (POSITIVE_FLAG(root))
	       {
		  NODE freedom;
		  SET_RIGHT_NODE(root,GetRightFreedom(root,&freedom,heightchanged));
		  SET_LEFT_NODE(freedom,LEFT_NODE(root));
		  SET_RIGHT_NODE(freedom,RIGHT_NODE(root));
		  if (*heightchanged) SET_ZERO_FLAG(freedom);
		  else SET_POSITIVE_FLAG(freedom);
		  DELETE_NODE(root);
		  return freedom;
	       }
	     if (NEGATIVE_FLAG(root))
	       {
		  NODE freedom;
		  SET_LEFT_NODE(root,GetLeftFreedom(root,&freedom,heightchanged));
		  SET_LEFT_NODE(freedom,LEFT_NODE(root));
		  SET_RIGHT_NODE(freedom,RIGHT_NODE(root));
		  if (*heightchanged) SET_ZERO_FLAG(freedom);
		  else SET_NEGATIVE_FLAG(freedom);
		  DELETE_NODE(root);
		  return freedom;
	       }
	     if (ZERO_FLAG(root))
	       {
		  NODE freedom;
		  int takverkleind=0;
		  SET_LEFT_NODE(root,GetLeftFreedom(root,&freedom,&takverkleind));
		  SET_LEFT_NODE(freedom,LEFT_NODE(root));
		  SET_RIGHT_NODE(freedom,RIGHT_NODE(root));
		  if (takverkleind) SET_POSITIVE_FLAG(freedom);
		  else  SET_ZERO_FLAG(freedom);
		  DELETE_NODE(root);
		  return freedom;
	       }
	   default: exit(57);
	  }
     }

NODE DeleteAvlNode(NODE root, SEARCH_DATA wie)
     {
	int heightchanged=0;
	return DeleteAvlNode_naardelete(root,wie,&heightchanged);
     }

int AvlDepth(NODE root)
     {
	int l,r;
	if (ISNULLNODE(root)) return 0;
	l=AvlDepth(LEFT_NODE(root));
	r=AvlDepth(RIGHT_NODE(root));
	if (l>r) return l+1;
	return r+1;
     }

int ValidAvlTree(NODE root)
     {
	if (ISNULLNODE(root)) return 1;
	if (!ValidAvlTree(LEFT_NODE(root))) return 0;
	if (!ValidAvlTree(RIGHT_NODE(root))) return 0;
	if (POSITIVE_FLAG(root))
	  return (AvlDepth(LEFT_NODE(root))==(AvlDepth(RIGHT_NODE(root))-1));
	if (NEGATIVE_FLAG(root))
	  return (AvlDepth(LEFT_NODE(root))==(AvlDepth(RIGHT_NODE(root))+1));
	return (AvlDepth(LEFT_NODE(root))==AvlDepth(RIGHT_NODE(root)));
     }

int AvlTreeCount(NODE root)
     {
	if (ISNULLNODE(root)) return 0;
	return 1+AvlTreeCount(LEFT_NODE(root))+AvlTreeCount(RIGHT_NODE(root));
     }

#ifdef PRINT_NODE
void PrintAvlTree(NODE root, int diepte)
     {
	int t,l,r;
	if (ISNULLNODE(root)) return;
	PrintAvlTree(RIGHT_NODE(root),diepte+1);
	for(t=diepte;t>0;t--) printf(".");
	PRINT_NODE(root);
	l=AvlDepth(LEFT_NODE(root));
	r=AvlDepth(RIGHT_NODE(root));
	printf(" left=%d, right=%d, count=%d ",l,r,AvlTreeCount(root));
	if ((abs(l-r)>1) || 
	    (l<r && !POSITIVE_FLAG(root)) ||
	    (l>r && !NEGATIVE_FLAG(root)) ||
	    (l==r && !ZERO_FLAG(root)))
	  printf("*********");
	printf("\n");
	PrintAvlTree(LEFT_NODE(root),diepte+1);
     }
#endif
