"""Unique permutations is used to calculate the unique permutations of a list/vector and their count."""

from collections.abc import Generator
from dataclasses import dataclass


@dataclass
class UniqueElement:
    """Class for unique elements to keep track of occurrences."""

    value: int
    occurrences: int


def unique_perms(elements: list[int]) -> Generator[tuple[int, ...], None, None]:
    r"""Determine the number of unique permutations of a list.

    Examples:
        Consider the following vector

        \[
            \left[1, 1, 2, 2, 1, 2, 1, 3, 3, 3\right].
        \]

        The number of possible permutations possible with the above vector is \(4200\). This can be
        obtained using the `|toqito⟩` package as follows.

        ```python exec="1" source="above"
        from toqito.perms import unique_perms

        vec_nums = [1, 1, 2, 2, 1, 2, 1, 3, 3, 3]

        print(len(list(unique_perms(vec_nums))))
        ```

    Args:
        elements: List of integers.

    Returns:
        The number of possible permutations possible.

    """
    elem_set = set(elements)
    list_unique = [UniqueElement(value=i, occurrences=elements.count(i)) for i in elem_set]
    len_elems = len(elements)

    return perm_unique_helper(list_unique, [0] * len_elems, len_elems - 1)


def perm_unique_helper(
    list_unique: list[UniqueElement], result_list: list[int], elem_d: int,
) -> Generator[tuple[int, ...], None, None]:
    """Provide helper function for unique_perms.

    Args:
        list_unique: list[UniqueElement]
        result_list: list[int]
        elem_d: int

    """
    if elem_d < 0:
        yield tuple(result_list)
    else:
        for i in list_unique:
            if i.occurrences > 0:
                result_list[elem_d] = i.value
                i.occurrences -= 1
                yield from perm_unique_helper(list_unique, result_list, elem_d - 1)
                i.occurrences += 1
