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

#define MAX_COMMAND_NUM 2000000000

#define PLUS_DEPTH     1
#define MINUS_DEPTH  (-1)

typedef struct ST_TREE_POINT TreePoint_t;

struct ST_TREE_POINT{
	int key;				/* key value */
	int depth;				/* Depth */
	TreePoint_t *top;		/* Top   Point */
	TreePoint_t *left;		/* Left  Point */
	TreePoint_t *right;		/* Right Point */
}AVL_TREE;

static void InsertPorocess(TreePoint_t **ptTreePoint, int key);
static void InsertLeftProcess(TreePoint_t **ptTreePoint, int key, int depth);
static void InsertRightProcess(TreePoint_t **ptTreePoint, int key, int depth);
static void ExtractPorocess(TreePoint_t **ptTreePoint);
static void CompareTreeDepth(TreePoint_t **ptTreePoint, int depth_l, int depth_r);
static void SetTreePoint(TreePoint_t **Target, TreePoint_t **ptSetData);
static void ChangeDepth(TreePoint_t *ptTreePoint, int value);
static int  SearchMaxDepth(TreePoint_t *ptTreePoint);
static void AllFreeProcess(TreePoint_t **ptTreePoint);

int main(void)
{
	int key;							/* insert key */
	char command[8];					/* command name */
	TreePoint_t *ptTreeTemp;			/* Tree Temp Data */
	TreePoint_t *ptRootPoint;			/* Tree Root Point */
	int loop = 0;
	
	/* Init Root Tree */
	ptRootPoint = NULL;
	
	/* get Name & time */
	while(((scanf("%s %d", command, &key)) != EOF) && loop <= MAX_COMMAND_NUM)
	{
		/* set Root point */
		if(ptRootPoint != NULL)
		{
			while(( ptRootPoint->top != NULL ) || ( ptRootPoint->depth != 0 ))
			{
				ptRootPoint = ptRootPoint->top;
			}
		}
		
		if( strncmp(command, "end", 3) == 0 ) /* End Process */
		{
			break;
		}
		else if( strncmp(command, "insert", 6) == 0 ) /* Insert Process */
		{
			InsertPorocess( &ptRootPoint, key );
		}
		else if( strncmp(command, "extract", 7) == 0 ) /* Extract Process */
		{
			ExtractPorocess( &ptRootPoint );
		}
		else
		{
			/* error input */
		}
		loop++;
	}
	
	/* all free process */
	AllFreeProcess(&ptRootPoint);
	
    return 0;
}

static void InsertPorocess(TreePoint_t **ptTreePoint, int key)
{
	TreePoint_t *ptInsertData;			/* Insert Data */
	
	if(*ptTreePoint == NULL)
	{
		ptInsertData = (TreePoint_t *)malloc(sizeof(TreePoint_t));
		ptInsertData->key     = key;
		ptInsertData->depth   = 0;
		ptInsertData->top     = NULL;
		ptInsertData->left    = NULL;
		ptInsertData->right   = NULL;

		*ptTreePoint = ptInsertData;
	}
	else if(*ptTreePoint != NULL)
	{
		if((*ptTreePoint)->key > key)
		{
			InsertLeftProcess(ptTreePoint, key, 1);
		}
		else
		{
			InsertRightProcess(ptTreePoint, key, 1);
		}
	}
	
}

static void InsertLeftProcess(TreePoint_t **ptTreePoint, int key, int depth)
{
	TreePoint_t *ptInsertData;			/* Insert Data */
	
	if((*ptTreePoint)->left == NULL)
	{
		ptInsertData = (TreePoint_t *)malloc(sizeof(TreePoint_t));
		ptInsertData->key     = key;
		ptInsertData->depth   = depth;
		ptInsertData->top     = *ptTreePoint;
		ptInsertData->left    = NULL;
		ptInsertData->right   = NULL;
		
		(*ptTreePoint)->left = ptInsertData;
		
		/* if MAX depth change */
		if((*ptTreePoint)->right == NULL && depth > 1)
		{
			CompareTreeDepth(&((*ptTreePoint)->top), depth, 0);
		}
	}
	else
	{
		(*ptTreePoint) = (*ptTreePoint)->left;
		
		if((*ptTreePoint)->key > key)
		{
			InsertLeftProcess(ptTreePoint, key, (depth + 1));
		}
		else
		{
			InsertRightProcess(ptTreePoint, key, (depth + 1));
		}
	}
}

