#! Python 3
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt
import time

ti = time.time()


def NP_method(expression, derivative, guess, eps=0.001, iterations=30, graph=True):
    f = lambda x: eval(expression)

    diff = lambda x: eval(derivative)

    view = (0, 1)
    domain = np.linspace(view[0], view[1], num=1000)
    range = list()
    for i in domain:
        range.append(f(i))
    diff_range = list()
    for i in domain:
        diff_range.append(diff(i))

    if graph:
        plt.plot(domain, range)
        plt.plot(domain, diff_range)
        plt.grid(True, which='both')
        plt.axhline(y=0, color='k')
        plt.axvline(x=0, color='k')
        plt.show()

    old_guess = guess - 10
    control = 0

    print('Calculando...')
    while eps < abs((guess - old_guess) / 2):
        if control < iterations:
            old_guess = guess
            guess -= f(guess) / diff(guess)
            control += 1
            err = abs((guess - old_guess) / guess)
            print('Valor actual: {cr_guess}. \n Loop Nº{loop}.\n Error: {err}'.format(
                cr_guess=sym.N(guess), loop=control, err=err))
            if err < eps:
                print('Valor final: {guess}'.format(guess=sym.N(guess)))
                break

        else:
            print('End of loop.')
            print('Valor final: {guess}'.format(guess=sym.N(guess)))
            break


def puntofijo(expression, derivative, guess, iteration=30, eps=0.01, err_graph=False):
    control = 0

    f = lambda x: eval(expression)
    diff = lambda x: eval(derivative)
    if not abs(diff(guess)) < 1:
        print('Ésta función no converge.')
        return None

    print('Ésta función converge. \n Calculando...')

    old_guess = guess
    guess = f(guess)
    err_steps = list()

    while eps < abs((guess - old_guess) / guess):
        if control < iteration:
            old_guess = guess
            guess = f(guess)
            control += 1
            err = abs((guess - old_guess) / guess)
            err_steps.append(err)
            print('Valor actual: {cr_guess}. \n Loop Nº{loop}.\n Error: {err}'.format(
                cr_guess=guess, loop=control, err=err))
            if err < eps:
                print('Valor final: {guess}'.format(guess=guess))
                break

        else:
            print('End of loop.')
            break

    print('Valor final: {guess}'.format(guess=guess))
    if err_graph:
        domain_err = np.arange(len(err_steps))
        plt.plot(domain_err, err_steps)
        plt.grid(True, which='both')
        plt.axhline(y=0, color='k')
        plt.axvline(x=0, color='k')
        plt.xlabel('Iteraciones')
        plt.ylabel('Error relativo')
        plt.xscale('log')
        plt.show()
        plt.close()


ecuacion = '-0.13952954729309466/np.sqrt(x**5) - 0.86*np.log(0.07027027027027026/x + (9.168716265367385e-6*x)/np.sqrt(x**5))'
derivada = '(0.34882386823273664 * x ** 4) / (x ** 5) ** 1.5 - (0.86 * (-0.07027027027027026 / x ** 2 - 0.00001375307439805108 / np.sqrt(x ** 5))) /   (0.07027027027027026 / x + (9.168716265367385e-6 * x) / np.sqrt(x ** 5))'

NP_method(ecuacion, derivada, 0.3)
