# Path: cxroots/IterativeMethods.py
# def muller(
#     x1,
#     x2,
#     x3,
#     f,
#     steptol=1e-12,
#     roottol=1e-12,
#     maxIter=20,
#     attemptBest=False,
#     verbose=False,
#     callback=None,
# ):
#     """
#     A wrapper for mpmath's implementation of Muller's method.  
# 
#     Parameters
#     ----------
#     x1 : float or complex
#         An initial point for iteration, should be close to a root of f.
#     x2 : float or complex
#         An initial point for iteration, should be close to a root of f.  
#         Should not equal x1.
#     x3 : float or complex
#         An initial point for iteration, should be close to a root of f.  
#         Should not equal x1 or x2.
#     f : function
#         Function of a single variable which we seek to find a root of.
#     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.
#     """
#     from mpmath import mp, mpmathify
#     from mpmath.calculus.optimization import Muller
# 
#     # mpmath insists on functions accepting mpc
#     f_mpmath = lambda z: mpmathify(f(complex(z)))
# 
#     mull = Muller(mp, f_mpmath, (x1, x2, x3), verbose=False)
#     iteration = 0
#     x0 = x3
# 
#     x, err = x0, abs(f(x0))
#     err0, dx0 = inf, inf
#     try:
#         for x, dx in mull:
#             err = abs(f_mpmath(x))
# 
#             if verbose:
#                 print(iteration, "x", x, "|f(x)|", err, "dx", dx)
# 
#             if callback is not None and callback(x, dx, err, iteration + 1):
#                 break
# 
#             if (
#                 not attemptBest
#                 and (abs(dx) < steptol or err < roottol)
#                 or iteration > maxIter
#             ):
#                 break
# 
#             if attemptBest and (abs(dx0) < steptol or err0 < roottol) and err >= err0:
#                 # The previous iteration was a better appproximation the current one so
#                 # assume that that was as close to the root as we are going to get.
#                 x, err = x0, err0
#                 break
# 
#             iteration += 1
#             x0 = x
# 
#             if attemptBest:
#                 # record previous error for comparison
#                 dx0, err0 = dx, err
# 
#     except ZeroDivisionError:
#         # ZeroDivisionError comes up if the error is evaluated to be zero
#         pass
# 
#     if verbose:
#         print("Final approximation: x=", complex(x), "|f(x)|=", float(err))
# 
#     # cast mpc and mpf back to regular complex and float
#     return complex(x), float(err)


import pytest
from cxroots.IterativeMethods import muller
from numpy import pi, cos



def test_muller():
    f = lambda x: cos(x) - x ** 2 + 1j * x ** 3

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

# Path: cxroots/IterativeMethods.py
# def muller(
#     x1,
#     x2,
#     x3,
#     f,
#     steptol=1e-12,
#     roottol=1e-12,
#     maxIter=20,
#     attemptBest=False,
#     verbose=False,
#     callback=None,
# ):
#     """
#     A wrapper for mpmath's implementation of Muller's method.  
# 
#     Parameters
#     ----------
#     x1 : float or complex
#         An initial point for iteration, should be close to a root of f.
#     x2 : float or complex
#         An initial point for iteration, should be close to a root of f.  
#         Should not equal x1.
#     x3 : float or complex
#         An initial point for iteration, should be close to a root of f.  
#         Should not equal x1 or x2.
#     f : function
#         Function of a single variable which we seek to find a root of.
#     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.
#     """
#     from mpmath import mp, mpmathify
#     from mpmath.calculus.optimization import Muller
# 
#     # mpmath insists on functions accepting mpc
#     f_mpmath = lambda z: mpmathify(f(complex(z)))
# 
#     mull = Muller(mp, f_mpmath, (x1, x2, x3), verbose=False)
#     iteration = 0
#     x0 = x3
# 
#     x, err = x0, abs(f(x0))
#     err0, dx0 = inf, inf
#     try:
#         for x, dx in mull:
#             err = abs(f_mpmath(x))
# 
#             if verbose:
#                 print(iteration, "x", x, "|f(x)|", err, "dx", dx)
# 
#             if callback is not None and callback(x, dx, err, iteration + 1):
#                 break
# 
#             if (
#                 not attemptBest
#                 and (abs(dx) < steptol or err < roottol)
#                 or iteration > maxIter
#             ):
#                 break
# 
#             if attemptBest and (abs(dx0) < steptol or err0 < roottol) and err >= err0:
#                 # The previous iteration was a better appproximation the current one so
#                 # assume that that was as close to the root as we are going to get.
#                 x, err = x0, err0
#                 break
# 
#             iteration += 1
#             x0 = x
# 
#             if attemptBest:
#                 # record previous error for comparison
#                 dx0, err0 = dx, err
# 
#     except ZeroDivisionError:
#         # ZeroDivisionError comes up if the error is evaluated to be zero
#         pass
# 
#     if verbose:
#         print("Final approximation: x=", complex(x), "|f(x)|=", float(err))
# 
#     # cast mpc and mpf back to regular complex and float
#     return complex(x), float(err)


# Path: cxroots/tests/test_muller.py
import pytest
from cxroots.IterativeMethods import muller
from numpy import pi, cos




def test_muller():
    f = lambda x: cos(x) - x ** 2 + 1j * x ** 3

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

    x, err = muller(0.5, pi / 4, 0.6, f)