def _one_step_adv_example(
        self,
        x: "torch.Tensor",
        x_init: "torch.Tensor",
        y: "torch.Tensor",
        eps: int | float | np.ndarray,
        eps_step: int | float | np.ndarray,
        random_init: bool,
        training_mode: bool,
    ) -> "torch.Tensor":
        """
        Compute adversarial examples for one iteration.

        :param x: Current adversarial examples.
        :param x_init: An array with the original inputs.
        :param y: Target values (class labels) one-hot-encoded of shape `(nb_samples, nb_classes)` or indices of shape
                  (nb_samples,). Only provide this parameter if you'd like to use true labels when crafting adversarial
                  samples. Otherwise, model predictions are used as labels to avoid the "label leaking" effect
                  (explained in this paper: https://arxiv.org/abs/1611.01236).
        :param eps: Maximum perturbation that the attacker can introduce.
        :param eps_step: Attack step size (input variation) at each iteration.
        :param random_init: Random initialisation within the epsilon ball. For random_init=False starting at the
                            original input.
        :param training_mode: training mode of classifier for attack generation. training_mode=False puts classifier in
                                eval mode
        :return: Adversarial examples.
        """
        import torch

        if random_init:
            n = x.shape[0]
            m = np.prod(x.shape[1:]).item()

            random_perturbation_array = (
                random_sphere(n, m, eps, self._train_params["norm"]).reshape(x.shape).astype(ART_NUMPY_DTYPE)
            )
            random_perturbation = torch.from_numpy(random_perturbation_array).to(self._classifier.device)
            x_adv = x + random_perturbation

            if self._classifier.clip_values is not None:
                clip_min, clip_max = self._classifier.clip_values
                x_adv = torch.max(
                    torch.min(x_adv, torch.tensor(clip_max).to(self._classifier.device)),
                    torch.tensor(clip_min).to(self._classifier.device),
                )

        else:
            x_adv = x

        # Get perturbation
        perturbation = self._compute_perturbation(x_adv, x_init, y, training_mode)

        # Apply perturbation and clip
        x_adv = self._apply_perturbation(x_adv, perturbation, eps_step)

        # Do projection
        perturbation = self._projection(x_adv - x_init, eps, self._train_params["norm"])

        # Recompute x_adv
        x_adv = perturbation + x_init

        return x_adv

        Compute adversarial examples for one iteration.

        :param x: Current adversarial examples.
        :param x_init: An array with the original inputs.
        :param y: Target values (class labels) one-hot-encoded of shape `(nb_samples, nb_classes)` or indices of shape
                  (nb_samples,). Only provide this parameter if you'd like to use true labels when crafting adversarial
                  samples. Otherwise, model predictions are used as labels to avoid the "label leaking" effect
                  (explained in this paper: https://arxiv.org/abs/1611.01236).
        :param eps: Maximum perturbation that the attacker can introduce.
        :param eps_step: Attack step size (input variation) at each iteration.
        :param random_init: Random initialisation within the epsilon ball. For random_init=False starting at the
                            original input.
        :param training_mode: training mode of classifier for attack generation. training_mode=False puts classifier in
                                eval mode
        :return: Adversarial examples.
        
art/defences/trainer/adversarial_trainer_oaat_pytorch.py