// ConsoleApplication.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdio.h"
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <string>
#include <vector>
#include <array>

using namespace std;
using NUM = double;

static const NUM  EPS = 1e-10;

template<typename T>
struct Point {
	T x, y;
	Point<T> operator+(const Point<T>& rhs)const {
		Point<T> p;
		p.x = this->x + rhs.x;
		p.y = this->y + rhs.y;
		return p;
	}
	Point<T> operator-(const Point<T>& rhs)const {
		Point<T> p;
		p.x = this->x - rhs.x;
		p.y = this->y - rhs.y;
		return p;
	}
	Point<T> operator*(const T rhs) const {
		Point<T> p;
		p.x = this->x * rhs;
		p.y = this->y * rhs;
		return p;
	}
	Point<T> operator/(const T rhs) const {
		Point<T> p;
		p.x = this->x / rhs;
		p.y = this->y / rhs;
		return p;
	}
	Point<T> operator-() const {
		Point<T> p;
		p.x = this->x * -1.0;
		p.y = this->y * -1.0;
		return p;
	}
	static bool compByCrd(const Point<T>& p1, const Point<T>& p2) {
		if (abs(p1.x - p2.x) < EPS) return p1.y < p2.y;
		return p1.x < p2.x;
	}
	//Point<T>& operator+=(const rhs) {
	//	x += rhs;
	//	y += rhs;
	//	return *this;
	//}
	//Point<T>& operator-=(const rhs) {
	//	const_cast<Point<T>>(*this) += -rhs;
	//	return *this;
	//	/*x -= rhs;
	//	y -= rhs;
	//	return *this;*/
	//}
};

template <typename T>
using Vector = Point<T>;

template<typename T>
Point<T> operator*(const T val, const Point<T>& p)
{
	return p*val;
}

template<typename T>
struct Segment {
	Point<T> p1, p2;
};

template<typename T>
struct Circle {
	Point<T> center;
	T radius;
};

template<typename T>
bool isZero(const T val)
{
	return fabs(val) < 1e-10;
}

template<typename T>
T dot(const Point<T>& p0, const Point<T>& p1)
{
	double x0 = p0.x;
	double y0 = p0.y;
	double x1 = p1.x;
	double y1 = p1.y;
	return x0 * x1 + y0 * y1;
}

template<typename T>
T cross(const Point<T>& p0, const Point<T>& p1)
{
	double x0 = p0.x;
	double y0 = p0.y;
	double x1 = p1.x;
	double y1 = p1.y;
	return x0 * y1 - y0 * x1;
}

template<typename T>
T norm(const Point<T>& p)
{
	double x = p.x;
	double y = p.y;
	return x*x + y*y;
}

template<typename T>
T absolute(const Point<T>& p)
{
	return sqrt(norm(p));
}

template<typename T>
Point<T> unit(const Point<T>& p)
{
	return p / absolute(p);
}

template<typename T>
void readPoint(Point<T>& p)
{
	cin >> p.x;
	cin >> p.y;
}

template<typename T>
void readSegment(Segment<T>& s)
{
	cin >> s.p1.x;
	cin >> s.p1.y;
	cin >> s.p2.x;
	cin >> s.p2.y;
}

template<typename T>
void readCircle(Circle<T>& c)
{
	cin >> c.center.x;
	cin >> c.center.y;
	cin >> c.radius;
}

template<typename T>
void outputComponentesOfPoint(const Point<T>& p)
{
	cout << fixed;
	cout << setprecision(8) << p.x;
	cout << " ";
	cout << setprecision(8) << p.y;
	cout << endl;
}

//template<typename T>
//T getDistanceFromPointToLine(const Point<T>& p, const Segment<T>& s) {
//	const Point<T> a = p - s.p1;
//	const Point<T> b = s.p2 - s.p1;
//	const T outer = cross(a, b);
//	return abs( outer / absolute(b) );
//}
//
//template<typename T>
//Point<T> getIntersectionPointOfLineSegments(const Segment<T>& s1, const Segment<T>& s2)
//{
//	const T d1 = getDistanceFromPointToLine(s1.p1, s2);
//	const T d2 = getDistanceFromPointToLine(s1.p2, s2);
//	return s1.p1 + (s1.p2 - s1.p1) * (d1 / (d1 + d2));
//}
//
//
//template<typename T>
//T distanceOfPoints(const Point<T>& p1, const Point<T>& p2)
//{
//	return absolute(p2 - p1);
//}


