#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i < (b); ++i)
#define per(i, a, b) for(int i = (b) - 1; i >= (a); --i)
#define sz(a) (int)a.size()
#define de(x) cout << #x << " = " << x << endl
#define dd(x) cout << #x << " = " << x << " "
#define endl "\n"
#define all(a) a.begin(), a.end()
#define pw(a) (1ll << (a))
typedef long long ll;
typedef pair<int, int> pii;
typedef double db;
typedef vector<int> vi;

const db eps = 1e-8;

int sign(db x) {
	return (x > eps) - (x < -eps);
}

struct P {
	db x, y;
	P() {}
	P(db x, db y) : x(x), y(y) {}
	void read() {
		cin >> x >> y;
	}
	P operator + (const P &c) const {
		return P(x + c.x, y + c.y);
	}
	P operator - (const P &c) const {
		return P(x - c.x, y - c.y);
	}
	P operator * (const db &c) const {
		return P(x * c, y * c);
	}
	P operator / (const db &c) const {
		return P(x / c, y / c);
	}
	db len2() {
		return x * x + y * y;
	}
	db len() {
		return sqrt(x * x + y * y);
	}
	P turn90() {
		return P(-y, x);
	}
	bool operator < (const P &c) const {
		int o = sign(x - c.x);
		return o ? o < 0 : sign(y - c.y) < 0;
	}
};
struct L {
	P a, b;
	L() {}
	L(P a, P b) : a(a), b(b) {}
	void read() {
		a.read();
		b.read();
	}
};
struct C {
	P o;
	db r;
	C() {}
	C(P o, db r) : o(o), r(r) {}
	void read() { 
		o.read();
		cin >> r;
	}
};
db det(P a, P b) {
	return a.x * b.y - a.y * b.x;
}
db det(P o, P a, P b) {
	return det(a - o, b - o);
}
db dot(P a, P b) {
	return a.x * b.x + a.y * b.y;
}
bool onSeg(const L &l, const P &p) {
	return sign(det(p - l.a, l.b - l.a)) == 0 && sign(dot(p - l.a, p - l.b)) <= 0;
}
P isLL(const L &l1, const L &l2) {
	db s1 =  det(l2.b - l2.a, l1.a - l2.a);
	db s2 = -det(l2.b - l2.a, l1.b - l2.a);
	return (l1.a * s2 + l1.b * s1) / (s1 + s2);
}
P proj(const L &l, const P &p) {
	return l.a + (l.b - l.a) * (dot(p - l.a, l.b - l.a) / (l.b - l.a).len2());
}
db disToLine(const L &l, const P &p) {
	return fabs(det(p - l.a, l.b - l.a) / (l.b - l.a).len());
}
db disToSeg(const L &l, const P &p) {
	return sign(dot(p - l.a, l.b - l.a)) * sign(dot(p - l.b, l.a - l.b)) == 1 ? disToLine(l, p) : min((p - l.a).len(), (p - l.b).len());
}
bool tanCP(const C &c, const P &p0, P &p1, P &p2) {
	db x = (p0 - c.o).len2(), d = x - c.r * c.r;
	if(d < eps) return 0;
	P p = (p0 - c.o) * (c.r * c.r / x);
	P delta = ((p0 - c.o) * (-c.r * sqrt(d) / x)).turn90();
	p1 = c.o + p + delta;
	p2 = c.o + p - delta;
	return 1;
}
vector<L> extanCC(const C &c1, const C &c2) {
	vector<L> ret;
	if(sign(c1.r - c2.r) == 0) {
		P dir = c2.o - c1.o;
		dir = (dir * (c1.r / dir.len())).turn90();
		ret.pb(L(c1.o + dir, c2.o + dir));
		ret.pb(L(c1.o - dir, c2.o - dir));
	} else {
		P p = (c1.o * -c2.r + c2.o * c1.r) / (c1.r - c2.r);
		P p1, p2, q1, q2;
		if(tanCP(c1, p, p1, p2) && tanCP(c2, p, q1, q2)) {
			if(c1.r < c2.r) swap(p1, p2), swap(q1, q2);
			ret.pb(L(p1, q1));
			ret.pb(L(p2, q2));
		}
	}
	return ret;
}
vector<L> intanCC(const C &c1, const C &c2) {
	vector<L> ret;
	P p = (c1.o * c2.r + c2.o * c1.r) / (c1.r + c2.r);
	P p1, p2, q1, q2;
	if(tanCP(c1, p, p1, p2) && tanCP(c2, p, q1, q2)) {
		ret.pb(L(p1, q1));
		ret.pb(L(p2, q2));
	}
	return ret;
}
vector<P> tanCC(const C &c1, const C &c2) { // 求圆与圆的切点
	vector<P> res;
	db dis = (c1.o - c2.o).len();
	if(sign(dis - (c1.r + c2.r)) == 0) {
		res.pb(c1.o + (c2.o - c1.o) * c1.r / (c1.r + c2.r));
	}
	if(sign(dis - fabs(c1.r - c2.r) == 0)) {
		res.pb(c1.o + (c2.o - c1.o) * c1.r / (c1.r - c2.r));
	}
	return res;
}
int contain(vector<P> poly, P p) {
	int ret = 0, n = sz(poly);
	rep(i, 0, n) {
		P u = poly[i], v = poly[(i + 1) % n];
		if(onSeg(L(u, v), p)) return 1;
		if(sign(u.y - v.y) <= 0) swap(u, v);
		if(sign(p.y - u.y) > 0 || sign(p.y - v.y) <= 0) continue;
		ret += sign(det(p, v, u)) > 0;
	}
	return (ret & 1) * 2;
}
vector<P> convexCut(const vector<P> &ps, L l) {
	vector<P> qs;
	int n = sz(ps);
	rep(i, 0, n) {
		P p1 = ps[i], p2 = ps[(i + 1) % n];
		int d1 = sign(det(l.a, l.b, p1)), d2 = sign(det(l.a, l.b, p2));
		if(d1 >= 0) qs.pb(p1);
		if(d1 * d2 < 0) qs.pb(isLL(L(p1, p2), l));
	}
	return qs;
}
db area(const vector<P> &poly) {
	int n = sz(poly);
	db res = 0;
	rep(i, 0, n) res += det(poly[i], poly[(i + 1) % n]);
	return fabs(res) / 2;
}
vector<P> convexHull(vector<P> ps) {
	int n = sz(ps);
	if(n <= 1) return ps;
	sort(all(ps));
	vector<P> qs;
	for(int i = 0; i < n; qs.pb(ps[i++])) {
		while(sz(qs) > 1 && sign(det(qs[sz(qs) - 2], qs.back(), ps[i])) < 0) qs.pop_back();
	}
	for(int i = n - 2, t = sz(qs); i >= 0; qs.pb(ps[i--])) {
		while(sz(qs) > t && sign(det(qs[sz(qs) - 2], qs.back(), ps[i])) < 0) qs.pop_back();
	}
	qs.pop_back();
	return qs;
}

int main() {
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int n;
	cin >> n;
	vector<P> poly(n);
	rep(i, 0, n) poly[i].read();
	poly = convexHull(poly);
	int p = 0;
	rep(i, 0, sz(poly)) {
		if(poly[i].y < poly[p].y || (poly[i].y == poly[p].y && poly[i].x < poly[p].x)) p = i;
	}
	cout << sz(poly) << endl;
	rep(i, p, sz(poly)) cout << poly[i].x << " " << poly[i].y << endl;
	rep(i, 0, p) cout << poly[i].x << " " << poly[i].y << endl;
	return 0;
}


Measured memory_bytes: 7440