code: def convex_hull(points):
      
      turn_left, turn_right, turn_none = (1, -1, 0)
  
      def cmp(a, b):
          return (a > b) - (a < b)
  
      def turn(p, q, r):
          return cmp((q[0] - p[0])*(r[1] - p[1]) - (r[0] - p[0])*(q[1] - p[1]), 0)
  
      def keep_left(hull, r):
          while len(hull) > 1 and turn(hull[-2], hull[-1], r) != turn_left:
              hull.pop()
          if not len(hull) or hull[-1] != r:
              hull.append(r)
          return hull
  
      points = sorted(points)
      l = reduce(keep_left, points, [])
      u = reduce(keep_left, reversed(points), [])
      return l.extend(u[i] for i in range(1, len(u) - 1)) or l
docs: Returns points on convex hull in counterclockwise order according to Graham's scan algorithm.
  
      Reference: https://gist.github.com/arthur-e/5cf52962341310f438e96c1f3c3398b8
  
      .. note:: This implementation only works in 2-dimensional space.
  
      :param points: list of 2-dimensional points
      :type points: list, tuple
      :return: convex hull of the input points
      :rtype: list