#include <bits/stdc++.h>
using namespace std;

#define Fi first
#define Se second
#define pb push_back
#define mp make_pair
#define rep(x, a, b) for(int x = (a); x <= (b); ++ x)
#define per(x, a, b) for(int x = (a); x >= (b); -- x)
#define rop(x, a, b) for(int x = (a); x < (b); ++ x)
#define por(x, a, b) for(int x = (a); x > (b); -- x) 

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned int unt;
typedef double db;
typedef pair <int, int> pii;
typedef vector <int> vi;

const db eps  = 1e-8;
const db _PI  = 3.1415926535897932384;
const db _E   = 2.7182818284590452354;
const int inf = 0x3f3f3f3f;
const LL INF  = 0x3f3f3f3f3f3f3f3fll;

int dcmp(db x) {
	if(x > eps) return 1;
	if(x < -eps) return -1;
	return 0;
}

int dcmp(db x, db y) {
	return dcmp(x - y);
}

struct Point {
	db x, y;
	Point(db _x = 0, db _y = 0) : x(_x), y(_y) {}
	void print() { printf("%.0f %.0f", x, y); }
};
struct Line {
	Point p1, p2;
	Line() {};
	Line(Point _p1, Point _p2) : p1(_p1), p2(_p2) {};
};

typedef Point Vector;
typedef Line Segment;
typedef vector<Point> Polygon;

bool cmp_y(Point a, Point b) {
	return dcmp(a.y, b.y) == 0 ? a.x < b.x : a.y < b.y;
}
bool cmp_x(Point a, Point b) {
	return dcmp(a.x, b.x) == 0 ? a.y < b.y : a.x < b.x;
}

// CCW
const int CCW_COUNTER_CLOCKWISE = 1;
const int CCW_CLOCKWISE = -1;
const int CCW_ONLINE_FRONT = -2;
const int CCW_ONLINE_BACK = 2;
const int CCW_ON_SEGMENT = 0;

