import numpy as np

class BpfPointsError(ValueError): pass
class BpfInversionError(ValueError): pass


class BpfInterface:
    def ntodx(self, N: int) -> float: ...
    def dxton(self, dx: float) -> int: ...
    def bounds(self) -> tuple[float, float]: ...
        
    @property
    def x0(self) -> float: ...
       
    
    @property
    def x1(self) -> float: ...
       
    def __add__(a: BpfInterface | float, b: BpfInterface | float) -> BpfInterface: ...
    
    def __sub__(a: BpfInterface | float, b: BpfInterface | float) -> BpfInterface: ...
    
    def __mul__(a: BpfInterface | float, b: BpfInterface | float) -> BpfInterface: ...

    def __div__(a: BpfInterface | float, b: BpfInterface | float) -> BpfInterface: ...

    def __truediv__(a: BpfInterface | float, b: BpfInterface | float) -> BpfInterface: ...

    def __pow__(a: BpfInterface | float, b: BpfInterface | float, module: float) -> BpfInterface: ...

    def __neg__(self: BpfInterface) -> BpfInterface: ...
    
    def __mod__(self, other: BpfInterface | float) -> BpfInterface:
    
    def __abs__(self) -> BpfInterface: ...
    
    def __or__(a: BpfInterface, b: BpfInterface|float) -> BpfInterface: ...
    
    def __rshift__(a: BpfInterace, b: BpfInterface|float) -> BpfInterface: ...

    def __lshift__(a: BpfInterace, b: BpfInterface|float) -> BpfInterface: ...

    def __xor__(a: BpfInterace, b: BpfInterface|float) -> BpfInterface: ...
                
    def _get_points_for_rendering(self, n: int = -1) -> tuple[np.ndarray, np.ndarray]: ...
        
    def render(self, xs: int | np.ndarray | list[float], interpolation: str = 'linear') -> np.ndarray: ...
    
    def plot(self, kind: str = 'line', n: int = -1, show: bool = True, axes=None, **keys) -> None: ...

    def sampled(self, double dx, interpolation='linear') -> BpfInterface: ...
        
    def sample_between(self, x0: float, x1: float, dx: float, out: np.ndarray|float = None) -> np.ndarray: ...
        
    def sampled_between(self, x0: float, x1, double dx, interpolation='linear') -> BpfInterface: ...

    def mapn_between(self, n: int, x0: float, x1: float, out: np.ndarray = None) -> np.ndarray: ...
        
    def map(self, xs: int|np.ndarray, out: np.ndarray = None) -> np.ndarray: ...
        
    def concat(self, other: BpfInterface)-> BpfInterface: ...
        
    def round(self) -> BpfInterface: ...
        
    def rand(self) -> BpfInterface: ...
        
    def cos(self) -> BpfInterface: ...
        
    def sin(self) -> BpfInterface: ...  
        
    def ceil(self) -> BpfInterface: ...
     
    def expon(self) -> BpfInterface: ...
        
    def floor(self) -> BpfInterface: ...
    
    def tanh(self) -> BpfInterface: ... 
        
    def abs(self) -> BpfInterface: ...  
    
    def sqrt(self) -> BpfInterface: ... 
        
    def acos(self) -> BpfInterface: ... 
        
    def asin(self) -> BpfInterface: ... 
        
    def tan(self) -> BpfInterface: ...  
        
    def sinh(self) -> BpfInterface: ... 

    def log10(self) -> BpfInterface: ... 

    def log(self, base: float = 2.718281828459045) -> BpfInterface: ... 
    
    def m2f(self) -> BpfInterface: ... 

    def f2m(self) -> BpfInterface: ... 
    def db2amp(self) -> BpfInterface: ... 
    def amp2db(self) -> BpfInterface: ... 
    def clip(self, y0: float = float('-inf'), y1: float = float('inf')) -> BpfInterface: ...
    def derivative(self) -> BpfInterface: ...
    def integrated(self) -> BpfInterface: ...
    def integrate(self) -> float: ...
    def integrate_between(self, x0: float, x1: float, N: int = 0) -> float: ...
    def mean(self) -> float: ...
    def zeros(self, h: float = 0.01, N: int = 0, 
              x0: float = float('nan'), x1: float = float('nan'), maxzeros: int = 0) -> list[float]: ...
    def max(self, b: BpfInterface|float) -> BpfInterface: ...
    def min(self, b: BpfInterface|float) -> BpfInterface: ...
    def __call__(self, other: float) -> float: ...
    
    def keep_slope(self, epsilon: float 0.) -> BpfInterface ...
        
    def outbound(self, y0: float, y1: float) -> BpfInterface: ...
        
    def apply(self, func) -> BpfInterface: ...
        
    def preapply(self, func) -> BpfInterface ...:
        
    def periodic(self) -> BpfInterface: ...
       
    def stretched(self, rx: föpat, fixpoint: float = 0.) -> BpfInterface: ...
        
    def fit_between(self, x0: float, x1: float) -> BpfInterface: ...
      
    def shifted(self, dx: float) -> BpfInterface: ...
        
    def inverted(self) -> BpfInterface: ...
        
    @classmethod
    def fromseq(cls, *points: float|tuple[float, float], **kws) -> BpfInterface: ...
        
    def copy(self) -> BpfInterface: ...

        
