#include <bits/stdc++.h>
using namespace std;
#define debug(a) cout << #a": " << a << endl;
#define mst(a, b) memset(a, b, sizeof(a))
#define ALL(x) x.begin(), x.end()
#define lc rt << 1
#define rc rt << 1 | 1
#define X first
#define Y second
inline int lowbit(int x) { return x & -x; }
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1e6 + 10;
const int M = 3e5 + 10;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
const int mod = 1e9 + 7;
typedef double db;
#define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y))
#define crossOp(p1, p2, p3) sign(cross(p1, p2, p3))
const db eps = 1e-9;
const db pi = acos(-1.0);

inline int sign(db a) { return a < -eps ? -1 : a > eps; }
inline int cmp(db a, db b) { return sign(a - b); }
struct P {
    db x, y;
    P() {}
    P(db _x, db _y) : x(_x), y(_y) {}
    P operator + (P p) { return {x + p.x, y + p.y}; }
    P operator - (P p) { return {x - p.x, y - p.y}; }
    P operator * (db d) { return {x * d, y * d}; }
    P operator / (db d) { return {x / d, y / d}; }

    bool operator < (P p) const {
        int c = cmp(y, p.y);
        if (c) return c == -1;
        return cmp(x, p.x) == -1;
    }

    bool operator == (P o) const {
        return cmp(x, o.x) == 0 && cmp(y, o.y) == 0;
    }

    db distTo(P p) { return (*this - p).abs(); }
    db alpha() { return atan2(y, x); }
    void read() { cin >> x >> y; }
    db abs() { return sqrt(abs2()); }
    db abs2() { return x * x + y * y; }
    P rot90() { return P(-y, x); }
    P unit() { return *this / abs(); }
    int quad() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); }
    db dot(P p) { return x * p.x + y * p.y; }
    db det(P p) { return x * p.y - y * p.x; }
    P rot(db an) { return {x * cos(an) - y * sin(an), x * sin(an) + y * cos(an)}; }
};
P proj(P p1, P p2, P q) {
    P dir = p2 - p1;
    return p1 + dir * (dir.dot(q - p1) / dir.abs2());
}
P reflect(P p1, P p2, P q) {
    return proj(p1, p2, q) * 2 - q;
}
bool isMiddle(db a, db m, db b) {
    return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m);
}
bool isMiddle(P a, P m, P b) {
    return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
}
bool onSeg(P p1, P p2, P q) {
    return crossOp(p1, p2, q) == 0 && isMiddle(p1, q, p2);
}
db nearest(P p1, P p2, P q) {
    P h = proj(p1, p2, q);
    if (isMiddle(p1, h, p2))
        return q.distTo(h);
    return min(p1.distTo(q), p2.distTo(q));
}
bool intersect(db l1, db r1, db l2, db r2) {
    if (l1 > r1) swap(l1, r1); if (l2 > r2) swap(l2, r2);
    return !(cmp(r1, l2) == -1 || cmp(r2, l1) == -1);
}
bool isSS(P p1, P p2, P q1, P q2) {
    return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y) &&
            crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <= 0 && crossOp(q1, q2, p1)
            * crossOp(q1, q2, p2) <= 0;
}
db disSS(P p1, P p2, P q1, P q2) {
    if (isSS(p1, p2, q1, q2)) return 0;
    return min({nearest(p1, p2, q1), nearest(p1, p2, q2), nearest(q1, q2, p1), nearest(q1, q2, p2)});
}
P isLL(P p1, P p2, P q1, P q2) {
    db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
    return (p1 * a2 + p2 * a1) / (a1 + a2);
}
db area(vector<P> ps) {
    db ret = 0;
    int n = ps.size();
    for (int i = 0; i < n; i++)
        ret += ps[i].det(ps[(i + 1) % n]);
    return ret / 2;
}
vector<P> convexHullNonStrict(vector<P> ps) {
	//caution: need to unique the Ps first
    int n = ps.size(); if(n <= 1) return ps;
    sort(ps.begin(), ps.end());
    vector<P> qs(n * 2); int k = 0;
    for (int i = 0; i < n; qs[k++] = ps[i++])
        while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k;
    for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--])
       	while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k;
    qs.resize(k - 1);
    return qs;
}

int main() {
#ifdef purple_bro
    freopen("in.txt","r",stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n;

    cin >> n;

    vector<P> ps;

    for (int i = 0; i < n; i++) {
        P p;
        p.read();
        ps.emplace_back(p);
    }

    sort(ALL(ps));
    ps.erase(unique(ALL(ps)), ps.end());

    ps = convexHullNonStrict(ps);

    vector<P> tmp = ps;
    sort(ALL(tmp));

    int id = -1;

    for (int i = 0; i < ps.size(); i++) if (ps[i] == tmp[0]) {
        id = i;
        break;
    }

    cout << ps.size() << "\n";

    for (int i = 0; i < ps.size(); i++)
        cout << ps[(i + id) % ps.size()].x << " " << ps[(i + id) % ps.size()].y << "\n";

    return 0;
}




Measured memory_bytes: 6796