#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
#include<set>
#include<map>
#define re register
#define ll long long
using namespace std;
inline int gi(){
	int f=1,sum=0;char ch=getchar();
	while(ch>'9'|| ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return sum*f;
}
const double eps=1e-10,Pi=acos(-1.0);
struct node{
	double x,y;
	node operator+(node b){return (node){x+b.x,y+b.y};}
	node operator-(node b){return (node){x-b.x,y-b.y};}
	node operator*(double b){return (node){x*b,y*b};}
	double len(){return sqrt(x*x+y*y);}
};
int fh(double a){return fabs(a)<eps?0:a>eps?1:-1;}
double cross(node a,node b){return a.x*b.y-a.y*b.x;}
double dot(node a,node b){return a.x*b.x+a.y*b.y;}
node projection(node p1,node p2,node p){node v=p2-p1;return p1+v*(dot(v,p-p1)/dot(v,v));}
node reflection(node p1,node p2,node p){node P=projection(p1,p2,p);P=P*2-p;return P;}
void Counter_Clockwise(node p1,node p2,node p){
	if(cross(p2-p1,p-p1)>eps){puts("COUNTER_CLOCKWISE");return;}
	if(cross(p2-p1,p-p1)<-eps){puts("CLOCKWISE");return;}
	if(dot(p2-p1,p-p1)<-eps){puts("ONLINE_BACK");return;}
	if(dot(p2-p1,p2-p1)>=dot(p-p1,p2-p1)){puts("ON_SEGMENT");return;}
	puts("ONLINE_FRONT");
}
bool insegment(node A,node B,node p){
	if(fh(cross(B-A,p-A)))return false;
	return ((fh(p.x-A.x)>=0 && fh(B.x-p.x)>=0) || (fh(p.x-A.x)<=0 && fh(B.x-p.x)<=0)) && ((fh(p.y-A.y)>=0 && fh(B.y-p.y)>=0) || (fh(p.y-A.y)<=0 && fh(B.y-p.y)<=0));
}
int parallel(node A1,node A2,node B1,node B2){
	if(!fh(cross(B2-B1,A2-A1)))return 2;
	if(!fh(dot(B2-B1,A2-A1)))return 1;
	return 0;
}
bool intersection(node A1,node A2,node B1,node B2){
	if(!fh(cross(B2-B1,A2-A1))){
		if(insegment(A1,A2,B1) || insegment(A1,A2,B2) || insegment(B1,B2,A1) || insegment(B1,B2,A2))return true;
		return false;
	}
	return fh(cross(A1-B1,B2-B1)*cross(A2-B1,B2-B1))<=0 && (fh(cross(B1-A1,A2-A1)*cross(B2-A1,A2-A1))<=0);
}
node getintersection(node A1,node A2,node B1,node B2){
	node v=A2-A1;
	return A1+v*(cross(B2-B1,B1-A1)/cross(B2-B1,A2-A1));
}
double distance(node A1,node A2,node B1,node B2){
	if(intersection(A1,A2,B1,B2))return 0.0;
	node p;double ans=1e18;
	ans=min(ans,(A1-B1).len());ans=min(ans,(A1-B2).len());ans=min(ans,(A2-B1).len());ans=min(ans,(A2-B2).len());
	p=projection(A1,A2,B1);
	if(insegment(A1,A2,p))ans=min(ans,(B1-p).len());
	p=projection(A1,A2,B2);
	if(insegment(A1,A2,p))ans=min(ans,(B2-p).len());
	p=projection(B1,B2,A1);
	if(insegment(B1,B2,p))ans=min(ans,(A1-p).len());
	p=projection(B1,B2,A2);
	if(insegment(B1,B2,p))ans=min(ans,(A2-p).len());
	return ans;
}
double area(node *p,int n){
	double area=0;
	for(int i=1;i<n;i++)area+=cross(p[i]-p[1],p[i+1]-p[1]);
	area+=cross(p[n]-p[1],p[1]-p[1]);
	return area*0.5;
}
bool is_convex(node *p,int n){
	for(int i=2;i<n;i++)if(cross(p[i]-p[i-1],p[i+1]-p[i-1])<0)return false;
	if(cross(p[n]-p[n-1],p[1]-p[n-1])<0)return false;
	if(cross(p[1]-p[n],p[2]-p[n])<0)return false;
	return true;
}
int inConvex(node O,node *p,int n){
	int cnt=0;
	for(int i=0;i<n;i++){
		if(insegment(p[i],p[(i+1)%n],O))return 1;
		node A=p[i]-O,B=p[(i+1)%n]-O;
		if(A.y>B.y)swap(A,B);
		if(A.y<eps && B.y>eps && cross(A,B)>eps)cnt++;
	}
	return cnt&1?2:0;
}
bool cmp(node a,node b){return a.y<b.y || (!fh(a.y-b.y) && a.x<b.x);}
node c[100010],p[100010];int n;
void Convex(){
	int top=0;
	sort(p+1,p+n+1,cmp);
	for(int i=1;i<=n;i++){
		while(top>1 && cross(c[top]-c[top-1],p[i]-c[top-1])<-eps)top--;
		c[++top]=p[i];
	}
	int tmp=top;
	for(int i=n-1;i>=1;i--){
		while(top>tmp && cross(c[top]-c[top-1],p[i]-c[top-1])<-eps)top--;
		c[++top]=p[i];
	}
	printf("%d\n",--top);
	for(int i=1;i<=top;i++)printf("%d %d\n",(int)c[i].x,(int)c[i].y);
}
int main(){
	n=gi();
	for(int i=1;i<=n;i++)p[i].x=gi(),p[i].y=gi();
	Convex();
	return 0;
}



Measured memory_bytes: 5692