#include<bits/stdc++.h>
#define rep(i, n) for(int i = 1, i##_end_ = (n); i <= i##_end_; ++i)
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;


const double eps = 1e-10;
int sgn(double x) {
    if(fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}
struct Point {
    double x, y;
    Point(double xp=0, double yp=0): x(xp), y(yp) { }
    Point operator + (const Point& rhs) const { return Point(x+rhs.x, y+rhs.y); }
    Point operator - (const Point& rhs) const { return Point(x-rhs.x, y-rhs.y); }
    Point operator * (const double& k) const { return Point(x*k, y*k); }
    Point operator / (const double& k) const { return Point(x/k, y/k); }
    bool operator < (const Point& rhs) const { return x < rhs.x || (x==rhs.x && y<rhs.y); }
    bool operator == (const Point& rhs) const {return sgn(x - rhs.x) == 0 && sgn(y - rhs.y) == 0; }
    Point turn(double rad) { return Point(x*cos(rad)-y*sin(rad), x*sin(rad)+y*cos(rad)); }
    Point turn90() { return Point(-y, x); }
    void scan() { scanf("%lf%lf", &x, &y); }
};
typedef Point Vector;

double dot(Vector x, Vector y) { return x.x*y.x + x.y*y.y; }
double length(Vector x) { return sqrt(dot(x, x)); }
double dist2(Point A, Point B) { return dot(A - B, A - B); }
double cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }
double angle(Vector A, Vector B) { return acos(dot(A, B) / length(A) / length(B)); }

Point LineProjection(Point P, Point A, Point B) {
    Vector v = B - A;
    return A + v * (dot(P-A, v) / dot(v, v));
}
Point LineReflection(Point P, Point A, Point B) {
    Point proj = LineProjection(P, A, B);
    return proj + proj - P;
}

int Clockwise(Point O, Point A, Point B) {
    /* Clockwise: -1
       Counterclockwise: 1
       Otherwise: 0
    */
    return sgn(cross(A - O, B - O));
}

void SegCheck(Point a, Point b, Point p) {
    Vector d = b - a, e = p - a;
    if(sgn(dot(d, e)) == -1) puts("ONLINE_BACK");
    else if(sgn(length(e) - length(d)) == 1) puts("ONLINE_FRONT");
    else puts("ON_SEGMENT");
}

int JudgeLLParOrth(Point a1, Point a2, Point b1, Point b2) {
    /* 2: parallel
       1: orthogonal
       0: otherwise
    */
    Vector v = a2 - a1, w = b2 - b1;
    if(sgn(cross(v, w)) == 0) return 2;
    if(sgn(dot(v, w)) == 0) return 1;
    return 0;
}

bool CheckSSProperIntersection(Point a1, Point a2, Point b1, Point b2) {
    double c1 = cross(a1 - b1, b2 - b1), c2 = cross(a2 - b1, b2 - b1);
    double c3 = cross(b1 - a1, a2 - a1), c4 = cross(b2 - a1, a2 - a1);
    return sgn(c1) * sgn(c2) == -1 && sgn(c3) * sgn(c4) == -1;
}
bool OnSeg(Point P, Point A, Point B) { // (A, B) not inclusive
    Vector v = B - P, w = A - P;
    if(sgn(cross(v, w)) != 0) return false;
    return sgn(dot(v, w)) == -1;
}

bool CheckSSIntersection(Point a1, Point a2, Point b1, Point b2) {
    if(a1 == b1 || a1 == b2 || a2 == b1 || a2 == b2) return true;
    if(OnSeg(a1, b1, b2) || OnSeg(a2, b1, b2) || OnSeg(b1, a1, a2) || OnSeg(b2, a1, a2))
        return true;
    return CheckSSProperIntersection(a1, a2, b1, b2);
}

// Ensure lines intersect first!!! cross(v, w) != 0
Point LLIntersection(Point a1, Point a2, Point b1, Point b2) {
    Vector v = a2 - a1, w = b2 - b1, u = a1 - b1;
    assert(cross(v, w) != 0);
    double t1 = cross(w, u) / cross(v, w);
    return a1 + v * t1;
} // Line: 2 Points

Point LLIntPV(Point P, Vector v, Point Q, Vector w) {
    Vector u = P - Q;
    assert(cross(v, w) != 0);
    double t = cross(w, u) / cross(v, w);
    return P + v * t;
} // Line: Point + Vector