class BpfBase(BpfInterface):

    def __init__(self, xs: np.ndarray|list[float], ys: np.ndarray | list[float]): ...
        
    @property
    def descriptor(self) -> str: ... 
            
    def mapn_between(self, n: int, xstart: float, xend: float, out: np.ndarray = None) -> np.ndarray: ...
       
    def points(self) -> tuple[np.ndarray, np.ndarray]: ...
        
    def clone_with_new_data(self, xs: np.ndarray, ys: np.ndarray) -> BpfInterface: ...
        
    def insertpoint(self, x: float, y: float) -> BpfInterface: ...
        
    def removepoint(self, x: float) -> BpfInterface: ...
        
    def segments(self) -> Iterator[tuple[float, float, float]]: ...

    @property
    def exp(self) -> float: ...
        

class Linear(BpfBase): ...

    def inverted(self) -> Linear: ...
        
    def flatpairs(self) -> np.ndarray: ...
        

class Smooth(BpfBase): ...

   
class Smoother(BpfBase): ...

    
class Halfcos(BpfBase): ...
    

class Halfcosm(Halfcos): ...
    

class Expon(BpfBase): ...


class Exponm(Expon): ...
    

class NoInterpol(BpfBase): ...

    
class Nearest(BpfBase): ...


class Sampled(BpfInterface): 
    @property
    def ys(self) -> np.ndarray: ...
    
    def __init__(self, samples: np.ndarray, dx: float, x0: float = 0., interpolation: str = 'linear'): ...
        
    @property
    def samplerate(self) -> float: ...
        
    @property
    def xs(self) -> np.ndarray: ...
        
    
    @property
    def dx(self) -> float: ...
    
    def set_interpolation(self, interpolation: str) -> Sampled: ...
        
    
    def flatpairs(self) -> np.ndarray: ...
        

class Spline(BpfInterface): ...

    

class USpline(BpfInterface): ...

    
class Slope(BpfInterface): ...
    @property
    def slope(self) -> float: ...

    @slope.setter
    def slope(self, value: float) -> None: ...
    
    @property
    def offset(self) -> float:

    @offset.setter
    def offset(self, value: float) -> None: ...
    

    
class _BpfCompose(BpfInterface): ...


class _BpfConcat(BpfInterface): ...


class _BpfBlend(BpfInterface): ...

        
class Multi(BpfInterface): ...


class Const(BpfInterface): ...


class _MultipleBpfs(BpfInterface):

    def __init__(self, bpfs: list[BpfInterface]): ...
    

class Max(_MultipleBpfs): ...
    
class Min(_MultipleBpfs): ...

class Stack(_MultipleBpfs): ...


def brentq(bpf: BpfInterface, x0: float, xa: float, xb: float, xtol=9.9999999999999998e-13, 
           rtol=4.4408920985006262e-16, max_iter=100) -> tuple[float,int]: ...
    

def blend(a: BpfInterface, b: BpfInterface, mix=0.5) -> BpfInterface: ...



def bpf_zero_crossings(b: BpfInterface. h: float = 0.01, N: int = 0, 
                       x0: float = float('nan'), x1: float = float('nan'), 
                       maxzeros: int = 0) -> list[float]: ...
    
