from typing import List


class ConvexHull:
    def __init__(self, points: List[float] = []) -> None:
        """
        Constructor for the ConvexHull. Provides an interface for building
        the convex hull from a list of floats.

        The convex hull of a shape is the smallest convex set that contains it.
        See https://en.wikipedia.org/wiki/Convex_hull for more information on
        convex hull.

        Args:
                points (List[float], optional): [description]. Defaults to [].
        """
        # Sort the points lexicographically
        # (tuples are compared lexicographically).
        # The 'set' here is for removal of duplicates to detect the case
        # we have just one unique point.
        self.points = sorted(set(tuple(map(tuple, points))))

    def hull(self) -> List[float]:
        """
        Builds the convex hull.

        Returns:
                List[float]: List of floats representing the boundaries
                of the convex hull.
        """
        # Boring case: no points or a single point, possibly
        # repeated multiple times.
        if len(self.points) <= 1:
            return self.points

        # 2D cross product of OA and OB vectors, i.e. z-component of their
        # 3D cross product. Returns a positive value, if OAB makes a
        # counter-clockwise turn, negative for clockwise turn, and
        # zero if the points are collinear.
        def cross(o, a, b):
            return (
                (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
            )

        # Build lower hull
        lower = []
        for p in self.points:
            while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
                lower.pop()
            lower.append(p)

        # Build upper hull
        upper = []
        for p in reversed(self.points):
            while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0:
                upper.pop()
            upper.append(p)

        # Concatenation of the lower and upper hulls gives the convex hull.
        # Last point of each list is omitted because it is repeated at
        # the beginning of the other list.
        return lower[:-1] + upper[:-1]