static void InsertRightProcess(TreePoint_t **ptTreePoint, int key, int depth)
{
	TreePoint_t *ptInsertData;			/* Insert Data */
	
	if((*ptTreePoint)->right == NULL)
	{
		ptInsertData = (TreePoint_t *)malloc(sizeof(TreePoint_t));
		ptInsertData->key     = key;
		ptInsertData->depth   = depth;
		ptInsertData->top     = *ptTreePoint;
		ptInsertData->left    = NULL;
		ptInsertData->right   = NULL;

		(*ptTreePoint)->right = ptInsertData;
		
		/* if MAX depth change */
		if((*ptTreePoint)->left == NULL && depth > 1)
		{
			CompareTreeDepth(&((*ptTreePoint)->top), 0, depth);
		}
	}
	else
	{
		(*ptTreePoint) = (*ptTreePoint)->right;
		
		if((*ptTreePoint)->key > key)
		{
			InsertLeftProcess(ptTreePoint, key, (depth + 1));
		}
		else
		{
			InsertRightProcess(ptTreePoint, key, (depth + 1));
		}
	}
}

static void ExtractPorocess(TreePoint_t **ptTreePoint)
{
	TreePoint_t * ptTempTree;
	
	if((*ptTreePoint) != NULL)
	{
		/* serch max key */
		while((*ptTreePoint)->right != NULL)
		{
			(*ptTreePoint) = (*ptTreePoint)->right;
		}
		
		printf("%d\n", (*ptTreePoint)->key);
		
		if((*ptTreePoint)->left != NULL)		/* delete point left side exist */
		{
			ptTempTree = (*ptTreePoint)->left;
			if((*ptTreePoint)->top != NULL)		/* Not Root Point */
			{
				/* deleate right side */
				(*ptTreePoint) = (*ptTreePoint)->top;
				free((*ptTreePoint)->right);
				(*ptTreePoint)->right = ptTempTree;
				ptTempTree->top = *ptTreePoint;
				ChangeDepth((ptTempTree),  MINUS_DEPTH);
				CompareTreeDepth(ptTreePoint, 0, ptTempTree->depth);
			}
			else
			{
				/* set left side to  Root */
				free((*ptTreePoint));
				*ptTreePoint = ptTempTree;
				(*ptTreePoint)->top = NULL;
				ChangeDepth((ptTempTree),  MINUS_DEPTH);
			}
		}
		else
		{
			if((*ptTreePoint)->top != NULL)		/* Not Root Point */
			{
				(*ptTreePoint) = (*ptTreePoint)->top;
				free((*ptTreePoint)->right);
				(*ptTreePoint)->right = NULL;
				if((*ptTreePoint)->left != NULL)
				{
					CompareTreeDepth(ptTreePoint, 0, (*ptTreePoint)->depth);
				}
			}
			else 	/* deleate Root Point */
			{
				free(*ptTreePoint);
				*ptTreePoint = NULL;
			}
		}
	}
}

