#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#include <climits>
#include <cstring>
#include <cassert>

#define rep(i, m, n) for(int i=int(m);i<int(n);i++)
#define all(c) begin(c),end(c)

template<typename T1, typename T2>
inline void chmin(T1 &a, T2 b) { if (a > b) a = b; }

template<typename T1, typename T2>
inline void chmax(T1 &a, T2 b) { if (a < b) a = b; }

typedef long long int ll;
using ll = long long int;
using ull = long long unsigned int;
using Int = long long int;
using namespace std;
#define INF (1 << 30) - 1
#define INFl (ll)5e15
#define DEBUG 0
#define dump(x)  cerr << #x << " = " << (x) << endl
#define MOD 1000000007


//edit


namespace Geometory {
    const double EPS = 1e-10;

    inline bool equals(double a, double b) {
        return fabs(a - b) < EPS;
    }

    struct Point {
        double x, y;

        Point(double x = 0.0, double y = 0.0) : x(x), y(y) {}

        Point operator+(const Point &p) {
            return Point(x + p.x, y + p.y);
        }

        Point operator-(const Point &p) {
            return Point(x - p.x, y - p.y);
        }

        Point operator*(const double k) {
            return Point(x * k, y * k);
        }

        Point operator/(const double k) {
            return Point(x / k, y / k);
        }

        double norm() const {
            return x * x + y * y;
        }

        double abs() const {
            return sqrt(norm());
        }

        double dot(Point p) {
            return x * p.x + y * p.y;
        }

        double cross(Point p) {
            return x * p.y - y * p.x;
        }

        bool operator<(const Point &p) const {
            return x != p.x ? x < p.x : y < p.y;
        }

        bool operator==(const Point &p) const {
            return equals(x, p.x) && equals(y, p.y);
//            return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
        }


    };

    using Vector = Point;

    struct Segment {
        Point p1, p2;

        Segment() {}

        Segment(Point p1, Point p2) : p1(p1), p2(p2) {}

        Segment(double x1, double y1, double x2, double y2) : p1(x1, y1), p2(x2, y2) {}
    };

    using Line = Segment;


    double norm(Vector p) {
        return p.x * p.x + p.y * p.y;
    }

    double abs(Vector a) {
        return sqrt(norm(a));
    }

    double dot(Vector a, Vector b) {
        return a.dot(b);
    }

    double cross(Vector a, Vector b) {
        return a.cross(b);
    }

    //直行判定
    bool is_orthogonal(Vector a, Vector b) {
        return equals(dot(a, b), 0.0);
    }

    bool is_orthogonal(Point a1, Point a2, Point b1, Point b2) {
        return is_orthogonal(a1 - a2, b1 - b2);
    }

    bool is_orthogonal(Segment s1, Segment s2) {
        return equals(dot(s1.p2 - s1.p1, s2.p2 - s2.p1), 0.0);
    }

    //平行判定
    bool is_parallel(Vector a, Vector b) {
        return equals(cross(a, b), 0.0);
    }

    bool is_parallel(Point a1, Point a2, Point b1, Point b2) {
        return is_parallel(a1 - a2, b1 - b2);
    }

    bool is_parallel(Segment s1, Segment s2) {
        return equals(cross(s1.p2 - s1.p1, s2.p2 - s2.p1), 0.0);
    }

    //反時計回り
    static const int CCW = 1; // Counter Clockwise
    static const int CW = -1; // Clockwise
    static const int OB = 2; // Online Back
    static const int OF = -2; // Online Front
    static const int OS = 0; // On Segment

    // p0を中心としたとき、p2は{反時計回り、時計回り、p2->p0->p1で同一直線上、p0->p1->p2で直線上、p2がp0p1線分上} (後ろの3つの説明は雑)
    int ccw(Point p0, Point p1, Point p2) {
        Vector a = p1 - p0;
        Vector b = p2 - p0;
        if (cross(a, b) > EPS) return CCW;
        if (cross(a, b) < -EPS) return CW;
        if (dot(a, b) < -EPS) return OB;
        if (a.norm() < b.norm()) return OF;

        return OS;
    }

    //交差判定
    bool is_intersect(Point p1, Point p2, Point p3, Point p4) {
        return ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
               ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0;
    }

    //交差判定
    bool is_intersect(Segment s1, Segment s2) {
        return is_intersect(s1.p1, s1.p2, s2.p1, s2.p2);
    }

    //射影を求める
    //pからsに垂直に線を引いたときの座標を返す
    Point project(Segment s, Point p) {
        Vector base = s.p2 - s.p1;
        double r = dot(p - s.p1, base) / norm(base);
        return s.p1 + base * r;
    }

    // 反射
    // pを線分sを対称軸として線対称の位置にある点を求める。
    Point reflect(Segment s, Point p) {
//        return p + (project(s, p) - p) * 2;
        return project(s, p) * 2 - p;
    }


    //距離シリーズ
    //2点間の距離
    double get_distance(Point a, Point b) {
        return abs(a - b);
    }

    // 直線lと点pの距離
    double get_distanceLP(Line l, Point p) {
        return abs(cross(l.p2 - l.p1, p - l.p1) / abs(l.p2 - l.p1));
    }

    // 線分sと点pの距離
    double get_distanceSP(Segment s, Point p) {
        if (dot(s.p2 - s.p1, p - s.p1) < 0.0) return abs(p - s.p1);
        if (dot(s.p1 - s.p2, p - s.p2) < 0.0) return abs(p - s.p2);
        return get_distanceLP(s, p);
    }

