import math

def funcion(x):
    return (10*(math.sin(x+3))*(math.exp((x/2)+1)))

def biseccion(intA, intB, errorA, noMaxIter):
        if(funcion(intA)*funcion(intB)<0):
            print("---------------------------------------")
            print("X0: ",intA," X1: ",intB)
            print("No. max. iteraciones: ",noMaxIter)
            print("Error aceptable: ",errorA)
            noIter = 0
            errorTmp = 1
            intTmp = 0
            oldInt = intA
            while(noIter<noMaxIter and errorTmp>errorA and funcion(intTmp)!=0):
                intTmp = (intB+intA)/2
                if(funcion(intA)*funcion(intTmp)<0):
                    intB = intTmp
                else:
                    intA = intTmp
                noIter+=1
                errorTmp=abs(intTmp-oldInt)
                oldInt = intTmp
            print("---------------------------------------")
            print('La raíz es: ',intTmp)
            print('F(raiz) es:' ,funcion(intTmp))
            print('Error: ',errorTmp)
            print('No. de iteraciones realizadas: ',noIter)
            print("---------------------------------------")
        else:
            print('En el intervalo dado la función no presenta cambio de signo')
            print('No hay raices que encontrar')
            
biseccion(-4,-2,math.pow(10,-15),1000)
biseccion(0,1,math.pow(10,-15),1000)

"""
Explanation: <h2>3. Aproximaciones para hallar las 2 raices mas cercanas a cero de 10*sin(x+3)* e^((x/2)+1) con exactitud de 10^(-15) con los métodos de bisección, falsa posición, Newton-Raphson y secante.</h2>

<h3>Método de bisección con puntos iniciales (-4,-2) y (0,1):</h3>
End of explanation
"""


import math

def funcion(x):
    return (10*(math.sin(x+3))*(math.exp((x/2)+1)))

def reglaFalsa(intA, intB, errorA, noMaxIter):
    if(funcion(intA)*funcion(intB)<0):
        print("---------------------------------------")
        print("X0: ",intA," X1: ",intB)
        print("No. max. iteraciones: ",noMaxIter)
        print("Error aceptable: ",errorA)
        noIter = 0
        errorTmp = 1
        intTmp = 0
        oldInt = intA
        try:
            while(noIter<noMaxIter and errorTmp>errorA and funcion(intTmp)!=0):
                intTmp = intA-((funcion(intA)*(intB-intA))/(funcion(intB)-funcion(intA)))
                if(funcion(intA)*funcion(intTmp)>0):
                    intB = intTmp
                else:
                    intA = intTmp
                noIter+=1
                errorTmp=abs((intTmp-oldInt)/intTmp)*100
                oldInt = intTmp
            print("---------------------------------------")
            print('La raíz es: ',intTmp)
            print('F(raiz) es:' ,funcion(intTmp))
            print('Error: ',errorTmp)
            print('No. de iteraciones realizadas: ',noIter)
            print("---------------------------------------")
        except ZeroDivisionError:
            print("---------------------------------------")
            print("¡División entre cero! : Posible raiz encontrada")    
            print('La raíz es: ',intTmp)
            print('F(raiz) es:' ,funcion(intTmp))
            print('Error: ',errorTmp)
            print('No. de iteraciones realizadas: ',noIter)
            print("---------------------------------------")
        
    else:
        print('En el intervalo dado la función no presenta cambio de signo')
        print('No hay raices que encontrar')
            
reglaFalsa(-4,-2,math.pow(10,-15),10000)
reglaFalsa(0,1,math.pow(10,-15),10000)

"""
Explanation: <h3>Método de falsa posición con puntos iniciales (-4,-2) y (0,1):</h3>
End of explanation
"""


import math

def funcion(x):
    return (10*(math.sin(x+3))*(math.exp((x/2)+1)))

def funcionDeriv(x):
    return (5*(math.exp((x/2)+1))*(math.sin(x+3)+(2*math.cos(x+3))))

def newtonRaphson(val, errorA, noMaxIter):
    noIter = 0
    errorTmp = 0
    print("---------------------------------------")
    print("Punto inicial: ",val)
    print("No. max. iteraciones: ",noMaxIter)
    print("Error aceptable: ",errorA)
    while(noIter<noMaxIter):
        valTmp = val-((funcion(val))/(funcionDeriv(val)))
        if abs(valTmp-val) < errorA:
            print("---------------------------------------")
            print('La raíz es: ',valTmp)
            print('F(raiz) es:' ,funcion(valTmp))
            print('Error: ',abs(valTmp-val))
            print('No. de iteraciones realizadas: ',noIter)
            print("---------------------------------------")
            break
        errorTmp = abs(valTmp-val)
        val = valTmp
        noIter+=1
    
newtonRaphson(-4, math.pow(10,-15),10000)
newtonRaphson(0, math.pow(10,-15),10000)

"""
Explanation: <h3>Método de Newton-Raphson con puntos iniciales -4 y 0:</h3>
End of explanation
"""


import math

def funcion(x):
    return (10*(math.sin(x+3))*(math.exp((x/2)+1)))

def secante(primerVal, segundoVal, errorA, noMaxIter):
    noIter = 2
    errorTmp = 0
    valTmp = 0
    print("---------------------------------------")
    print("X0: ",primerVal," X1: ",segundoVal)
    print("No. max. iteraciones: ",noMaxIter)
    print("Error aceptable: ",errorA)
    try:
        while(noIter<noMaxIter):
            valTmp = segundoVal-((funcion(segundoVal)*(segundoVal-primerVal))/(funcion(segundoVal)-funcion(primerVal)))
            if abs(valTmp-segundoVal) < errorA:
                print("---------------------------------------")
                print('La raíz es: ',valTmp)
                print('F(raiz) es:' ,funcion(valTmp))
                print('Error: ',errorTmp)
                print('No. de iteraciones realizadas: ',noIter)
                print("---------------------------------------")
                break
            errorTmp = abs(valTmp-segundoVal)
            primerVal = segundoVal
            segundoVal = valTmp
            errorTmp=abs(segundoVal-primerVal)
            noIter+=1
    except ZeroDivisionError:
        print("---------------------------------------")
        print("¡División entre cero! : Posible raiz encontrada")    
        print('La raíz es: ',valTmp)
        print('F(raiz) es:' ,funcion(valTmp))
        print('Error: ',errorTmp)
        print('No. de iteraciones realizadas: ',noIter)
        print("---------------------------------------")
    
secante(-4,-2,math.pow(10,-15),10000)
secante(0,1,math.pow(10,-15),10000)

"""
Explanation: <h3>Método de la secante con puntos iniciales (-4,-2) y (0,1):</h3>
End of explanation
"""


