#include <iostream>
#include <cstring>
#include <cassert>
#include <cmath>
using namespace std;
using ll = long long;

const ll INF = 100000LL * 10000LL * 100LL;
const int ON_SEGMENT = 0, CW = 1, CCW = 2;
const double EPS = 1e-9;

inline bool eq(double a, double b){
  return abs(a-b) < EPS;
}

template<typename T, typename S>
class Pair {
  public:
    T first;
    S second;
    Pair(){}
    Pair(T first, S second){
      this->first = first;
      this->second = second;
    }
    Pair(const Pair<T, S>& other){
      this->first = other.first;
      this->second = other.second;
    }
    bool operator<=(const Pair& other)const{
      return first <= other.first;
    }
    bool operator<(const Pair& other)const{
      return first < other.first;
    }
    Pair &operator=(const Pair& other){
      if(this != &other){
        first = other.first;
        second = other.second;
      }
      return *this;
    }
};

template<typename T>
class Point {
  public:
    T x;
    T y;
    Point(): x(0.0), y(0.0){}
    Point(T x, T y): x(x), y(y){}
    Point(const Point& other): x(other.x), y(other.y){}
    Point &operator=(const Point& other){
      if(this != &other){
        x = other.x;
        y = other.y;
      }
      return *this;
    }
    bool operator<(const Point& other)const{
      if (eq(y, other.y)){
        return x < other.x;
      }
      return y < other.y;
    }
    // bool operator<=(const Point& other)const{
    //   return x <= other.x;
    // }
    T cross(const Point& other)const{
      return x*other.y - other.x * y;
    }
};

template <typename T>
int orientation(Point<T> a, Point<T> b, Point<T> c){
  T ba_x = b.x - a.x;
  T ba_y = b.y - a.y;
  T ca_x = c.x - a.x;
  T ca_y = c.y - a.y;
  double o = ba_x * ca_y - ba_y * ca_x;
  if(abs(o) < EPS) return ON_SEGMENT;
  return o > 0 ? CCW : CW;
}

template <typename T>
class Vector {
  private:
  unsigned int end;
  unsigned int alloc;
  public:
  T* A;
  Vector(): end(0), alloc(1), A((T *)malloc(sizeof(T))){}
  // Vector(){
  //   end = 0;
  //   alloc = 1;
  //   A = (T *)malloc(sizeof(T));
  // }
  Vector(const unsigned int initial_size){
    end = initial_size;
    alloc = max((unsigned int)1, initial_size);
    A = (T *)malloc(sizeof(T) * initial_size);
  }
  Vector(const Vector &other){
    end = other.end;
    alloc = other.alloc;
    A = (T *)malloc(sizeof(T) * alloc);
    memcpy(A, other.A, sizeof(T) * alloc);
  }
  Vector &operator=(const Vector &other){
    if(this != &other){
      end = other.end;
      alloc = other.alloc;
      A = (T *)malloc(sizeof(T) * alloc);
      memcpy(A, other.A, sizeof(T) * alloc);
    }
    return *this;
  }
  unsigned int size()const{
    return end;
  }
  void push_back(T v){
    if(end < alloc){
    }else{
      if(alloc == 0 || A == NULL){
        A = (T *)malloc(sizeof(T));
        alloc++;
      }else{
      unsigned int nalloc = alloc * 2;
      T* nA = (T *)malloc(sizeof(T) * nalloc);
      memcpy(nA, A, sizeof(T) * end);
      free(A);
      A = nA;
      alloc = nalloc;
      }
    }
    A[end] = v;
    end++;
  }
  void pop_back(){
    if(end == 0) return;
    end--;
  }
  T &operator[](const unsigned int idx)const{
    return A[idx];
  }
  ~Vector(){
    free(A);
  }
};


template<typename T>
class Stack {
  private:
    Vector<T> A;
  public:
    Stack(){}
    void push(T v){
      A.push_back(v);
    }
    unsigned int size(){
      return A.size();
    }
    T top(){
      return A[A.size()-1];
    }
    T pop(){
      T res = top();
      A.pop_back();
      return res;
    }
    T &operator[](const unsigned int idx)const{
      return A[idx];
    }
};

template <typename T>
class Heap {
  public:
  Vector<T> A;

  public:
  Heap(){
    A = Vector<T>(1);
  }

  void push(T v){
    unsigned int idx = A.size();
    A.push_back(v);

    while(1){
      if(idx == 1) break;
      unsigned int  par = idx / 2;
      if(A[par] < A[idx]) break;
      swap(A[idx], A[par]);
      idx = par;
    }
  };

  T top(){
    assert(A.size() > 1);
    return A[1];
  }
  T pop(){
    assert(A.size() > 1);
    T res = top();
    remove();
    return res;
  }
  bool empty(){
    return A.size() <= 1;
  }
  void remove(){
    T v = A[A.size() - 1];
    unsigned int idx = 1;
    A.pop_back();
    A[idx] = v;
    while(1){
      unsigned int  least = idx;
      unsigned int l = 2*idx, r = 2*idx+1;
      // if(l < A.size() && A[l] <= A[least]){
      if(l < A.size() && !(A[least] < A[l])){
        least = l;
      }
      // if(r < A.size() && A[r] <= A[least]){
      if(r < A.size() && !(A[least] < A[r])){
        least = r;
      }

      if(least == idx) break;
      swap(A[idx], A[least]);
      idx = least;
    }
  }
#ifdef EBUG
  void print(){
    for(int i = 1; i < A.size(); i++){cout << A[i] << " ";}
    cout << endl;
  }
#endif
};



