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

typedef struct Node_ Node;
struct Node_ {
    int data;
    Node* next;
};

// Link list structure
typedef struct LList_ {
    Node* head;
} LList;

typedef struct Stack{
	LList* top;
}stack;


typedef struct Queue{
	LList* front;
	LList* rear;
}queue;


Node* node_new(int data)
{
	Node *new;
    new = (Node*)malloc(sizeof(Node));
	new->data=data;
	new->next= NULL;
	return new;
}

LList* llist_new()
{
	LList *list;
    list = (LList*)malloc(sizeof(LList));
	list->head= NULL;
	return list;
}

int llist_size( LList* lst )
{
	int length=0;
	Node* current;
   	current = (Node*)malloc(sizeof(Node));	//// redundant malloc due to next line.
	current=lst->head;			//// check lst is non-null.
	while(current!=NULL)			//// this pattern is used elsewhere -- move it to a function / macro.
	{
		length++;
		current=current->next;
	}
	free(current);				//// current is null!
	return length;
}

void llist_print( LList* lst )
{
	Node* current;
	current=(Node*)malloc(sizeof(Node));	//// redundant malloc.
	current=lst->head;			//// check lst.
	while(current!=NULL)			//// common pattern.
	{
		printf("%d ", current->data);
		current=current->next;
	}
	printf("\n");
	free(current);				//// current is null.
	return;
}

int llist_get( LList* lst, int idx )
{
	if(idx > llist_size(lst))	return -1;	//// this special return value needs to be documented about the function.
	int i=0;
	Node* current;
   	current = (Node*)malloc(sizeof(Node));		////
	current=lst->head;				////
	while(i!=idx)					//// what if idx is not present!
	{
		i++;
		current=current->next;
	}
	return (current->data);				//// current could, in general, be null.
}

void llist_append( LList* lst, int data )
{
	Node* current;
	Node* new;					//// avoid "new" as variable name.
   	current = (Node*)malloc(sizeof(Node));		////
	current = lst->head;				////
	new = node_new(data);
	if(current==NULL)
	{
		lst->head=new;
		return;					//// return value may indicate which of the two execution paths were taken.
	}
	while(current->next!=NULL)			//// pattern.
	{
		current=current->next;
	}
	current->next=new;
	return;     
}

void llist_prepend( LList* lst, int data )
{
	Node* current;
	Node* new;
    current = (Node*)malloc(sizeof(Node));		////
	current = lst->head;				////
	new = node_new(data);
	new->next=current;
	lst->head=new;					////
	return;
}

void llist_insert( LList* lst, int idx, int data )
{
   	if(idx > llist_size(lst))	return;		//// note that llist_size itself involves list traversal.
   	idx = idx+1;
   	int i;
   	Node* current;
   	current = lst->head;				////
   	Node* new = node_new(data);			////
   	if(idx == 1)					//// this special case can be avoided.
   	{
       new->next = (lst->head);   
       lst->head = new;
       return ;
   	}   
   	for(i=1; i<idx-1; i++)				////
   	{
		current = current->next;
   	}
    new->next = current->next;
    current->next = new;
   	return;   
}

void llist_remove_last( LList* lst )
{
	Node* current;
	Node* previous;
	current=lst->head;				////
	if(current->next==NULL)	return;			//// check if current is null. Is this correct code? lst->head should change when the list contained a single element.
	while((current->next)!=NULL)
	{
		previous=current;
		current=current->next;
	}
	previous->next=NULL;				//// overall, this code should not work in end-cases.
	return;	         
}

void llist_remove_first( LList* lst )
{
	if(lst->head==NULL)	return;
	lst->head=(lst->head)->next;			//// generating garbage. original node is not freed.
	return; 					//// a good design practice is to return the list reference / pointer, this way one can apply repeated functions on the same list. e.g., (lst->remove_first())->append().
}

void llist_remove( LList* lst, int idx )
{
	Node* current;
	Node* previous;
	int i;
	current=lst->head;				////
	if(idx==0)
	{
		llist_remove_first( lst );
		return;
	}
	for(i=0;i<idx;i++)				//// check for current also, unless the invariant is maintained.
	{
		previous=current;
		current=current->next;	
	}
	if((current->next)==NULL)			//// check if current is null.
	{
		llist_remove_last( lst );
		return;
	}
	previous->next=current->next;			//// check if previous is null.

	return;
}


