Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:40:42

0001 //  (C) Copyright John Maddock 2006.
0002 //  (C) Copyright Matt Borland 2024.
0003 //  Use, modification and distribution are subject to the
0004 //  Boost Software License, Version 1.0. (See accompanying file
0005 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP
0008 #define BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP
0009 
0010 #ifdef _MSC_VER
0011 #pragma once
0012 #endif
0013 
0014 #include <boost/math/tools/config.hpp>
0015 #include <boost/math/tools/complex.hpp> // test for multiprecision types in complex Newton
0016 #include <boost/math/tools/type_traits.hpp>
0017 #include <boost/math/tools/cstdint.hpp>
0018 #include <boost/math/tools/numeric_limits.hpp>
0019 #include <boost/math/tools/tuple.hpp>
0020 #include <boost/math/special_functions/sign.hpp>
0021 #include <boost/math/policies/policy.hpp>
0022 #include <boost/math/policies/error_handling.hpp>
0023 
0024 #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0025 #include <boost/math/special_functions/next.hpp>
0026 #include <boost/math/tools/toms748_solve.hpp>
0027 #endif
0028 
0029 namespace boost {
0030 namespace math {
0031 namespace tools {
0032 
0033 namespace detail {
0034 
0035 namespace dummy {
0036 
0037    template<int n, class T>
0038    BOOST_MATH_GPU_ENABLED typename T::value_type get(const T&) BOOST_MATH_NOEXCEPT(T);
0039 }
0040 
0041 template <class Tuple, class T>
0042 BOOST_MATH_GPU_ENABLED void unpack_tuple(const Tuple& t, T& a, T& b) BOOST_MATH_NOEXCEPT(T)
0043 {
0044    using dummy::get;
0045    // Use ADL to find the right overload for get:
0046    a = get<0>(t);
0047    b = get<1>(t);
0048 }
0049 template <class Tuple, class T>
0050 BOOST_MATH_GPU_ENABLED void unpack_tuple(const Tuple& t, T& a, T& b, T& c) BOOST_MATH_NOEXCEPT(T)
0051 {
0052    using dummy::get;
0053    // Use ADL to find the right overload for get:
0054    a = get<0>(t);
0055    b = get<1>(t);
0056    c = get<2>(t);
0057 }
0058 
0059 template <class Tuple, class T>
0060 BOOST_MATH_GPU_ENABLED inline void unpack_0(const Tuple& t, T& val) BOOST_MATH_NOEXCEPT(T)
0061 {
0062    using dummy::get;
0063    // Rely on ADL to find the correct overload of get:
0064    val = get<0>(t);
0065 }
0066 
0067 template <class T, class U, class V>
0068 BOOST_MATH_GPU_ENABLED inline void unpack_tuple(const boost::math::pair<T, U>& p, V& a, V& b) BOOST_MATH_NOEXCEPT(T)
0069 {
0070    a = p.first;
0071    b = p.second;
0072 }
0073 template <class T, class U, class V>
0074 BOOST_MATH_GPU_ENABLED inline void unpack_0(const boost::math::pair<T, U>& p, V& a) BOOST_MATH_NOEXCEPT(T)
0075 {
0076    a = p.first;
0077 }
0078 
0079 template <class F, class T>
0080 BOOST_MATH_GPU_ENABLED void handle_zero_derivative(F f,
0081    T& last_f0,
0082    const T& f0,
0083    T& delta,
0084    T& result,
0085    T& guess,
0086    const T& min,
0087    const T& max) noexcept(BOOST_MATH_IS_FLOAT(T) 
0088    #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0089    && noexcept(std::declval<F>()(std::declval<T>()))
0090    #endif
0091    )
0092 {
0093    if (last_f0 == 0)
0094    {
0095       // this must be the first iteration, pretend that we had a
0096       // previous one at either min or max:
0097       if (result == min)
0098       {
0099          guess = max;
0100       }
0101       else
0102       {
0103          guess = min;
0104       }
0105       unpack_0(f(guess), last_f0);
0106       delta = guess - result;
0107    }
0108    if (sign(last_f0) * sign(f0) < 0)
0109    {
0110       // we've crossed over so move in opposite direction to last step:
0111       if (delta < 0)
0112       {
0113          delta = (result - min) / 2;
0114       }
0115       else
0116       {
0117          delta = (result - max) / 2;
0118       }
0119    }
0120    else
0121    {
0122       // move in same direction as last step:
0123       if (delta < 0)
0124       {
0125          delta = (result - max) / 2;
0126       }
0127       else
0128       {
0129          delta = (result - min) / 2;
0130       }
0131    }
0132 }
0133 
0134 } // namespace
0135 
0136 template <class F, class T, class Tol, class Policy>
0137 BOOST_MATH_GPU_ENABLED boost::math::pair<T, T> bisect(F f, T min, T max, Tol tol, boost::math::uintmax_t& max_iter, const Policy& pol) noexcept(policies::is_noexcept_error_policy<Policy>::value && BOOST_MATH_IS_FLOAT(T) 
0138 #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0139 && noexcept(std::declval<F>()(std::declval<T>()))
0140 #endif
0141 )
0142 {
0143    T fmin = f(min);
0144    T fmax = f(max);
0145    if (fmin == 0)
0146    {
0147       max_iter = 2;
0148       return boost::math::make_pair(min, min);
0149    }
0150    if (fmax == 0)
0151    {
0152       max_iter = 2;
0153       return boost::math::make_pair(max, max);
0154    }
0155 
0156    //
0157    // Error checking:
0158    //
0159    constexpr auto function = "boost::math::tools::bisect<%1%>";
0160    if (min >= max)
0161    {
0162       return boost::math::detail::pair_from_single(policies::raise_evaluation_error(function,
0163          "Arguments in wrong order in boost::math::tools::bisect (first arg=%1%)", min, pol));
0164    }
0165    if (fmin * fmax >= 0)
0166    {
0167       return boost::math::detail::pair_from_single(policies::raise_evaluation_error(function,
0168          "No change of sign in boost::math::tools::bisect, either there is no root to find, or there are multiple roots in the interval (f(min) = %1%).", fmin, pol));
0169    }
0170 
0171    //
0172    // Three function invocations so far:
0173    //
0174    std::uintmax_t count = max_iter;
0175    if (count < 3)
0176       count = 0;
0177    else
0178       count -= 3;
0179 
0180    while (count && (0 == tol(min, max)))
0181    {
0182       T mid = (min + max) / 2;
0183       T fmid = f(mid);
0184       if ((mid == max) || (mid == min))
0185          break;
0186       if (fmid == 0)
0187       {
0188          min = max = mid;
0189          break;
0190       }
0191       else if (sign(fmid) * sign(fmin) < 0)
0192       {
0193          max = mid;
0194       }
0195       else
0196       {
0197          min = mid;
0198          fmin = fmid;
0199       }
0200       --count;
0201    }
0202 
0203    max_iter -= count;
0204 
0205 #ifdef BOOST_MATH_INSTRUMENT
0206    std::cout << "Bisection required " << max_iter << " iterations.\n";
0207 #endif
0208 
0209    return boost::math::make_pair(min, max);
0210 }
0211 
0212 template <class F, class T, class Tol>
0213 BOOST_MATH_GPU_ENABLED inline boost::math::pair<T, T> bisect(F f, T min, T max, Tol tol, boost::math::uintmax_t& max_iter)  noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value && BOOST_MATH_IS_FLOAT(T)
0214 #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0215 && noexcept(std::declval<F>()(std::declval<T>()))
0216 #endif
0217 )
0218 {
0219    return bisect(f, min, max, tol, max_iter, policies::policy<>());
0220 }
0221 
0222 template <class F, class T, class Tol>
0223 BOOST_MATH_GPU_ENABLED inline boost::math::pair<T, T> bisect(F f, T min, T max, Tol tol) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value && BOOST_MATH_IS_FLOAT(T) 
0224 #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0225 && noexcept(std::declval<F>()(std::declval<T>()))
0226 #endif
0227 )
0228 {
0229    boost::math::uintmax_t m = (boost::math::numeric_limits<boost::math::uintmax_t>::max)();
0230    return bisect(f, min, max, tol, m, policies::policy<>());
0231 }
0232 
0233 
0234 template <class F, class T>
0235 BOOST_MATH_GPU_ENABLED T newton_raphson_iterate(F f, T guess, T min, T max, int digits, boost::math::uintmax_t& max_iter) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value && BOOST_MATH_IS_FLOAT(T)
0236 #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0237 && noexcept(std::declval<F>()(std::declval<T>()))
0238 #endif
0239 )
0240 {
0241    BOOST_MATH_STD_USING
0242 
0243    constexpr auto function = "boost::math::tools::newton_raphson_iterate<%1%>";
0244    if (min > max)
0245    {
0246       return policies::raise_evaluation_error(function, "Range arguments in wrong order in boost::math::tools::newton_raphson_iterate(first arg=%1%)", min, boost::math::policies::policy<>());
0247    }
0248 
0249    T f0(0), f1, last_f0(0);
0250    T result = guess;
0251 
0252    T factor = static_cast<T>(ldexp(1.0, 1 - digits));
0253    T delta = tools::max_value<T>();
0254    T delta1 = tools::max_value<T>();
0255    T delta2 = tools::max_value<T>();
0256 
0257    //
0258    // We use these to sanity check that we do actually bracket a root,
0259    // we update these to the function value when we update the endpoints
0260    // of the range.  Then, provided at some point we update both endpoints
0261    // checking that max_range_f * min_range_f <= 0 verifies there is a root
0262    // to be found somewhere.  Note that if there is no root, and we approach 
0263    // a local minima, then the derivative will go to zero, and hence the next
0264    // step will jump out of bounds (or at least past the minima), so this
0265    // check *should* happen in pathological cases.
0266    //
0267    T max_range_f = 0;
0268    T min_range_f = 0;
0269 
0270    boost::math::uintmax_t count(max_iter);
0271 
0272 #ifdef BOOST_MATH_INSTRUMENT
0273    std::cout << "Newton_raphson_iterate, guess = " << guess << ", min = " << min << ", max = " << max
0274       << ", digits = " << digits << ", max_iter = " << max_iter << "\n";
0275 #endif
0276 
0277    do {
0278       last_f0 = f0;
0279       delta2 = delta1;
0280       delta1 = delta;
0281       detail::unpack_tuple(f(result), f0, f1);
0282       --count;
0283       if (0 == f0)
0284          break;
0285       if (f1 == 0)
0286       {
0287          // Oops zero derivative!!!
0288          detail::handle_zero_derivative(f, last_f0, f0, delta, result, guess, min, max);
0289       }
0290       else
0291       {
0292          delta = f0 / f1;
0293       }
0294 #ifdef BOOST_MATH_INSTRUMENT
0295       std::cout << "Newton iteration " << max_iter - count << ", delta = " << delta << ", residual = " << f0 << "\n";
0296 #endif
0297       if (fabs(delta * 2) > fabs(delta2))
0298       {
0299          // Last two steps haven't converged.
0300          T shift = (delta > 0) ? (result - min) / 2 : (result - max) / 2;
0301          if ((result != 0) && (fabs(shift) > fabs(result)))
0302          {
0303             delta = sign(delta) * fabs(result); // protect against huge jumps!
0304          }
0305          else
0306             delta = shift;
0307          // reset delta1/2 so we don't take this branch next time round:
0308          delta1 = 3 * delta;
0309          delta2 = 3 * delta;
0310       }
0311       guess = result;
0312       result -= delta;
0313       if (result <= min)
0314       {
0315          delta = 0.5F * (guess - min);
0316          result = guess - delta;
0317          if ((result == min) || (result == max))
0318             break;
0319       }
0320       else if (result >= max)
0321       {
0322          delta = 0.5F * (guess - max);
0323          result = guess - delta;
0324          if ((result == min) || (result == max))
0325             break;
0326       }
0327       // Update brackets:
0328       if (delta > 0)
0329       {
0330          max = guess;
0331          max_range_f = f0;
0332       }
0333       else
0334       {
0335          min = guess;
0336          min_range_f = f0;
0337       }
0338       //
0339       // Sanity check that we bracket the root:
0340       //
0341       if (max_range_f * min_range_f > 0)
0342       {
0343          return policies::raise_evaluation_error(function, "There appears to be no root to be found in boost::math::tools::newton_raphson_iterate, perhaps we have a local minima near current best guess of %1%", guess, boost::math::policies::policy<>());
0344       }
0345    }while(count && (fabs(result * factor) < fabs(delta)));
0346 
0347    max_iter -= count;
0348 
0349 #ifdef BOOST_MATH_INSTRUMENT
0350    std::cout << "Newton Raphson required " << max_iter << " iterations\n";
0351 #endif
0352 
0353    return result;
0354 }
0355 
0356 template <class F, class T>
0357 BOOST_MATH_GPU_ENABLED inline T newton_raphson_iterate(F f, T guess, T min, T max, int digits) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value && BOOST_MATH_IS_FLOAT(T)
0358 #ifndef BOOST_MATH_HAS_GPU_SUPPORT
0359 && noexcept(std::declval<F>()(std::declval<T>()))
0360 #endif
0361 )
0362 {
0363    boost::math::uintmax_t m = (boost::math::numeric_limits<boost::math::uintmax_t>::max)();
0364    return newton_raphson_iterate(f, guess, min, max, digits, m);
0365 }
0366 
0367 // TODO(mborland): Disabled for now
0368 // Recursion needs to be removed, but there is no demand at this time
0369 #ifdef BOOST_MATH_HAS_NVRTC
0370 }}} // Namespaces
0371 #else
0372 
0373 namespace detail {
0374 
0375    struct halley_step
0376    {
0377       template <class T>
0378       static T step(const T& /*x*/, const T& f0, const T& f1, const T& f2) noexcept(BOOST_MATH_IS_FLOAT(T))
0379       {
0380          using std::fabs;
0381          T denom = 2 * f0;
0382          T num = 2 * f1 - f0 * (f2 / f1);
0383          T delta;
0384 
0385          BOOST_MATH_INSTRUMENT_VARIABLE(denom);
0386          BOOST_MATH_INSTRUMENT_VARIABLE(num);
0387 
0388          if ((fabs(num) < 1) && (fabs(denom) >= fabs(num) * tools::max_value<T>()))
0389          {
0390             // possible overflow, use Newton step:
0391             delta = f0 / f1;
0392          }
0393          else
0394             delta = denom / num;
0395          return delta;
0396       }
0397    };
0398 
0399    template <class F, class T>
0400    T bracket_root_towards_min(F f, T guess, const T& f0, T& min, T& max, std::uintmax_t& count) noexcept(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())));
0401 
0402    template <class F, class T>
0403    T bracket_root_towards_max(F f, T guess, const T& f0, T& min, T& max, std::uintmax_t& count) noexcept(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0404    {
0405       using std::fabs;
0406       using std::ldexp;
0407       using std::abs;
0408       using std::frexp;
0409       if(count < 2)
0410          return guess - (max + min) / 2; // Not enough counts left to do anything!!
0411       //
0412       // Move guess towards max until we bracket the root, updating min and max as we go:
0413       //
0414       int e;
0415       frexp(max / guess, &e);
0416       e = abs(e);
0417       T guess0 = guess;
0418       T multiplier = e < 64 ? static_cast<T>(2) : static_cast<T>(ldexp(T(1), e / 32));
0419       T f_current = f0;
0420       if (fabs(min) < fabs(max))
0421       {
0422          while (--count && ((f_current < 0) == (f0 < 0)))
0423          {
0424             min = guess;
0425             guess *= multiplier;
0426             if (guess > max)
0427             {
0428                guess = max;
0429                f_current = -f_current;  // There must be a change of sign!
0430                break;
0431             }
0432             multiplier *= e > 1024 ? 8 : 2;
0433             unpack_0(f(guess), f_current);
0434          }
0435       }
0436       else
0437       {
0438          //
0439          // If min and max are negative we have to divide to head towards max:
0440          //
0441          while (--count && ((f_current < 0) == (f0 < 0)))
0442          {
0443             min = guess;
0444             guess /= multiplier;
0445             if (guess > max)
0446             {
0447                guess = max;
0448                f_current = -f_current;  // There must be a change of sign!
0449                break;
0450             }
0451             multiplier *= e > 1024 ? 8 : 2;
0452             unpack_0(f(guess), f_current);
0453          }
0454       }
0455 
0456       if (count)
0457       {
0458          max = guess;
0459          if (multiplier > 16)
0460             return (guess0 - guess) + bracket_root_towards_min(f, guess, f_current, min, max, count);
0461       }
0462       return guess0 - (max + min) / 2;
0463    }
0464 
0465    template <class F, class T>
0466    T bracket_root_towards_min(F f, T guess, const T& f0, T& min, T& max, std::uintmax_t& count) noexcept(BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0467    {
0468       using std::fabs;
0469       using std::ldexp;
0470       using std::abs;
0471       using std::frexp;
0472       if (count < 2)
0473          return guess - (max + min) / 2; // Not enough counts left to do anything!!
0474       //
0475       // Move guess towards min until we bracket the root, updating min and max as we go:
0476       //
0477       int e;
0478       frexp(guess / min, &e);
0479       e = abs(e);
0480       T guess0 = guess;
0481       T multiplier = e < 64 ? static_cast<T>(2) : static_cast<T>(ldexp(T(1), e / 32));
0482       T f_current = f0;
0483 
0484       if (fabs(min) < fabs(max))
0485       {
0486          while (--count && ((f_current < 0) == (f0 < 0)))
0487          {
0488             max = guess;
0489             guess /= multiplier;
0490             if (guess < min)
0491             {
0492                guess = min;
0493                f_current = -f_current;  // There must be a change of sign!
0494                break;
0495             }
0496             multiplier *= e > 1024 ? 8 : 2;
0497             unpack_0(f(guess), f_current);
0498          }
0499       }
0500       else
0501       {
0502          //
0503          // If min and max are negative we have to multiply to head towards min:
0504          //
0505          while (--count && ((f_current < 0) == (f0 < 0)))
0506          {
0507             max = guess;
0508             guess *= multiplier;
0509             if (guess < min)
0510             {
0511                guess = min;
0512                f_current = -f_current;  // There must be a change of sign!
0513                break;
0514             }
0515             multiplier *= e > 1024 ? 8 : 2;
0516             unpack_0(f(guess), f_current);
0517          }
0518       }
0519 
0520       if (count)
0521       {
0522          min = guess;
0523          if (multiplier > 16)
0524             return (guess0 - guess) + bracket_root_towards_max(f, guess, f_current, min, max, count);
0525       }
0526       return guess0 - (max + min) / 2;
0527    }
0528 
0529    template <class Stepper, class F, class T>
0530    T second_order_root_finder(F f, T guess, T min, T max, int digits, std::uintmax_t& max_iter) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0531    {
0532       BOOST_MATH_STD_USING
0533 
0534 #ifdef BOOST_MATH_INSTRUMENT
0535         std::cout << "Second order root iteration, guess = " << guess << ", min = " << min << ", max = " << max
0536         << ", digits = " << digits << ", max_iter = " << max_iter << "\n";
0537 #endif
0538       static const char* function = "boost::math::tools::halley_iterate<%1%>";
0539       if (min >= max)
0540       {
0541          return policies::raise_evaluation_error(function, "Range arguments in wrong order in boost::math::tools::halley_iterate(first arg=%1%)", min, boost::math::policies::policy<>());
0542       }
0543 
0544       T f0(0), f1, f2;
0545       T result = guess;
0546 
0547       T factor = ldexp(static_cast<T>(1.0), 1 - digits);
0548       T delta = (std::max)(T(10000000 * guess), T(10000000));  // arbitrarily large delta
0549       T last_f0 = 0;
0550       T delta1 = delta;
0551       T delta2 = delta;
0552       bool out_of_bounds_sentry = false;
0553 
0554    #ifdef BOOST_MATH_INSTRUMENT
0555       std::cout << "Second order root iteration, limit = " << factor << "\n";
0556    #endif
0557 
0558       //
0559       // We use these to sanity check that we do actually bracket a root,
0560       // we update these to the function value when we update the endpoints
0561       // of the range.  Then, provided at some point we update both endpoints
0562       // checking that max_range_f * min_range_f <= 0 verifies there is a root
0563       // to be found somewhere.  Note that if there is no root, and we approach 
0564       // a local minima, then the derivative will go to zero, and hence the next
0565       // step will jump out of bounds (or at least past the minima), so this
0566       // check *should* happen in pathological cases.
0567       //
0568       T max_range_f = 0;
0569       T min_range_f = 0;
0570 
0571       std::uintmax_t count(max_iter);
0572 
0573       do {
0574          last_f0 = f0;
0575          delta2 = delta1;
0576          delta1 = delta;
0577 #ifndef BOOST_MATH_NO_EXCEPTIONS
0578          try
0579 #endif
0580          {
0581             detail::unpack_tuple(f(result), f0, f1, f2);
0582          }
0583 #ifndef BOOST_MATH_NO_EXCEPTIONS
0584          catch (const std::overflow_error&)
0585          {
0586             f0 = max > 0 ? tools::max_value<T>() : -tools::min_value<T>();
0587             f1 = f2 = 0;
0588          }
0589 #endif
0590          --count;
0591 
0592          BOOST_MATH_INSTRUMENT_VARIABLE(f0);
0593          BOOST_MATH_INSTRUMENT_VARIABLE(f1);
0594          BOOST_MATH_INSTRUMENT_VARIABLE(f2);
0595 
0596          if (0 == f0)
0597             break;
0598          if (f1 == 0)
0599          {
0600             // Oops zero derivative!!!
0601             detail::handle_zero_derivative(f, last_f0, f0, delta, result, guess, min, max);
0602          }
0603          else
0604          {
0605             if (f2 != 0)
0606             {
0607                delta = Stepper::step(result, f0, f1, f2);
0608                if (delta * f1 / f0 < 0)
0609                {
0610                   // Oh dear, we have a problem as Newton and Halley steps
0611                   // disagree about which way we should move.  Probably
0612                   // there is cancelation error in the calculation of the
0613                   // Halley step, or else the derivatives are so small
0614                   // that their values are basically trash.  We will move
0615                   // in the direction indicated by a Newton step, but
0616                   // by no more than twice the current guess value, otherwise
0617                   // we can jump way out of bounds if we're not careful.
0618                   // See https://svn.boost.org/trac/boost/ticket/8314.
0619                   delta = f0 / f1;
0620                   if (fabs(delta) > 2 * fabs(result))
0621                      delta = (delta < 0 ? -1 : 1) * 2 * fabs(result);
0622                }
0623             }
0624             else
0625                delta = f0 / f1;
0626          }
0627    #ifdef BOOST_MATH_INSTRUMENT
0628          std::cout << "Second order root iteration, delta = " << delta << ", residual = " << f0 << "\n";
0629    #endif
0630          // We need to avoid delta/delta2 overflowing here:
0631          T convergence = (fabs(delta2) > 1) || (fabs(tools::max_value<T>() * delta2) > fabs(delta)) ? fabs(delta / delta2) : tools::max_value<T>();
0632          if ((convergence > 0.8) && (convergence < 2))
0633          {
0634             // last two steps haven't converged.
0635             if (fabs(min) < 1 ? fabs(1000 * min) < fabs(max) : fabs(max / min) > 1000)
0636             {
0637                if(delta > 0)
0638                   delta = bracket_root_towards_min(f, result, f0, min, max, count);
0639                else
0640                   delta = bracket_root_towards_max(f, result, f0, min, max, count);
0641             }
0642             else
0643             {
0644                delta = (delta > 0) ? (result - min) / 2 : (result - max) / 2;
0645                if ((result != 0) && (fabs(delta) > result))
0646                   delta = sign(delta) * fabs(result) * 0.9f; // protect against huge jumps!
0647             }
0648             // reset delta2 so that this branch will *not* be taken on the
0649             // next iteration:
0650             delta2 = delta * 3;
0651             delta1 = delta * 3;
0652             BOOST_MATH_INSTRUMENT_VARIABLE(delta);
0653          }
0654          guess = result;
0655          result -= delta;
0656          BOOST_MATH_INSTRUMENT_VARIABLE(result);
0657 
0658          // check for out of bounds step:
0659          if (result < min)
0660          {
0661             T diff = ((fabs(min) < 1) && (fabs(result) > 1) && (tools::max_value<T>() / fabs(result) < fabs(min)))
0662                ? T(1000)
0663                : (fabs(min) < 1) && (fabs(tools::max_value<T>() * min) < fabs(result))
0664                ? ((min < 0) != (result < 0)) ? -tools::max_value<T>() : tools::max_value<T>() : T(result / min);
0665             if (fabs(diff) < 1)
0666                diff = 1 / diff;
0667             if (!out_of_bounds_sentry && (diff > 0) && (diff < 3))
0668             {
0669                // Only a small out of bounds step, lets assume that the result
0670                // is probably approximately at min:
0671                delta = 0.99f * (guess - min);
0672                result = guess - delta;
0673                out_of_bounds_sentry = true; // only take this branch once!
0674             }
0675             else
0676             {
0677                if (fabs(float_distance(min, max)) < 2)
0678                {
0679                   result = guess = (min + max) / 2;
0680                   break;
0681                }
0682                delta = bracket_root_towards_min(f, guess, f0, min, max, count);
0683                result = guess - delta;
0684                if (result <= min)
0685                   result = float_next(min);
0686                if (result >= max)
0687                   result = float_prior(max);
0688                guess = min;
0689                continue;
0690             }
0691          }
0692          else if (result > max)
0693          {
0694             T diff = ((fabs(max) < 1) && (fabs(result) > 1) && (tools::max_value<T>() / fabs(result) < fabs(max))) ? T(1000) : T(result / max);
0695             if (fabs(diff) < 1)
0696                diff = 1 / diff;
0697             if (!out_of_bounds_sentry && (diff > 0) && (diff < 3))
0698             {
0699                // Only a small out of bounds step, lets assume that the result
0700                // is probably approximately at min:
0701                delta = 0.99f * (guess - max);
0702                result = guess - delta;
0703                out_of_bounds_sentry = true; // only take this branch once!
0704             }
0705             else
0706             {
0707                if (fabs(float_distance(min, max)) < 2)
0708                {
0709                   result = guess = (min + max) / 2;
0710                   break;
0711                }
0712                delta = bracket_root_towards_max(f, guess, f0, min, max, count);
0713                result = guess - delta;
0714                if (result >= max)
0715                   result = float_prior(max);
0716                if (result <= min)
0717                   result = float_next(min);
0718                guess = min;
0719                continue;
0720             }
0721          }
0722          // update brackets:
0723          if (delta > 0)
0724          {
0725             max = guess;
0726             max_range_f = f0;
0727          }
0728          else
0729          {
0730             min = guess;
0731             min_range_f = f0;
0732          }
0733          //
0734          // Sanity check that we bracket the root:
0735          //
0736          if (max_range_f * min_range_f > 0)
0737          {
0738             return policies::raise_evaluation_error(function, "There appears to be no root to be found in boost::math::tools::newton_raphson_iterate, perhaps we have a local minima near current best guess of %1%", guess, boost::math::policies::policy<>());
0739          }
0740       } while(count && (fabs(result * factor) < fabs(delta)));
0741 
0742       max_iter -= count;
0743 
0744    #ifdef BOOST_MATH_INSTRUMENT
0745       std::cout << "Second order root finder required " << max_iter << " iterations.\n";
0746    #endif
0747 
0748       return result;
0749    }
0750 } // T second_order_root_finder
0751 
0752 template <class F, class T>
0753 T halley_iterate(F f, T guess, T min, T max, int digits, std::uintmax_t& max_iter) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0754 {
0755    return detail::second_order_root_finder<detail::halley_step>(f, guess, min, max, digits, max_iter);
0756 }
0757 
0758 template <class F, class T>
0759 inline T halley_iterate(F f, T guess, T min, T max, int digits) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0760 {
0761    std::uintmax_t m = (std::numeric_limits<std::uintmax_t>::max)();
0762    return halley_iterate(f, guess, min, max, digits, m);
0763 }
0764 
0765 namespace detail {
0766 
0767    struct schroder_stepper
0768    {
0769       template <class T>
0770       static T step(const T& x, const T& f0, const T& f1, const T& f2) noexcept(BOOST_MATH_IS_FLOAT(T))
0771       {
0772          using std::fabs;
0773          T ratio = f0 / f1;
0774          T delta;
0775          if ((x != 0) && (fabs(ratio / x) < 0.1))
0776          {
0777             delta = ratio + (f2 / (2 * f1)) * ratio * ratio;
0778             // check second derivative doesn't over compensate:
0779             if (delta * ratio < 0)
0780                delta = ratio;
0781          }
0782          else
0783             delta = ratio;  // fall back to Newton iteration.
0784          return delta;
0785       }
0786    };
0787 
0788 }
0789 
0790 template <class F, class T>
0791 T schroder_iterate(F f, T guess, T min, T max, int digits, std::uintmax_t& max_iter) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0792 {
0793    return detail::second_order_root_finder<detail::schroder_stepper>(f, guess, min, max, digits, max_iter);
0794 }
0795 
0796 template <class F, class T>
0797 inline T schroder_iterate(F f, T guess, T min, T max, int digits) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0798 {
0799    std::uintmax_t m = (std::numeric_limits<std::uintmax_t>::max)();
0800    return schroder_iterate(f, guess, min, max, digits, m);
0801 }
0802 //
0803 // These two are the old spelling of this function, retained for backwards compatibility just in case:
0804 //
0805 template <class F, class T>
0806 T schroeder_iterate(F f, T guess, T min, T max, int digits, std::uintmax_t& max_iter) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0807 {
0808    return detail::second_order_root_finder<detail::schroder_stepper>(f, guess, min, max, digits, max_iter);
0809 }
0810 
0811 template <class F, class T>
0812 inline T schroeder_iterate(F f, T guess, T min, T max, int digits) noexcept(policies::is_noexcept_error_policy<policies::policy<> >::value&& BOOST_MATH_IS_FLOAT(T) && noexcept(std::declval<F>()(std::declval<T>())))
0813 {
0814    std::uintmax_t m = (std::numeric_limits<std::uintmax_t>::max)();
0815    return schroder_iterate(f, guess, min, max, digits, m);
0816 }
0817 
0818 #ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS
0819 /*
0820    * Why do we set the default maximum number of iterations to the number of digits in the type?
0821    * Because for double roots, the number of digits increases linearly with the number of iterations,
0822    * so this default should recover full precision even in this somewhat pathological case.
0823    * For isolated roots, the problem is so rapidly convergent that this doesn't matter at all.
0824    */
0825 template<class ComplexType, class F>
0826 ComplexType complex_newton(F g, ComplexType guess, int max_iterations = std::numeric_limits<typename ComplexType::value_type>::digits)
0827 {
0828    typedef typename ComplexType::value_type Real;
0829    using std::norm;
0830    using std::abs;
0831    using std::max;
0832    // z0, z1, and z2 cannot be the same, in case we immediately need to resort to Muller's Method:
0833    ComplexType z0 = guess + ComplexType(1, 0);
0834    ComplexType z1 = guess + ComplexType(0, 1);
0835    ComplexType z2 = guess;
0836 
0837    do {
0838       auto pair = g(z2);
0839       if (norm(pair.second) == 0)
0840       {
0841          // Muller's method. Notation follows Numerical Recipes, 9.5.2:
0842          ComplexType q = (z2 - z1) / (z1 - z0);
0843          auto P0 = g(z0);
0844          auto P1 = g(z1);
0845          ComplexType qp1 = static_cast<ComplexType>(1) + q;
0846          ComplexType A = q * (pair.first - qp1 * P1.first + q * P0.first);
0847 
0848          ComplexType B = (static_cast<ComplexType>(2) * q + static_cast<ComplexType>(1)) * pair.first - qp1 * qp1 * P1.first + q * q * P0.first;
0849          ComplexType C = qp1 * pair.first;
0850          ComplexType rad = sqrt(B * B - static_cast<ComplexType>(4) * A * C);
0851          ComplexType denom1 = B + rad;
0852          ComplexType denom2 = B - rad;
0853          ComplexType correction = (z1 - z2) * static_cast<ComplexType>(2) * C;
0854          if (norm(denom1) > norm(denom2))
0855          {
0856             correction /= denom1;
0857          }
0858          else
0859          {
0860             correction /= denom2;
0861          }
0862 
0863          z0 = z1;
0864          z1 = z2;
0865          z2 = z2 + correction;
0866       }
0867       else
0868       {
0869          z0 = z1;
0870          z1 = z2;
0871          z2 = z2 - (pair.first / pair.second);
0872       }
0873 
0874       // See: https://math.stackexchange.com/questions/3017766/constructing-newton-iteration-converging-to-non-root
0875       // If f' is continuous, then convergence of x_n -> x* implies f(x*) = 0.
0876       // This condition approximates this convergence condition by requiring three consecutive iterates to be clustered.
0877       Real tol = (max)(abs(z2) * std::numeric_limits<Real>::epsilon(), std::numeric_limits<Real>::epsilon());
0878       bool real_close = abs(z0.real() - z1.real()) < tol && abs(z0.real() - z2.real()) < tol && abs(z1.real() - z2.real()) < tol;
0879       bool imag_close = abs(z0.imag() - z1.imag()) < tol && abs(z0.imag() - z2.imag()) < tol && abs(z1.imag() - z2.imag()) < tol;
0880       if (real_close && imag_close)
0881       {
0882          return z2;
0883       }
0884 
0885    } while (max_iterations--);
0886 
0887    // The idea is that if we can get abs(f) < eps, we should, but if we go through all these iterations
0888    // and abs(f) < sqrt(eps), then roundoff error simply does not allow that we can evaluate f to < eps
0889    // This is somewhat awkward as it isn't scale invariant, but using the Daubechies coefficient example code,
0890    // I found this condition generates correct roots, whereas the scale invariant condition discussed here:
0891    // https://scicomp.stackexchange.com/questions/30597/defining-a-condition-number-and-termination-criteria-for-newtons-method
0892    // allows nonroots to be passed off as roots.
0893    auto pair = g(z2);
0894    if (abs(pair.first) < sqrt(std::numeric_limits<Real>::epsilon()))
0895    {
0896       return z2;
0897    }
0898 
0899    return { std::numeric_limits<Real>::quiet_NaN(),
0900             std::numeric_limits<Real>::quiet_NaN() };
0901 }
0902 #endif
0903 
0904 
0905 #if !defined(BOOST_MATH_NO_CXX17_IF_CONSTEXPR)
0906 // https://stackoverflow.com/questions/48979861/numerically-stable-method-for-solving-quadratic-equations/50065711
0907 namespace detail
0908 {
0909 #if defined(BOOST_GNU_STDLIB) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
0910 inline float fma_workaround(float x, float y, float z) { return ::fmaf(x, y, z); }
0911 inline double fma_workaround(double x, double y, double z) { return ::fma(x, y, z); }
0912 #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
0913 inline long double fma_workaround(long double x, long double y, long double z) { return ::fmal(x, y, z); }
0914 #endif
0915 #endif            
0916 template<class T>
0917 inline T discriminant(T const& a, T const& b, T const& c)
0918 {
0919    T w = 4 * a * c;
0920 #if defined(BOOST_GNU_STDLIB) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
0921    T e = fma_workaround(-c, 4 * a, w);
0922    T f = fma_workaround(b, b, -w);
0923 #else
0924    T e = std::fma(-c, 4 * a, w);
0925    T f = std::fma(b, b, -w);
0926 #endif
0927    return f + e;
0928 }
0929 
0930 template<class T>
0931 std::pair<T, T> quadratic_roots_imp(T const& a, T const& b, T const& c)
0932 {
0933 #if defined(BOOST_GNU_STDLIB) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
0934    using boost::math::copysign;
0935 #else
0936    using std::copysign;
0937 #endif
0938    using std::sqrt;
0939    if constexpr (std::is_floating_point<T>::value)
0940    {
0941       T nan = std::numeric_limits<T>::quiet_NaN();
0942       if (a == 0)
0943       {
0944          if (b == 0 && c != 0)
0945          {
0946             return std::pair<T, T>(nan, nan);
0947          }
0948          else if (b == 0 && c == 0)
0949          {
0950             return std::pair<T, T>(0, 0);
0951          }
0952          return std::pair<T, T>(-c / b, -c / b);
0953       }
0954       if (b == 0)
0955       {
0956          T x0_sq = -c / a;
0957          if (x0_sq < 0) {
0958             return std::pair<T, T>(nan, nan);
0959          }
0960          T x0 = sqrt(x0_sq);
0961          return std::pair<T, T>(-x0, x0);
0962       }
0963       T discriminant = detail::discriminant(a, b, c);
0964       // Is there a sane way to flush very small negative values to zero?
0965       // If there is I don't know of it.
0966       if (discriminant < 0)
0967       {
0968          return std::pair<T, T>(nan, nan);
0969       }
0970       T q = -(b + copysign(sqrt(discriminant), b)) / T(2);
0971       T x0 = q / a;
0972       T x1 = c / q;
0973       if (x0 < x1)
0974       {
0975          return std::pair<T, T>(x0, x1);
0976       }
0977       return std::pair<T, T>(x1, x0);
0978    }
0979    else if constexpr (boost::math::tools::is_complex_type<T>::value)
0980    {
0981       typename T::value_type nan = std::numeric_limits<typename T::value_type>::quiet_NaN();
0982       if (a.real() == 0 && a.imag() == 0)
0983       {
0984          using std::norm;
0985          if (b.real() == 0 && b.imag() && norm(c) != 0)
0986          {
0987             return std::pair<T, T>({ nan, nan }, { nan, nan });
0988          }
0989          else if (b.real() == 0 && b.imag() && c.real() == 0 && c.imag() == 0)
0990          {
0991             return std::pair<T, T>({ 0,0 }, { 0,0 });
0992          }
0993          return std::pair<T, T>(-c / b, -c / b);
0994       }
0995       if (b.real() == 0 && b.imag() == 0)
0996       {
0997          T x0_sq = -c / a;
0998          T x0 = sqrt(x0_sq);
0999          return std::pair<T, T>(-x0, x0);
1000       }
1001       // There's no fma for complex types:
1002       T discriminant = b * b - T(4) * a * c;
1003       T q = -(b + sqrt(discriminant)) / T(2);
1004       return std::pair<T, T>(q / a, c / q);
1005    }
1006    else // Most likely the type is a boost.multiprecision.
1007    {    //There is no fma for multiprecision, and in addition it doesn't seem to be useful, so revert to the naive computation.
1008       T nan = std::numeric_limits<T>::quiet_NaN();
1009       if (a == 0)
1010       {
1011          if (b == 0 && c != 0)
1012          {
1013             return std::pair<T, T>(nan, nan);
1014          }
1015          else if (b == 0 && c == 0)
1016          {
1017             return std::pair<T, T>(0, 0);
1018          }
1019          return std::pair<T, T>(-c / b, -c / b);
1020       }
1021       if (b == 0)
1022       {
1023          T x0_sq = -c / a;
1024          if (x0_sq < 0) {
1025             return std::pair<T, T>(nan, nan);
1026          }
1027          T x0 = sqrt(x0_sq);
1028          return std::pair<T, T>(-x0, x0);
1029       }
1030       T discriminant = b * b - 4 * a * c;
1031       if (discriminant < 0)
1032       {
1033          return std::pair<T, T>(nan, nan);
1034       }
1035       T q = -(b + copysign(sqrt(discriminant), b)) / T(2);
1036       T x0 = q / a;
1037       T x1 = c / q;
1038       if (x0 < x1)
1039       {
1040          return std::pair<T, T>(x0, x1);
1041       }
1042       return std::pair<T, T>(x1, x0);
1043    }
1044 }
1045 }  // namespace detail
1046 
1047 template<class T1, class T2 = T1, class T3 = T1>
1048 inline std::pair<typename tools::promote_args<T1, T2, T3>::type, typename tools::promote_args<T1, T2, T3>::type> quadratic_roots(T1 const& a, T2 const& b, T3 const& c)
1049 {
1050    typedef typename tools::promote_args<T1, T2, T3>::type value_type;
1051    return detail::quadratic_roots_imp(static_cast<value_type>(a), static_cast<value_type>(b), static_cast<value_type>(c));
1052 }
1053 
1054 #endif
1055 
1056 } // namespace tools
1057 } // namespace math
1058 } // namespace boost
1059 
1060 #endif // BOOST_MATH_HAS_NVRTC
1061 
1062 #endif // BOOST_MATH_TOOLS_NEWTON_SOLVER_HPP