from collections.abc import Callable as Callable, Iterable
from typing import Literal

def unique_list[T](list_to_clean: Iterable[T], method: Literal['id', 'hash', 'str'] = 'str') -> list[T]:
    ''' Remove duplicates from the list while keeping the order using ids, hash, or str

\tArgs:
\t\tlist_to_clean\t(Iterable[T]):\t\t\t\t\tThe list to clean
\t\tmethod\t\t\t(Literal["id", "hash", "str"]):\tThe method to use to identify duplicates
\tReturns:
\t\tlist[T]: The cleaned list

\tExamples:
\t\t>>> unique_list([1, 2, 3, 2, 1], method="id")
\t\t[1, 2, 3]

\t\t>>> s1 = {1, 2, 3}
\t\t>>> s2 = {2, 3, 4}
\t\t>>> s3 = {1, 2, 3}
\t\t>>> unique_list([s1, s2, s1, s1, s3, s2, s3], method="id")
\t\t[{1, 2, 3}, {2, 3, 4}, {1, 2, 3}]

\t\t>>> s1 = {1, 2, 3}
\t\t>>> s2 = {2, 3, 4}
\t\t>>> s3 = {1, 2, 3}
\t\t>>> unique_list([s1, s2, s1, s1, s3, s2, s3], method="str")
\t\t[{1, 2, 3}, {2, 3, 4}]
\t'''
def at_least_n[T](iterable: Iterable[T], predicate: Callable[[T], bool], n: int) -> bool:
    """ Return True if at least n elements in iterable satisfy predicate.
\tIt's like the built-in any() but for at least n matches.

\tStops iterating as soon as n matches are found (short-circuit evaluation).

\tArgs:
\t\titerable\t(Iterable[T]):\t\t\tThe iterable to check.
\t\tpredicate\t(Callable[[T], bool]):\tThe predicate to apply to items.
\t\tn\t\t\t(int):\t\t\t\t\tMinimum number of matches required.

\tReturns:
\t\tbool: True if at least n elements satisfy predicate, otherwise False.

\tExamples:
\t\t>>> at_least_n([1, 2, 3, 4, *[i for i in range(5, int(1e5))]], lambda x: x % 2 == 0, 2)
\t\tTrue
\t\t>>> at_least_n([1, 3, 5, 7], lambda x: x % 2 == 0, 1)
\t\tFalse
\t"""
