from typing import Union, List


class Cubic:
    def __init__(self, curves: List[Union[float, int]]):
        self.curves = curves

    def get_value(self, time: Union[float, int]) -> Union[float, int]:
        start_gradient: float = 0.0
        end_gradient: float = 0.0
        start: float = 0.0
        mid: float = 0.0
        end: float = 1.0

        if time <= 0.0:
            if self.curves[0] > 0.0:
                start_gradient = self.curves[1] / self.curves[0]
            elif self.curves[1] == 0.0 and self.curves[2] > 0.0:
                start_gradient = self.curves[3] / self.curves[2]
            return start_gradient * time

        if time >= 1.0:
            if self.curves[2] < 1.0:
                end_gradient = (self.curves[3] - 1.0) / (self.curves[2] - 1.0)
            elif self.curves[2] == 1.0 and self.curves[0] < 1.0:
                end_gradient = (self.curves[1] - 1.0) / (self.curves[0] - 1.0)
            return 1.0 + end_gradient * (time - 1.0)

        while start < end:
            mid = (start + end) / 2
            x_est = self.calculate(self.curves[0], self.curves[2], mid)
            if abs(time - x_est) < 0.00001:
                return self.calculate(self.curves[1], self.curves[3], mid)
            if x_est < time:
                start = mid
            else:
                end = mid
        return self.calculate(self.curves[1], self.curves[3], mid)

    @staticmethod
    def calculate(a: Union[float, int], b: Union[float, int], m: Union[float, int]) -> Union[float, int]:
        return 3.0 * a * (1 - m) * (1 - m) * m + 3.0 * b * (1 - m) * m * m + m * m * m

# Example usage:
# cubic_instance = Cubic([0.1, 0.2, 0.3, 0.4])
# value = cubic_instance.get_value(0.5)
# print(value)


if __name__ == "__main__":
    pass
