#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <numeric>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>

#define mp       make_pair
#define pb       push_back
#define all(x)   (x).begin(),(x).end()
#define rep(i,n) for(int i=0;i<(n);i++)
#define repi(i,a,b) for(int i = (a); i < (b); i++)

using namespace std;

const int INF=1<<29;
const double EPS=1e-9;

const int dx[]={1,0,-1,0},dy[]={0,-1,0,1};

typedef complex<double> point;
typedef vector<point> vertex;
/*
x 座標は p.real(), real(p), y 座標は p.imag(), imag(p) とする．
足し算，引き算，スカラー倍は定義済み．
operator < は入っていないので自分で namespace std に突っ込む．*/
namespace std{
    bool operator < (const point &lhs,const point &rhs){
        if(real(lhs) == real(rhs)){
            return imag(lhs) < imag(rhs);
        }else{
            return real(lhs) < real(rhs);
        }
    }
}
// 線分と点との距離
double dist_linesegment_and_point(point a1,point a2,point b);



// ベクタの長さ
double vector_length(point a){
    return abs(a);
}

// 二点間距離
double point_distance(point a,point b){
    return abs(a-b);
}

// 単位ベクトル
point unit_vector(point a){
    return a / abs(a);
}

// 法線ベクトル
pair<point,point> normal_vector(point a){
    point n1 = a * point(0,1);
    point n2 = a * point(0,-1);
    return make_pair(n1,n2);
}

// 点が一緒かどうか
bool point_eq(point a,point b){
    return abs(a-b) < EPS;
}
// 内積 (dot product) : a・b = |a||b|cosΘ
double dot(point a,point b){
    return real(conj(a)*b);
}

// 外積 (cross product) : |a×b| = |a||b|sinΘ
double cross(point a,point b){
    return imag(conj(a)*b);
}
//ラジアンに変換
double radian(double angle){
    return angle/180.0*M_PI;
}
//度数に変換
double degree(double radian){
    return radian*180.0/M_PI;
}

// a1,a2を端点とする線分(la)とb1,b2を端点(lb)とする線分の交差判定
bool is_intersected_linesegment(point a1,point a2,point b1,point b2){
    if(max(a1.real(),a2.real()) + EPS < min(b1.real(),b2.real())) return false;
    if(max(b1.real(),b2.real()) + EPS < min(a1.real(),a2.real())) return false;
    if(max(a1.imag(),a2.imag()) + EPS < min(b1.imag(),b2.imag())) return false;
    if(max(b1.imag(),b2.imag()) + EPS < min(a1.imag(),a2.imag())) return false;
    return (cross(a2-a1,b1-a1)*cross(a2-a1,b2-a1) < EPS) and
    (cross(b2-b1,a1-b1)*cross(b2-b1,a2-b1) < EPS);

}


// a1,a2を端点とする線分(la)とb1,b2を端点とする線分(lb)の交点計算
point intersection_point_linesegment(point a1,point a2,point b1,point b2) {
    if(a1 == b1 or a1 == b2) return a1;
    if(a2 == b1 or a2 == b2) return a2;
    point b = b2-b1;
    double d1 = abs(cross(b, a1-b1));
    double d2 = abs(cross(b, a2-b1));
    double t = d1 / (d1 + d2);
    return a1 + (a2-a1) * t;
}

// 線分同士の最短距離
double dist_linesegment_and_linesegment(point a1,point a2,point b1,point b2){
    if(is_intersected_linesegment(a1,a2,b1,b2)){
        return 0;
    }
    return min(min(dist_linesegment_and_point(a1,a2,b1),
               dist_linesegment_and_point(a1,a2,b2)),
    min(dist_linesegment_and_point(b1,b2,a1),
        dist_linesegment_and_point(b1,b2,a2)));
}


// 2直線の直交判定 : a⊥b <=> dot(a, b) = 0
// verify
bool is_orthogonal(point a1,point a2,point b1,point b2) {
    return abs(dot(a1-a2,b1-b2))< EPS;
}

// 2直線の平行判定 : a//b <=> cross(a, b) = 0
//verify
bool is_parallel(point a1,point a2,point b1,point b2) {
    return abs(cross(a2-a1,b2-b1)) < EPS;
}

