#include <complex>
#include <cmath>
#include <algorithm>
#include <vector>

template <class T>
inline T sq(T x) { return x * x; }

using Real = long double;
using Point = std::complex<Real>;
using Segment = std::pair<Point, Point>;
using Polygon = std::vector<Point>;

namespace std {
bool operator<(const Point& a, const Point& b) {
    return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
}
}  // namespace std

const Real EPS = 1e-10;
const Point origin(0, 0);

// 宇宙船演算子
inline Real compare(Real a, Real b) {
    if (std::abs(a - b) < EPS) return 0;
    return a - b > 0 ? 1 : -1;
}

inline Real dist(Point a, Point b) {
    return std::abs(a - b);
}

inline Real length(Segment s) {
    return dist(s.first, s.second);
}

// inner product
inline Real dot(Point x, Point y) {
    return std::real(std::conj(x) * (y));
}

// outer product
inline Real cross(Point x, Point y) {
    return std::imag(std::conj(x) * (y));
}

// lに対するxの正射影
Point proj(Segment s, Point p) {
    Real ratio = dot(s.second - s.first, p - s.first) / sq(length(s));
    return s.first + (s.second - s.first) * ratio;
}

// lを軸としてxに対称な点
Point refl(Segment s, Point p) {
    Point t = proj(s, p);
    return t + (t - p);
}

// lに対するpの位置
// 0: on segment
// 1: counter clockwise  -1: clockwise
// 2: online front       -2: online back
int side(Segment s, Point p) {
    Real c = cross(s.second - s.first, p - s.first);
    if (compare(c, 0) != 0) return compare(c, 0);

    Real d = dot(s.second - s.first, p - s.first);
    if (compare(d, 0) < 0) return -2;

    return (compare(length(Segment(s.first, p)), length(s)) > 0 ? 2 : 0);
}

inline bool isorthogonal(Segment s1, Segment s2) {
    return compare(dot(s1.second - s1.first, s2.second - s2.first), 0) == 0;
}

inline bool isparallel(Segment s1, Segment s2) {
    return compare(cross(s1.second - s1.first, s2.second - s2.first), 0) == 0;
}

inline bool intersect(Segment s1, Segment s2) {
    return (side(s1, s2.first) * side(s1, s2.second) <= 0) &&
           (side(s2, s1.first) * side(s2, s1.second) <= 0);
}

Point intersection(Segment s1, Segment s2) {
    Real c1 = cross(s2.second - s2.first, s1.second - s1.first);
    Real c2 = cross(s2.second - s2.first, s1.first - s2.first);
    return s1.first + (s1.second - s1.first) * (-c2 / c1);
}

// distanceのオーバーロード
Real dist(Segment s, Point p) {
    Point t = proj(s, p);
    if (side(s, t) == 0) return dist(p, t);
    return std::min(dist(p, s.first), dist(p, s.second));
}

Real dist(Segment s1, Segment s2) {
    if (intersect(s1, s2)) return 0;
    return std::min({dist(s1, s2.first),
                     dist(s1, s2.second),
                     dist(s2, s1.first),
                     dist(s2, s1.second)});
}

// signed area
inline Real triangle(Point a, Point b, Point c) {
    return cross(b - a, c - a) / 2;
}

Real area(const Polygon& g) {
    Real S = 0;
    for (int i = 0; i < g.size(); ++i) {
        S += triangle(origin, g[i], g[(i + 1) % g.size()]);
    }
    return S;
}

bool isconvex(const Polygon& g) {
    for (int i = 0; i < g.size(); ++i) {
        if (side(Segment(g[i], g[(i + 1) % g.size()]),
                 g[(i + 2) % g.size()]) == -1) return false;
    }
    return true;
}

// 0: out  1: on  2: in
int contain(const Polygon& g, Point p) {
    bool in = false;
    for (int i = 0; i < g.size(); ++i) {
        if (side(Segment(g[i], g[(i + 1) % g.size()]), p) == 0) return 1;

        Point a = g[i] - p, b = g[(i + 1) % g.size()] - p;
        if (compare(a.imag(), b.imag()) > 0) std::swap(a, b);
        if (compare(a.imag(), 0) <= 0 &&
            compare(b.imag(), 0) > 0 &&
            compare(cross(a, b), 0) < 0) in = !in;
    }
    return in ? 2 : 0;
}

// linearok: 同一直線上の頂点を取るか
Polygon convexhull(Polygon& g, bool linearok) {
    std::sort(g.begin(), g.end());
    int N = g.size();

    Polygon ret(N * 2);
    int k = 0;
    for (int i = 0; i < N; ++i) {
        while (k >= 2 &&
               compare(cross(ret[k - 1] - ret[k - 2], g[i] - ret[k - 2]), 0) < !linearok) --k;
        // linearokならcross = 0も回収する
        ret[k++] = g[i];
    }
    int t = k + 1;
    for (int i = N - 2; i >= 0; --i) {
        while (k >= t &&
               compare(cross(ret[k - 1] - ret[k - 2], g[i] - ret[k - 2]), 0) < !linearok) --k;
        ret[k++] = g[i];
    }

    // 先頭が被っているので除く
    ret.resize(k - 1);
    return ret;
}


#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    int N;
    cin >> N;

    Polygon g(N);
    for (auto& p : g) {
        Real x, y;
        cin >> x >> y;
        p = Point(x, y);
    }

    Polygon h = convexhull(g, true);
    cout << h.size() << endl;

    int start = 0;
    for (int i = 1; i < h.size(); ++i) {
        if (h[i].imag() < h[start].imag()) start = i;
    }

    for (int i = 0; i < h.size(); ++i) {
        Point p = h[(start + i) % h.size()];
        cout << p.real() << " " << p.imag() << endl;
    }
    return 0;
}



Measured memory_bytes: 12304