#include <iostream>
#include <vector>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 200000 + 10;
const double EPS = 1e-7;
const double INF = 1e12;
struct Vec {
    double x,y;
    Vec() {}
    Vec(double x, double y) { 
        this->x = x, this->y = y; 
    }
    void read() { 
        scanf("%lf %lf", &x, &y); 
    }
    void prt() {
        printf("%d %d\n", (int)x, (int)y); 
    }
    double len() { 
        return sqrt(x * x + y * y); 
    }
    double len2() { 
        return x * x + y * y; 
    }
    bool operator <(const Vec& o)const {
        if(x == o.x)
            return y < o.y;
        return x < o.x;
    }
    Vec operator +(const Vec& o)const { 
        return Vec(x + o.x, y + o.y); 
    }
    Vec operator -(const Vec& o)const { 
        return Vec(x - o.x, y - o.y); 
    }
    Vec operator *(const double& k)const { 
        return Vec(k * x, k * y); 
    }
    double operator *(const Vec& o)const {
        return x * o.x + y * o.y; 
    }
    double operator ^(const Vec& o)const {
        return x * o.y - y * o.x; 
    }
    Vec rotate(double ang){ 
        return Vec(x * cos(ang) - y * sin(ang), x * sin(ang) + y * cos(ang)); 
    }
    Vec change(double l) { 
        if(len() < EPS) return *this; 
        return (*this) * (l/len()); 
    }
};


struct Line {
    Vec A1, A2;
    Line() {}
    Line(Vec A1, Vec A2) {
        this->A1 = A1, this->A2 = A2;
    }
    double len() {
        return (A2-A1).len();
    }
    double Len2() {
        return (A2-A1).len2();
    }
};

// P to L
double disToLine(Vec P, Line L) {
    return abs((P-L.A1)^(L.A2-L.A1)) / L.len();
}
Vec projection(Vec P, Line L) {
    double shadowLen = (P - L.A1) * (L.A2 - L.A1);
    return L.A1 + (L.A2-L.A1).change(shadowLen);
}
Vec rejection(Vec P, Line L) {
    return projection(P,L) * 2 - P;
}
bool onseg(Vec P, Line L) {
    if(disToLine(P, L) > EPS) return 0;
    return (P-L.A1) * (P-L.A2) <= 0;
}
Vec Lineintersect(Line L1, Line L2) {
    // 0 ~ (L2.A1 - L1.A1) ^ (L2.A2 - L1.A1);
    // 1 ~ (L2.A1 - L1.A2) ^ (L2.A2 - L1.A2);
    double F0 = (L2.A1 - L1.A1) ^ (L2.A2 - L1.A1);
    double F1 = (L2.A1 - L1.A2) ^ (L2.A2 - L1.A2);
    if (abs(F1 - F0) < EPS) return Vec(INF, INF);
    return L1.A1 + (L1.A2 - L1.A1) * (- F0 / (F1 - F0));
}
Vec Segintersect(Line L1, Line L2) {
    Vec P = Lineintersect(L1, L2);
    if (P.x == INF) return P;
    if (onseg(P, L1) && onseg(P, L2)) return P;
    return Vec(INF, INF);
}
bool disToSeg(Vec P, Line L) {
    double ans = min((P-L.A1).len(), (P-L.A2).len());
    ans = min(ans, (P - projection(P,L)).len());
    return ans;
}
// 

vector<Vec> convexHull(vector<Vec> v) {
    sort(v.begin(), v.end());
    vector<Vec> ans; ans.resize(2*v.size());
    int top = 0, n = v.size();
    if(n <= 1) return v;

    for(int i = 0; i < n; ans[top++] = v[i++]) {
        while (top > 1 && ((ans[top-1] - ans[top-2]) ^ (v[i] - ans[top-1])) < -EPS) -- top;
    }
    for(int i = n-2, t = top; i >= 0; ans[top++] = v[i--]) {
        while (top > t && ((ans[top-1] - ans[top-2]) ^ (v[i] - ans[top-1])) < -EPS) -- top;
    } 
    while(ans.size() >= top) ans.pop_back();
    return ans;
}

int n; vector<Vec> v;
int main(){
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++) {
        Vec P; P.read(); v.push_back(P);
    }
    vector<Vec> ret = convexHull(v);
    printf("%d\n", (int)ret.size());

    double mx = INF, my = INF; int bst;
    for(int i = 0; i < ret.size(); i ++) {
        if(ret[i].y < my || (ret[i].y == my && ret[i].x < mx))
            mx = ret[i].x, my = ret[i].y, bst = i;
    }
    for(int i=0;i<ret.size();i++)
        ret[(i+bst)%(int)ret.size()].prt();
}




Measured memory_bytes: 9224