#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <functional>
#include <vector>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <tuple>
#include <cassert>
#include <exception>
#include <iomanip>
// #include "./競プロ用テンプレート/Geometry.h"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> P;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<string> vs;
typedef vector<P> vp;
#define rep(i,a,n) for(ll i = (a);i < (n);i++)
#define per(i,a,n) for(ll i = (a);i > (n);i--)
#define lep(i,a,n) for(ll i = (a);i <= (n);i++)
#define pel(i,a,n) for(ll i = (a);i >= (n);i--)
#define clr(a,b) memset((a),(b),sizeof(a))
#define pb push_back
#define mp make_pair
#define all(c) (c).begin(),(c).end()
#define sz size()
#define print(X) cout << (X) << endl
static const int INF = 1e+9+7;
ll n,m,l;
string s,t;
int d[200010],dp[1010][1010];
double w[1000],v[1000];
double box[200010];

// #define EPS (1e-10)
// #define equals(a,b) (fabs(a,b) < EPS)

// class Point{
// public:
//   double x,y;

//   Point(double x = 0,double y = 0): x(x),y(y) {}

//   Point operator + (const Point &p){ return Point(x+p.x , y+p.y); }
//   Point operator - (const Point &p){ return Point(x-p.x , y-p.y); }
//   Point operator * (double a){ return Point(x * a , y * a); }
//   Point operator / (double a){ return Point(x / a , y / a); }

//   double abs(){ return sqrt(norm()); }
//   double norm(){ return x * x + y * y; }

//   bool operator < (const Point &p) const{ return (x != p.x) ? x < p.x : y < p.y; }
//   bool operator == (const Point &p) const{ return (fabs(x - p.x) < EPS) && (fabs(y - p.y) < EPS); }
//   friend istream& operator >> (istream &is,Point &p){ return (is >> p.x >> p.y); }
//   friend ostream& operator << (ostream &os,Point &p){ return (os << p.x << " " << p.y); }
// };

// class Circle{
// public:
//   Point c;
//   double r;
//   Circle(Point c = Point(),double r = 0.0): c(c),r(r){}
//   friend istream& operator >> (istream &is,Circle c){return (is >> c.c >> c.r);}
//   friend ostream& operator << (ostream &os,Circle c){return (os << c.c << " " << c.r);}
// };

// typedef Point Vector;

// double norm(Point p){
//   return p.x * p.x + p.y * p.y;
// }

// double abs(Point p){
//   return sqrt(norm(p));
// }

// using namespace Geometry;

namespace Geometry{
  #define equals(a,b) (fabs((a) - (b)) < EPS)
  #define BOTTOM 0
  #define LEFT 1
  #define RIGHT 2
  #define TOP 3
  #define gprint(X) cout << fixed << setprecision(0) << (X) << endl
  #define gprints(X,Y) cout << fixed << setprecision(0) << (X) << " " << (Y) << endl
  const double EPS = 1e-10;
  static const int COUNTER_CLOCKWISE = 1;
  static const int CLOCKWISE = -1;
  static const int ONLINE_BACK = 2;
  static const int ONLINE_FRONT = -2;
  static const int ON_SEGMENT = 0;
  
  class Point{
  public:
    double x,y;
    Point(double x = 0,double y = 0): x(x),y(y){}
    Point operator + (Point p){ return Point(x + p.x , y + p.y); }
    Point operator - (Point p){ return Point(x - p.x , y - p.y); }
    Point operator += (Point p){ return Point(x += p.x,y += p.y); }
    Point operator -= (Point p){ return Point(x -= p.x,y -= p.y); }
    Point operator * (double a){ return Point(a * x , a * y); }
    Point operator / (double a){ return Point(x / a , y / a); }
    bool operator < (const Point &p) const{ return x != p.x ? x < p.x : y < p.y; }
    bool operator == (const Point &p) const{ return equals(x,p.x) && equals(y,p.y); }

    double abs(){ return sqrt(norm()); }
    double norm(){ return x * x + y * y; }
    friend ostream& operator << (ostream &os,Point &p){ return os << p.x << " " << p.y; }
    friend istream& operator >> (istream &is,Point &p){ return is >> p.x >> p.y; }
  };

  struct Segment{
    Point p1,p2;
    Segment(){};
    Segment(Point p1,Point p2): p1(p1) , p2(p2){};
    friend ostream& operator << (ostream &os,Segment &seg){ return os << seg.p1 << " " << seg.p2; }
    friend istream& operator >> (istream &is,Segment &seg){ return is >> seg.p1 >> seg.p2; }
  };

  class Circle{
  public:
    Point c;
    double r;
    Circle(Point c = Point(),double r = 0.0): c(c),r(r){}
    friend ostream& operator << (ostream &os,Circle &c){ return os << c.c << " " << c.r; }
    friend istream& operator >> (istream &is,Circle &c){ return is >> c.c >> c.r; }
  };

  typedef Point Vector;
  typedef Segment Line;
  typedef vector<Point> Polygon;