// a1,a2を通る直線とb1,b2を通る直線の交差判定
bool is_intersected_line(point a1,point a2,point b1,point b2) {
    return not is_parallel(a1,a2,b1,b2);
}

// a1,a2を通る直線とb1,b2を通る直線の交点計算
point intersection_line(point a1,point a2,point b1,point b2) {
    point a = a2 - a1,b = b2 - b1;
    return a1 + a * cross(b, b1-a1) / cross(b, a);
}

// 直線と点との距離
double dist_line_and_point(point a1,point a2,point b){
    return abs(cross(a2-a1,b-a1)) / abs(a2-a1);
}

// 線分と点との距離
double dist_linesegment_and_point(point a1,point a2,point b){
    if(dot(a2-a1,b-a1) < EPS) return abs(b-a1);
    if(dot(a1-a2,b-a2) < EPS) return abs(b-a2);
    return dist_line_and_point(a1,a2,b);
}

// 直線と点の最短距離を実現する直線の点(すいせんの足)(みけんしょう)
point nearest_point_line_and_point(point a1,point a2,point b){
    return a1 + (a2-a1) * dot((a2-a1),(b-a1)) / norm(a2-a1);
}

// 線分と点の最短距離を実現する線分嬢の点(みけんしょう)
point nearest_point_linesegment_and_point(point a1,point a2,point b){
    if(dot(a2-a1,b-a1) < EPS) return a1;
    if(dot(a1-a2,b-a2) < EPS) return a2;
    return nearest_point_line_and_point(a1,a2,b);
}
// 円と線分の交差判定
bool is_cross_linesegment_and_circle(point c,double r,point a1,point a2){
    return (dist_linesegment_and_point(a1,a2,c) < r+EPS and
            (r < abs(c-a1) + EPS or r < abs(c-a2) + EPS));
}

//線の進行方向
int ccw(point p0, point p1, point p2)
{
    point a = p1 - p0;
    point b = p2 - p0;
    if (cross(a, b) > EPS) return (+1); //p0, p1, p2 が反時計周り
    if (cross(a, b) < -EPS) return (-1);//p0, p1, p2 が時計回り
    if (dot(a, b) < -EPS) return (+2); //p2--p0--p1 の順で一直線上
    if (norm(a) < norm(b)) return (-2); //p0--p1--p2 の順で一直線上
    return (0); //p0--p2--p1 の順で一直線上
}

// 点が真に多角形(凸?)の中にはいっているか
bool is_inner_point_vertex(const vector<point> &ps,point a){
    int cc = ccw(ps[0],ps[1],a);
    if(not(cc == 1 or cc == -1)) return false;
    for(size_t i=0;i<ps.size();i++){
        if(cc != ccw(ps[i],ps[(i+1)%ps.size()],a)) return false;
    }
    return true;
}

// 点が辺上、もしくは内部にある。(未検証)
bool is_inner_point_vertex_or_line(const vector<point> &ps,point a){
    for(size_t i=0;i<ps.size();i++){
        if(dist_linesegment_and_point(ps[i],ps[(i+1)%ps.size()],a) < EPS){
            return true;
        }
    }
    return is_inner_point_vertex(ps,a);
}


// 凸包 (UVA 109)
vector<point> convex_hull(vector<point> ps){
    int n = ps.size();
    int k = 0;
    sort(ps.begin(),ps.end());
    vector<point> ch(2*n);
    for(int i=0;i<n;ch[k++] = ps[i++]){//lower-hull
        while(k >= 2 and ccw(ch[k-2],ch[k-1],ps[i]) ==-1) --k;
    }
    for(int i=n-2,t=k+1;i>=0;ch[k++]=ps[i--]){//upper-hull
        while(k >= t and ccw(ch[k-2],ch[k-1],ps[i]) == -1) --k;
    }
    ch.resize(k-1);
    return ch;
}

