#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <climits>
 
using namespace std;
 
typedef long long lint;
 
//定数
const double EPS = 1e-10;
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};
const int INF = 1001001001;
const lint INFLL = 1001001001001001001ll;
 
//初期化関連
#define zclear(a) memset((a), 0 ,sizeof(a))
#define mclear(a) memset((a), -1, sizeof(a))
 
//デバッグ出力
#define show(x)  cerr << #x << " = " << (x) << endl;
#define debug(x) cerr << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl;
 
bool eq(double a, double b) //a == b
{
    return (fabs(a - b) <= EPS);
}
 
bool neq(double a, double b) //a != b
{
    return (!eq(a, b));
}
   
bool lt(double a, double b) //a < b
{
    return (a - b < -EPS);
}
   
bool leq(double a, double b) //a <= b
{
    return (lt(a, b) || eq(a, b));
}
   
bool gt(double a, double b) //a > b
{
    return (a - b > +EPS);
}
   
bool geq(double a, double b) //a >= b
{
    return (gt(a, b) || eq(a, b));
}
 
class Point{
public:
    double x, y;
    Point(double x = 0, double y = 0): x(x), y(y){}
    Point operator + (Point p){return Point(x + p.x, y + p.y);}
    Point operator - (Point p){return Point(x - p.x, y - p.y);}
    Point operator * (double a){return Point(x * a, y * a);}
    Point operator / (double a){return Point(x / a, y / a);}
    Point operator * (const Point &a){
        return Point(x * a.x - y * a.y, x * a.y + y * a.x);
    }
    bool operator < (const Point &p) const {
        return (x != p.x ? x < p.x : y < p.y);
    }
    bool operator == (const Point &p) const {
        return (fabs(x - p.x) < EPS && fabs(y - p.y) < EPS);
    }
};
 
typedef Point Vector;
typedef vector<Point> Polygon;
 
double norm(Vector a){return (a.x * a.x + a.y * a.y);}
double abs(Vector a){return (sqrt(norm(a)));}
double abs(Vector a, Vector b){return (sqrt(norm(a - b)));}
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 - a.y * b.x);}
double atan(Vector a){return atan2(a.y, a.x);}
bool sortX(Point a, Point b){return (a.x != b.x ? lt(a.x, b.x) : lt(a.y, b.y));}
bool sortY(Point a, Point b){return (a.y != b.y ? lt(a.y, b.y) : lt(a.x, b.x));}
Point baseP;
bool sortT(Point a, Point b){
	return (gt(cross(a - baseP, b - baseP), 0));
}

int ccw(Point p0, Point p1, Point p2)
{
    Vector a = p1 - p0;
    Vector 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 の順で一直線上
}
 
Polygon normalize(Polygon &v)
{
    int pt = 0;
    for (int i = 1; i < v.size(); i++){
        if (v[pt].y > v[i].y || (v[pt].y == v[i].y && v[pt].x > v[i].x)){
            pt = i;
        }
    }
    swap(v[pt], v[0]);
    baseP = v[0];
    sort(v.begin() + 1, v.end(), sortT);
    return (v);
}
 
Polygon conhel(Polygon& ps)
{
    sort(ps.begin(), ps.end());
    int k = 0, n = (int)ps.size();
    Polygon qs(n * 2);
       
    for (int i = 0; i < n; i++){
      while (k > 1 && cross(qs[k - 1] - qs[k - 2], ps[i] - qs[k - 1]) < -EPS) k--;
      qs[k++] = ps[i];
    }
   
    for (int i = n - 2, t = k; i >= 0; i--){
      while (k > t && cross(qs[k - 1] - qs[k - 2], ps[i] - qs[k - 1]) < -EPS) k--;
      qs[k++] = ps[i];
    }
   
    qs.resize(k - 1);
    return (qs);
}
 
double getArea(Polygon t){
    double ans = 0.0;
    for (unsigned int i = 0; i < t.size(); i++)
        ans += cross(t[i] , t[(i + 1) % t.size()]);
    return (fabs(ans) / 2);
}
 
