# integrating integrals
def integrate(function, lowlim, uplim, n=10000):
    h = (uplim - lowlim) / n
    s = function(lowlim) + function(uplim)

    for i in range(1, n):
        x = lowlim + i * h
        if i % 2 == 0:
            s += 2 * function(x)
        else:
            s += 4 * function(x)

    return s * h / 3
    
def summation(lowlim, uplim, function, max_terms=10**6):
    total = 0
    count = 0
    
    if uplim == float("inf"):
        n = lowlim
        while count < max_terms:
            try:
                term = function(n)
            except ZeroDivisionError:
                n += 1
                continue
            
            total += term
            
            # Stop if terms are negligible
            if abs(term) < 1e-10:
                break
                
            n += 1
            count += 1
        
        return total
    
    else:
        for n in range(int(lowlim), int(uplim) + 1):
            total += function(n)
        return total
        
def converges(f, tolerance=1e-6, max_terms=10**6):
    total = 0
    prev = 0
    
    for n in range(1, max_terms):
        try:
            total += f(n)
        except ZeroDivisionError:
            continue
        
        # Check if partial sum stabilizes
        if abs(total - prev) < tolerance:
            return True
        
        prev = total
    
    return False
    
def lim(f, xlim, tol=1e-5):
    # Try direct substitution
    try:
        return f(xlim)
    except ZeroDivisionError:
        pass  # expected for limits
    except Exception as e:
        return None  # or raise e if you want strict behavior

    hs = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5]
    left_vals = []
    right_vals = []

    for h in hs:
        try:
            left_vals.append(f(xlim - h))
        except ZeroDivisionError:
            continue

        try:
            right_vals.append(f(xlim + h))
        except ZeroDivisionError:
            continue

    if not left_vals or not right_vals:
        return None

    left = left_vals[-1]
    right = right_vals[-1]

    if abs(left - right) < tol:
        return (left + right) / 2

    return None