'''
Programa experimental para calcular valores de la función exponencial en forma secuencial, recursiva, recursiva optimizada y mediante
la función exp(), provista por el módulo math de Python. El objetivo del experimento es comparar resultados.
Se suman términos de la serie de Maclaurin mientras sus valores absolutos sean mayores que 10**(-12), es decir, mientras afecten al
resultado hasta el duodécimo dígito significativo.

e**x es igual a suma desde k=0 hasta infinito de x**k/k!; como no se puede calcular infinitos términos, se corta el cálculo
al obtener el primer término cuyo valor absoluto sea menor o igual que 10**(-12)
'''

# FUNCIONES
def exp_s(x):
    ''' Cálculo de e**x en forma secuencial por serie de Maclaurin'''
    potencia = 1.0
    factorial = 1
    término = 1.0 # término 0
    nt = 0
    serie = término
    while abs(término)>1e-12: # último término calculado
        potencia *= x
        nt += 1
        factorial *= nt
        término = potencia/factorial
        serie += término
    print(f'Se calcularon secuencialmente {nt+1} términos') # De 0 a nt son nt+1 términos
    return serie

def fact(n): # 4, 3, 2, 1
    ''' Cálculo de n! '''
    if n==0 or n==1: return 1 # 1
    else: return n*fact(n-1) #4*fact(3), 3*fact(2), 2*fact(1)

def exp_r(x, nt=0):  
    ''' Cálculo de e**x en forma recursiva por serie de Maclaurin '''
    término = x**nt / fact(nt)
    if abs(término)>1e-12: return exp_r(x, nt+1)+término 
    else:
        print(f'\nSe calcularon recursivamente {nt+1} términos para obtener')
        return término

def exp_ro(x, nt=0):
    ''' Cálculo de e**x en forma recursiva optimizada por serie de Maclaurin
        b**x = (b**(x/2)*b**(x/2)) '''
    if abs(x)>1.0:
        y = exp_ro(x/2) # reduce el argumento acercándolo al entorno de mayor convergencia
        return y*y # introduce operaciones aritméticas que pueden implicar pérdida de precisión (verificar en pruebas)
    else:
        término = x**nt / fact(nt)
        if abs(término)>1e-12: return exp_ro(x, nt+1)+término
        else:
            print(f'\nSe calcularon recursivamente {nt+1} términos para x={x}, para obtener')
            return término

# PROGRAMA
from math import exp
print('Evaluaciones de la función exponencial')

x = float(input('\nIngrese valor de x para obtener e^x: '))
while x!=0.0:
    print(f'{exp_s(x):.12f})')
    print(f'{exp_r(x):.12f}')
    print(f'{exp_ro(x):.12f}')
    print(f'\n{exp(x):.12f} (según la función exp() del módulo math de Python)')
    x = float(input('\n\nIngrese valor de x para obtener e^x o 0 para terminar: '))
    
    