bool isIntersectLL(Point p1, Point p2, Point p3, Point p4)
{// 同一直線の場合は交差していると判定する.
    return (abs(cross(p2 - p1, p4 - p3)) > EPS || // 傾きが異なる
        abs(cross(p2 - p1, p4 - p1)) < EPS); // 同じ直線である
}
bool isIntersectLS(Point p1, Point p2, Point p3, Point p4)
{// 同一直線上にある場合も交差と判定
    return (cross(p2 - p1, p3 - p1) * cross(p2 - p1, p4 - p1) < EPS);
}
bool isIntersectLP(Point p1, Point p2, Point p)
{
    return (neq(abs(ccw(p1, p2, p)), 1));
}
bool isIntersectSS(Point p1, Point p2, Point p3, Point p4)
{
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
             ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
bool isIntersectSP(Point p1, Point p2, Point p)
{
    return (eq(ccw(p1, p2, p), 0));
}
bool isInsideCP(Point c, double r, Point p)
{
    return (leq(abs(c - p), r));
}
bool isInsideCC(Point c1, double r1, Point c2, double r2)
{
    return (leq(abs(c1 - c2), r1 + r2) && geq(abs(c1 - c2), abs(r1 - r2)));
}
bool isInsidePolyP(Polygon gon, Point p)
{
    for (int i = 0; i < gon.size(); i++){
        if (ccw(gon[i], gon[(i + 1) % gon.size()], p) == -1) return (false);
    }
    return (true);
}
 
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 上への反射
{
    return (p3 + (projection(p1, p2, p3) - p3) * 2);
}
double distanceLP(Point p1, Point p2, Point p)
{
    return (abs(p - projection(p1, p2, p)));
}
double distanceLL(Point l1, Point l2, Point m1, Point m2)
{
    return (isIntersectLL(l1, l2, m1, m2) ? 0 : distanceLP(l1, l2, m1));
}
double distanceLS(Point l1, Point l2, Point s1, Point s2)
{
    if (isIntersectLS(l1, l2, s1, s2)) return (0);
    return (min(distanceLP(l1, l2, s1), distanceLP(l1, l2, s2)));
}
double distanceSP(Point s1, Point s2, Point p)
{
    Point r = projection(s1, s2, p);
    if (isIntersectSP(s1, s2, r)) return (abs(r - p));
    return (min(abs(s1 - p), abs(s2 - p)));
}
double distanceSS(Point s1, Point s2, Point t1, Point t2){
    if (isIntersectSS(s1, s2, t1, t2)) return (0);
    return (min(min(distanceSP(s1, s2, t1), distanceSP(s1, s2, t2)),
            min(distanceSP(t1, t2, s1), distanceSP(t1, t2, s2))));
}
Point interPointLL(Point a1, Point a2, Point b1, Point b2)
{
    double d = cross(a2 - a1, b2 - b1);
    if (abs(d) < EPS) throw "Segment is Paralell!";
    Point a = a2 - a1, b = b2 - b1;
    double t = cross(b, b1 - a1) / cross(b, a);
    return (a1 + a * t);
}
vector<Point> interPointCC(Point c1, double r1, Point c2, double r2)
{
    vector<Point> res;
    if (abs(c1 - c2) < EPS) return (vector<Point>()); // 交点が絶対にない
    double d = abs(c1 - c2);
    double rc = (d * d + r1 * r1 - r2 * r2) / (2 * d);
    double rs = sqrt(r1 * r1 - rc * rc);
    Point diff = (c2 - c1) / d;
    res.push_back(Point(c1 + diff * Point(rc, rs)));
    res.push_back(Point(c1 + diff * Point(rc, -rs)));
    return (res);
}
vector<Point> crosspointCL(Point a, double ra, Point b1, Point b2)
{
    vector<Point> res;
    double dist = distanceLP(b1, b2, a);
    if (leq(dist, ra)){
        Point s = projection(b1, b2, a);
        dist = sqrt(ra * ra - dist * dist);
        Point t = (b2 - b1) / abs(b2 - b1);
        res.push_back(s + t * dist);
        res.push_back(s - t * dist);
    }
    return (res);
}
 
Point unitVector(Vector t)
{
    double u = abs(t);
    return (Point(t.x / u , t.y / u));
}
 
Point normalVector(Point p, Point a, Point b)
{
    Point v = unitVector(b - a);
    v = cross(v, p - a) > 0 ? Point(v.y, -v.x) : Point(-v.y, v.x);
    return v * distanceLP(p, a, b);
}
   
double getCos(Vector a, Vector b)
{
    return (dot(a, b) / (abs(a) * abs(b)));
}
   
double getSin(Vector a, Vector b)
{
    double t = getCos(a, b);
    return (sqrt(1.0 - t * t));
}
   
double getArg(Vector v)
{
    return (atan2(v.y, v.x));
}
 
Point rotate(Point t, Point p, double r)
{
    double ta = cos(r) * (t.x - p.x) - sin(r) * (t.y - p.y) + p.x;
    double tb = sin(r) * (t.x - p.x) + cos(r) * (t.y - p.y) + p.y;
    return (Point(ta, tb));
}
 
int main()
{
    int n;
     
    scanf("%d", &n);
    Polygon p(n), q;
    for (int i = 0; i < n; i++){
        scanf("%lf %lf", &p[i].x, &p[i].y);
    }
    
    q = conhel(p);
	/*q = normalize(q);
	
	for (int i = 0; i < n; i++){
		for (int j = 0; j < q.size(); j++){
			if (q[j] == p[i] || q[(j + 1) % n] == p[i]) break;
			if (ccw(q[j], q[(j + 1) % n], p[i]) == 0){
				q.insert(q.begin() + j + 1, p[i]);
				break;
			}
		}
	}
	*/
	printf("%d\n", q.size());
	
	int mins = 0;
	for (int i = 1; i < q.size(); i++) if (Point(q[i].y, q[i].x) < Point(q[mins].y, q[mins].x)) mins = i;
	for (int i = 0; i < q.size(); i++)
		printf("%.lf %.lf\n", q[(i + mins) % q.size()].x, q[(i + mins) % q.size()].y);
    return (0);
}

Measured memory_bytes: 6016