static void CompareTreeDepth(TreePoint_t **ptTreePoint, int depth_l, int depth_r)
{
	TreePoint_t * ptOldTopTree;
	TreePoint_t * ptNewTopTree;
	TreePoint_t * ptTempTree;
	
	if((*ptTreePoint) != NULL)
	{
		if(depth_l == 0)
		{
			if((*ptTreePoint)->left != NULL)
			{
				/* serch max left depth */
				depth_l = SearchMaxDepth((*ptTreePoint)->left);
			}
			else
			{
				depth_l = (*ptTreePoint)->depth;
			}
		}
		if(depth_r == 0)
		{
			if((*ptTreePoint)->right != NULL)
			{
				/* serch max right depth */
				depth_r = SearchMaxDepth(*ptTreePoint);
			}
			else
			{
				depth_r = (*ptTreePoint)->depth;
			}
		}
		
		/* equilibrium tree */
		if(depth_r - depth_l >= 2)
		{
			/* right is new Top tree */
			ptNewTopTree = (*ptTreePoint)->right;
			if(ptNewTopTree->right != NULL)
			{
				ptOldTopTree = (*ptTreePoint);
				
				/* change Tree top */
				ptTempTree = ptNewTopTree->left;
				ptNewTopTree->top = ptOldTopTree->top;
				ptNewTopTree->left = ptOldTopTree;
				(ptNewTopTree->depth)--;
				ChangeDepth(ptNewTopTree->right,  MINUS_DEPTH);
				
				/* change Tree Old Top */
				ptOldTopTree = ptNewTopTree->left; /* need change point by segmentation */
				ptOldTopTree->top = ptNewTopTree;
				ptOldTopTree->right = ptTempTree;
				(ptOldTopTree->depth)++;
				ChangeDepth(ptOldTopTree->left,  PLUS_DEPTH);
			}
			else
			{
				/* change Tree New Top */
				ptNewTopTree = ptNewTopTree->left;
				ptOldTopTree = (*ptTreePoint)->right;
				ptTempTree = ptNewTopTree->right;
				ptNewTopTree->top  = ptOldTopTree->top;
				ptNewTopTree->right = ptOldTopTree;
				(ptNewTopTree->depth)--;
				
				/* change Tree Old Top */
				ptOldTopTree = ptNewTopTree->right; /* need change point by segmentation */
				ptOldTopTree->top = ptNewTopTree;
				ptOldTopTree->left = ptTempTree;
				(ptOldTopTree->depth)++;
				
				CompareTreeDepth(&(ptNewTopTree->top), 0, 0);
			}
		}
		else if(depth_l - depth_r >= 2)
		{
			/* left is new Top tree */
			ptNewTopTree = (*ptTreePoint)->left;
			if(ptNewTopTree->left != NULL)
			{
				/* change Tree New Top */
				ptOldTopTree = (*ptTreePoint);
				ptTempTree = ptNewTopTree->right;
				ptNewTopTree->top = ptOldTopTree->top;
				ptNewTopTree->right = ptOldTopTree;
				(ptNewTopTree->depth)--;
				ChangeDepth(ptNewTopTree->left,  MINUS_DEPTH);
				
				/* change Tree Old Top */
				ptOldTopTree = ptNewTopTree->right; /* need change point by segmentation */
				ptOldTopTree->top = ptNewTopTree;
				ptOldTopTree->left = ptTempTree;
				(ptOldTopTree->depth)++;
				ChangeDepth(ptOldTopTree->right,  PLUS_DEPTH);
			}
			else
			{
				/* change Tree New Top */
				ptNewTopTree = ptNewTopTree->right;
				ptOldTopTree = (*ptTreePoint)->left;
				ptTempTree = ptNewTopTree->left;
				ptNewTopTree->top  = ptOldTopTree->top;
				ptNewTopTree->left = ptOldTopTree;
				(ptNewTopTree->depth)--;
				
				/* change Tree Old Top */
				ptOldTopTree = ptNewTopTree->left; /* need change point by segmentation */
				ptOldTopTree->top = ptNewTopTree;
				ptOldTopTree->right = ptTempTree;
				(ptOldTopTree->depth)++;
				
				CompareTreeDepth(&(ptNewTopTree->top), 0, 0);
			}
		}
	}
}

static void SetTreePoint(TreePoint_t **Target, TreePoint_t **ptSetData)
{
	TreePoint_t *swap;
	
	swap = *Target;
	*Target = *ptSetData;
	*ptSetData = swap;
}

static void ChangeDepth(TreePoint_t *ptTreePoint, int value)
{
	if(ptTreePoint != NULL)
	{
		ptTreePoint->depth += value;
		
		if(ptTreePoint->left != NULL)
		{
			ChangeDepth(ptTreePoint->left, value);
		}
		if(ptTreePoint->right != NULL)
		{
			ChangeDepth(ptTreePoint->right, value);
		}
	}
}

static int SearchMaxDepth(TreePoint_t *ptTreePoint)
{
	int depth_l;
	int depth_r;
	int max_depth;
	
	/* search left side */
	if(ptTreePoint->left != NULL)
	{
		depth_l = SearchMaxDepth(ptTreePoint->left);
	}
	else
	{
		depth_l = ptTreePoint->depth;
	}
	
	/* search right side */
	if(ptTreePoint->right != NULL)
	{
		depth_r = SearchMaxDepth(ptTreePoint->right);
	}
	else
	{
		depth_r = ptTreePoint->depth;
	}
	
	/* compare left and right depth */
	if(depth_l < depth_r)
	{
		max_depth = depth_r;
	}
	else
	{
		max_depth = depth_l;
	}
	
	return max_depth;
}

static void AllFreeProcess(TreePoint_t **ptTreePoint)
{
	if((*ptTreePoint)->left != NULL)
	{
		AllFreeProcess(&((*ptTreePoint)->left));
	}
	if((*ptTreePoint)->right != NULL)
	{
		AllFreeProcess(&((*ptTreePoint)->right));
	}
	
	free(*ptTreePoint);
}


