import functools


def convex_hull(points):
    """
    Computes the convex hull of a list of 2D points, by implementing Andrew's monotone chain algorithm.

    :param points: list of (x, y) tuples, representing the points
    :return: list of (x, y) tuples, representing the convex hull
    """

    def turn_left(p, q, r):
        """
        Returns `True` if the three points `p`, `q`, `r` constitute a 'left turn'.

        To do so, it computes the z-coordinate of the cross product of the two vectors (pq) & (pr).
        If the result is 0, the points are collinear. If it is positive, the three points constitute
        a 'left turn' (counter-clockwise), otherwise a 'right turn' (clockwise).
        """
        return (q[0] - p[0]) * (r[1] - p[1]) - (r[0] - p[0]) * (q[1] - p[1]) > 0

    def x_then_y(a, b):
        if a[0] < b[0] or (a[0] == b[0] and a[1] < b[1]):
            return -1
        elif a == b:
            return 0
        else:
            return 1

    sorted_points = sorted(points, key=functools.cmp_to_key(x_then_y))

    # Build lower hull
    lower_hull = []
    for pt in sorted_points:
        while len(lower_hull) > 1 and not turn_left(lower_hull[-2], lower_hull[-1], pt):
            lower_hull.pop()
        lower_hull.append(pt)

    # Build upper hull
    upper_hull = []
    for pt in reversed(sorted_points):
        while len(upper_hull) > 1 and not turn_left(upper_hull[-2], upper_hull[-1], pt):
            upper_hull.pop()
        upper_hull.append(pt)

    return lower_hull[:-1] + upper_hull[:-1]