Convex Hull: Find the smallest convex polygon containing all given points. The polygon should have all points of P on its corners and boundary. Input: n, followed by n lines of x and y coordinates. Output: Number of points on the convex polygon's boundary, followed by their coordinates in counter-clockwise order. Constraints: 3 ≤ n ≤ 100000, -10000 ≤ xi, yi ≤ 10000, no repeated points.

# Buggy version:
def cross(a, b):
  return a.real * b.imag - a.imag*b.real

def convex_hull(p):
  pp = sorted(p, key=lambda x:(x.imag, x.real))
  n = len(pp)
  ans = [0] * (n+1)
  j = 0

  #上側を入れていく
  for i in range(n):
    while j > 1 and cross(ans[j-1] - ans[j-2], pp[i]-ans[j-1]) < 0: #常に次の点が下側に
      j -= 1
    ans[j] = pp[i]
    j += 1
  k = j
  #下側を入れていく
  for i in range(n-2, -1, -1):
    while j > k and cross(ans[j-1]-ans[j-2], pp[i]-ans[j-1]) < 0: #常に次の点が下側に
      j -= 1
    ans[j] = pp[i]
    j +=1
  return ans[0:j-1]

n = int(input())
p=[]
for i in range(n):
  x,y = list(map(int, input().split()))
  p.append(complex(x,y))
ans = convex_hull(p)


for i in ans:
  print(int(i.real), int(i.imag))



# Fixed version:
def cross(a, b):
  return a.real * b.imag - a.imag*b.real

def convex_hull(p):
  pp = sorted(p, key=lambda x:(x.imag, x.real))
  n = len(pp)
  ans = [0] * (n+1)
  j = 0

  #上側を入れていく
  for i in range(n):
    while j > 1 and cross(ans[j-1] - ans[j-2], pp[i]-ans[j-1]) < 0: #常に次の点が下側に
      j -= 1
    ans[j] = pp[i]
    j += 1
  k = j
  #下側を入れていく
  for i in range(n-2, -1, -1):
    while j > k and cross(ans[j-1]-ans[j-2], pp[i]-ans[j-1]) < 0: #常に次の点が下側に
      j -= 1
    ans[j] = pp[i]
    j +=1
  return ans[0:j-1]

n = int(input())
p=[]
for i in range(n):
  x,y = list(map(int, input().split()))
  p.append(complex(x,y))
ans = convex_hull(p)
print(len(ans))

for i in ans:
  print(int(i.real), int(i.imag))

# Buggy version:
from collections import deque

def isCLKWISE(ph):
    return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n : P.append([int(x) for x in input().split()])
Q = deque(ConvexHullScan(P))
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q.rotate(idx)
for r in R : print(r[0],r[1])

# Fixed version:
from collections import deque

def isCLKWISE(ph):
    a = [ph[-1][0]-ph[-3][0],ph[-1][1]-ph[-3][1]]
    b = [ph[-2][0]-ph[-3][0],ph[-2][1]-ph[-3][1]]
    crs = a[0]*b[1] - a[1]*b[0]
    if crs < 0 : return False
    else : return True
     
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
 
n = int(input())
P = deque([])
for i in range(n):
    P.append([int(x) for x in input().split()])
     
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R:
    print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph):
    if (ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) > 0 : return True
    return False
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Fixed version:
def isCLKWISE(ph):
    a = ph[-1][0]-ph[-3][0],ph[-1][1]-ph[-3][1]
    b = ph[-2][0]-ph[-3][0],ph[-2][1]-ph[-3][1]
    crs = b[1]*a[0] - a[1]*b[0]
    if crs < 0 : return False
    else : return True
     
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
 
n = int(input())
P = []
for i in range(n):
    P.append([int(x) for x in input().split()])
     
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R:
    print(r[0],r[1])

# Buggy version:
import heapq

def isCLKWISE(ph):
    a = [ph[-1][0]-ph[-3][0],ph[-1][1]-ph[-3][1]]
    b = [ph[-2][0]-ph[-3][0],ph[-2][1]-ph[-3][1]]
    crs = a[0]*b[1] - a[1]*b[0]
    if crs < 0 :
        return False
    else :
        return True
    
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) :
                break
            else :
                del phU[-2]
                if len(phU) == 2 :
                    break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) :
                break
            else :
                del phL[-2]
                if len(phL) == 2 :
                    break
    ph = phU + phL[1:-1]
    return ph

n = int(input())
P = []
for i in range(n):
    heappush(P,[int(x) for x in input().split()])
    
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
for q in Q:
    idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
    R = Q[idx:] + Q[:idx]
for r in R:
    print(r[0],r[1])

# Fixed version:
def isCLKWISE(ph):
    a = ph[-1][0] - ph[-3][0]
    d = - ph[-3][1] + ph[-2][1]
    c = ph[-2][0] - ph[-3][0]
    b = - ph[-3][1] + ph[-1][1] 
    crs = a*d - b*c
    if crs < 0 : return False
    else : return True
     
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
 
