def _projection(
        self, values: "torch.Tensor", eps: int | float | np.ndarray, norm_p: int | float | str
    ) -> "torch.Tensor":
        """
        Project `values` on the L_p norm ball of size `eps`.

        :param values: Values to clip.
        :param eps: Maximum norm allowed.
        :param norm_p: L_p norm to use for clipping supporting 1, 2, `np.Inf` and "inf".
        :return: Values of `values` after projection.
        """
        import torch

        values_tmp = values.reshape(values.shape[0], -1)

        if norm_p == 2:
            if isinstance(eps, np.ndarray):
                raise NotImplementedError(
                    "The parameter `eps` of type `np.ndarray` is not supported to use with norm 2."
                )

            values_tmp = values_tmp * torch.min(
                torch.tensor([1.0], dtype=torch.float32).to(self._classifier.device),
                eps / (torch.norm(values_tmp, p=2, dim=1) + EPS),
            ).unsqueeze_(-1)

        elif norm_p == 1:
            if isinstance(eps, np.ndarray):
                raise NotImplementedError(
                    "The parameter `eps` of type `np.ndarray` is not supported to use with norm 1."
                )

            values_tmp = values_tmp * torch.min(
                torch.tensor([1.0], dtype=torch.float32).to(self._classifier.device),
                eps / (torch.norm(values_tmp, p=1, dim=1) + EPS),
            ).unsqueeze_(-1)

        elif norm_p in [np.inf, "inf"]:
            if isinstance(eps, np.ndarray):
                eps_array = eps * np.ones_like(values.cpu())
                eps = eps_array.reshape([eps_array.shape[0], -1])

            values_tmp = values_tmp.sign() * torch.min(
                values_tmp.abs(), torch.tensor([eps], dtype=torch.float32).to(self._classifier.device)
            )

        else:
            raise NotImplementedError(
                "Values of `norm_p` different from 1, 2 and `np.inf` are currently not supported."
            )

        values = values_tmp.reshape(values.shape)

        return values

        Project `values` on the L_p norm ball of size `eps`.

        :param values: Values to clip.
        :param eps: Maximum norm allowed.
        :param norm_p: L_p norm to use for clipping supporting 1, 2, `np.Inf` and "inf".
        :return: Values of `values` after projection.
        
art/defences/trainer/adversarial_trainer_oaat_pytorch.py