  double norm(Vector a){ return  a.x * a.x + a.y * a.y; }
  double abs(Vector a){ return sqrt(norm(a)); }
  double dot(Vector a,Vector b){ return a.x * b.x + a.y * b.y; }
  double cross(Vector a,Vector b){ return a.x * b.y - a.y * b.x; }
  bool Orthogonal(Vector a,Vector b){ return equals(dot(a,b),0.0); }
  bool Orthogonal(Point a1,Point a2,Point b1,Point b2){ return Orthogonal(a1 - a2,b1 - b2); }
  bool Orthogonal(Segment s1,Segment s2){ return equals(dot(s1.p2 - s1.p1,s2.p2 - s2.p1) , 0.0); }
  bool Parallel(Vector a,Vector b){ return equals(cross(a,b),0.0); }
  bool Parallel(Point a1,Point a2,Point b1,Point b2){ return Parallel(a1 - a2 , b1 - b2); }
  bool Parallel(Segment s1,Segment s2){ return equals(cross(s1.p2 - s1.p1 , s2.p2 - s2.p1),0.0); }
  bool less(const Point &a,const Point &b){ return (a.x != b.x) ? a.x < b.x : a.y < b.y; }
  bool greater(const Point &a,const Point b){ return !less(a,b); }

  Point project(Segment s,Point p){
    Vector base = s.p2 - s.p1;
    double r = dot(p - s.p1 , base) / norm(base);
    return s.p1 + base * r;
  }

  Point reflect(Segment s,Point p){
    return p + (project(s,p) - p) * 2.0;
  }

  int ccw(Point &p0,Point p1,Point p2){
    p1 -= p0;
    p2 -= p0;
    if(cross(p1,p2) > EPS)return COUNTER_CLOCKWISE;
    if(cross(p1,p2) < -EPS)return CLOCKWISE;
    if(dot(p1,p2) < -EPS)return ONLINE_BACK;
    if(p1.norm() < p2.norm())return ONLINE_FRONT;
    return ON_SEGMENT;
  }

  bool intersect(Point p1,Point p2,Point p3,Point p4){
    return (ccw(p1,p2,p3) * ccw(p1,p2,p4) <= 0 && 
            ccw(p3,p4,p1) * ccw(p3,p4,p2) <= 0);
  }
  bool intersect(Segment s1,Segment s2){ return intersect(s1.p1 , s1.p2 , s2.p1 , s2.p2); }

  double getDistance(Vector a,Vector b){ return abs(a - b); }
  double getDistanceLP(Line l,Point p){ return abs(cross(l.p2 - l.p1 , p - l.p1) / abs(l.p2 - l.p1)); }
  double getDistanceSP(Segment s,Point p){
    if(dot(s.p2 - s.p1,p - s.p1) < 0.0) return abs(p - s.p1);
    if(dot(s.p1 - s.p2,p - s.p2) < 0.0) return abs(p - s.p2);
    return getDistanceLP(s,p);
  }
  double getDistance(Segment s1,Segment s2){
    if(intersect(s1,s2))return 0.0;
    return min(min(getDistanceSP(s1,s2.p1),getDistanceSP(s1,s2.p2)),
               min(getDistanceSP(s2,s1.p1),getDistanceSP(s2,s1.p2)));
  }

  Point getCrossPoint(Segment s1,Segment s2){
    Vector base = s2.p2 - s2.p1;
    double d1 = abs(cross(base,s1.p1 - s2.p1));
    double d2 = abs(cross(base,s1.p2 - s2.p1));
    double t = d1 / (d1 + d2);
    return s1.p1 + (s1.p2 - s1.p1) * t;
  }

  pair<Point,Point> getCrossPoints(Circle c,Line l){
    Vector pr = project(l,c.c);
    Vector e = (l.p2 - l.p1) / abs(l.p2 - l.p1);
    double base = sqrt(c.r * c.r - norm(pr - c.c));
    return make_pair(pr + e * base , pr - e * base);
  }

  double arg(Vector p){ return atan2(p.y,p.x); }
  Vector polar(double a,double r){ return Point(cos(r) * a, sin(r) * a); }

  pair<Point,Point> getCrossPoints(Circle c1,Circle c2){
    double d = abs(c1.c - c2.c);
    double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    double t = arg(c2.c - c1.c);
    return make_pair(c1.c + polar(c1.r,t + a),c1.c + polar(c1.r,t - a));
  }
  
  int contains(Polygon g,Point p){
    int n = g.size();
    bool x = false;
    for(int i = 0;i < n;i++){
      Point a = g[i] - p, b = g[(i + 1) % n] - p;
      if(abs(cross(a,b)) < EPS && dot(a,b) < EPS)return 1;
      if(a.y > b.y)swap(a,b);
      if(a.y < EPS && EPS < b.y && cross(a,b) > EPS)x = !x;
    }
    return (x ? 2 : 0);
  }

  //=====================================================//
  //==================== Andrew Scan ====================//
  //=====================================================//

  double Add(double a,double b){
    if(fabs(a+b)<EPS*(fabs(a)+fabs(b)))return 0.0;
    return a+b;
  }

  double Across(Vector a,Vector b){
    return Add(a.x*b.y,-a.y*b.x);
  }
 
