def _go_to_boundary(self, x: np.ndarray, y: np.ndarray, grad: np.ndarray) -> np.ndarray:
        """
        Move towards decision boundary.

        :param x: Current example to be moved towards the decision boundary.
        :param y: The true label.
        :param grad: Gradient towards decision boundary.
        :return: Example moved towards decision boundary.
        """
        epsilon = 5
        nb_calls = 0
        x_perturbed = x

        if self.norm in [np.inf, "inf"]:
            grads = np.sign(grad) / np.linalg.norm(grad.flatten(), ord=2)
        else:
            grads = grad  # self.norm in [1, 2]

        while not self._is_adversarial(x_perturbed, y):
            nb_calls += 1
            if nb_calls > 100:
                logger.info("Moving towards decision boundary failed because of too many iterations.")
                break

            x_perturbed = x + (nb_calls * epsilon * grads[0])
            x_perturbed = np.clip(x_perturbed, a_min=self.clip_min, a_max=self.clip_max)

        self.nb_calls += nb_calls

        return x_perturbed

        Move towards decision boundary.

        :param x: Current example to be moved towards the decision boundary.
        :param y: The true label.
        :param grad: Gradient towards decision boundary.
        :return: Example moved towards decision boundary.
        
art/attacks/evasion/geometric_decision_based_attack.py