# Path: cxroots/IterativeMethods.py
# def newton(
#     x0,
#     f,
#     df,
#     steptol=1e-12,
#     roottol=1e-12,
#     maxIter=20,
#     attemptBest=False,
#     verbose=False,
#     callback=None,
# ):
#     """
#     Find an approximation to a point xf such that f(xf)=0 for a 
#     scalar function f using Newton-Raphson iteration starting at 
#     the point x0.
# 
#     Parameters
#     ----------
#     x0 : float or complex
#         Initial point for Newton iteration, should be as close as
#         possible to a root of f
#     f : function
#         Function of a single variable which we seek to find a root of.
#     df : function
#         Function of a single variable, df(x), providing the
#         derivative of the function f(x) at the point x
#     steptol: float, optional
#         The routine ends if the step size, dx, between sucessive
#         iterations satisfies abs(dx) < steptol and attemptBest is False.
#     roottol: float, optional
#         The routine ends if abs(f(x)) < roottol and attemptBest is False.
#     maxIter : int, optional
#         The routine ends after maxIter iterations.
#     attemptBest : bool, optional
#         If True then routine ends if the error of the previous iteration, 
#         x0, was at least as good as the current iteration, x, in the 
#         sense that abs(f(x)) >= abs(f(x0)) and the previous iteration 
#         satisfied either abs(dx0) < steptol or abs(f(x0)) < roottol.  In 
#         this case the previous iteration is returned as the approximation 
#         of the root.
#     verbose : bool, optional
#         Print x, dx and f(x) at each step of the iteration.
#     callback : function, optional
#         After each iteration callback(x, dx, f(x), iteration) will be 
#         called where 'x' is the current iteration of the estimated root, 
#         'dx' is the step size between the previous and current 'x' and 
#         'iteration' the number of iterations that have been taken.  If 
#         the callback function evaluates to True then the routine will end.
# 
#     Returns
#     -------
#     complex
#         The approximation to a root of f.
#     float
#         abs(f(x)) where x is the final approximation for the root of f.
#     """
#     x, y = x0, f(x0)
#     dx0, y0 = inf, y
#     for iteration in range(maxIter):
#         dx = -y / df(x)
#         x += dx
#         y = f(x)
# 
#         if verbose:
#             print("x", x, "f(x)", y, "dx", dx)
# 
#         if callback is not None and callback(x, dx, y, iteration + 1):
#             break
# 
#         if not attemptBest and (abs(dx) < steptol or abs(y) < roottol):
#             break
# 
#         if (
#             attemptBest
#             and (abs(dx0) < steptol or abs(y0) < roottol)
#             and abs(y) > abs(y0)
#         ):
#             break
# 
#         if attemptBest:
#             # store previous dx and y
#             dx0, y0 = dx, y
# 
#     if verbose:
#         print("Final approximation: x=", x, "|f(x)|=", abs(y))
# 
#     return x, abs(y)


import pytest
from cxroots.IterativeMethods import newton
from numpy import pi, cos, sin



def test_newton():
    # result from keisan online calculator: http://keisan.casio.com/exec/system/1244946907
    f = lambda x: cos(x) - x
    df = lambda x: -sin(x) - 1

    iterations = []
    callback = lambda x, dx, y, iteration: iterations.append(x)

# Path: cxroots/IterativeMethods.py
# def newton(
#     x0,
#     f,
#     df,
#     steptol=1e-12,
#     roottol=1e-12,
#     maxIter=20,
#     attemptBest=False,
#     verbose=False,
#     callback=None,
# ):
#     """
#     Find an approximation to a point xf such that f(xf)=0 for a 
#     scalar function f using Newton-Raphson iteration starting at 
#     the point x0.
# 
#     Parameters
#     ----------
#     x0 : float or complex
#         Initial point for Newton iteration, should be as close as
#         possible to a root of f
#     f : function
#         Function of a single variable which we seek to find a root of.
#     df : function
#         Function of a single variable, df(x), providing the
#         derivative of the function f(x) at the point x
#     steptol: float, optional
#         The routine ends if the step size, dx, between sucessive
#         iterations satisfies abs(dx) < steptol and attemptBest is False.
#     roottol: float, optional
#         The routine ends if abs(f(x)) < roottol and attemptBest is False.
#     maxIter : int, optional
#         The routine ends after maxIter iterations.
#     attemptBest : bool, optional
#         If True then routine ends if the error of the previous iteration, 
#         x0, was at least as good as the current iteration, x, in the 
#         sense that abs(f(x)) >= abs(f(x0)) and the previous iteration 
#         satisfied either abs(dx0) < steptol or abs(f(x0)) < roottol.  In 
#         this case the previous iteration is returned as the approximation 
#         of the root.
#     verbose : bool, optional
#         Print x, dx and f(x) at each step of the iteration.
#     callback : function, optional
#         After each iteration callback(x, dx, f(x), iteration) will be 
#         called where 'x' is the current iteration of the estimated root, 
#         'dx' is the step size between the previous and current 'x' and 
#         'iteration' the number of iterations that have been taken.  If 
#         the callback function evaluates to True then the routine will end.
# 
#     Returns
#     -------
#     complex
#         The approximation to a root of f.
#     float
#         abs(f(x)) where x is the final approximation for the root of f.
#     """
#     x, y = x0, f(x0)
#     dx0, y0 = inf, y
#     for iteration in range(maxIter):
#         dx = -y / df(x)
#         x += dx
#         y = f(x)
# 
#         if verbose:
#             print("x", x, "f(x)", y, "dx", dx)
# 
#         if callback is not None and callback(x, dx, y, iteration + 1):
#             break
# 
#         if not attemptBest and (abs(dx) < steptol or abs(y) < roottol):
#             break
# 
#         if (
#             attemptBest
#             and (abs(dx0) < steptol or abs(y0) < roottol)
#             and abs(y) > abs(y0)
#         ):
#             break
# 
#         if attemptBest:
#             # store previous dx and y
#             dx0, y0 = dx, y
# 
#     if verbose:
#         print("Final approximation: x=", x, "|f(x)|=", abs(y))
# 
#     return x, abs(y)


# Path: cxroots/tests/test_newton.py
import pytest
from cxroots.IterativeMethods import newton
from numpy import pi, cos, sin




def test_newton():
    # result from keisan online calculator: http://keisan.casio.com/exec/system/1244946907
    f = lambda x: cos(x) - x
    df = lambda x: -sin(x) - 1

    iterations = []
    callback = lambda x, dx, y, iteration: iterations.append(x)

    x, err = newton(pi / 4, f, df, callback=callback)