double PLDist(Point P, Point A, Point B) { // Point-Line
    return fabs(cross(B - P, A - P) / length(A - B));
}
double PSDist(Point P, Point A, Point B) { // Point-Segment
    if(A == B) return length(P - A);
    if(sgn(dot(A-P, B-A)) == 1) return length(A - P);
    if(sgn(dot(B-P, B-A)) == -1) return length(B - P);
    return fabs(cross(B-P, A-P) / length(A-B));
}
double SSDist(Point a1, Point a2, Point b1, Point b2) {
    // segment - segment
    if(CheckSSIntersection(a1, a2, b1, b2)) return 0.0;
    return min(min(PSDist(a1,b1,b2), PSDist(a2,b1,b2)), min(PSDist(b1,a1,a2), PSDist(b2,a1,a2)));
}


// Polygon Operations
typedef vector<Point> Polygon;
double PolygonArea(const Polygon& p) {
    int sz = p.size();
    double tot = 0.0;
    for(int i = 1; i < sz - 1; ++i)
        tot += cross(p[i]-p[0], p[i+1]-p[0]);
    return fabs(tot / 2.0);
}
bool IsConvex(Polygon po, int strict = 1) {
    // po should be given in counterclockwise !!!!
    int sz = po.size(); po.push_back(po[0]), po.push_back(po[1]);
    for(int i = 0; i < sz; ++i)
        if(sgn(cross(po[i+1]-po[i], po[i+2]-po[i])) < strict)
            return false;
    return true;
}
int InPolygon(Point pt, Polygon po) {
    /* 2: in polygon
     * 1: on segment
     * 0: not in polygon
    */
    int sz = po.size(), wn = 0; po.push_back(po[0]);
    for(int i = 0; i < sz; ++i) {
        Point u = po[i], v = po[i + 1];
        if(OnSeg(pt, u, v) || pt == u) return 1;
        if(sgn(u.y-v.y) == 1) swap(u, v);
        if(pt.y<=u.y || pt.y>v.y) continue;
        if(sgn(cross(pt-u, v-u)) == -1) wn ^= 1;
    }
    return wn << 1;
}
Polygon ConvexHull(Polygon pts, int strict = 1) {
    sort(pts.begin(), pts.end());
    int pn = pts.size(), m = -1;
    vector<Point> res(pn << 1);
    for(int i = 0; i < pn; ++i) {
        while(m > 0 && sgn(cross(res[m]-res[m-1], pts[i]-res[m-1]))<strict) m--;
        res[++m] = pts[i];
    }
    int k = m;
    for(int i = pn - 2; i >= 0; --i) {
        while(m > k && sgn(cross(res[m]-res[m-1], pts[i]-res[m-1]))< strict) m--;
        res[++m] = pts[i];
    }
    res.resize(m + (pn==1));
    return res;
}

// Circle
struct Circle {
    Point o;
    double r;
    Circle(Point O, double R): o(O), r(R) { }
    Circle(Point A, Point B, Point C) { // 三点确定圆
        assert(sgn(cross(B-A, C-A)) != 0);
        Point mab = (A + B) / 2.0, ab = (B - A).turn90();
        Point mbc = (B + C) / 2.0, bc = (C - B).turn90();
        o = LLIntPV(mab, ab, mbc, bc);
        r = length(A - o);
    }
    Point at(double theta) { return o + Point(cos(theta)*r, sin(theta)*r); }
    bool out(Point pt) { return sgn(length(o-pt) - r) == 1;}
};


mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
Circle MinCircle(vector<Point> pt) { // 最小圆覆盖，随机增量法
    int sz = pt.size();
    shuffle(pt.begin(), pt.end(), rng);
    Circle cc(pt[0], 0.0);
    for(int i = 0; i < sz; ++i) if(cc.out(pt[i])) {
        cc = Circle(pt[i], 0.0);
        for(int j = 0; j < i; ++j) if(cc.out(pt[j])) {
            cc = Circle((pt[i] + pt[j]) / 2.0, length(pt[i]-pt[j]) / 2.0);
            for(int k = 0; k < j; ++k) {
                if(cc.out(pt[k])) {
                    cc = Circle(pt[i], pt[j], pt[k]);
                }
            }
        }
    }
    return cc;
}



int main() {
    Point x;
    Polygon pol;
    int q; scanf("%d", &q);
    while(q--) {
        x.scan();
        pol.push_back(x);
    }
    Polygon ch = ConvexHull(pol, 0), ans;
    int sz = ch.size(), idx = 0;
    Point my = ch[0];
    for(int i = 1; i < sz; ++i) {
        if(ch[i].y < my.y || (ch[i].y==my.y&&ch[i].x<my.x))
            my = ch[i], idx = i;
    }
    for(int i = idx; i < sz; ++i) ans.push_back(ch[i]);
    for(int i = 0; i < idx; ++i) ans.push_back(ch[i]);
    printf("%d\n", sz);
    for(int i = 0; i < sz; ++i)
        printf("%.0f %.0f\n", ans[i].x, ans[i].y);
    return 0;
}


Measured memory_bytes: 10116