    //線分と線分の距離
    double get_distance(Segment s1, Segment s2) {
        if (is_intersect(s1, s2)) return 0.0;
        return min({get_distanceSP(s1, s2.p1),
                    get_distanceSP(s1, s2.p2),
                    get_distanceSP(s2, s1.p1),
                    get_distanceSP(s2, s1.p2),
                   });
    }

    // 線分と線分の交点
    Point get_cross_point(Segment s1, Segment s2) {
        Vector base = s2.p2 - s2.p1;
        double d1 = abs(cross(base, s1.p1 - s2.p1));
        double d2 = abs(cross(base, s1.p2 - s2.p1));

        double t = d1 / (d1 + d2);
        return s1.p1 + (s1.p2 - s1.p1) * t;
    }

    struct Circle {
        Point c;
        double r;

        Circle(Point c = Point(), double r = 0.0) : c(c), r(r) {}
    };

    //円と線分の交差判定
    bool is_intersect(Circle c, Line l) {
        double d = get_distanceLP(l, c.c);
        return d - c.r < EPS;
    }

    bool is_intersect(Circle c1, Circle c2) {
        double d = get_distance(c1.c, c2.c);
        return d - (c1.r + c2.r) < EPS;
    }

    //円と線分の交点
    pair<Point, Point> get_cross_points(Circle c, Line l) {
//        assert(is_intersect(c, l));
        if (!is_intersect(c, l)) exit(-1);
        Vector pr = project(l, c.c);
        Vector e = (l.p2 - l.p1) / abs(l.p2 - l.p1);
        double base = sqrt(c.r * c.r - norm(pr - c.c));
        return make_pair(pr + e * base, pr - e * base);
    }

    double arg(Vector p) {
        return atan2(p.y, p.x);
    }

    Vector polar(double a, double r) {
        return Point(cos(r) * a, sin(r) * a);
    }

    pair<Point, Point> get_cross_points(Circle c1, Circle c2) {
        assert(is_intersect(c1, c2));
        double d = abs(c1.c - c2.c);
        double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
        double t = arg(c2.c - c1.c);
        return make_pair(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
    }

    using Polygon = vector<Point>;

    static const int OUT = 0;
    static const int ON = 1;
    static const int IN = 2;

    // 点pが多角形gにあるかを判定
    // 計算量はO(|g|)
    int contains(Polygon g, Point p) {
        int n = g.size();
        bool x = false;
        for (int i = 0; i < n; ++i) {
            Point a = g[i] - p, b = g[(i + 1) % n] - p;
            if (abs(cross(a, b)) < EPS && dot(a, b) < EPS) return ON;
            if (a.y > b.y) swap(a, b);
            if (a.y < EPS && EPS < b.y && cross(a, b) > EPS) x = !x;
        }
        return x ? IN : OUT;
    }

    // 点集合sの凸集合を求める。
    // Andrew's Algorithmを使用
    // 計算量はO(|s|log(|s|))
    Polygon get_convex_hull(Polygon s) {
        if (s.size() < 3) return s;
        Polygon u, l;
        sort(s.begin(), s.end());

        u.push_back(s[0]);
        u.push_back(s[1]);
        l.push_back(s[s.size() - 1]);
        l.push_back(s[s.size() - 2]);

        //凸包の上部
        for (int i = 2; i < s.size(); ++i) {
            for (int n = u.size(); n >= 2 && ccw(u[n - 2], u[n - 1], s[i]) == CCW; --n) {
                u.pop_back();
            }
            u.push_back(s[i]);
        }

        //凸包の下部
        for (int i = s.size() - 3; i >= 0; --i) {
            for (int n = l.size(); n >= 2 && ccw(l[n - 2], l[n - 1], s[i]) == CCW; --n) {
                l.pop_back();
            }
            l.push_back(s[i]);
        }
        reverse(l.begin(), l.end());
        for (int i = u.size() - 2; i >= 1; --i) l.push_back(u[i]);

        return l;
    }

    std::ostream &operator<<(std::ostream &os, const Point &p) {
//        os << "(" << p.x << ", " << p.y << ")";
        os << "" << p.x << " " << p.y << "";
        return os;
    }

};

using namespace Geometory;


class Solve {
public:
    void solve() {
        Int n;
        cin >> n;
        Polygon g(n);
        for (int i = 0; i < n; ++i) {
            cin >> g[i].x >> g[i].y;
        }

        Polygon convex_hull = get_convex_hull(g);
        Int si = 0;
        for (int i = 0; i < convex_hull.size(); ++i) {
            if (convex_hull[i].y < convex_hull[si].y ||
                (convex_hull[i].y == convex_hull[si].y && convex_hull[i].x < convex_hull[si].x)) {
                si = i;
            }
        }

        rotate(convex_hull.begin(), convex_hull.begin() + si, convex_hull.end());

//        sort(all(convex_hull), [](auto a, auto b) {
//            return a.y == b.y ? a.x < b.x : a.y < b.y;
//        });

        cout << convex_hull.size() << endl;

        for (auto p : convex_hull) {
            Int x = p.x, y = p.y;
//            cout << p << endl;
            cout << x << " " << y << endl;
        }


    }

};


int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);

    Solve().solve();


    return 0;
}



Measured memory_bytes: 8036