from typing import TypeVar

int8 = 'int8'
int16 = 'int16'
int32 = 'int32'
int64 = 'int64'
int_ = int64
float32 = 'float32'
float64 = 'float64'
float_ = float64
bool_ = 'bool'

_Number = TypeVar('_Number', int, float, bool)
_Dtype = str
_ShapeLike = tuple[int, ...]

class ndarray:
    # Dunder Methods
    def __add__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __sub__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __mul__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __truediv__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __pow__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __matmul__(self, other: 'ndarray') -> 'ndarray': ...

    def __and__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __or__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __xor__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __invert__(self) -> 'ndarray': ...

    def __eq__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    def __ne__(self, other: _Number | 'ndarray') -> 'ndarray': ...
    
    def __getitem__(self, key): ...
    def __setitem__(self, key, value): ...
    def __len__(self) -> int: ...

    def __repr__(self) -> str: ...
    def __str__(self) -> str: ...

    # Properties
    @property
    def dtype(self) -> _Dtype: ...
    @property
    def ndim(self) -> int: ...
    @property
    def size(self) -> int: ...
    @property
    def shape(self) -> tuple[int, ...]: ...

    # Boolean operations
    def all(self, axis: int = 0) -> bool: ...
    def any(self, axis: int = 0) -> bool: ...

    # Aggregate Functions
    def sum(self, axis: int = 0) -> _Number | 'ndarray': ...
    def min(self, axis: int = 0) -> _Number | 'ndarray': ...
    def max(self, axis: int = 0) -> _Number | 'ndarray': ...
    def mean(self, axis: int = 0) -> _Number | 'ndarray': ...
    def std(self, axis: int = 0) -> _Number | 'ndarray': ...
    def var(self, axis: int = 0) -> _Number | 'ndarray': ...

    # Searching and Sorting
    def argmin(self, axis: int = 0) -> int | 'ndarray': ...
    def argmax(self, axis: int = 0) -> int | 'ndarray': ...
    def argsort(self, axis: int = 0) -> 'ndarray': ...
    def sort(self, axis: int = 0) -> None: ...

    # Shape Manipulation
    def reshape(self, shape: _ShapeLike) -> 'ndarray': ...
    def resize(self, shape: _ShapeLike) -> None: ...
    def repeat(self, repeats: int, axis: int = 0) -> 'ndarray': ...
    def transpose(self, axes: _ShapeLike = None) -> 'ndarray': ...
    def squeeze(self, axis: int = 0) -> 'ndarray': ...
    def flatten(self) -> 'ndarray': ...
    
    # Miscellaneous
    def astype(self, dtype: _Dtype) -> 'ndarray': ...
    def round(self, decimals: int = 0) -> 'ndarray': ...
    def copy(self) -> 'ndarray': ...
    def tolist(self) -> list[_Number]: ...


# Array Creation
def array(object, dtype: _Dtype = None) -> 'ndarray': ...
def zeros(shape: _ShapeLike, dtype: _Dtype = float_) -> 'ndarray': ...
def ones(shape: _ShapeLike, dtype: _Dtype = float_) -> 'ndarray': ...
def full(shape: _ShapeLike, fill_value: _Number, dtype: _Dtype = None) -> 'ndarray': ...
def identity(n: int, dtype: _Dtype = float_) -> 'ndarray': ...

def arange(start: int, stop: int, step: int, dtype: _Dtype = int_) -> 'ndarray': ...
def linspace(start: _Number, stop: _Number, num: int, endpoint: bool = True, dtype: _Dtype = float_) -> 'ndarray': ...

# Trigonometry
def sin(x: 'ndarray') -> 'ndarray': ...
def cos(x: 'ndarray') -> 'ndarray': ...
def tan(x: 'ndarray') -> 'ndarray': ...
def arcsin(x: 'ndarray') -> 'ndarray': ...
def arccos(x: 'ndarray') -> 'ndarray': ...
def arctan(x: 'ndarray') -> 'ndarray': ...

# Exponents and Logarithms
def exp(x: 'ndarray') -> 'ndarray': ...
def log(x: 'ndarray') -> 'ndarray': ...
def log2(x: 'ndarray') -> 'ndarray': ...
def log10(x: 'ndarray') -> 'ndarray': ...

# Miscellaneous
def abs(x: 'ndarray') -> 'ndarray': ...
def ceil(x: 'ndarray') -> 'ndarray': ...
def floor(x: 'ndarray') -> 'ndarray': ...

def concatenate(arrays: tuple['ndarray', ...], axis: int = 0) -> 'ndarray': ...
def corrcoef(x: 'ndarray', y: 'ndarray') -> 'ndarray': ...


class random:
    @staticmethod
    def rand(*size) -> 'ndarray': ...
    @staticmethod
    def randn(*size) -> 'ndarray': ...
    @staticmethod
    def randint(low: int, high: int, size: _ShapeLike) -> 'ndarray': ...
    @staticmethod
    def normal(loc: _Number, scale: _Number, size: _ShapeLike) -> 'ndarray': ...
    @staticmethod
    def uniform(low: _Number, high: _Number, size: _ShapeLike) -> 'ndarray': ...
