/*************************************************************************
 *  Kompilacja:  javac GrahamaScan.java
 *  Wykonanie:    java GrahamScan < input.txt
 * 
 *  Tworzy punkty ze standardowego wejścia i wyznacza otoczkę wypukłą
 *  za pomocą algorytmu Grahama.
 *
 *************************************************************************/

import java.util.Arrays;

public class GrahamScan {
    private Stack<Point> hull = new Stack<Point>();

    public GrahamScan(Point[] pts) {

        // Kopiowanie zabezpieczające
        int N = pts.length;
        Point[] points = new Point[N];
        for (int i = 0; i < N; i++)
            points[i] = pts[i];

        // Wstępne przetwarzanie, tak aby element points[0] miał najmniejszą współrzędną y; 
		// równe elementy są porównywane według współrzędnej x.
        // Element points[0] jest skrajnym punktem otoczki wypukłej
        // (problem można łatwo rozwiązać w czasie liniowym)
        Arrays.sort(points);

        // Sortowanie według kąta biegunowego z względem punktu bazowego points[0];
        // równe elementy są porównywane według odległości od punktu points[0]
        Arrays.sort(points, 1, N, points[0].BY_CCW);

        hull.push(points[0]);       // p[0] to pierwszy punkt skrajny

        // Znajdowanie indeksu k1 pierwszgo punktu różnego od points[0]
        int k1;
        for (k1 = 1; k1 < N; k1++)
            if (!points[0].equals(points[k1])) break;
        if (k1 == N) return;        // Wszystkie punkty są równe

        // Znajdowanie indeksu k2 pierwszego punktu, który nie jest współliniowy wobec punktów points[0] i points[k1]
        int k2;
        for (k2 = k1 + 1; k2 < N; k2++)
            if (Point.ccw(points[0], points[k1], points[k2]) != 0) break;
        hull.push(points[k2-1]);    // points[k2-1] to drugi skrajny punkt

        // Algorytm Grahama; zauważ, że points[N-1] to skrajny punkt różny od punktu points[0]
        for (int i = k2; i < N; i++) {
            Point top = hull.pop();
            while (Point.ccw(top, hull.peek(), points[i]) <= 0) {
                top = hull.pop();
            }
            hull.push(top);
            hull.push(points[i]);
        }
    }

    // Zwracanie obiektu Iterable ze skrajnymi punktami otoczki wypukłej w porządku przeciwnym do ruchu wskazówek zegara
    public Iterable<Point> hull() {
        // Odwracanie kolejności i odrzucanie ostatniego punktu (powtarza się dwukrotnie)
        Stack<Point> s = new Stack<Point>();
        for (Point p : hull) s.push(p);
        return s;
    }

    // Klient testowy
    public static void main(String[] args) {
        int N = StdIn.readInt();
        Point[] points = new Point[N];
        for (int i = 0; i < N; i++) {
            int x = StdIn.readInt();
            int y = StdIn.readInt();
            points[i] = new Point(x, y);
        }
        GrahamScan graham = new GrahamScan(points);
        for (Point p : graham.hull())
            StdOut.println(p);
    }

}