# method to find a bracket that encloses a root of f
from math import *

def findEnclosingBracket(f, x, factor):
    """ Finds a bracket [a b] that encloses a root of f : f(a)*f(b) < 0.
        A typical value for the factor is 2.
        The initial guess must be on the correct side of the real line : the algorithm will never cross the zero during the search.
        The factor is increased every 10 iterations in order to speed up convergence for when the root is orders of magnitude away from the initial guess.
        If the bracket returned by the function is too wide, try reducing the factor (while always keeping it > 1).
    """
    a = x
    b = x*factor                    # try searching in the positive direction
    fa = f(a)
    fb = f(b)

    if fa*fb < 0:                   # if the original bracket is already enclosing the solution
        return (a, b, fa, fb)

    if fabs(fa) < fabs(fb):         # if fa is closer, search in the other direction
        a = x/factor
        b = x
        fb = fa                     # f(b) = f(x)
        fa = f(a)

    i = 0
    while i in range(100) and fa*fb > 0:
        if fabs(fa) < fabs(fb):     # fa is closer to 0 than fb, extend the interval in the a direction (reducing it)
            a = a / factor
            fa = f(a)
        else:                       # fb is closer to 0 than fa, extend the interval in the b direction (augmenting it)
            b = b * factor
            fb = f(b)
        i = i+1

        if i % 10 == 0:             # every 10 iterations, bump up the factor to speed up convergence
            factor = factor * 5

    return (a, b, fa, fb)

f = lambda x : exp(sin(x))-2
a, b, fa, fb = findEnclosingBracket(f, 0.005, 2)
print((a,b)); print((fa, fb))

f = lambda x : exp(-x)-1e-200
a, b, fa, fb = findEnclosingBracket(f, 0.001, 2)
print((a,b)); print((fa, fb))

a, b, fa, fb = findEnclosingBracket(f, 0.001, 5)
print((a,b)); print((fa, fb))

f = lambda x : x**(1/3)-1000
a, b, fa, fb = findEnclosingBracket(f, 1e-10, 2)
print((a,b)); print((fa, fb))

print('exp(x)-2')

def f(x):
    fx = exp(x)-2
    print((x, fx))
    return fx

a, b, fa, fb = findEnclosingBracket(f, .1, 2); print((a,b)); print((fa, fb))
a, b, fa, fb = findEnclosingBracket(f, 4, 2); print((a,b)); print((fa, fb))

# from toms748 import TOMS748_solve1
# def f(x):
#     print(x)
#     return x**(1/3)-1000
# TOMS748_solve1(f,a,b)