  int Accw(Point p0,Point p1,Point p2){
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if(Across(a,b) > EPS)return 1;
    return -1;
  }
 
  Polygon andrewScan(Polygon s){
    Polygon u,l;
    if(s.size() < 3)return s;
    sort(s.begin(),s.end());
    u.push_back(s[0]);
    u.push_back(s[1]);
    l.push_back(s[s.size()-1]);
    l.push_back(s[s.size()-2]);
    for(int i = 2;i < s.size();i++){
      for(int n = u.size();n >= 2 && Accw(u[n-2],u[n-1],s[i]) != -1;n--){
        u.pop_back();
      }
      u.push_back(s[i]);
    }
 
    for(int i = s.size() - 3;i >= 0;i--){
      for(int n=l.size();n >= 2 && Accw(l[n-2],l[n-1],s[i]) != -1;n--){
        l.pop_back();
      }
      l.push_back(s[i]);
    }
    reverse(l.begin(),l.end());
    for(int i = u.size() - 2;i >= 1;i--){
      l.push_back(u[i]);
    }
    return l;
  }

  void printAndrew(vector<Point> res){
    int f=0;
    for(int i = 1;i < res.size();i++){
      if(res[f].y > res[i].y || (res[f].y == res[i].y && res[f].x > res[i].x)){
        f = i;
      }
    }
    for(int i = 0;i < res.size();i++){
      printf("%d %d\n",(int)res[(i + f)%res.size()].x,(int)res[(i + f)%res.size()].y);
    }
  }

  //=====================================================//
  //======================== END ========================//
  //=====================================================//

  // Polygon andrewScan(Polygon s){
  //   Polygon u,l;
  //   if(s.size() < 3)return s;
  //   sort(s.begin(),s.end());
  //   u.push_back(s[0]);
  //   u.push_back(s[1]);

  //   l.push_back(s[s.size() - 1]);
  //   l.push_back(s[s.size() - 2]);

  //   for(int i = 2;i < s.size();i++){
  //     for(int n = u.size();n >= 2 && ccw(u[n-2],u[n-1],s[i]) != CLOCKWISE;n--){
  //       u.pop_back();
  //     }
  //     u.push_back(s[i]);
  //   }

  //   for(int i = s.size() - 3; i >= 0;i--){
  //     for(int n = l.size();n >= 2 && ccw(l[n-2],l[n-1],s[i]) != CLOCKWISE;n--){
  //       l.pop_back();
  //     }
  //     l.push_back(s[i]);
  //   }
  //   reverse(l.begin(),l.end());
  //   for(int i = u.size() - 2;i >= 1;i--)l.push_back(u[i]);
  //   return l;
  // }

  class EndPoint{
  public:
    Point p;
    int seg,st;
    EndPoint(){}
    EndPoint(Point p,int seg,int st): p(p),seg(seg),st(st){}

    bool operator < (const EndPoint &ep) const{
      if(p.y == ep.p.y){
        return st < ep.st;
      }else{
        return p.y < ep.p.y;
      }
    }
  };

  EndPoint EP[2 * 100000];

  int manhattanIntersection(vector<Segment> S){
    int n = S.size();
    for(int i = 0,k = 0;i < n;i++){
      if(S[i].p1.y == S[i].p2.y){
        if(S[i].p1.x > S[i].p2.x)swap(S[i].p1,S[i].p2);
      }else if(S[i].p1.y > S[i].p2.y){
        swap(S[i].p1,S[i].p2);
      }

      if(S[i].p1.y == S[i].p2.y){
        EP[k++] = EndPoint(S[i].p1,i,LEFT);
        EP[k++] = EndPoint(S[i].p2,i,RIGHT);
      }else{
        EP[k++] = EndPoint(S[i].p1,i,BOTTOM);
        EP[k++] = EndPoint(S[i].p2,i,TOP);
      }
    }

    sort(EP,EP + (2 * n));

    set<int> BT;
    BT.insert(1000000001);
    int cnt = 0;
    for(int i = 0;i < 2 * n;i++){
      if(EP[i].st == TOP){
        BT.erase(EP[i].p.x);
      }else if(EP[i].st == BOTTOM){
        BT.insert(EP[i].p.x);
      }else if(EP[i].st == LEFT){
        set<int>::iterator b = lower_bound(BT.begin(),BT.end(),S[EP[i].seg].p1.x);
        set<int>::iterator e = upper_bound(BT.begin(),BT.end(),S[EP[i].seg].p2.x);
        cnt += distance(b,e);
      }
    }
    return cnt;
  }
};

using namespace Geometry;

/*
Point is OK.
Segment is OK.
Circle is OK.

Orthogonal is OK.
Parallex is OK.
Reflect is OK.
Intersect is OK.
CCW is OK.

*/

int main(){
  Point pnt;
  vector<Point> vec,ans;
  cin >> n;
  rep(i,0,n){
    cin >> pnt;
    vec.pb(pnt);
  }
  ans = andrewScan(vec);
  print(ans.sz);
  printAndrew(ans);
  return 0;
}


Measured memory_bytes: 14060