n = int(input())
P = []
for i in range(n):
    P.append([int(x) for x in input().split()])
     
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R:
    print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph):
    return (ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Fixed version:
#CGL_4-A Convex Polygon - Convex Hull
def isCLKWISE(ph):
    if (ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0 : return False
    return True
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph):
    return ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) >= 0
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Fixed version:
def isCLKWISE(ph):
    if (ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) >= 0 : return True
    return False
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph):
    return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P):
    P.sort()
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P).reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Fixed version:
def isCLKWISE(ph):
    return (ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) >= 0
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = []
for i in n:
    P.append([int(x) for x in input().split()])
      
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        if isCLKWISE(phU) : continue
        while(True):
            del phU[-2]
            if len(phU) == 2 : break
            else :
                if isCLKWISE(phU) : break

    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        if isCLKWISE(phU) : continue
        while(True):
            del phL[-2]
            if len(phL) == 2 : break
            else :
                if isCLKWISE(phL) : break

                
                
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = [[int(x) for x in input().split()] for _ in n]
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Fixed version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P) :
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:] :
        phU.append(p)
        while(True) :
            if isCLKWISE(phU) : break
            else :
                del phU[-2]
                if len(phU) == 2 : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1] :
        phL.append(p)
        while(True) :
            if isCLKWISE(phL) : break
            else :
                del phL[-2]
                if len(phL) == 2 : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = [[int(x) for x in input().split()] for _ in n]
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph) : return (ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        if not isCLKWISE(phU) : continue
        while(True):
            del phU[-2]
            try:
                if isCLKWISE(phU) : break
            except IndexError : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        if not isCLKWISE(phL) : continue
        while(True):
            del phL[-2]
            try:
                if isCLKWISE(phL) : break
            except IndexError : break
      
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = [[int(x) for x in input().split()] for _ in n]
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Fixed version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        if isCLKWISE(phU) : continue
        while(True):
            del phU[-2]
            if len(phU) == 2 : break
            else :
                if isCLKWISE(phU) : break

    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        if isCLKWISE(phL) : continue
        while(True):
            del phL[-2]
            if len(phL) == 2 : break
            else :
                if isCLKWISE(phL) : break

                
                
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = [[int(x) for x in input().split()] for _ in n]
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P) :
 P = sorted(P)
 phU = []
 phU.append(P[0])
 phU.append(P[1])
 for p in P[2:] :
  phU.append(p)
  if isCLKWISE(phU) : continue
  while(True)???:
   del phU[-2]
   try???:
    if isCLKWISE(phU) : break
   except IndexError : break
 phL = []
 phL.append(P[-1])
 phL.append(P[-2])
 for p in P[-3::-1] :
  phL.append(p)
  if isCLKWISE(phL) : continue
  while(True) :
   del phL[-2]
   try:
    if isCLKWISE(phL) : break
   except IndexError : break
 del phL[0]
 del phL[-1]
 ph = phU + phL
 return ph
  
n = range(int(input()))
P = [[int(x) for x in input().split()] for _ in n]
P = ConvexHullScan(P)
P.reverse()
print(len(P))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(P)])[2]
for p in P[idx:] + P[:idx] : print(p[0],p[1])

# Fixed version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            try :
                if isCLKWISE(phU) : break
                else : del phU[-2]
            except IndexError : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            try :
                if isCLKWISE(phL) : break
                else : del phL[-2]
            except IndexError : break
    ph = phU + phL[1:-1]
    return ph
  
n = range(int(input()))
P = [[int(x) for x in input().split()] for _ in n]
Q = ConvexHullScan(P)
Q.reverse()
print(len(Q))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(Q)])[2]
R = Q[idx:] + Q[:idx]
for r in R : print(r[0],r[1])

# Buggy version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P) :
    P = sorted(P)
    phU = []
    phU.append(P[0])
    phU.append(P[1])
    for p in P[2:] :
        phU.append(p)
        if isCLKWISE(phU) : continue
        while(True) :
            del phU[-2]
            try :
                if isCLKWISE(phU) : break
            except IndexError : break
    phL = []
    phL.append(P[-1])
    phL.append(P[-2])
    for p in P[-3::-1] :
        phL.append(p)
        if isCLKWISE(phL) : continue
        while(True) :
            del phL[-2]
            try:
                if isCLKWISE(phL) : break
            except IndexError : break
        del phL[0]
        del phL[-1]
        ph = phU + phL
        return ph

P = [[int(x) for x in input().split()] for _ in range(int(input()))]
P = ConvexHullScan(P)
P.reverse()
print(len(P))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(P)])[2]
for p in P[idx:] + P[:idx] : print(p[0],p[1])

# Fixed version:
def isCLKWISE(ph) : return not ((ph[-1][0] - ph[-3][0])*(- ph[-3][1] + ph[-2][1]) - (ph[-2][0] - ph[-3][0])*(- ph[-3][1] + ph[-1][1] ) < 0)
      