//template<typename T>
//T getDistanceFromPointToSegment(const Point<T>& p, const Segment<T>& s) {
//	const T zero = static_cast<T>(0);
//	const Point<T> a = p - s.p1;
//	const Point<T> b = s.p2 - s.p1;
//	const Point<T> c = p - s.p2;
//	const T innerAB = dot(a, b);
//	const T innerc_b = dot(c, -b);
//
//	if (innerAB < zero) {
//		return distance(p, s.p1);
//	}
//	else if (innerc_b < zero) {
//		return distance(p, s.p2);
//	}
//	else {
//		return fabs(cross(a, b) / absolute(b) );
//	}
//}
//

//template<typename T>
//double getParameterOfProjectedPointToSegment(const Point<T>& p, const Segment<T>& s)
//{
//	const Point<T> a = p - s.p1;
//	const Point<T> b = s.p2 - s.p1;
//	const T normB = norm(b);
//	const T ab = dot(a, b);
//	return ab / normB;
//}
//
//template<typename T>
//int getPositionOnSegment(const Point<T>& p, const Segment<T>& s)
//{
//	const T position = getParameterOfProjectedPointToSegment(p, s);
//	if (position < 0.0) {
//		return -1;
//	}
//	else if (1.0 < position) {
//		return 1;
//	}
//	else {
//		return 0;
//	}
//}

static const int counter_clockwise = 2;
static const int clockwise = -2;
static const int online_back = -1;
static const int online_front = 1;
static const int onSegment = 0;

template<typename T>
int getPointPositionToSegment(const Point<T>& p, const Segment<T>& s)
{
	return getPointPositionToSegment(p, s.p1, s.p2);
}

template<typename T>
int getPointPositionToSegment(const Point<T>& p, const Point<T>& sp1, const Point<T>& sp2)
{
	const Point<NUM> a = sp2 - sp1;
	const Point<NUM> b = p - sp1;
	const NUM outer = cross(a, b);
	if (outer > EPS) return counter_clockwise;
	if (outer < -EPS) return clockwise;
	if (dot(a, b) < -EPS) return online_back;
	if (norm(b) > norm(a) + EPS) return online_front;
	return onSegment;
}
//
//template<typename T>
//bool intersects(const Segment<T>& s1, const Segment<T>& s2) {
//	return getPointPositionToSegment(s2.p1, s1) * getPointPositionToSegment(s2.p2, s1) <= 0
//		&& getPointPositionToSegment(s1.p1, s2) * getPointPositionToSegment(s1.p2, s2) <= 0;
//}

//
//template<typename T>
//T getDistanceFromSegmentToSegment(const Segment<T>& s1, const Segment<T>& s2) {
//	if (intersects(s1, s2)) {
//		return static_cast<T>(0.0);
//	}else{
//		return min(
//			min(getDistanceFromPointToSegment(s1.p1, s2), getDistanceFromPointToSegment(s1.p2, s2))
//			, min(getDistanceFromPointToSegment(s2.p1, s1), getDistanceFromPointToSegment(s2.p2, s1))
//		);
//	}
//}

//template<typename T>
//void projectPointToSegment(const Point<T>& p, const Segment<T>& s, Point<T>& foot, T& param)
//{
//	const Point<T> a = p - s.p1;
//	const Point<T> b = s.p2 - s.p1;
//	const T normB = norm(b);
//	const T ab = dot(a, b);
//	param = ab / normB;
//	foot = s.p1 + (s.p2 - s.p1)*param;
//}

//template<typename T>
//Point<T> projectPointToSegment(const Point<T>& p, const Segment<T>& s)
//{
//	const Point<T> a = p - s.p1;
//	const Point<T> b = s.p2 - s.p1;
//	return s.p1 + (s.p2 - s.p1)*(dot(a, b) /norm(b));
//}
//
//template<typename T>
//T getRadianFromCosineLaw(const T a, const T b, const T c)//get radian oppose to a
//{
//	return acos( 0.5*(b*b + c*c - a*a) / (b*c) );
//}

//template<typename T>
//array<Point<T>, 2> getIntersectionOfCircleAndLine(const Circle<T>& c, const Segment<T>& s)
//{
//	const T h = getDistanceFromPointToLine(c.center, s);
//	// supposing c,radius >= h
//	const T d = sqrt(c.radius*c.radius - h*h);
//	const Point<T> v = unit(s.p2 - s.p1);
//	const Point<T> foot = projectPointToSegment(c.center, s);
//	
//	array<Point<T>, 2> ret;
//	ret.at(0) = foot + v*d;
//	ret.at(1) = foot - v*d;
//	return ret;
//}

