#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<set>

using namespace std;
typedef long long LL;

const double Pi = acos(-1.0);
const int INf = 0x7fffffff;
const double eps = 1e-10;

int sgn(double d) {
    if(fabs(d) < eps)
        return 0;
    if(d > 0)
        return 1;
    return -1;
}

int dcmp(double x, double y) {
    if(fabs(x - y) < eps)
        return 0;
    if(x > y)
        return 1;
    return -1;
}

struct Point {
    double x, y;
    Point(double _x = 0, double _y = 0):x(_x), y(_y){}
};

typedef Point Vector;

Vector operator + (Vector A, Vector B) {
    return Vector(A.x + B.x, A.y + B.y);
}

Vector operator - (Point A, Point B) {
    return Vector(A.x - B.x, A.y - B.y);
}

Vector operator * (Vector A, double p) {
    return Vector(A.x * p, A.y * p);
}

Vector operator / (Vector A, double p) {
    return Vector(A.x / p, A.y / p);
}

bool operator == (const Point &a, const Point &b) {
    if(sgn(a.x-b.x) == 0 && sgn(a.y-b.y) == 0)
        return true;
    return false;
}

double Dot(Vector A, Vector B) {
    return A.x * B.x + A.y * B.y;
}

double Cross(Vector A, Vector B) {
    return A.x * B.y - B.x * A.y;
}

double Length(Vector A) {
    return sqrt(Dot(A, A));
}

double Angle(Vector A, Vector B) {
    return acos(Dot(A, B) / Length(A) / Length(B));
}

double Area2(Point A, Point B, Point C) {
    return Cross(B - A, C - A);
}

Vector Rotate(Vector A, double rad) { // 逆时针旋转rad
    return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) - A.y * cos(rad));
}

Vector Normal(Vector A) {      //A左转90°的单位法向量
    double L = Length(A);
    return Vector(-A.y / L, A.x / L);
}

bool ToLeftTest(Point a, Point b, Point c) {
    return Cross(b - a, c - a) > 0;
}

struct Line {
    Point v, p;
    Line(Point v, Point p) : v(v), p(p){}
    Point point(double t) {
        return v + (p - v) * t;
    }
};

Point GetLineIntersection(Point P, Vector v, Point Q, Vector w) {
    Vector u = P - Q;
    double t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}

double DistanceToLine(Point P, Point A, Point B) {
    Vector v1 = B - A, v2 = P - A;
    return fabs(Cross(v1, v2) / Length(v1));
}


double DistanceToSegment(Point P, Point A, Point B) {
    if(A == B)
        return Length(P - A);
    Vector v1 = B - A, v2 = P - A, v3 = P - B;
    if(sgn(Dot(v1, v2)) < 0)
        return Length(v2);
    if(sgn(Dot(v1, v3)) > 0)
        return Length(v3);
    return DistanceToLine(P, A, B);
}

Point GetLineProjection(Point P, Point A, Point B) {
    Vector v = B - A;
    return A + v * (Dot(v, P - A) / Dot(v, v));
}

bool OnSegment(Point p, Point a1, Point a2){
    return sgn(Cross(a1-p, a2-p)) == 0 && sgn(Dot(a1-p, a2-p)) < 0;
}

int PointAtSegment(Point p, Point a, Point b) {
    Vector v1 = p - a, v2 = b - a;
    if(sgn(Cross(v1, v2)) == -1)
        return -1;
    else if(sgn(Cross(v1, v2)) == 1)
        return 1;
    else if(sgn(Dot(v1, v2)) == -1)
        return -1;
    else if(sgn(Length(v1) - Length(v2)) == 1)
        return 1;
    return 0;
}

bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2){
    return PointAtSegment(b1, a1, a2) * PointAtSegment(b2, a1, a2) <= 0 && PointAtSegment(a1, b1, b2) * PointAtSegment(a2, b1, b2) <= 0;
}

double DistancePointToSegment(Point p, Point a, Point b) {
    if(sgn(Dot(p - a, b - a)) == -1)
        return Length(p - a);
    else if(sgn(Dot(p - b, a - b)) == -1)
        return Length(p - b);
    return DistanceToLine(p, a, b);
}

double SegmentsDistance(Point a1, Point a2, Point b1, Point b2) {
    if(SegmentProperIntersection(a1, a2, b1, b2))
        return 0;
    return min(min(DistancePointToSegment(a1, b1, b2), DistancePointToSegment(a2, b1, b2)), min(DistancePointToSegment(b1, a1, a2), DistancePointToSegment(b2, a1, a2)));
}

double PolygonArea(Point *p, int n) {
    double s = 0.0;
    for (int i = 1; i < n - 1; ++i)
        s += Cross(p[i] - p[0], p[i + 1] - p[0]) / 2.0;
    return s;
}

Point p[100005];

bool AndrewCmp(Point a, Point b) {
    if(dcmp(a.x, b.x) == 0)
        return dcmp(a.y, b.y) < 0;
    return dcmp(a.x, b.x) < 0;
}



Point Stack[100005];

int Andrew(int n) {
    sort(p, p + n, AndrewCmp);
    int top = 0;
    for (int i = 0; i < n; ++i) {
        while(top > 1 && sgn(Cross(Stack[top - 1] - Stack[top - 2], p[i] - Stack[top - 2])) < 0)
            --top;
        Stack[top++] = p[i];
    }
    int k = top;
    for (int i = n - 2; i >= 0; --i) {
        while(top > k && sgn(Cross(Stack[top - 1] - Stack[top - 2], p[i] - Stack[top - 2])) < 0)
            --top;
        Stack[top++] = p[i];
    }
    if(n > 1)
        top--;
    printf("%d\n", top);
    int s = 0;
    for (int i = 0; i < top; ++i) {
        if(dcmp(Stack[s].y, Stack[i].y) > 0 || (dcmp(Stack[s].y, Stack[i].y) == 0 && dcmp(Stack[s].x, Stack[i].x) > 0))
            s = i;
    }
    for (int i = 0; i < top; ++i) {
        printf("%d %d\n", (int)Stack[(i + s) % top].x, (int)Stack[(i + s) % top].y);
    }
        return top;
}

/* Point g[105];

int PointContainPolygon(Point p, int n) {
    bool x = false;
    for (int i = 0; i < n; ++i) {
        Vector b = g[(i + 1) % n] - p, a = g[i] - p;
        if (sgn(Cross(a, b)) == 0 && sgn(Dot(a, b)) <= 0)
            return 1;
        if(dcmp(a.y, b.y) > 0)
            swap(a, b);
        if (sgn(a.y) <= 0 && sgn(b.y) > 0 && sgn(Cross(a, b)) == 1)
            x = !x;
    }
    return x ? 2 : 0;
} */

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%lf%lf", &p[i].x, &p[i].y);
    }
    Andrew(n);
    return 0;
}


Measured memory_bytes: 6344