// remember,pts make convex.
// (http://judge.u-aizu.ac.jp/onlinejudge/cdescription.jsp?cid=ACAC002&pid=C)
double convex_diameter(const vector<point>& pts){
    const int n = pts.size();
    int is=0,js=0; // initial antipode.
    for(int i=1;i<n;i++){
        if(pts[i].imag() > pts[is].imag()) is = i;
        if(pts[i].imag() < pts[js].imag()) js = i;
    }
    double maxd = abs(pts[is]-pts[js]);
    int i,j,maxi,maxj;
    i = maxi = is;
    j = maxj = js;
    do{
        if(cross(pts[(i+1)%n]-pts[i],
           pts[(j+1)%n]-pts[j]) >= 0){
            j = (j+1)%n;
    }else{
        i = (i+1)%n;
    }
    if(abs(pts[i]-pts[j]) > maxd){
        maxd = abs(pts[i]-pts[j]);
        maxi = i;maxj = j;
    }
} while(not(i == is and j == js));
    // pts[maxi],pts[maxj] is pair of max diff.
return maxd;
}

// 円と円の交点(2点ある前提)
vector<point> circles_point(point c1,double r1,point c2,double r2){
    double d = abs(c1-c2);
    double s = (r1+r2+d) / 2;
    double S = sqrt(s*(s-r1)*(s-r2)*(s-d));
    double h = 2 * S / d;
    point v = (c2-c1) / (abs(c2-c1));

    double m = sqrt(r1*r1 - h*h);

    vector<point> ret;
    ret.push_back(c1 + m*v+h*v*point(0,1));
    ret.push_back(c1 + m*v-h*v*point(0,1));
    return ret;
}

// clockwiseだと負
double triangle_area(point a,point b,point c){
    return cross(b-a,c-a)/2;
}

// clockwiseだと負
double vertex_area(vector<point> v){
    double ret = 0;
    for(int i=1;i<v.size()-1;i++){
        ret += triangle_area(v[0],v[i],v[i+1]);
    }
    return ret;
}
/*多角形の面積を求める*/
//verify
double area_vertex(const vertex& g){
    double S =0;
    for(int i =0;i <g.size();i++){
        S +=(cross(g[i],g[(i+1)%g.size()]));
    }
    return abs(S/2.0);
}
//円同士が互いに含まれているかどうか
//中心が(xa,ya)で半径r1の円Aと、中心が(xb,yb) で半径 r2 の円 B が与えられたとき、 B が A の中にあるとき 2、A が B の中にあるとき -2、A の円周と B の円周が交わっている場合 1、A と B が重なっていないとき 0 
//verify
int Circles_Intersection(point a,double r1,point b,double r2){
    double dp =abs(a-b);//中心間の距離
    double dr =r1+r2;//円の半径の合計
    if(dp >dr){
        return 0;
    }else if(dp +r1 <r2){
        return -2;
    }else if(dp +r2 <r1){
        return 2;
    }else if(dp <=dr){
        return 1;
    }
    return -100;
}
point projection(point p1, point p2, point p3) //直線p1p2 上への写像の点
{
    double t = dot(p3 - p1, p2 - p1) / norm(p2 - p1);
    return (p1 + (p2 - p1) * t);
}
point reflection(point p1, point p2, point p3) //直線p1p2 上への反射した点(線分p1p2に対してp3を線対称)
{

    return (p3 + 2.0*(projection(p1, p2, p3) - p3));
}
//凸性判定
/*与えられた多角形 P が凸かどうかを判定する．一周回ってみて，右向きの辺がないことを確かめればよい*/
#define curr(P, i) P[(i) % P.size()]
#define next(P, i) P[(i+1) % P.size()]
#define prev(P, i) P[(i+P.size()-1) % P.size()]
bool isconvex(vertex &P) {

    for (int i = 0; i < P.size(); ++i){
        int d =ccw(prev(P, i), curr(P, i), next(P, i));
        if(d==-1) return false;
    }
    return true;
}
int main(){
    int n;
    cin>>n;
    vertex g;
    for(int i =0;i < n;i++){
        int x,y;

        cin>>x>>y;
        point a(x,y);
        g.push_back(a);
    }
    vertex convex =convex_hull(g);
    cout <<convex.size()<<endl;
    for(int i =0;i <convex.size();i++){
        cout <<convex[i].real()<<" "<<convex[i].imag()<<endl;
    }
    return 0;
}