#ifdef EBUG
void test_vector(){
  Vector<Vector<int>> v;
  for(int i = 0; i < 4; i++){
    Vector<int> w;
    for(int j = 0; j < 4; j++){
      w.push_back(i*4 + j);
    }
    v.push_back(w);
  }
  for(int i = 0; i < 4; i++){
    for(int j = 0; j < 4; j++){
      cout << v[i][j] << " ";
    }
    cout << endl;
  }
}

void test_heap(){
  const int size = 12;
  int elements[size] = {0, 6, 2, 7, 4, 1, 9, 8, 5, 9999, 6, 3};
  Heap<int> h;
  for(int i = 0; i < size; i++){
    h.push(elements[i]);
  }

  for(int i = 0; i < size; i++){
    cout << h.pop() << endl;
  }
}
#endif

template <typename T>
class ArgAndDistAndPoint {
  public:
    double arg;
    double dist;
    Point<T> pts;
    ArgAndDistAndPoint(){}
    ArgAndDistAndPoint(double arg, double dist, Point<T> pts): arg(arg), dist(dist), pts(pts){}
    ArgAndDistAndPoint(const ArgAndDistAndPoint& other): arg(other.arg), dist(other.dist), pts(other.pts){ }

    // bool operator<=(const ArgAndDistAndPoint& other)const{
    //   if(eq(arg, other.arg)){
    //     return dist - other.dist <= EPS;
    //   }
    //   return arg - other.arg <= EPS;
    // }
    bool operator<(const ArgAndDistAndPoint& other)const{
      if(eq(arg, other.arg)){
        return dist < other.dist;
      }else{
        return arg < other.arg;
      }
    }
    ArgAndDistAndPoint &operator=(const ArgAndDistAndPoint& other){
      if(this != &other){
        arg = other.arg;
        dist = other.dist;
        pts = other.pts;
      }
      return *this;
    }
};

Vector<Point<int>> arg_sort(const Vector<Point<int>> &pts){
  Vector<ArgAndDistAndPoint<int>> ap;
  for(int i = 1; i < (int)pts.size(); i++){
    int dy = pts[i].y - pts[0].y;
    int dx = pts[i].x - pts[0].x;
    double arg = atan2(dy, dx);
    double dist = sqrt(dx*dx + dy*dy);
    Point<int> p = pts[i];
    ap.push_back(ArgAndDistAndPoint<int>(arg, dist, p));
  }

  // Vector<Pair<double, Point<int>>> ap; // first = argument, second = pts
  // for(int i = 1; i < (int)pts.size(); i++){
  //   int dy = pts[i].y - pts[0].y;
  //   int dx = pts[i].x - pts[0].x;
  //   double arg = atan2(dy, dx);
  //   Point<int> p = pts[i];
  //   ap.push_back(Pair<double, Point<int>>(arg, p));
  // }

  Vector<Point<int>> res(pts.size());
  {
    Heap<ArgAndDistAndPoint<int>> h;
    for(int i = 0; i < (int)ap.size(); i++){
      h.push(ap[i]);
    }

#ifdef EBUG
    for(int i = 0; i < (int)h.A.size(); i++){
      cout << "(" << h.A[i].pts.x << " " << h.A[i].pts.y << ") ";
    }
    cout << endl;
#endif

    res[0] = pts[0];
    for(int i = 1; i < (int)res.size(); i++){
      res[i] = h.pop().pts;
    }
  }

  return res;
}

int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  int n; cin >> n;
  Vector<Point<int>> pts(n);
  for(int i = 0; i < n; i++){
    int x, y; cin >> x >> y;
    pts[i] = Point<int>(x, y);
  }
  Vector<Point<int>> pts_sorted;
  {
    Heap<Point<int>> h;
    for(int i = 0; i < n; i++){
      h.push(pts[i]);
    }
    for(int i = 0; i < n; i++){
      pts_sorted.push_back(h.pop());
    }
  }

  int k = 0;
  Stack<Point<int>> st;
  st.push(pts_sorted[0]);
  st.push(pts_sorted[1]);
  // st.push(pts_sorted[2]);
  for(int i  =2; i < n; i++){
    Point<int> top = st.pop();
    Point<int> nxt = st.pop();
    st.push(nxt); st.push(top);
    while(orientation(pts_sorted[i], top, nxt) == CW){
      st.pop();
      if(st.size() < 2) break;
      top = st.pop();
      nxt = st.pop();
      st.push(nxt); st.push(top);
    }
    st.push(pts_sorted[i]);
  }

  int t = st.size();
  for(int i = n - 2; i >= 0; i--){
    Point<int> top = st.pop();
    Point<int> nxt = st.pop();
    st.push(nxt); st.push(top);
    while(orientation(pts_sorted[i], top, nxt) == CW){
      st.pop();
      //if(st.size() <= t) break;
      top = st.pop();
      nxt = st.pop();
      st.push(nxt), st.push(top);
    }
    st.push(pts_sorted[i]);
  }
  cout << st.size() - 1 << '\n';
  for(int i = st.size()-1; i >= 1; i--){
    cout << st[i].x << " " << st[i].y << '\n';
  }
}



Measured memory_bytes: 5952