// create a new queue
queue* queue_new()
{
	queue* Q = (queue*)malloc(sizeof(queue));
	Q->front = llist_new();
	Q->rear = llist_new();
	Q->rear->head = Q->front->head;
	return Q;
}

// add an element to the queue
void enqueue(queue* Q, int data)
{
	llist_append( Q->front, data);
	Q->rear->head = Q->front->head;
	while( Q->rear->head->next != NULL)			//// pattern. check for nullity.
	{
		Q->rear->head = Q->rear->head->next;
	}
	return ;
}

// remove the front element from the queue
int dequeue(queue* Q)
{
	int temp = Q->front->head->data;			//// use a better term than temp.
	llist_remove_first(Q->front);
	return temp;
}

// Check if queue is empty
bool queue_is_empty(queue* Q)
{
	if(Q->front->head == NULL)	return 1;		//// return Q->front->head == NULL;
	return 0;
}

// find the size of the queue
int queue_size(queue* Q)
{
	int size;
	size = llist_size( Q->front );
	return size;						//// return llist_size(Q->front);
}

// print queue element
void queue_print(queue* Q)
{
	llist_print( Q->front );
	return;							//// return the number of elements printed.
}


stack* stack_new()
{
	stack* S = (stack*)malloc(sizeof(stack));
	S->top = llist_new();
	return S;
}

// push an element on the stack
void stack_push(stack* S, int data)
{
	llist_prepend( S->top, data);
	return;
}

// pop the top element from the stack
int stack_pop(stack* S)
{
	int pop = S->top->head->data;				//// avoid pop as a variable. Use element or popped or ...
	llist_remove_first( S->top );
	return pop;
}

// Check if stack is empty
bool stack_is_empty(stack* S)
{
	if ( stack_size(S) == 0)	return 1;		////
	return 0;
}

// find the size of the stack
int stack_size(stack* S)
{
	int size;
	size = llist_size( S->top );
	return size;						////
}

// print stack element
void stack_print(stack* S)
{
	llist_print( S->top );
	return;
}

int main()
{
	int n, i, j = 0;					//// using empty lines in the code is good for digestive subsystem.
	scanf("%d",&n);
	stack** arr = (stack**)malloc(n*sizeof(stack*));
	stack* Sd = stack_new(); 
	queue* Q = queue_new();
	getchar();						//// if you want to eat up the newline, use fflush.
	int sar[n];
	for ( i = 0 ; i < n ; i++ )				//// use spacing consistently: see line 138.
	{
		arr[i] = stack_new();				//// split this code into functions. A reasonable practice is to move every loop into a small function.
		char line[500];			
		fgets ( line, 500, stdin );
		int len = strlen(line);
		if(i!=(n-1)){line[len-1]='\0';}
		char str[10];
		for ( j = 0 ; (line[j]!='\0') && (j<len) ; j++)
		{
			int k = 0;
			str[k] = line[j];
			j++; k++;				//// some commenting would help understand the logic.
			for( ; ((line[j] !=' ') && (line[j] !='\0') && (j < (len)) ) ; j++, k++)
			{
				str[k] = line[j];
			}
			//printf("%s ",str);
			str[k] = '\0';
								//// mention the invariant / assertion on str here.	
			stack_push(Sd, atoi(str));
		}
		while(!stack_is_empty(Sd))
		{
			stack_push(arr[i], stack_pop(Sd));
		}
		if ( i != 0 )
		{	
			Node* cur = arr[0]->top->head;
			while(cur!=NULL)
			{
				if(cur->data != arr[i]->top->head->data)	break;
				stack_pop(arr[i]);
				cur = cur->next;
			}		
		}
	}
	for ( i = 1 ; i < n ; i++ )
	{
		sar[i-1] = stack_size(arr[i]);
	}
	int l,m;						//// use more meaningful names.
	for ( l = 1 ; l < n ; l++ )
	{
		for ( m = l ; m < n ; m++ )
		{
			if ( stack_size(arr[l]) > stack_size(arr[m]) )
			{
				stack* S = arr[l];
				arr[l] = arr[m];
				arr[m] = S;			//// do you nead a swap macro / function?
			}
		}
	}
	for ( i = 0 ; i < n ; i++ )
	{
		while(!stack_is_empty(arr[i]))
		{
			printf("%d ",stack_pop(arr[i]));
		}
	}
	return 0;
}

