def mid_points(
        self,
        x0: np.ndarray,
        x1: np.ndarray,
        epsilons: np.ndarray,
        bounds: tuple[float, float],
    ) -> np.ndarray:
        """
        returns a point between x0 and x1 where epsilon = 0 returns x0 and epsilon = 1 returns x1
        """
        if self.norm == 0:
            # get epsilons in right shape for broadcasting
            epsilons = epsilons.reshape(epsilons.shape + (1,) * (x0.ndim - 1))

            threshold = (bounds[1] - bounds[0]) * epsilons
            mask = np.abs(x1 - x0) < threshold
            new_x = np.where(mask, x1, x0)
        elif self.norm == 1:
            # get epsilons in right shape for broadcasting
            epsilons = epsilons.reshape(epsilons.shape + (1,) * (x0.ndim - 1))

            threshold = (bounds[1] - bounds[0]) * (1 - epsilons)
            mask = np.abs(x1 - x0) > threshold
            new_x = np.where(mask, x0 + np.sign(x1 - x0) * (np.abs(x1 - x0) - threshold), x0)
        elif self.norm == 2:
            # get epsilons in right shape for broadcasting
            epsilons = epsilons.reshape(epsilons.shape + (1,) * (x0.ndim - 1))
            new_x = epsilons * x1 + (1 - epsilons) * x0
        elif self.norm in ["inf", np.inf]:
            delta = x1 - x0
            min_, max_ = bounds
            s = max_ - min_
            # get epsilons in right shape for broadcasting
            epsilons = epsilons.reshape(epsilons.shape + (1,) * (x0.ndim - 1))

            clipped_delta = np.where(delta < -epsilons * s, -epsilons * s, delta)
            clipped_delta = np.where(clipped_delta > epsilons * s, epsilons * s, clipped_delta)
            new_x = x0 + clipped_delta
        else:
            raise ValueError("Value of `norm` is not supported.")

        return new_x.astype(config.ART_NUMPY_DTYPE)

        returns a point between x0 and x1 where epsilon = 0 returns x0 and epsilon = 1 returns x1
        
art/attacks/evasion/brendel_bethge.py