from typing import Iterable, Any, Union, Tuple


# haven't yet figured this one out...
# from typing_extensions import Protocol
# class Expression(Protocol):
#     def __add__(self, other: Expression) -> Expression: ...
#     def __sub__(self, other: Expression) -> Expression: ...
#     def __neg__(self) -> Expression: ...
#     def __pow__(self, other: Any): Expression: ...
#     def __truediv__(self, other: Any) -> 'Matrix': ...


Expression = Any


class Symbol:
    def __init__(self, name: str) -> None: ...
    def __add__(self, other: Any) -> Expression: ...
    def __sub__(self, other: Any) -> Expression: ...
    def __neg__(self) -> Expression: ...
    def __mul__(self, other: Any) -> Expression: ...
    def __truediv__(self, other: Any) -> Expression: ...
    def __pow__(self, other: Any): Expression: ...


class Matrix:
    T: 'Matrix'
    is_Matrix: bool
    free_symbols: Iterable[Symbol]
    shape: Tuple[int, ...]

    def __init__(self, elements: Iterable) -> None: ...
    def __iter__(self) -> Expression: ...
    def __next__(self) -> Expression: ...
    def __setitem__(self, *args) -> None: ...
    def __getitem__(self, *args) -> Expression: ...
    def __matmul__(self, other: 'Matrix') -> 'Matrix': ...
    def __add__(self, other: Any) -> 'Matrix': ...
    def __sub__(self, other: Any) -> 'Matrix': ...
    def __mul__(self, other: Any) -> 'Matrix': ...
    def __truediv__(self, other: Any) -> 'Matrix': ...
    def __neg__(self) -> 'Matrix': ...
    def jacobian(self, *args, **kwargs) -> 'Matrix': ...
    def reshape(self, *args) -> 'Matrix': ...
    def dot(self, other: 'Matrix') -> Expression: ...
    def xreplace(self, *args: Any) -> 'Matrix': ...


MutableDenseMatrix = Matrix


def sin(x: Any) -> Expression: ...
def cos(x: Any) -> Expression: ...
def tan(x: Any) -> Expression: ...
def asin(x: Any) -> Expression: ...
def acos(x: Any) -> Expression: ...
def atan(x: Any) -> Expression: ...
def sqrt(x: Any) -> Expression: ...


def zeros(*x: int) -> Matrix: ...
def symbols(x: str) -> Iterable[Symbol]: ...
def diag(*x: Expression) -> Matrix: ...
def count_ops(x: Matrix) -> int: ...
def hessian(x: Expression, y: Matrix) -> Matrix: ...
def trigsimp(x: Union[Expression, Matrix]) -> Union[Expression, Matrix]: ...
def init_printing(x: bool = True) -> Any: ...
def diff(e: Expression, q: Matrix) -> Matrix: ...
