from typing import Callable, Hashable, Sequence, Optional, Union, overload, TypeVar, Tuple

_StringType = Sequence[Hashable]
S1 = TypeVar("S1")
S2 = TypeVar("S2")

@overload
def levenshtein(s1: _StringType, s2: _StringType, *, weights: Optional[Tuple[int, int, int]] = (1,1,1), processor: Optional[bool] = None, max: Optional[int] = None) -> int: ...
@overload
def levenshtein(s1: S1, s2: S2, *, weights: Optional[Tuple[int, int, int]] = (1,1,1), processor: Callable[[Union[S1, S2]], _StringType], max: Optional[int] = None) -> int: ...

@overload
def normalized_levenshtein(s1: _StringType, s2: _StringType, *, weights: Optional[Tuple[int, int, int]] = (1,1,1), processor: Optional[bool] = None, score_cutoff: Optional[float] = 0) -> float: ...
@overload
def normalized_levenshtein(s1: S1, s2: S2, *, weights: Optional[Tuple[int, int, int]] = (1,1,1), processor: Callable[[Union[S1, S2]], _StringType], score_cutoff: Optional[float] = 0) -> float: ...

@overload
def levenshtein_editops(s1: _StringType, s2: _StringType, *, processor: Optional[bool] = None) -> List[Tuple[str, int, int]]: ...
@overload
def levenshtein_editops(s1: S1, s2: S2, *, processor: Callable[[Union[S1, S2]], _StringType]) -> List[Tuple[str, int, int]]: ...

@overload
def hamming(s1: _StringType, s2: _StringType, *, processor: Optional[bool] = None, max: Optional[int] = None) -> int: ...
@overload
def hamming(s1: S1, s2: S2, *, processor: Callable[[Union[S1, S2]], _StringType], max: Optional[int] = None) -> int: ...

@overload
def normalized_hamming(s1: _StringType, s2: _StringType, *, processor: Optional[bool] = None, score_cutoff: Optional[float] = 0) -> float: ...
@overload
def normalized_hamming(s1: S1, s2: S2, *, processor: Callable[[Union[S1, S2]], _StringType], score_cutoff: Optional[float] = 0) -> float: ...

@overload
def jaro_similarity(s1: _StringType, s2: _StringType, *, processor: Optional[bool] = None, score_cutoff: Optional[float] = 0) -> float: ...
@overload
def jaro_similarity(s1: S1, s2: S2, *, processor: Callable[[Union[S1, S2]], _StringType], score_cutoff: Optional[float] = 0) -> float: ...

@overload
def jaro_winkler_similarity(s1: _StringType, s2: _StringType, *, prefix_weight: float = 0.1, processor: Optional[bool] = None, score_cutoff: Optional[float] = 0) -> float: ...
@overload
def jaro_winkler_similarity(s1: S1, s2: S2, *, prefix_weight: float = 0.1, processor: Callable[[Union[S1, S2]], _StringType], score_cutoff: Optional[float] = 0) -> float: ...
