#include <bits/stdc++.h>

template <typename T>
struct point {
  T x, y;
  
  point(const T& x_ = {0}, const T& y_ = {0}) : x(x_), y(y_) {}
  
  template <typename T_>
  explicit point(const point<T_>& P) 
  : x(static_cast<T>(P.x)), y(static_cast<T>(P.y)) {}

  template <typename T_>
  explicit operator point<T_> () const {
    return {static_cast<T_>(x), static_cast<T_>(y)};
  }

  template <typename U, typename V>
  explicit point(const std::pair<U, V>& P) 
  : x(static_cast<T>(P.first)), y(static_cast<T>(P.second)) {}

  template <typename T_>
  explicit operator std::pair<T_, T_> () const {
    return std::pair<T_, T_>{static_cast<T_>(x), static_cast<T_>(y)};
  }

  friend std::istream& operator >> (std::istream& input, point& P) {
    return input >> P.x >> P.y;
  }

  friend std::ostream& operator << (std::ostream& output, point& P) {
    output << "(" << P.x << ", " << P.y << ")";
    return output;
  }

  static std::enable_if_t<std::is_integral<T>::value, std::int32_t>
  sign(T x) {
    return x < 0 ? -1 : (0 < x);
  }

  // template <typename T_ = T, typename R = std::enable_if_t<std::is_floating_point<T_>::value, std::int32_t>>
  // static auto sign(T_ x) -> R {
  //   constexpr double epsilon = 1e-9;
  //   return x < -epsilon ? -1 : (epsilon < x);
  // }

  // template <typename T_ = T, typename R = std::enable_if<std::is_floating_point<T_>::value, std::int32_t>>
  // auto sign(T_ x) -> R {
  //   constexpr double epsilon = 1e-9;
  //   return x < -epsilon ? -1 : (epsilon < x);
  // }

  // static std::int32_t sign(T x) {
  //   if constexpr (std::is_floating_point<T>::value) {
  //     constexpr double epsilon = 1e-9; // TODO find a good epsilon
  //     return x < -epsilon ? -1 : (epsilon < x);
  //   }
  //   return x < T{0} ? -1 : (T{0} < 0);
  // }

  void normalize() {
    // if constexpr (std::is_integral<T>::value) {
    //   auto g = std::__gcd(std::abs(x), std::abs(y));
    //   x /= g;
    //   y /= g;
    // }
    // if constexpr (std::is_floating_point<T>::value) {
    //   auto l = length();
    //   assert(sign(l) != 0);
    //   x /= l;
    //   y /= l;
    // }
  }
  
  point& operator += (const point& P) {
    x += P.x;
    y += P.y;
    return *this;
  }

  point& operator -= (const point& P) {
    x -= P.x;
    y -= P.y;
    return *this;
  }

  point& operator *= (const T& k) {
    x *= k;
    y *= k;
    return *this;
  }

  point& operator /= (const T& k) {
    x /= k;
    y /= k;
    return *this;
  }

  point operator + (const point& P) const {
    return {x + P.x, y + P.y};
  }

  point operator - (const point& P) const {
    return {x - P.x, y - P.y};
  }

  point operator * (const T& k) const {
    return {x * k, y * k};
  }

  bool operator == (const point& P) const {
    return (x == P.x && y == P.y);
  }

  T norm() const {
    return dot(*this, *this);
  }
  
  auto length() const -> double {
    return sqrt(static_cast<double>(norm()));
  }

  static auto dot(const point& P, const point& Q) -> T {
    return P.x * Q.x + P.y * Q.y;
  }

  static auto cross(const point& P, const point& Q) -> T {
    return P.x * Q.y - P.y * Q.x;
  }

  static auto half_plane(const point& P) -> int const {
    if (P.y != 0) {
      return P.y < 0 ? -1 : +1;
    }
    return P.x < 0 ? -1 : +1;
  }

  static auto xy_comparator() {
    return [] (const point& P, const point& Q) {
      if (P.x != Q.x) {
        return P.x < Q.x;
      }
      return P.y < Q.y;
    };
  }

  static auto yx_comparator() {
    return [] (const point& P, const point& Q) {
      if (P.y != Q.y) {
        return P.y < Q.y;
      }
      return P.x < Q.x;
    };
  }
 
  static auto angle_comparator(const point& O) {
    return [O] (const point& P, const point& Q) {
      auto hP = half_plane(P);
      auto hQ = half_plane(Q);
      if (hP != hQ) {
        return hP < hQ;
      }
      auto c = sign(cross(P - O, Q - O));
      if (c != 0) {
        return (c > 0);
      }
      return sign(norm(P - O) - norm(Q - O)) == -1;
    };
  }
};

template <typename T>
std::vector<point<T>> convex_hull(std::vector<point<T>> pts) {
  sort(pts.begin(), pts.end(), point<T>::yx_comparator());
  
  std::vector<point<T>> hull(2 * pts.size());
  std::size_t size = 0;

  hull[size++] = pts[0];

  for (std::size_t step = 0; step < 2; step++) {
    auto prev_size = size;
    for (std::size_t i = 1; i < pts.size(); i++) {
      while (size > prev_size && point<T>::sign(point<T>::cross(pts[i] - hull[size - 2], hull[size - 1] - hull[size - 2])) < 0) {
        size--;
      }
      hull[size++] = pts[i];
    }
    std::reverse(pts.begin(), pts.end());
  }

  if (size > 1 && hull[0] == hull[size - 1]) {
    size--;
  }

  hull.resize(size);

  return hull;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cout.tie(nullptr);

  std::size_t n;
  std::cin >> n;

  std::vector<point<std::int32_t>> pts(n);
  for (auto& P: pts) {
    std::cin >> P;
  }

  auto hull = convex_hull(pts);

  std::cout << hull.size() << "\n";
  for (int i = 0; i < hull.size(); i++) {
    int j = (static_cast<int>(hull.size()) - i) % static_cast<int>(hull.size());
    std::cout << hull[j].x << " " << hull[j].y << "\n";
  }
}


Measured memory_bytes: 5892