Vector operator + (Vector a, Vector b) {
	return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (Vector a, Vector b) {
	return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, db b) {
	return Vector(a.x * b, a.y * b);
}
Vector operator / (Vector a, db b) {
	return Vector(a.x / b, a.y / b);
}
db operator * (Vector a, Vector b) {
	return a.x * b.x + a.y * b.y;
}
db operator ^ (Vector a, Vector b) {
	return a.x * b.y - a.y * b.x;
}
db dis(Point a, Point b) {
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
db dis2(Point a, Point b) {
	return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
db dis(Vector a) {
	return sqrt(a.x * a.x + a.y * a.y);
}
db dis2(Vector a) {
	return a.x * a.x + a.y * a.y;
}
Point Project(Line L, Point p) {
	Vector Base = L.p2 - L.p1;
	db len = Base * (p - L.p1);
	return L.p1 + Base * (len / dis2(L.p1, L.p2));
}
Point Reflect(Line L, Point p) {
	return p + (Project(L, p) - p) * 2.0;
}

int ccw(Point p0, Point p1, Point p2) {
	Vector p01 = p1 - p0, p02 = p2 - p0;
	if(dcmp(p01 ^ p02) == 1) return CCW_COUNTER_CLOCKWISE; // 逆时针 
	if(dcmp(p01 ^ p02) == -1) return CCW_CLOCKWISE;        // 顺时针 
	if(dcmp(p01 * p02) == -1) return CCW_ONLINE_BACK;
	if(dcmp(dis2(p01), dis2(p02)) == -1) return CCW_ONLINE_FRONT;
	return CCW_ON_SEGMENT;
}

bool isOrthogonal(Vector a, Vector b) {
	return dcmp(a * b) == 0;
}
bool isOrthogonal(Point a1, Point a2, Point b1, Point b2) {
	return isOrthogonal(a2 - a1, b2 - b1);
}
bool isOrthogonal(Line a, Line b) {
	return isOrthogonal(a.p2 - a.p1, b.p2 - b.p1);
}
bool isParallel(Vector a, Vector b) {
	return dcmp(a ^ b) == 0;
}
bool isParallel(Point a1, Point a2, Point b1, Point b2) {
	return isParallel(a2 - a1, b2 - b1);
}
bool isParallel(Line a, Line b) {
	return isParallel(a.p2 - a.p1, b.p2 - b.p1);
}
bool IntersectSS(Point a1, Point a2, Point b1, Point b2) {
	return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 && ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}
bool IntersectSS(Segment a, Segment b) {
	return IntersectSS(a.p1, a.p2, b.p1, b.p2);
}
Point getCrossPointSS(Segment a, Segment b) {
	Vector Base = b.p2 - b.p1;
	db S1 = fabs(Base ^ (a.p1 - b.p1));
	db S2 = fabs(Base ^ (a.p2 - b.p1));
	return a.p1 + (a.p2 - a.p1) * (S1 / (S1 + S2));
}
Point getCrossPointLL(Line a, Line b) {
	db S1 = (b.p2 - b.p1) ^ (a.p2 - a.p1);
	db S2 = (b.p2 - b.p1) ^ (b.p2 - a.p1);
	if(dcmp(S1) == 0 && dcmp(S2) == 0) return a.p1;
	return a.p1 + (a.p2 - a.p1) * (S2 / S1); 
}
db disLP(Line a, Point p) {
	return fabs(((a.p2 - a.p1) ^ (p - a.p1)) / dis(a.p1, a.p2));
}
db disSP(Segment a, Point p) {
	if(dcmp((a.p2 - a.p1) * (p - a.p1)) == -1) return dis(a.p1, p);
	if(dcmp((a.p1 - a.p2) * (p - a.p2)) == -1) return dis(a.p2, p);
	return disLP(a, p);
}
db disSS(Segment a, Segment b) {
	if(IntersectSS(a, b)) return 0.0;
	return min({disSP(b, a.p1), disSP(b, a.p2), disSP(a, b.p1), disSP(a, b.p2)});
}
db Area(Polygon p) {
	db S = p.back() ^ p[0];
	rop(i, 0, (int) p.size() - 1) S += p[i] ^ p[i + 1];
	return fabs(S) / 2;
}
bool isConvex(Polygon p) {
	int n = p.size();
	rop(i, 0, n) if(ccw(p[(i + n - 1) % n], p[i], p[(i + 1) % n]) == CCW_CLOCKWISE) return 0;
	return 1;
}
int Contain(Polygon g, Point p) {
	int n = g.size();
	int cnt = 0;
	rop(i, 0, n) {
		Point a = g[i] - p, b = g[(i + 1) % n] - p;
		if(ccw(a, b, Point(0, 0)) == CCW_ON_SEGMENT) return 1;
		if(dcmp(a.y - b.y) == 0) continue;
		if(dcmp(a.y) == 0 && dcmp(a.x) >= 0) { 
			if(dcmp(b.y) == -1) ++ cnt;
			continue;
		}
		if(dcmp(b.y) == 0 && dcmp(b.x) >= 0) {
			if(dcmp(a.y) == -1) ++ cnt;
			continue;
		}
		if(a.y > b.y) swap(a, b);
		if(dcmp(a.y) <= 0 && dcmp(b.y) >= 0 && dcmp(a ^ b) == 1) ++ cnt;
	}
	return (cnt & 1) ? 2 : 0;
	// 2 : IN
	// 1 : ON
	// 0 : OUT
}
Polygon ConvexHull(Polygon p) {
	int n = p.size();
	Polygon g(n * 2);
	sort(p.begin(), p.end(), cmp_y);
	int c = 0;
	rop(i, 0, n) {
		while(c > 1 && dcmp((g[c - 1] - g[c - 2]) ^ (p[i] - g[c - 2])) < 0) -- c;
		g[c ++] = p[i];
	}
	int k = c;
	per(i, n - 2, 0) {
		while(c > k && dcmp((g[c - 1] - g[c - 2]) ^ (p[i] - g[c - 1])) < 0) -- c;
		g[c ++] = p[i];
	}
	g.resize(c - 1);
	return g;
}


int main() {
	int n; scanf("%d", &n);
	Polygon p(n);
	rop(i, 0, n) scanf("%lf%lf", &p[i].x, &p[i].y);
	p = ConvexHull(p);
	n = p.size();
	printf("%d\n", n);
	rop(i, 0, n) p[i].print(), puts("");
	return 0;
}


Measured memory_bytes: 9112