#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <set>
#include <algorithm>
#include <array>
#include <complex>
#include <string>
#include <utility>
#include <map>
#include <queue>
#include <list>
#include <functional>
#include <numeric>
#include <stack>
#include <tuple>

using namespace std;
int dx[4] = { -1,0,1,0 };
int dy[4] = { 0,1,0,-1 };
const int INF = 100000000;
const long long LINF = 1000000000000000000;
const int MOD = (int)1e9 + 7;
const double EPS = 1e-6;
using pii = std::pair<int, int>;
using ll = long long;
using pLL = std::pair<ll, ll>;
#define SORT(v) std::sort(v.begin(), v.end())
#define RSORT(v) std::sort(v.rbegin(), v.rend())
using Point = complex<double>; //点
using Poly = vector<Point>;
#define X real() //実部
#define Y imag() //虚部
namespace std {
    bool operator<(const Point& a, const Point& b) {
        return a.X != b.X ? a.X < b.X : a.Y < b.Y;
    }

    bool cmp_y(const Point& a, const Point& b) {
        return a.Y != b.Y ? a.Y < b.Y : a.X < b.X;
    }
}
double dot(Point a, Point b) { //内積
    return (a.X * b.X + a.Y * b.Y);
}
using Line = pair<Point, Point>; //直線
Point Projection(Line s, Point p) { //点から直線への射影
    Point vase = s.second - s.first;
    p -= s.first;
    return s.first + dot(p, vase) / norm(vase) * vase;
}
Point Reflection(Line s, Point p) { //直線に対して線対称な点
    return p + (Projection(s, p) - p) * 2.0;
}
double cross(Point a, Point b) { //外積
    return a.X * b.Y - a.Y * b.X;
}
int ccw(Point a, Point b, Point c) { //直線に対する点の位置
    if (cross(b - a, c - a) > EPS)return 1;
    if (cross(b - a, c - a) < -EPS)return -1;
    if (dot(b - a, c - a) < -EPS)return 2;
    if (abs(b - a) + EPS < abs(c - a))return -2;
    return 0;
}
bool isOrthogonal(Line a, Line b) { //直行判定
    return dot(a.second - a.first, b.second - b.first) ? 0 : 1;
}
bool isParallel(Line a, Line b) { //平行判定
    return cross(a.second - a.first, b.second - b.first) ? 0 : 1;
}
bool isCross(Line a, Line b) { //交差判定
    if (ccw(a.first, a.second, b.first) * ccw(a.first, a.second, b.second) <= 0 and
        ccw(b.first, b.second, a.first) * ccw(b.first, b.second, a.second) <= 0) {
        return true;
    }
    return false;
}
Point CrossPoint(Line a, Line b) { //直線の交点
    double x, d;
    Point p1, p2, p3, p4;
    p1 = a.first;
    p2 = a.second;
    p3 = b.first;
    p4 = b.second;
    x = (p4.Y - p3.Y) * (p4.X - p1.X) - (p4.X - p3.X) * (p4.Y - p1.Y);
    d = (p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y);
    return p1 + x / d * (p2 - p1);
}
double PointLineDis(Line l, Point p) {
    Point a = l.first, b = l.second, c = p;
    return abs(cross(c - a, b - a)) / abs(b - a);
}
double PointSegDis(Line a, Point p) {
    Point p1 = a.first, p2 = a.second;
    if (dot(p2 - p1, p - p1) < EPS)return abs(p - p1);
    if (dot(p1 - p2, p - p2) < EPS)return abs(p - p2);
    return PointLineDis(a, p);
}
double SegSegDis(Line a, Line b) {
    double res = PointSegDis(a, b.first);
    cout << res << endl;
    res = min(res, PointSegDis(a, b.second));
    cout << res << endl;
    res = min(res, PointSegDis(b, a.first));
    cout << res << endl;
    res = min(res, PointSegDis(b, a.second));
    cout << res << endl;
    return res;
}
double PolyArea(Poly p) {
    if (p.size() < 3)return 0;
    double res = cross(p[p.size() - 1], p[0]);
    for (size_t i = 0; i < p.size() - 1; ++i) {
        res += cross(p[i], p[i + 1]);
    }
    return res / 2.0;
}
bool isConvex(Poly p) {
    for (size_t i = 0; i < p.size(); ++i) {
        if (ccw(p[i], p[(i + 1) % p.size()], p[(i + 2) % p.size()]) == -1)return false;
    }
    return true;
}
int inPoly(Poly& p, Point a) {
    int count = 0;
    Point cp;
    int n = (int)p.size();
    p.push_back(p[0]);
    Point b = Point(a.X + 1, a.Y);
    for (int i = 0; i < n; ++i) {
        Point s = p[i] - a, t = p[i + 1] - a;
        if (ccw(s, t, Point(0, 0)) == 0)return 1;
        if (s.Y > t.Y + EPS)swap(s, t);
        if ((s.Y * t.Y < 0 || (s.Y * t.Y<EPS && t.Y>EPS)) && cross(s, t) < EPS)count++;
    }
    if (count % 2)return 2;
    else return 0;
}
Poly ConvexHull(Poly v) {
    int n = v.size(), k = 0;
    sort(v.begin(), v.end(), cmp_y);
    Poly r(2 * n);
    for (int i = 0; i < n; i++) {
        while (k > 1 && cross(r[k - 1] - r[k - 2], v[i] - r[k - 2]) < -EPS)k--;
        r[k++] = v[i];
    }
    for (int i = n - 2, t = k; i >= 0; i--) {
        while (k > t && cross(r[k - 1] - r[k - 2], v[i] - r[k - 2]) < -EPS)k--;
        r[k++] = v[i];
    }
    r.resize(k - 1);
    return r;
}
/*
Poly ConvexHull(Poly p) {
    int n = p.size(), k = 0;
    sort(p.begin(),p.end(),cmp_y);
    Poly r(2 * n);
    for (int i = 0; i < n; ++i) {
        while (k >= 2 && cross(r[k - 1]- r[k - 2], p[i]-r[k - 2]) <= 0)k--;
        r[k++] = p[i];
    }
    for (int i = n - 2, t = k + 1; i > 0; --i) {
        while (k >= t && cross(r[k - 1] - r[k - 2], p[i-1] - r[k - 2]) <= 0)k--;
        r[k++] = p[i - 1];
    }
    r.resize(k-1);
    return r;
}*/

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    Poly poly;
    for (int i = 0; i < n; ++i) {
        double x1, y1;
        cin >> x1 >> y1;
        Point p = Point(x1, y1);
        poly.push_back(p);
        //cout << p.X << p.Y << endl;
    }
    Poly ch = ConvexHull(poly);
    cout << ch.size() << endl;
    for (size_t i = 0; i < ch.size(); ++i) {
        printf("%d %d\n", (int)ch[i].X, (int)ch[i].Y);
    }
    return 0;
}


Measured memory_bytes: 9276