using System;
using System.Linq;
using System.Collections.Generic;
using static System.Console;
using System.Text;
using System.IO;


namespace AOJ
{
    using Vector = Point;
    using Line = Segment;
    using Polygon = List<Point>;

    class Consts
    {
        public static readonly double EPS = 1e-10;
    }
    class Point:IEquatable<Point>
    {
        double x, y;
        public double X { get { return this.x; } set { this.x = value; } }
        public double Y { get { return this.y; } set { this.y = value; } }
        public Point(double x = 0, double y = 0)
        {
            this.x = x;
            this.y = y;
        }

        static public Point operator +(Point p1, Point p2) => new Point(p1.x + p2.x, p1.y + p2.y);
        static public Point operator -(Point p1, Point p2) => new Point(p1.x - p2.x, p1.y - p2.y);
        static public Point operator *(Point p, double a) => new Point(a * p.x, a * p.y);
        static public Point operator *(double a, Point p) => p * a;
        static public Point operator /(Point p, double a) => new Point(p.x / a, p.y / a);
        static public bool operator <(Point p1, Point p2) => p1.x != p2.x ? p1.x < p2.x : p1.y < p2.y;
        static public bool operator >(Point p1, Point p2) => p2 < p1;

        static public bool operator ==(Point p1, Point p2) => Math.Abs(p1.x - p2.x) < Consts.EPS && Math.Abs(p1.y - p2.y) < Consts.EPS;
        static public bool operator !=(Point p1, Point p2) => !(p1 == p2);

        public double abs() => Math.Sqrt(norm());
        public double norm() => this.x * this.x + this.y * this.y;

        public bool Equals(Point obj)
        {
            return this == obj;
        }
        public override bool Equals(object obj)
        {
            if ((object)obj == null || this.GetType() != obj.GetType())
            {
                return false;
            }
            var c = (Point)obj;
            return this == c;
        }
        public override int GetHashCode()
        {
            return this.x.GetHashCode() ^ this.y.GetHashCode();
        }
    }
    struct Segment
    {
        public Segment(Point p1,Point p2)
        {
            this.p1 = p1;
            this.p2 = p2;
        }
        public Point p1, p2;
    }
    class Circle
    {
        public Point c;
        public double r;
        public Circle(Point c, double r)
        {
            this.c = c;
            this.r = r;
        }
    }

