import sys
from _typeshed import (
    SupportsAdd,
    SupportsGetItem,
    SupportsMod,
    SupportsMul,
    SupportsRAdd,
    SupportsRMod,
    SupportsRMul,
    SupportsRSub,
    SupportsSub,
)
from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence
from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller
from typing import Any, AnyStr, ParamSpec, Protocol, SupportsAbs, SupportsIndex, TypeAlias, TypeVar, overload, type_check_only
from typing_extensions import TypeIs

_R = TypeVar("_R")
_T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)
_T_contra = TypeVar("_T_contra", contravariant=True)
_K = TypeVar("_K")
_V = TypeVar("_V")
_P = ParamSpec("_P")

# The following protocols return "Any" instead of bool, since the comparison
# operators can be overloaded to return an arbitrary object. For example,
# the numpy.array comparison dunders return another numpy.array.

@type_check_only
class _SupportsDunderLT(Protocol):
    def __lt__(self, other: Any, /) -> Any: ...

@type_check_only
class _SupportsDunderGT(Protocol):
    def __gt__(self, other: Any, /) -> Any: ...

@type_check_only
class _SupportsDunderLE(Protocol):
    def __le__(self, other: Any, /) -> Any: ...

@type_check_only
class _SupportsDunderGE(Protocol):
    def __ge__(self, other: Any, /) -> Any: ...

_SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT

@type_check_only
class _SupportsInversion(Protocol[_T_co]):
    def __invert__(self) -> _T_co: ...

@type_check_only
class _SupportsNeg(Protocol[_T_co]):
    def __neg__(self) -> _T_co: ...

@type_check_only
class _SupportsPos(Protocol[_T_co]):
    def __pos__(self) -> _T_co: ...

# All four comparison functions must have the same signature, or we get false-positive errors
def lt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
def le(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
def eq(a: object, b: object, /) -> Any: ...
def ne(a: object, b: object, /) -> Any: ...
def ge(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
def gt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ...
def not_(a: object, /) -> bool: ...
def truth(a: object, /) -> bool: ...
def is_(a: object, b: object, /) -> bool: ...
def is_not(a: object, b: object, /) -> bool: ...
def abs(a: SupportsAbs[_T], /) -> _T: ...

@overload
def add(a: SupportsAdd[_T_contra, _T_co], b: _T_contra, /) -> _T_co: ...
@overload
def add(a: _T_contra, b: SupportsRAdd[_T_contra, _T_co], /) -> _T_co: ...

def and_(a, b, /): ...
def floordiv(a, b, /): ...
def index(a: SupportsIndex, /) -> int: ...
def inv(a: _SupportsInversion[_T_co], /) -> _T_co: ...
def invert(a: _SupportsInversion[_T_co], /) -> _T_co: ...
def lshift(a, b, /): ...

@overload
def mod(a: SupportsMod[_T_contra, _T_co], b: _T_contra, /) -> _T_co: ...
@overload
def mod(a: _T_contra, b: SupportsRMod[_T_contra, _T_co], /) -> _T_co: ...

@overload
def mul(a: SupportsMul[_T_contra, _T_co], b: _T_contra, /) -> _T_co: ...
@overload
def mul(a: _T_contra, b: SupportsRMul[_T_contra, _T_co], /) -> _T_co: ...

def matmul(a, b, /): ...
def neg(a: _SupportsNeg[_T_co], /) -> _T_co: ...
def or_(a, b, /): ...
def pos(a: _SupportsPos[_T_co], /) -> _T_co: ...
def pow(a, b, /): ...
def rshift(a, b, /): ...

@overload
def sub(a: SupportsSub[_T_contra, _T_co], b: _T_contra, /) -> _T_co: ...
@overload
def sub(a: _T_contra, b: SupportsRSub[_T_contra, _T_co], /) -> _T_co: ...

def truediv(a, b, /): ...
def xor(a, b, /): ...
def concat(a: Sequence[_T], b: Sequence[_T], /) -> Sequence[_T]: ...
def contains(a: Container[object], b: object, /) -> bool: ...
def countOf(a: Iterable[object], b: object, /) -> int: ...

@overload
def delitem(a: MutableSequence[Any], b: int, /) -> None: ...
@overload
def delitem(a: MutableSequence[Any], b: slice[int | None], /) -> None: ...
@overload
def delitem(a: MutableMapping[_K, Any], b: _K, /) -> None: ...

@overload
def getitem(a: Sequence[_T], b: slice[int | None], /) -> Sequence[_T]: ...
@overload
def getitem(a: SupportsGetItem[_K, _V], b: _K, /) -> _V: ...

def indexOf(a: Iterable[_T], b: _T, /) -> int: ...

@overload
def setitem(a: MutableSequence[_T], b: int, c: _T, /) -> None: ...
@overload
def setitem(a: MutableSequence[_T], b: slice[int | None], c: Sequence[_T], /) -> None: ...
@overload
def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ...

def length_hint(obj: object, default: int = 0, /) -> int: ...
def iadd(a, b, /): ...
def iand(a, b, /): ...
def iconcat(a, b, /): ...
def ifloordiv(a, b, /): ...
def ilshift(a, b, /): ...
def imod(a, b, /): ...
def imul(a, b, /): ...
def imatmul(a, b, /): ...
def ior(a, b, /): ...
def ipow(a, b, /): ...
def irshift(a, b, /): ...
def isub(a, b, /): ...
def itruediv(a, b, /): ...
def ixor(a, b, /): ...

if sys.version_info >= (3, 11):
    def call(obj: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...

def _compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...

if sys.version_info >= (3, 14):
    def is_none(a: object, /) -> TypeIs[None]: ...
    def is_not_none(a: _T | None, /) -> TypeIs[_T]: ...
