#include<bits/stdc++.h>
using namespace std;
typedef complex<double> P;
typedef vector<P> vec;

namespace std{
  bool operator < (const P &a,const P &b){
    return (a.imag()==b.imag()?
            a.real()<b.real():
            a.imag()<b.imag());
  }
};

double eps=1e-8;
double PI=acos(-1);

bool eq(double a,double b){return (b-a<eps&&a-b<eps);}
bool eq(P a,P b){return (abs(a-b)<eps);}

double dot(P a,P b){return real(b*conj(a));}
double cross(P a,P b){return imag(b*conj(a));}
P project(P a,P b,P c){b-=a;c-=a;return a+b*real(c/b);}
P reflect(P a,P b,P c){b-=a;c-=a;return a+conj(c/b)*b;}
int ccw(P a,P b,P c){
  b-=a,c-=a,a=c*conj(b);
  if(a.imag()>eps)return 1;//ccw
  if(a.imag()<-eps)return -1;//cw
  if(a.real()<-eps)return 2;//ob
  if(abs(b)+eps<abs(c))return -2;//of
  return 0;//os
}

//segment ab , point c
double dist(P a,P b,P c){
  if(dot(b-a,c-a)<0)return abs(c-a);
  if(dot(a-b,c-b)<0)return abs(c-b);
  return abs(cross(b-a,c-a))/abs(b-a);
}

bool isintersect(P a,P b,P c,P d){
  return ((ccw(a,b,c)*ccw(a,b,d)<=0)&&(ccw(c,d,a)*ccw(c,d,b)<=0));
}

//segment ab , segment cd
P intersect(P a,P b,P c,P d){
  a-=d;b-=d;c-=d;
  return d+a+(b-a)*imag(a/c)/imag(a/c-b/c);
}

double dist(P a,P b,P c,P d){
  if(isintersect(a,b,c,d))return 0;
  double ab=min(dist(a,b,c),dist(a,b,d));
  double cd=min(dist(c,d,a),dist(c,d,b));
  return min(ab,cd);
}

double calcArea(vec &t){
  double res=0;
  int n=t.size();
  for(int i=0;i<n;i++)res+=cross(t[i],t[(i+1==n?0:i+1)]);
  return abs(res/2.0);
}

double Arg(P a,P b,P c){
  b-=a,c-=a;
  return arg(c*conj(b));
}

int inPolygon(vec &t,P p){
  int n=t.size();
  double sum=0;  
  for(int i=0;i<n;i++){
    P a=t[i],b=t[(i+1==n?0:i+1)];
    if( ccw(a,b,p) == 0 )return 1;// on
    sum+= Arg(p,a,b);
  }
  if( abs(sum) < eps )return 0;//out
  else  return 2;//in
}

typedef pair<double,P> Pair;

vec andrewScan(vec &t){
  int N=t.size(),C=0;
  vec R(N);
  for(int i=0;i<N;i++){
    while(2<=C&&ccw(R[C-2],R[C-1],t[i])==-1)C--;
    R[C++]=t[i];
  }
  vec res(C);
  for(int i=0;i<C;i++)res[i]=R[i];
  return res;
}

vec ConvexHull2(vec t){
  sort(t.begin(),t.end());
  vec u=andrewScan(t);
  reverse(t.begin(),t.end());
  vec l=andrewScan(t);
  for(int i=1;i+1<(int)l.size();i++)u.push_back(l[i]);
  return u;
}

vec ConvexHull(vec t){
  vector<Pair> u;
  vec R;
  int N=t.size(),K=0,C=0;
  for(int i=1;i<N;i++)if(t[i]<t[K])K=i;
  for(int i=0;i<N;i++)u.push_back(Pair(arg(t[i]-t[K]),t[i]));
  R.push_back(t[K]),C++;
  sort(u.begin(),u.end());
  for(int i=0;i<N;i++){
    P p=u[i].second;
    if(2<=C&&ccw(R[C-2],R[C-1],p)==0)continue;
    if(2<=C&&ccw(R[C-2],R[C-1],p)==2)continue;
    while(2<=C&&ccw(R[C-2],R[C-1],p)!=1)R.pop_back(),C--;
    R.push_back(p),C++;    
  }
  return R;
}

int main(){
  int n;
  cin>>n;
  vec t;
  double x,y;
  for(int i=0;i<n;i++){
    cin>>x>>y;
    t.push_back(P(x,y));
  }
  vec ans=ConvexHull2(t);
  vec ans2=ConvexHull(t);
  assert( eq(calcArea(ans),calcArea(ans2) ) );
  int nn=ans2.size();
  for(int i=0;i<nn;i++){
    int j=(i+1)%nn,k=(j+1)%nn;
    
    assert(!eq(ans2[i],ans2[j]));
    assert(!eq(ans2[i],ans2[k]));
    if( eq( cross(ans2[i]-ans2[j],ans2[j]-ans2[k]) , 0 ) ){
      assert(0);
    }
  }
  
  cout<<ans.size()<<endl;
  for(int i=0;i<(int)ans.size();i++){
    cout<<(int)ans[i].real()<<' '<<(int)ans[i].imag()<<endl;
  }
  return 0;
}

Measured memory_bytes: 9888