    class VecCalc
    {
        static bool equals(double a, double b) => Math.Abs(a - b) < Consts.EPS;
        //内積
        static public double dot(Vector a, Vector b)
        {
            return a.X * b.X + a.Y * b.Y;
        }
        //外積
        static public double cross(Vector a, Vector b)
        {
            return a.X * b.Y - a.Y * b.X;
        }
        //16.2 直行判定
        static public bool isOrthogonal(Vector a, Vector b)
        {
            return equals(dot(a, b), 0.0);
        }
        static public bool isOrthogonal(Point a1, Point a2, Point b1, Point b2)
        {
            return isOrthogonal(a1 - a2, b1 - b2);
        }
        static public bool isOrthogonal(Segment s1, Segment s2)
        {
            return equals(dot(s1.p2 - s1.p1, s2.p2 - s2.p1), 0.0);
        }
        //16.2 平行判定
        static public bool isParallel(Vector a, Vector b)
        {
            return equals(cross(a, b), 0.0);
        }
        static public bool isParallel(Point a1, Point a2, Point b1, Point b2)
        {
            return isParallel(a1 - a2, b1 - b2);
        }
        static public bool isParallel(Segment s1, Segment s2)
        {
            return equals(cross(s1.p2 - s1.p1, s2.p2 - s2.p1), 0.0);
        }
        //16.3 射影
        static public Point project(Segment s, Point p)
        {
            Vector b = s.p2 - s.p1;
            double r = dot(p - s.p1, b) / b.norm();
            return s.p1 + b * r;
        }
        //16.4 反射
        static public Point reflection(Segment s, Point p)
        {
            return 2 * project(s, p) - p;
            //mid = project(s, p) - p;
            //return mid + mid - p;
        }
        //16.5 距離
        //2点間の距離
        static public double getDistance(Point a, Point b)
        {
            return (a - b).abs();
        }
        //直線lと点pの距離
        static public double getDistanceLP(Line l,Point p)
        {
            return Math.Abs(cross(l.p2 - l.p1, p - l.p1) / (l.p2 - l.p1).abs());
        }
        //線分sと点pの距離
        static public double getDistanceSP(Segment s, Point p)
        {
            if (dot(s.p2 - s.p1, p - s.p1) < 0.0) return (p - s.p1).abs();
            if (dot(s.p1 - s.p2, p - s.p2) < 0.0) return (p - s.p2).abs();
            return getDistanceLP(s, p);
        }
        static public double getDistance(Segment s1, Segment s2)
        {
            if (intersect(s1, s2)) return 0.0;
            return Math.Min(
                    Math.Min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2)),
                    Math.Min(getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2))
                );
        }
        //16.6 反時計回り
        static public readonly int COUNTER_CLOCKWISE=1;
        static public readonly int CLOCKWISE = -1;
        static public readonly int ONLINE_BACK = 2;
        static public readonly int ONLINE_FRONT = -2;
        static public readonly int ON_SEGMENT = 0;
        static public int ccw(Point p0, Point p1, Point p2)
        {
            Vector a = p1 - p0;
            Vector b = p2 - p0;
            var dot_ = dot(a, b);
            var cross_ = cross(a, b);
            if (Consts.EPS < cross_) return COUNTER_CLOCKWISE;
            if (cross_ < -Consts.EPS) return CLOCKWISE;
            if (dot_ < -Consts.EPS) return ONLINE_BACK;
            if (a.norm() < b.norm()) return ONLINE_FRONT;
            return ON_SEGMENT;
        }
        static public int ccw(Segment s, Point p)
        {
            return ccw(s.p1, s.p2, p);

        }
        //16.7 線分の交差判定
        static public bool intersect(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
                );
        }
        static public bool intersect(Segment s1, Segment s2)
        {
            return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
        }
        //16.8 線分の交点
        static public Point getCrossPoint(Segment s1, Segment s2)
        {
            Vector b = s2.p2 - s2.p1;
            double d1 = Math.Abs(cross(b, s1.p1 - s2.p1));
            double d2 = Math.Abs(cross(b, s1.p2 - s2.p1));
            double t = d1 / (d1 + d2);
            return s1.p1 + (s1.p2 - s1.p1) * t;
        }
        //16.9 円と直線の交点
        static public Segment getCrossPoints(Circle c, Line l)
        {
            Vector pr = project(l, c.c);
            System.Diagnostics.Debug.Assert((c.c - pr).abs() <= c.r);
            Vector e = (l.p2 - l.p1) / (l.p2 - l.p1).abs();
            double b = Math.Sqrt(c.r * c.r - (c.c - pr).norm());
            return new Segment(pr + e * b, pr - e * b);
        }
        //16.10 円と円の交点
        static double arg(Vector p) { return Math.Atan2(p.Y, p.X); }
        static Vector polar(double a, double r) { return new Point(Math.Cos(r) * a, Math.Sin(r) * a); }
        static public Segment getCrossPoints(Circle c1 , Circle c2)
        {
            double d = (c1.c - c2.c).abs();
            System.Diagnostics.Debug.Assert(d <= c1.r + c2.r);
            double a = Math.Acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
            double t = arg(c2.c - c1.c);
            return new Segment(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
        }
        //他ヘロンの公式を使ったり。高さだす、交点の投影点求める、投影点から平行線へのベクトル出す、みたいなの)
        //16.11 点の内包
        //IN:2,ON:1,OUT:0
        static public int contains(Polygon g, Point p)
        {
            int n = g.Count;
            bool x = false;
            for (var i = 0; i < n; ++i)
            {
                Point a = g[i] - p, b = g[(i + 1) % n] - p;
                if (Math.Abs(cross(a, b)) < Consts.EPS && dot(a, b) < Consts.EPS) return 1;
                if (b.Y < a.Y)
                {
                    var t = a;
                    a = b;
                    b = t;
                }
                if (a.Y < Consts.EPS && Consts.EPS < b.Y && Consts.EPS < cross(a, b)) x = !x;
            }
            return (x ? 2 : 0);
        }
        //他、xの最大・最小値を使って半直線を作り、各辺との交差、点を通る場合、辺と平行かつ通る場合を考えていく方法もあり
        //半直線：判定対象点とPolygonの右端との差を使って適当に作る
        //Polygonの各辺ごとに処理する
        //  ccwでライン上にあるか判定
        //  両方の点を通る場合(平行&通る):辺と点を合わせて1点と考える。次の辺は飛ばす
        //  点を通る場合：Ｖ、Λの場合は無視する。j+1の点を通る場合は次の処理に任す
        //16.12 凸包
        static public Polygon andrewScan(Polygon s)
        {
            Polygon u = new Polygon();
            Polygon l = new Polygon();

            if (s.Count < 3) return s;
            s = s.OrderBy(e => e.Y).ThenBy(e => e.X).ToList();
            var size = s.Count;
            u.Add(s[0]);
            u.Add(s[1]);
            l.Add(s[size - 1]);
            l.Add(s[size - 2]);
            for (var i = 2; i < size; ++i)
            {
                for (var n = u.Count; 2 <= n && ccw(u[n - 2], u[n - 1], s[i]) == COUNTER_CLOCKWISE; --n)
                {
                    u.RemoveAt(u.Count - 1);
                }
                u.Add(s[i]);
            }
            for (var i = size - 3; 0 <= i; --i)
            {
                for (var n = l.Count; 2 <= n && ccw(l[n - 2], l[n - 1], s[i]) == COUNTER_CLOCKWISE; --n)
                {
                    l.RemoveAt(l.Count - 1);
                }
                l.Add(s[i]);
            }
            l.Reverse();
            for (var i = u.Count - 2; 1 <= i; --i) l.Add(u[i]);
            return l;
        }
    }
    class Program
    {
        static public long[] Sarray() { return ReadLine().Trim().Split().Select(long.Parse).ToArray(); }
        static public List<long> Slist() { return ReadLine().Split().Select(long.Parse).ToList(); }

        static double sinheta(Vector a, Vector b)
        { return VecCalc.cross(a, b) / (a.abs() * b.abs()); }
        static void Main(string[] args)
        {
            var n = (int)Sarray()[0];
            var ps = new List<Point>();
            for (var i = 0; i < n; ++i)
            {
                var p = Sarray();
                ps.Add(new Point(p[0], p[1]));
            }
            var anss = VecCalc.andrewScan(ps);
            var num = anss.Count;
            WriteLine(num);
            foreach(var ans in anss)
                WriteLine($"{ans.X} {ans.Y}");
        }
    }
}


Measured memory_bytes: 34160