def ConvexHullScan(P):
    P = sorted(P)
    phU = [P[0],P[1]]
    for p in P[2:]:
        phU.append(p)
        while(True):
            try :
                if isCLKWISE(phU) : break
                else : del phU[-2]
            except IndexError : break
    phL = [P[-1],P[-2]]
    for p in P[-3::-1]:
        phL.append(p)
        while(True):
            try :
                if isCLKWISE(phL) : break
                else : del phL[-2]
            except IndexError : break
    ph = phU + phL[1:-1]
    return ph
  
P = [[int(x) for x in input().split()] for _ in range(int(input()))]
P = ConvexHullScan(P)
P.reverse()
print(len(P))
idx = min([[x[1][1],x[1][0],x[0]] for x in enumerate(P)])[2]
for p in P[idx:] + P[:idx] : print(p[0],p[1])

# Buggy version:
from math import sqrt
from collections import deque

def sub(a, b):
    return [a[0] - b[0],a[1] - b[1]]

def cross(a, b):
    return  a[0] * b[1] - a[1] * b[0]

def ccw(a, b, c):
    x = sub(b, a)
    y = sub(c, a)
    return cross(x, y) > 0

n = int(input())
c = [list(map(int, input().split())) for i in range(n)]
c.sort(key=lambda x: (x[0], x[1]))
U = deque(c[:2])

for i in range(2, n):
    j = len(U)
    while j >= 2 and ccw(U[-1],U[-2],c[i]):
        U.pop()
        j -= 1
    U.append(c[i])

c = c[::-1]
L = deque(c[:2])

for i in range(2, n):
    j = len(L)
    while j >= 2 and ccw(L[-1], L[-2], c[i]):
        L.pop()
        j -= 1
    L.append(c[i])

ans = U
for i in range(1,len(L)-1):
    x, y = L[i]
    ans.append([x,y])

i = ans.index(sorted(ans, key=lambda x: (x[1],x[0]))[0])
ans = list(ans)
for x,y in ans[:i] + ans[i:]:
    print(x, y)


# Fixed version:
from math import sqrt
from collections import deque

def sub(a, b):
    return [a[0] - b[0],a[1] - b[1]]

def cross(a, b):
    return  a[0] * b[1] - a[1] * b[0]

def ccw(a, b, c):
    x = sub(b, a)
    y = sub(c, a)
    return cross(x, y) > 0

n = int(input())
c = [list(map(int, input().split())) for i in range(n)]
c.sort(key=lambda x: (x[0], x[1]))
U = deque(c[:2])

for i in range(2, n):
    j = len(U)
    while j >= 2 and ccw(U[-1],U[-2],c[i]):
        U.pop()
        j -= 1
    U.append(c[i])

c = c[::-1]
L = deque(c[:2])

for i in range(2, n):
    j = len(L)
    while j >= 2 and ccw(L[-1], L[-2], c[i]):
        L.pop()
        j -= 1
    L.append(c[i])

ans = U
for i in range(1,len(L)-1):
    x, y = L[i]
    ans.append([x,y])

print(len(ans))
i = ans.index(sorted(ans, key=lambda x: (x[1],x[0]))[0])
ans = list(ans)
for x,y in ans[i:] + ans[:i]:
    print(x, y)

# Buggy version:

def clockwise(v0, v1, v2):
    a1 = v1[0] - v0[0]
    a2 = v1[1] - v0[1]
    b1 = v2[0] - v0[0]
    b2 = v2[1] - v0[1]
    return (a1*b2 - b1*a2) < 0


n = int(input())
vors = [[int(a) for a in input().split() ] for _ in range(n)]
vors.sort(key=lambda v: (v[1], v[0]))

over = [vors[0], vors[1]]

for v in vors[2:]:
    while len(over) > 1 and not clockwise(over[-2], over[-1], v):
        over.pop()
    over.append(v)
        
under = [vors[0], vors[1]]

for v in vors[2:]:
    while len(under) > 1 and clockwise(under[-2], under[-1], v):
        under.pop()
    under.append(v)

print(len(over) + len(under) - 2)
for v in under[:-1]:
    print(v[0], v[1])

for v in reversed(over[1:]):
    print(v[0], v[1])
          

# Fixed version:
def sgined_area(v0, v1, v2):
    a1 = v1[0] - v0[0]
    a2 = v1[1] - v0[1]
    b1 = v2[0] - v0[0]
    b2 = v2[1] - v0[1]
    return a1*b2 - b1*a2

n = int(input())
vors = [[int(a) for a in input().split() ] for _ in range(n)]
vors.sort(key=lambda v: (v[1], v[0]))

over = [vors[0], vors[1]]

for v in vors[2:]:
    while len(over) > 1 and sgined_area(over[-2], over[-1], v) > 0:
        over.pop()
    over.append(v)
    
under = [vors[0], vors[1]]

for v in vors[2:]:
    while len(under) > 1 and sgined_area(under[-2], under[-1], v) < 0:
        under.pop()
    under.append(v)
    
print(len(over) + len(under) - 2)
for v in under[:-1]:
    print(v[0], v[1])

    
    
for v in reversed(over[1:]):
    print(v[0], v[1])