def projection_l1_1(values: np.ndarray, eps: int | float | np.ndarray) -> np.ndarray:
    """
    This function computes the orthogonal projections of a batch of points on L1-balls of given radii
    The batch size is  m = values.shape[0].  The points are flattened to dimension
    n = np.prod(value.shape[1:]).  This is required to facilitate sorting.

    If a[0] <= ... <= a[n-1], then the projection can be characterized using the largest  j  such that
    a[j+1] +...+ a[n-1] - a[j]*(n-j-1) >= eps. The  ith  coordinate of projection is equal to  0
    if i=0,...,j.

    :param values:  A batch of  m  points, each a ndarray
    :param eps:  The radii of the respective L1-balls
    :return: projections
    """
    # pylint: disable=invalid-name

    shp = values.shape
    a = values.copy()
    n = np.prod(a.shape[1:])
    m = a.shape[0]
    a = a.reshape((m, n))
    sgns = np.sign(a)
    a = np.abs(a)

    a_argsort = a.argsort(axis=1)
    a_sorted = np.zeros((m, n))
    for i in range(m):
        a_sorted[i, :] = a[i, a_argsort[i, :]]
    a_argsort_inv = a.argsort(axis=1).argsort(axis=1)
    mat = np.zeros((m, 2))

    #   if  a_sorted[i, n-1]  >= a_sorted[i, n-2] + eps,  then the projection is  [0,...,0,eps]
    done = early_done = False
    active = np.array([1] * m)
    after_vec = np.zeros((m, n))
    proj = a_sorted.copy()
    j = n - 2
    while j >= 0:
        mat[:, 0] += a_sorted[:, j + 1]  # =  sum(a_sorted[: i] :  i = j + 1,...,n-1
        mat[:, 1] = a_sorted[:, j] * (n - j - 1) + eps
        #  Find the max in each problem  max{ sum{a_sorted[:, i] : i=j+1,..,n-1} , a_sorted[:, j] * (n-j-1) + eps }
        row_maxes = np.max(mat, axis=1)
        #  Set to  1  if  max >  a_sorted[:, j] * (n-j-1) + eps  >  sum ;  otherwise, set to  0
        ind_set = np.sign(np.sign(row_maxes - mat[:, 0]))
        #  ind_set = ind_set.reshape((m, 1))
        #   Multiplier for activation
        act_multiplier = (1 - ind_set) * active
        act_multiplier = np.transpose([np.transpose(act_multiplier)] * n)
        #  if done, the projection is supported by the current indices  j+1,..,n-1   and the amount by which each
        #  has to be reduced is  delta
        delta = (mat[:, 0] - eps) / (n - j - 1)
        #    The vector of reductions
        delta_vec = np.transpose(np.array([delta] * (n - j - 1)))
        #   The sub-vectors:  a_sorted[:, (j+1):]
        a_sub = a_sorted[:, int(j + 1) :]
        #   After reduction by delta_vec
        a_after = a_sub - delta_vec
        after_vec[:, int(j + 1) :] = a_after
        proj += act_multiplier * (after_vec - proj)
        active = active * ind_set
        if sum(active) == 0:
            done = early_done = True
            break
        j -= 1
    if not early_done:
        delta = (mat[:, 0] + a_sorted[:, 0] - eps) / n
        ind_set = np.sign(np.maximum(delta, 0))
        act_multiplier = ind_set * active
        act_multiplier = np.transpose([np.transpose(act_multiplier)] * n)
        delta_vec = np.transpose(np.array([delta] * n))
        a_after = a_sorted - delta_vec
        proj += act_multiplier * (a_after - proj)
        done = True
    if not done:
        proj = active * (a_sorted - proj)

    for i in range(m):
        proj[i, :] = proj[i, a_argsort_inv[i, :]]

    proj = sgns * proj
    proj = proj.reshape(shp)

    return proj

    This function computes the orthogonal projections of a batch of points on L1-balls of given radii
    The batch size is  m = values.shape[0].  The points are flattened to dimension
    n = np.prod(value.shape[1:]).  This is required to facilitate sorting.

    If a[0] <= ... <= a[n-1], then the projection can be characterized using the largest  j  such that
    a[j+1] +...+ a[n-1] - a[j]*(n-j-1) >= eps. The  ith  coordinate of projection is equal to  0
    if i=0,...,j.

    :param values:  A batch of  m  points, each a ndarray
    :param eps:  The radii of the respective L1-balls
    :return: projections
    
art/utils.py