#include <bits/stdc++.h>
using namespace std;
#define repr(i,a,b) for(int i=a;i<b;i++)
#define rep(i,n) for(int i=0;i<n;i++)
#define reprrev(i,a,b) for(int i=b-1;i>=a;i--) // [a, b)
#define reprev(i,n) reprrev(i,0,n)
typedef long long ll;
typedef unsigned long long ull;
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }

/* attention
    long longのシフト演算には気をつけよう
    タイポした時のデバッグが死ぬほどきつくなるので変数名は最低3字くらい使った方がいいかも
*/

const ll mod = 1e9+7;

void chmod(ll &M){
    if(M >= mod) M %= mod;
    else if(M < 0){
        M += (abs(M)/mod + 1)*mod;
        M %= mod;
    }
}

ll modpow(ll x, ll n){
    if(n==0) return 1;
    ll res=modpow(x, n/2);

    if(n%2==0) return res*res%mod;
    else return res*res%mod*x%mod;
}

int getl(int i, int N) { return i==0? N-1:i-1; };
int getr(int i, int N) { return i==N-1? 0:i+1; };


// 線分 ab の偏角 返り値は[-π, π]
double argument(const pair<double, double> &a, const pair<double, double> &b){
    double ax=a.first, ay=a.second, bx=b.first, by=b.second;
    return atan2(by-ay, bx-ax);
}

/* <-----------------------------------------------------------------------------------> */
/* <-----------------------------------------------------------------------------------> */
/* <-----------------------------------------------------------------------------------> */
/* <-----------------------------------------------------------------------------------> */


long double const EPS = (1e-10);
bool equal(long double const a, long double const b) { return fabs(a-b) < EPS; }

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

    Point operator + (Point const & p) const { return Point{x + p.x, y + p.y}; }
    Point & operator += (Point const & p) { x += p.x; y += p.y; return *this; }
    Point operator - (Point const & p) const { return Point{x - p.x, y - p.y}; }
    Point & operator -= (Point const & p) { x -= p.x; y -= p.y; return *this; }
    Point operator * (long double a) const { return Point{a*x, a*y}; }
    Point & operator *= (long double a) { x *= a; y *= a; return *this; }
    Point operator / (long double a) const { return Point{x/a, y/a}; }

    long double norm() { return x*x + y*y; }
    long double abs() { return sqrt(norm()); }
    
    bool operator < (Point const & p) const { return x != p.x ? x < p.x : y < p.y; }
    bool operator == (Point const & p) const { return equal(x, p.x) && equal(y, p.y); }

    void print() { cout << x << ' ' << y << "\n"; }
};
using Vector = Point;

struct Segment{
    Point p1, p2;
    Segment(Point _p1, Point _p2)
        : p1(_p1), p2(_p2) {}
};
using Line = Segment;

struct Circle{
    Point c;
    double r;
    Circle(Point _c, double _r)
        : c(_c), r(_r) {}
};
using Polygon = vector<Point>;

// 内積 外積 外積は大きさを返す
// https://mathtrain.jp/gaiseki
long double dot(Vector a, Vector b) { return a.x*b.x + a.y*b.y; }
long double cross(Vector a, Vector b) { return a.x*b.y - a.y*b.x; }

// ベクトルの直交判定　平行判定
bool isOrthogonal(Vector const & a, Vector const & b) { return equal(dot(a, b), 0.0); }
bool isOrthogonal(Point const & a1, Point const & a2, Point const & b1, Point const & b2) { return isOrthogonal(a1-a2, b1-b2); }
bool isOrthogonal(Segment const & s1, Segment const & s2) { return isOrthogonal(s1.p1 - s1.p2, s2.p1 - s2.p2); }
bool isParallel(Vector const & a, Vector const & b) { return equal(cross(a, b), 0); }
bool isParallel(Point const & a1, Point const & a2, Point const & b1, Point const & b2) { return isParallel(a1-a2, b1-b2); }
bool isParallel(Segment const & s1, Segment const & s2) { return isParallel(s1.p1-s1.p2, s2.p1-s2.p2); }

// 線分 s に対する点 p の射影
Point project(Segment const & s, Point const & p){
    Vector base = s.p2 - s.p1;
    double r = dot(p - s.p1, base) / base.norm();
    return s.p1 + base * r;
}
// 線分 s を対称軸とした点 p の線対称点
Point reflect(Segment const & s, Point const & p) { return p + (project(s, p) - p) * 2.0; }