//template<typename T>
//Point<T> rotateVector(const Point<T>& p, const T rad)
//{
//	const T c = cos(rad);
//	const T s = sin(rad);
//
//	Point<T> ret;
//	ret.x = p.x*c - p.y*s;
//	ret.y = p.x*s + p.y*c;
//	return ret;
//}
//
//template<typename T>
//array<Point<T>, 2> getIntersectionOfCircles(const Circle<T>& c1, const Circle<T>& c2)
//{
//
//	const Point<T> v = c2.center - c1.center;
//	const T d = absolute(v);
//	const T rad = getRadianFromCosineLaw(c2.radius, c1.radius, d);
//	const Point<T> unitV = unit(v);
//
//	array<Point<T>, 2> ret;
//	ret.at(0) = c1.center + rotateVector(unitV, rad)*c1.radius;
//	ret.at(1) = c1.center + rotateVector(unitV, -rad)*c1.radius;
//	return ret;
//}

//template<typename T>
//int contains(const vector<Point<T>>& polygon, const Point<T>&p)
//{
//#if 1//original
//	bool isIn = false;
//	for (int end = polygon.size(), prev = end - 1, next = 0 ; next != end ; prev = next++) {
//		Point<T> a = polygon.at(prev) - p;
//		Point<T> b = polygon.at(next) - p;
//		if (a.y > b.y) swap(a, b);
//		const T outer = cross(a,b);
//		if (outer < -EPS) continue;
//		if (abs(outer) < EPS && dot(a, b) < EPS) return 1;
//		
//		//a.y < -EPS でなく、a.y < EPS としているのがポイント
//		//境界の取り方が非常に巧妙。構成点が半直線上に存在しても、折れ線が半直線をを横切るときのみ、交差と判定されるのは2回でなく1回になる
//		if (a.y < EPS && EPS < b.y)isIn = !isIn;
//	}
//	return isIn ? 2 : 0;
//#else//from textBook
//	bool isIn = false;
//	for (int end = polygon.size(), prev = end - 1, next = 0; next != end; prev = next++) {
//		Point<T> a = polygon.at(prev) - p;
//		Point<T> b = polygon.at(next) - 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 && EPS < b.y && cross(a, b) > EPS)isIn = !isIn;
//	}
//	return isIn ? 2 : 0;
//#endif
//}

//template<typename T>
//void outputPoints(const vector<Point<T>>& points) {
//	for_each(points.begin(), points.end(), [](const Point<T>& p) {cout << static_cast<int>(p.x) << " " << static_cast<int>(p.y) << endl; });
//}

template<typename T>
vector<Point<T>> convexHull(vector<Point<T>> points)
{
	const int n = points.size();
	sort(points.begin(), points.end(), Point<T>::compByCrd);

	vector<Point<T>> u; {
		u.reserve(points.size());
		u.insert(u.end(), points.begin(), points.begin() + 2);
		for (int i = 2; i != n; ++i) {
			for (int j = u.size() - 1; j != 0 ; --j) {
				const int pos = getPointPositionToSegment(points.at(i), u.at(j - 1), u.at(j));
				if (pos == clockwise || pos == online_front) break;
				u.pop_back();
			}
			u.push_back(points.at(i));
		}
	}

	vector<Point<NUM>> l; {
		l.reserve(points.size());
		l.insert(l.end(), points.rbegin(), points.rbegin() + 2);
		for (int i = points.size() - 3; i != -1; --i) {
			for (int j = l.size() - 1; j != 0 ; --j) {
				const int pos = getPointPositionToSegment(points.at(i), l.at(j - 1), l.at(j));
				if (pos == clockwise || pos == online_front) break;
				l.pop_back();
			}
			l.push_back(points.at(i));
		}
	}
	
	u.insert(u.end(), l.begin()+1, l.end() - 1);
	return u;
}



int main()
{
	

	int n;
	cin >> n;
	vector<Point<NUM>> points(n);
	for (auto& p : points) {
		readPoint(p);
	}

	vector<Point<NUM>> convex = convexHull(points);//clockwise
	
	reverse(convex.begin(), convex.end());
	vector<Point<NUM>>::iterator minYPoint = min_element(convex.begin(), convex.end(),
		[](const Point<NUM>& p1, const Point<NUM>& p2) {
		if( abs( p1.y - p2.y) < EPS ) return p1.x < p2.x;
		return p1.y < p2.y; 
	});
	
	cout << convex.size() << endl;
	function<void(Point<NUM>)> out = [](const Point<NUM>& p) {cout << static_cast<int>(p.x) << " " << static_cast<int>(p.y) << endl; };
	for_each(minYPoint, convex.end(), out);
	for_each(convex.begin(), minYPoint, out);
	
	return 0;
}


Measured memory_bytes: 7356