// 線分p0-p1 に対して線分 p0-p2 が反時計回りなのか直線上なのか線分上なのか...
static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;   // p2-p0-p1
static const int ONLINE_FRONT = -2; // p0-p1-p2
static const int ON_SEGMENT = 0;    // p0-p2-p1
int ccw(Point const & p0, Point const & p1, Point const & p2){
    Vector a = p1 - p0, b = p2 - p0;
    if(cross(a, b) > EPS) return COUNTER_CLOCKWISE;
    if(cross(a, b) < -EPS) return CLOCKWISE;
    if(dot(a, b) < -EPS) return ONLINE_BACK;
    if(a.norm() < b.norm()) return ONLINE_FRONT;
    return ON_SEGMENT;
}

// 線分 p1-p2 と線分 p3-p4 の交差判定（交点を持てば true）
bool intersect(Point const & p1, Point const & p2, Point const & p3, Point const & p4){
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
bool intersect(Segment const & s1, Segment const & s2) { return intersect(s1.p1, s1.p2, s2.p1, s2.p2); }

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

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

// 線分と線分の距離
long double getDistance(Segment s1, Segment s2) {
    if (intersect(s1, s2)) return 0.0;
    return min(min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)), min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2)));
}

// 線分と線分の交点　交点がある時
Point getCrossPoint(Segment s1, Segment s2) {
    Vector base = s2.p2 - s2.p1;
    long double d1 = abs(cross(base, s1.p1 - s2.p1));
    long double d2 = abs(cross(base, s1.p2 - s2.p1));
    long double t = d1 / (d1 + d2);
    return s1.p1 + (s1.p2 - s1.p1) * t;
}

//　円 c と直線 l の交点
pair<Point, Point> getCrossPoints(Circle c, Line l) {
    assert(getDistanceLP(l, c.c) < c.r + EPS);
    Vector pr = project(l, c.c);
    Vector e = (l.p2 - l.p1) / (l.p2 - l.p1).abs();
    long double base = sqrt(c.r*c.r - (pr-c.c).norm());
    return make_pair(pr + e*base, pr - e*base);
}

long double arg(Vector p) { return atan2(p.y, p.x); }
// a, r -> (rcos(a), rsin(a))
Vector polar(long double a, long double r) { return Point(a*cos(r), a*sin(r)); }

pair<Point, Point> getCrossPoints(Circle c1, Circle c2) {
    assert((c1.c - c2.c).norm() < (c1.r + c2.r) * (c1.r + c2.r) + EPS);
    long double d2 = (c1.c - c2.c).norm();
    long double a = acos((c1.r*c1.r + d2 - c2.r*c2.r) / (2*c1.r*sqrt(d2)));
    long double t = arg(c2.c - c1.c);
    return make_pair(c1.c + polar(c1.r, t+a), c1.c + polar(c1.r, t-a));
}

// IN 2, ON 1, OUT 0
// p が多角形 g に内包されるか
int contains(Polygon g, Point p) {
    int n = g.size();
    bool x = false;
    rep(i, n) {
        Point a = g[i] - p; Point b = g[(i+1)%n] - p;
        if (abs(cross(a, b)) < EPS && dot(a, b) < EPS) return 1;
        if (a.y > b.y) swap(a, b);
        if (a.y < EPS && b.y > EPS && cross(a, b) > EPS) x = !x;
    }
    return (x? 2:0);
}

// 多角形の凸包
// 辺上の点も求めるならば != CLOCKWISE を == COUNTER_CLOCKWISE に変える
Polygon andrewScan(Polygon s) {
    Polygon u, l;
    if (s.size() < 3) return s;
    sort(s.begin(), s.end()); // x, y を基準に昇順ソート
    // x が小さいものから２つ u に追加
    u.push_back(s[0]); u.push_back(s[1]);
    // x が大きいものから２つ l に追加
    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]) == COUNTER_CLOCKWISE; --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]) == COUNTER_CLOCKWISE; --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;
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);

    int n; cin >> n;
    Polygon g(n);
    rep(i, n) {
        int x, y; cin >> x >> y;
        g[i] = Point(x, y);
    }
    auto res = andrewScan(g);
    Polygon ans;
    int minv = 1000000, id = 0;
    rep(i, res.size()) {
        if (res[i].y < minv) { minv = res[i].y; id = i; }
    }
    for(int i=id; i<id+res.size(); ++i) ans.push_back(res[i%res.size()]);
    cout << ans.size() << endl;
    for(auto p: ans) p.print();

    return 0;
}





Measured memory_bytes: 14380