diff --git a/doc/background/remez.qbk b/doc/background/remez.qbk index 6dd2718d2..f7ebe4019 100644 --- a/doc/background/remez.qbk +++ b/doc/background/remez.qbk @@ -8,9 +8,9 @@ should consult your favorite textbook. Imagine that you want to approximate some function f(x) by way of a rational function R(x), where R(x) may be either a polynomial P(x) or a ratio of two -polynomials P(x)/Q(x) (a rational function). Initially we'll concentrate on the -polynomial case, as it's by far the easier to deal with, later we'll extend -to the full rational function case. +polynomials P(x)/Q(x) (a rational function). Initially we'll concentrate on the +polynomial case, as it's by far the easier to deal with, later we'll extend +to the full rational function case. We want to find the "best" rational approximation, where "best" is defined to be the approximation that has the least deviation @@ -61,11 +61,11 @@ the range \[-1, 1\]. Before we can begin the Remez method, we must obtain an initial value for the location of the extrema of the error function. We could "guess" -these, but a much closer first approximation can be obtained by first +these, but a much closer first approximation can be obtained by first constructing an interpolated polynomial approximation to f(x). In order to obtain the N+1 coefficients of the interpolated polynomial -we need N+1 points (x[sub 0]...x[sub N]): with our interpolated form +we need N+1 points (x[sub 0]...x[sub N]): with our interpolated form passing through each of those points that yields N+1 simultaneous equations: @@ -73,12 +73,12 @@ f(x[sub i]) = P(x[sub i]) = c[sub 0] + c[sub 1]x[sub i] ... + c[sub N]x[sub i][s Which can be solved for the coefficients c[sub 0]...c[sub N] in P(x). -Obviously this is not a minimax solution, indeed our only guarantee is that f(x) and +Obviously this is not a minimax solution, indeed our only guarantee is that f(x) and P(x) touch at N+1 locations, away from those points the error may be arbitrarily large. However, we would clearly like this initial approximation to be as close to f(x) as possible, and it turns out that using the zeros of an orthogonal polynomial -as the initial interpolation points is a good choice. In our example we'll use the -zeros of a Chebyshev polynomial as these are particularly easy to calculate, +as the initial interpolation points is a good choice. In our example we'll use the +zeros of a Chebyshev polynomial as these are particularly easy to calculate, interpolating for a polynomial of degree 4, and measuring /relative error/ we get the following error function: @@ -86,7 +86,7 @@ we get the following error function: Which has a peak relative error of 1.2x10[super -3]. -While this is a pretty good approximation already, judging by the +While this is a pretty good approximation already, judging by the shape of the error function we can clearly do better. Before starting on the Remez method propper, we have one more step to perform: locate all the extrema of the error function, and store @@ -101,7 +101,7 @@ achieve, and typically is very close to the minimax solution. However, if we want to optimise for /relative error/, or if the approximation is a rational function, then the initial Chebyshev solution can be quite far -from the ideal minimax solution. +from the ideal minimax solution. A more technical discussion of the theory involved can be found in this [@http://math.fullerton.edu/mathews/n2003/ChebyshevPolyMod.html online course].] @@ -118,7 +118,7 @@ To obtain the error term E, and the coefficients of the polynomial P(x). This gives us a new approximation to f(x) that has the same error /E/ at each of the control points, and whose error function ['alternates in sign] -at the control points. This is still not necessarily the minimax +at the control points. This is still not necessarily the minimax solution though: since the control points may not be at the extrema of the error function. After this first step here's what our approximation's error function looks like: @@ -131,10 +131,10 @@ dropped to 5.6x10[super -4]. [h4 Remez Step 2] -The second step is to locate the extrema of the new approximation, which we do +The second step is to locate the extrema of the new approximation, which we do in two stages: first, since the error function changes sign at each control point, we must have N+1 roots of the error function located between -each pair of N+2 control points. Once these roots are found by standard root finding +each pair of N+2 control points. Once these roots are found by standard root finding techniques, we know that N extrema are bracketed between each pair of roots, plus two more between the endpoints of the range and the first and last roots. The N+2 extrema can then be found using standard function minimisation techniques. @@ -195,10 +195,10 @@ polynomial alternatives. For example, if we takes our previous example of an approximation to e[super x], we obtained 5x10[super -4] accuracy with an order 4 polynomial. If we move two of the unknowns into the denominator to give a pair of order 2 polynomials, and re-minimise, then the peak relative error drops -to 8.7x10[super -5]. That's a 5 fold increase in accuracy, for the same number +to 8.7x10[super -5]. That's a 5 fold increase in accuracy, for the same number of terms overall. -[h4 Practical Considerations] +[h4:remez_practical Practical Considerations] Most treatises on approximation theory stop at this point. However, from a practical point of view, most of the work involves finding the right @@ -211,12 +211,12 @@ f(x) = R(x) But this will converge to a useful approximation only if f(x) is smooth. In addition round-off errors when evaluating the rational form mean that this -will never get closer than within a few epsilon of machine precision. +will never get closer than within a few epsilon of machine precision. Therefore this form of direct approximation is often reserved for situations where we want efficiency, rather than accuracy. The first step in improving the situation is generally to split f(x) into -a dominant part that we can compute accurately by another method, and a +a dominant part that we can compute accurately by another method, and a slowly changing remainder which can be approximated by a rational approximation. We might be tempted to write: @@ -235,7 +235,7 @@ to the constant /c/, that error will effectively get wiped out when R(x) is adde The difficult part is obviously finding the right g(x) to extract from your function: often the asymptotic behaviour of the function will give a clue, so -for example the function __erfc becomes proportional to +for example the function __erfc becomes proportional to e[super -x[super 2]]\/x as x becomes large. Therefore using: erfc(z) = (C + R(x)) e[super -x[super 2]]/x @@ -262,15 +262,15 @@ the roots, the approximation is nonetheless useless. Assuming that the approximation does not have any fatal errors, and that the only issue is converging adequately on the minimax solution, the aim is to get as close as possible to the minimax solution before beginning the Remez method. -Using the zeros of a Chebyshev polynomial for the initial interpolation is a +Using the zeros of a Chebyshev polynomial for the initial interpolation is a good start, but may not be ideal when dealing with relative errors and\/or rational (rather than polynomial) approximations. One approach is to skew the initial interpolation points to one end: for example if we raise the -roots of the Chebyshev polynomial to a positive power greater than 1 -then the roots will be skewed towards the middle of the \[-1,1\] interval, +roots of the Chebyshev polynomial to a positive power greater than 1 +then the roots will be skewed towards the middle of the \[-1,1\] interval, while a positive power less than one will skew them towards either end. More usefully, if we initially rescale the -points over \[0,1\] and then raise to a positive power, we can skew them to the left +points over \[0,1\] and then raise to a positive power, we can skew them to the left or right. Returning to our example of e[super x][space] over \[-1,1\], the initial interpolated form was some way from the minimax solution: @@ -287,7 +287,7 @@ our desired minimax solution (5x10[super -4]). [h4 Remez Method Checklist] -The following lists some of the things to check if the Remez method goes wrong, +The following lists some of the things to check if the Remez method goes wrong, it is by no means an exhaustive list, but is provided in the hopes that it will prove useful. @@ -312,7 +312,7 @@ location, try starting from a different location. * If a rational function won't converge, one can minimise a polynomial (which presents no problems), then rotate one term from the numerator to the denominator and minimise again. In theory one can continue moving -terms one at a time from numerator to denominator, and then re-minimising, +terms one at a time from numerator to denominator, and then re-minimising, retaining the last set of control points at each stage. * Try using a smaller interval. It may also be possible to optimise over one (small) interval, rescale the control points over a larger interval, @@ -325,41 +325,41 @@ over, say \[0, b\], for some smallish value /b/. The original references for the Remez Method and it's extension to rational functions are unfortunately in Russian: -Remez, E.Ya., ['Fundamentals of numerical methods for Chebyshev approximations], +Remez, E.Ya., ['Fundamentals of numerical methods for Chebyshev approximations], "Naukova Dumka", Kiev, 1969. -Remez, E.Ya., Gavrilyuk, V.T., ['Computer development of certain approaches -to the approximate construction of solutions of Chebyshev problems +Remez, E.Ya., Gavrilyuk, V.T., ['Computer development of certain approaches +to the approximate construction of solutions of Chebyshev problems nonlinearly depending on parameters], Ukr. Mat. Zh. 12 (1960), 324-338. -Gavrilyuk, V.T., ['Generalization of the first polynomial algorithm of -E.Ya.Remez for the problem of constructing rational-fractional +Gavrilyuk, V.T., ['Generalization of the first polynomial algorithm of +E.Ya.Remez for the problem of constructing rational-fractional Chebyshev approximations], Ukr. Mat. Zh. 16 (1961), 575-585. Some English language sources include: -Fraser, W., Hart, J.F., ['On the computation of rational approximations +Fraser, W., Hart, J.F., ['On the computation of rational approximations to continuous functions], Comm. of the ACM 5 (1962), 401-403, 414. -Ralston, A., ['Rational Chebyshev approximation by Remes' algorithms], +Ralston, A., ['Rational Chebyshev approximation by Remes' algorithms], Numer.Math. 7 (1965), no. 4, 322-330. -A. Ralston, ['Rational Chebyshev approximation, Mathematical -Methods for Digital Computers v. 2] (Ralston A., Wilf H., eds.), +A. Ralston, ['Rational Chebyshev approximation, Mathematical +Methods for Digital Computers v. 2] (Ralston A., Wilf H., eds.), Wiley, New York, 1967, pp. 264-284. Hart, J.F. e.a., ['Computer approximations], Wiley, New York a.o., 1968. -Cody, W.J., Fraser, W., Hart, J.F., ['Rational Chebyshev approximation +Cody, W.J., Fraser, W., Hart, J.F., ['Rational Chebyshev approximation using linear equations], Numer.Math. 12 (1968), 242-251. -Cody, W.J., ['A survey of practical rational and polynomial +Cody, W.J., ['A survey of practical rational and polynomial approximation of functions], SIAM Review 12 (1970), no. 3, 400-423. -Barrar, R.B., Loeb, H.J., ['On the Remez algorithm for non-linear +Barrar, R.B., Loeb, H.J., ['On the Remez algorithm for non-linear families], Numer.Math. 15 (1970), 382-391. -Dunham, Ch.B., ['Convergence of the Fraser-Hart algorithm for rational +Dunham, Ch.B., ['Convergence of the Fraser-Hart algorithm for rational Chebyshev approximation], Math. Comp. 29 (1975), no. 132, 1078-1082. G. L. Litvinov, ['Approximate construction of rational @@ -368,7 +368,7 @@ Russian Journal of Mathematical Physics, vol.1, No. 3, 1994. [endsect][/section:remez The Remez Method] -[/ +[/ Copyright 2006 John Maddock and Paul A. Bristow. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at diff --git a/doc/distributions/non_members.qbk b/doc/distributions/non_members.qbk index 653d24979..8937b3141 100644 --- a/doc/distributions/non_members.qbk +++ b/doc/distributions/non_members.qbk @@ -1,11 +1,11 @@ [section:nmp Non-Member Properties] -Properties that are common to all distributions are accessed via non-member +Properties that are common to all distributions are accessed via non-member getter functions: non-membership allows more of these functions to be added over time, as the need arises. Unfortunately the literature uses many different and confusing names to refer to a rather small number of actual concepts; refer -to the [link math_toolkit.dist_ref.nmp.concept_index concept index] to find the property you -want by the name you are most familiar with. +to the [link math_toolkit.dist_ref.nmp.concept_index concept index] to find the property you +want by the name you are most familiar with. Or use the [link math_toolkit.dist_ref.nmp.function_index function index] to go straight to the function you want if you already know its name. @@ -62,8 +62,8 @@ to go straight to the function you want if you already know its name. template RealType cdf(const ``['Distribution-Type]``& dist, const RealType& x); - -The __cdf is the probability that + +The __cdf is the probability that the variable takes a value less than or equal to x. It is equivalent to the integral from -infinity to x of the __pdf. @@ -79,11 +79,11 @@ normal distribution: template RealType cdf(const ``['Unspecified-Complement-Type]``& comp); - -The complement of the __cdf -is the probability that + +The complement of the __cdf +is the probability that the variable takes a value greater than x. It is equivalent -to the integral from x to infinity of the __pdf, or 1 minus the __cdf of x. +to the integral from x to infinity of the __pdf, or 1 minus the __cdf of x. This is also known as the survival function. @@ -118,7 +118,7 @@ the defined range for the distribution. [equation hazard] [caution -Some authors refer to this as the conditional failure +Some authors refer to this as the conditional failure density function rather than the hazard function.] [h4:chf Cumulative Hazard Function] @@ -133,14 +133,14 @@ the defined range for the distribution. [equation chf] -[caution +[caution Some authors refer to this as simply the "Hazard Function".] [h4:mean mean] template RealType mean(const ``['Distribution-Type]``& dist); - + Returns the mean of the distribution /dist/. This function may return a __domain_error if the distribution does not have @@ -150,14 +150,14 @@ a defined mean (for example the Cauchy distribution). template RealType median(const ``['Distribution-Type]``& dist); - + Returns the median of the distribution /dist/. [h4:mode mode] template RealType mode(const ``['Distribution-Type]``& dist); - + Returns the mode of the distribution /dist/. This function may return a __domain_error if the distribution does not have @@ -167,14 +167,14 @@ a defined mode. template RealType pdf(const ``['Distribution-Type]``& dist, const RealType& x); - -For a continuous function, the probability density function (pdf) returns -the probability that the variate has the value x. -Since for continuous distributions the probability at a single point is actually zero, + +For a continuous function, the probability density function (pdf) returns +the probability that the variate has the value x. +Since for continuous distributions the probability at a single point is actually zero, the probability is better expressed as the integral of the pdf between two points: see the __cdf. -For a discrete distribution, the pdf is the probability that the +For a discrete distribution, the pdf is the probability that the variate takes the value x. This function may return a __domain_error if the random variable is outside @@ -188,14 +188,14 @@ For example, for a standard normal distribution the pdf looks like this: template std::pair range(const ``['Distribution-Type]``& dist); - + Returns the valid range of the random variable over distribution /dist/. [h4:quantile Quantile] template RealType quantile(const ``['Distribution-Type]``& dist, const RealType& p); - + The quantile is best viewed as the inverse of the __cdf, it returns a value /x/ such that `cdf(dist, x) == p`. @@ -217,7 +217,7 @@ See also [link math_toolkit.stat_tut.overview.complements complements]. template RealType quantile(const ``['Unspecified-Complement-Type]``& comp); - + This is the inverse of the __ccdf. It is calculated by wrapping the arguments in a call to the quantile function in a call to /complement/. For example: @@ -250,8 +250,8 @@ distribution: template RealType standard_deviation(const ``['Distribution-Type]``& dist); - -Returns the standard deviation of distribution /dist/. + +Returns the standard deviation of distribution /dist/. This function may return a __domain_error if the distribution does not have a defined standard deviation. @@ -260,7 +260,7 @@ a defined standard deviation. template std::pair support(const ``['Distribution-Type]``& dist); - + Returns the supported range of random variable over the distribution /dist/. The distribution is said to be 'supported' over a range that is @@ -274,7 +274,7 @@ Outside are uninteresting zones where the pdf is zero, and the cdf zero or unity template RealType variance(const ``['Distribution-Type]``& dist); - + Returns the variance of the distribution /dist/. This function may return a __domain_error if the distribution does not have @@ -284,7 +284,7 @@ a defined variance. template RealType skewness(const ``['Distribution-Type]``& dist); - + Returns the skewness of the distribution /dist/. This function may return a __domain_error if the distribution does not have @@ -294,7 +294,7 @@ a defined skewness. template RealType kurtosis(const ``['Distribution-Type]``& dist); - + Returns the 'proper' kurtosis (normalized fourth moment) of the distribution /dist/. kertosis = [beta][sub 2][space]= [mu][sub 4][space] / [mu][sub 2][super 2] @@ -326,7 +326,7 @@ a defined kurtosis. template RealType kurtosis_excess(const ``['Distribution-Type]``& dist); - + Returns the kurtosis excess of the distribution /dist/. kurtosis excess = [gamma][sub 2][space]= [mu][sub 4][space] / [mu][sub 2][super 2][space]- 3 = kurtosis - 3 @@ -334,7 +334,7 @@ kurtosis excess = [gamma][sub 2][space]= [mu][sub 4][space] / [mu][sub 2][super Where [mu][sub i][space] is the i'th central moment of the distribution, and in particular [mu][sub 2][space] is the variance of the distribution. -The kurtosis excess is a measure of the "peakedness" of a distribution, and +The kurtosis excess is a measure of the "peakedness" of a distribution, and is more widely used than the "kurtosis proper". It is defined so that the kurtosis excess of a normal distribution is zero. @@ -344,7 +344,7 @@ a defined kurtosis excess. Kurtosis excess can have a value from -2 to + infinity. kurtosis = kurtosis_excess +3; - + The kurtosis excess of a normal distribution is zero. [h4:cdfPQ P and Q] @@ -361,12 +361,12 @@ the __quantile. [h4:cdf_inv Inverse CDF Function.] -The inverse of the cumulative distribution function, is the same as the +The inverse of the cumulative distribution function, is the same as the __quantile. [h4:survival_inv Inverse Survival Function.] -The inverse of the survival function, is the same as computing the +The inverse of the survival function, is the same as computing the [link math_toolkit.dist_ref.nmp.quantile_c quantile from the complement of the probability]. @@ -380,13 +380,13 @@ while the term __pdf applies to continuous distributions. [h4:lower_critical Lower Critical Value.] The lower critical value calculates the value of the random variable -given the area under the left tail of the distribution. +given the area under the left tail of the distribution. It is equivalent to calculating the __quantile. -[h4: upper_critical Upper Critical Value.] +[h4:upper_critical Upper Critical Value.] The upper critical value calculates the value of the random variable -given the area under the right tail of the distribution. It is equivalent to +given the area under the right tail of the distribution. It is equivalent to calculating the [link math_toolkit.dist_ref.nmp.quantile_c quantile from the complement of the probability]. @@ -394,8 +394,7 @@ probability]. Refer to the __ccdf. -[endsect][/section:nmp Non-Member Properties] - +[endsect] [/section:nmp Non-Member Properties] [/ non_members.qbk Copyright 2006 John Maddock and Paul A. Bristow. diff --git a/doc/fp_utilities/float_next.qbk b/doc/fp_utilities/float_next.qbk index 4c9de91a5..a3daa8541 100644 --- a/doc/fp_utilities/float_next.qbk +++ b/doc/fp_utilities/float_next.qbk @@ -20,6 +20,26 @@ __multiprecision __cpp_dec_float and__cpp_bin_float are fixed at runtime, but [*not] a type that extends the representation to provide an exact representation for any number, for example [@http://keithbriggs.info/xrc.html XRC eXact Real in C]. +The accuracy of mathematical functions can be assessed and displayed in terms of __ULP, +often as a ulps plot or by binning the differences as a histogram. +Samples are evaluated using the implementation under test and compared with 'known good' +representation obtained using a more accurate method. Other implementations, often using +arbitrary precision arithmetic, for example __WolframAlpha are one source of references +values. The other method, used widely in Boost.Math special functions, it to carry out +the same algorithm, but using a higher precision type, typically using Boost.Multiprecision +types like `cpp_bin_float_quad` for 128-bit (about 35 decimal digit precision), or +`cpp_bin_float_50` (for 50 decimal digit precision). + +When converted to a particular machine representation, say `double`, say using a `static_cast`, +the value is the nearest representation possible for the `double` type. This value +cannot be 'wrong' by more than half a __ulp, and can be obtained using the Boost.Math function `ulp`. +(Unless the algorithm is fundamentally flawed, something that should be revealed by 'sanity' +checks using some independent sources). + +See some discussion and example plots by Cleve Moler of Mathworks +[@https://blogs.mathworks.com/cleve/2017/01/23/ulps-plots-reveal-math-function-accurary/ +ulps plots reveal math-function accuracy]. + [section:nextafter Finding the Next Representable Value in a Specific Direction (nextafter)] [h4 Synopsis] @@ -220,13 +240,13 @@ Function `ulp` gives the size of a unit-in-the-last-place for a specified floati Returns one [@http://en.wikipedia.org/wiki/Unit_in_the_last_place unit in the last place] of ['x]. -Corner cases are handled as followes: +Corner cases are handled as follows: * If the argument is a NaN, then raises a __domain_error. * If the argument is an infinity, then raises an __overflow_error. * If the argument is zero then returns the smallest representable value: for example for type `double` this would be either `std::numeric_limits::min()` or `std::numeric_limits::denorm_min()` -depending whether denormals are supported (which have the values 2.`2250738585072014e-308` and `4.9406564584124654e-324` respectively). +depending whether denormals are supported (which have the values `2.2250738585072014e-308` and `4.9406564584124654e-324` respectively). * If the result is too small to represent, then returns the smallest representable value. * Always returns a positive value such that `ulp(x) == ulp(-x)`. diff --git a/doc/graphs/diode_iv_plot.png b/doc/graphs/diode_iv_plot.png new file mode 100644 index 000000000..a956e1b83 Binary files /dev/null and b/doc/graphs/diode_iv_plot.png differ diff --git a/doc/graphs/diode_iv_plot.svg b/doc/graphs/diode_iv_plot.svg new file mode 100644 index 000000000..8ae531a4b --- /dev/null +++ b/doc/graphs/diode_iv_plot.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0.3 +0.4 +0.5 +0.6 +0.7 +0.8 +0.9 +1 + +0 +4 +0 +-4 +-8 +-12 +-16 +-20 + +log(current) (A) + +voltage (V) + + + + + + + + + + + + + + + + + + +I₀(V) +Rs=0 Ω +Rs=2 Ω +Rs=10 Ω +Rs=51 Ω +Rs=249 Ω + +Diode current versus voltage + + + diff --git a/doc/graphs/dist_graphs.cpp b/doc/graphs/dist_graphs.cpp index 515d392ef..b54e18677 100644 --- a/doc/graphs/dist_graphs.cpp +++ b/doc/graphs/dist_graphs.cpp @@ -10,7 +10,7 @@ \author John Maddock and Paul A. Bristow */ // Copyright John Maddock 2008. -// Copyright Paul A. Bristow 2008, 2009, 2012 +// Copyright Paul A. Bristow 2008, 2009, 2012, 2016 // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -35,8 +35,9 @@ template struct is_discrete_distribution - : public boost::mpl::false_{}; + : public boost::mpl::false_{}; // Default is continuous distribution. +// Some discrete distributions. template struct is_discrete_distribution > : public boost::mpl::true_{}; @@ -68,7 +69,7 @@ struct value_finder private: Dist m_dist; typename Dist::value_type m_value; -}; +}; // value_finder template class distribution_plotter @@ -177,7 +178,7 @@ public: if(b > m_max_x) m_max_x = b; } - } + } // add void plot(const std::string& title, const std::string& file) { @@ -198,9 +199,12 @@ public: m_max_y = 1; } + std::cout << "Plotting " << title << " to " << file << std::endl; + svg_2d_plot plot; plot.image_x_size(750); plot.image_y_size(400); + plot.copyright_holder("John Maddock").copyright_date("2008").boost_license_on(true); plot.coord_precision(4); // Avoids any visible steps. plot.title_font_size(20); plot.legend_title_font_size(15); @@ -248,19 +252,17 @@ public: if(!is_discrete_distribution::value) { - // // Continuous distribution: - // for(std::list >::const_iterator i = m_distributions.begin(); i != m_distributions.end(); ++i) { double x = m_min_x; - double interval = (m_max_x - m_min_x) / 200; + double continuous_interval = (m_max_x - m_min_x) / 200; std::map data; while(x <= m_max_x) { data[x] = m_pdf ? pdf(i->second, x) : cdf(i->second, x); - x += interval; + x += continuous_interval; } plot.plot(data, i->first) .line_on(true) @@ -275,16 +277,14 @@ public: } else { - // // Discrete distribution: - // double x_width = 0.75 / m_distributions.size(); double x_off = -0.5 * 0.75; for(std::list >::const_iterator i = m_distributions.begin(); i != m_distributions.end(); ++i) { double x = ceil(m_min_x); - double interval = 1; + double discrete_interval = 1; std::map data; while(x <= m_max_x) { @@ -300,7 +300,7 @@ public: data[x + x_off + 0.00001] = p; data[x + x_off + x_width] = p; data[x + x_off + x_width + 0.00001] = 0; - x += interval; + x += discrete_interval; } x_off += x_width; svg_2d_plot_series& s = plot.plot(data, i->first); @@ -312,9 +312,9 @@ public: ++color_index; color_index = color_index % (sizeof(colors)/sizeof(colors[0])); } - } + } // descrete plot.write(file); - } + } // void plot(const std::string& title, const std::string& file) private: bool m_pdf; @@ -326,7 +326,7 @@ int main() { try { - + std::cout << "Distribution Graphs" << std::endl; distribution_plotter > gamma_plotter; gamma_plotter.add(boost::math::gamma_distribution<>(0.75), "shape = 0.75"); @@ -718,5 +718,4 @@ int main() hyperexponential_plotter3.plot("Hyperexponential Distribution PDF (Different Number of Phases, Same Mean)", "hyperexponential_pdf_samemean.svg"); */ - } // int main() diff --git a/doc/graphs/lambert_w0_errors_graph.png b/doc/graphs/lambert_w0_errors_graph.png new file mode 100644 index 000000000..2a18debbb Binary files /dev/null and b/doc/graphs/lambert_w0_errors_graph.png differ diff --git a/doc/graphs/lambert_w0_errors_graph.svg b/doc/graphs/lambert_w0_errors_graph.svg new file mode 100644 index 000000000..222b7df5a --- /dev/null +++ b/doc/graphs/lambert_w0_errors_graph.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0 +10 +20 +30 +40 +50 +60 +70 +80 +90 +100 +0 + +0 +2 +4 +0 +-2 +-4 + +W0 difference (bits) + +z + + + + +Lambert W0 function differences from 'best' for double. + + + diff --git a/doc/graphs/lambert_w0_prime_graph.png b/doc/graphs/lambert_w0_prime_graph.png new file mode 100644 index 000000000..b6cd492b7 Binary files /dev/null and b/doc/graphs/lambert_w0_prime_graph.png differ diff --git a/doc/graphs/lambert_w0_prime_graph.svg b/doc/graphs/lambert_w0_prime_graph.svg new file mode 100644 index 000000000..96a0df162 --- /dev/null +++ b/doc/graphs/lambert_w0_prime_graph.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0 +0.2 +0.4 +0.6 +0.8 +1 +0 +-0.2 + +0 +2 +4 +0 + +W0' + +z + + + + +Lambert W0 prime function. + + + diff --git a/doc/graphs/lambert_w_graph.png b/doc/graphs/lambert_w_graph.png new file mode 100644 index 000000000..0a9dd0527 Binary files /dev/null and b/doc/graphs/lambert_w_graph.png differ diff --git a/doc/graphs/lambert_w_graph.svg b/doc/graphs/lambert_w_graph.svg new file mode 100644 index 000000000..817e96cd9 --- /dev/null +++ b/doc/graphs/lambert_w_graph.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0 +1 +2 +3 +0 +-1 + +0 +1 +0 +-1 +-2 +-3 +-4 + +W + +z + + + + + + + + + +W0 branch +W-1 branch + +Lambert W function. + + + diff --git a/doc/graphs/lambert_w_graph_big_W.png b/doc/graphs/lambert_w_graph_big_W.png new file mode 100644 index 000000000..699a23129 Binary files /dev/null and b/doc/graphs/lambert_w_graph_big_W.png differ diff --git a/doc/graphs/lambert_w_graph_big_W.svg b/doc/graphs/lambert_w_graph_big_W.svg new file mode 100644 index 000000000..1102af06d --- /dev/null +++ b/doc/graphs/lambert_w_graph_big_W.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0 +2e3 +4e3 +6e3 +8e3 +1e4 +0 + +0 +1 +2 +3 +4 +5 +6 +7 +8 +0 +-1 + +W + +z + + + + +Lambert W0 function for larger z. + + + diff --git a/doc/graphs/lambert_wm1_errors_graph.png b/doc/graphs/lambert_wm1_errors_graph.png new file mode 100644 index 000000000..e5169e271 Binary files /dev/null and b/doc/graphs/lambert_wm1_errors_graph.png differ diff --git a/doc/graphs/lambert_wm1_errors_graph.svg b/doc/graphs/lambert_wm1_errors_graph.svg new file mode 100644 index 000000000..36924f354 --- /dev/null +++ b/doc/graphs/lambert_wm1_errors_graph.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0 +0 +-0.1 +-0.2 +-0.3 + +0 +2 +4 +0 +-2 +-4 + +W-1 difference (bits) + +z + + + + +Lambert W-1 function differences from 'best' for double. + + + diff --git a/doc/graphs/lambert_wm1_prime_graph.png b/doc/graphs/lambert_wm1_prime_graph.png new file mode 100644 index 000000000..0c8f2b466 Binary files /dev/null and b/doc/graphs/lambert_wm1_prime_graph.png differ diff --git a/doc/graphs/lambert_wm1_prime_graph.svg b/doc/graphs/lambert_wm1_prime_graph.svg new file mode 100644 index 000000000..d51dc1358 --- /dev/null +++ b/doc/graphs/lambert_wm1_prime_graph.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +0 +0 +-0.1 +-0.2 +-0.3 +-0.4 + +-5 +-10 +-15 +-20 + +W-1' + +z + + + + +Lambert W-1 prime function. + + + diff --git a/doc/graphs/sf_graphs.cpp b/doc/graphs/sf_graphs.cpp index 713665c60..57843cb5c 100644 --- a/doc/graphs/sf_graphs.cpp +++ b/doc/graphs/sf_graphs.cpp @@ -1,4 +1,5 @@ -// (C) Copyright John Maddock 2008. +// Copyright John Maddock 2008. +// Copyright Paul A. Bristow 2016 // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -11,6 +12,8 @@ # pragma warning (disable : 4224) // nonstandard extension used : formal parameter 'function_ptr' was previously defined as a type #endif +// #define BOOST_SVG_DIAGNOSTICS // define to provide diagnostic output from plotting. + #include #include #include @@ -27,52 +30,63 @@ class function_arity1_plotter public: function_arity1_plotter() : m_min_x(0), m_max_x(0), m_min_y(0), m_max_y(0), m_has_legend(false) {} - void add(boost::function f, double a, double b, const std::string& name) + //! Add a function to the plotter, compute the axes using range a to b and compute & add data points to map. + + void add(boost::function f, double x_lo, double x_hi, const std::string& name) { + std::cout << "Adding function " << name << ", x range " << x_lo << " to " << x_hi << std::endl; if(name.size()) m_has_legend = true; // // Now set our x-axis limits: - // if(m_max_x == m_min_x) { - m_max_x = b; - m_min_x = a; + m_max_x = x_hi; + m_min_x = x_lo; } else { - if(a < m_min_x) - m_min_x = a; - if(b > m_max_x) - m_max_x = b; + if(x_lo < m_min_x) + m_min_x = x_lo; + if(x_hi > m_max_x) + m_max_x = x_hi; } m_points.push_back(std::pair >(name, std::map())); std::map& points = m_points.rbegin()->second; - double interval = (b - a) / 200; - for(double x = a; x <= b; x += interval) + double interval = (x_hi - x_lo) / 200; + for(double x = x_lo; x <= x_hi; x += interval) { - // - // Evaluate the function, set the Y axis limits - // if needed and then store the pair of points: - // - double y = f(x); + double y = f(x); // Evaluate the function. + // Set the Y axis limits if needed. if((m_min_y == m_max_y) && (m_min_y == 0)) m_min_y = m_max_y = y; if(m_min_y > y) m_min_y = y; if(m_max_y < y) m_max_y = y; - points[x] = y; - } - } + points[x] = y; // Store the pair of points values. + } // for x +#ifdef BOOST_SVG_DIAGNOSTICS + std::cout << "Added function " << name + << ", x range " << x_lo << " to " << x_hi + << ", x min = " << m_min_x << ", x max = " << m_max_x + << ", y min = " << m_min_y << ", y max = " << m_max_y + << ", interval = " << interval + << std::endl; +#endif + } // void add(boost::function f, double a, double b, const std::string& name) + + //! Compute x and y min and max from a map of pre-computed data points. void add(const std::map& m, const std::string& name) { - if(name.size()) - m_has_legend = true; + if (name.size() != 0) + { + m_has_legend = true; + } m_points.push_back(std::pair >(name, m)); - std::map::const_iterator i = m.begin(); + std::map::const_iterator i = m.begin(); while(i != m.end()) { if((m_min_x == m_min_y) && (m_min_y == 0)) @@ -103,16 +117,16 @@ public: ++i; } - } + } // void add(const std::map& m, const std::string& name) + //! Plot pre-computed m_points data for function. void plot(const std::string& title, const std::string& file, - const std::string& x_lable = std::string(), - const std::string& y_lable = std::string()) + const std::string& x_lable = std::string(), const std::string& y_lable = std::string()) { using namespace boost::svg; static const svg_color colors[5] = - { + { // Colors for plot curves, used in turn. darkblue, darkred, darkgreen, @@ -120,6 +134,8 @@ public: chartreuse }; + std::cout << "Plotting Special Function " << title << " to file " << file << std::endl; + svg_2d_plot plot; plot.image_x_size(600); plot.image_y_size(400); @@ -139,7 +155,6 @@ public: plot.y_num_minor_ticks(3); // // Work out axis tick intervals: - // double l = std::floor(std::log10((m_max_x - m_min_x) / 10) + 0.5); double interval = std::pow(10.0, (int)l); if(((m_max_x - m_min_x) / interval) > 10) @@ -156,7 +171,7 @@ public: .legend_border_color(lightslategray) .legend_background_color(white); - int color_index = 0; + int color_index = 0; // Cycle through the colors for each curve. for(std::list > >::const_iterator i = m_points.begin(); i != m_points.end(); ++i) @@ -171,14 +186,14 @@ public: color_index = color_index % (sizeof(colors)/sizeof(colors[0])); } plot.write(file); - } + } // void plot(const std::string& title, const std::string& file, void clear() { m_points.clear(); m_min_x = m_min_y = m_max_x = m_max_y = 0; m_has_legend = false; - } + } // clear private: std::list > > m_points; @@ -241,495 +256,511 @@ double lbeta(double a, double b) int main() { - function_arity1_plotter plot; - double (*f)(double); - double (*f2)(double, double); - double (*f2u)(unsigned, double); - double (*f2i)(int, double); - double (*f3)(double, double, double); - double (*f4)(double, double, double, double); - double max_val; + try + { + function_arity1_plotter plot; - f = boost::math::zeta; - plot.add(f, find_end_point(f, 0.1, 40.0, false, 1.0), 10, ""); - plot.add(f, -20, find_end_point(f, -0.1, -40.0, false, 1.0), ""); - plot.plot("Zeta Function Over [-20,10]", "zeta1.svg", "z", "zeta(z)"); + // Functions may have varying numbers and types of parameters. + // plot.add calls must use the appropriate function type. + // Not all function types may be used, so can ignore any warning like + // "C4101: 'f4': unreferenced local variable" + double(*f)(double); // Simplest function type, suits most functions. + double(*f2)(double, double); + double(*f2u)(unsigned, double); + double(*f2i)(int, double); + double(*f3)(double, double, double); + double(*f4)(double, double, double, double); + double max_val; // Hold evaluated value of function for use in find_end_point. - plot.clear(); - plot.add(f, -14, 0, ""); - plot.plot("Zeta Function Over [-14,0]", "zeta2.svg", "z", "zeta(z)"); + f = boost::math::zeta; + plot.add(f, find_end_point(f, 0.1, 40.0, false, 1.0), 10, ""); + plot.add(f, -20, find_end_point(f, -0.1, -40.0, false, 1.0), ""); + plot.plot("Zeta Function Over [-20,10]", "zeta1.svg", "z", "zeta(z)"); - f = boost::math::tgamma; - max_val = f(6); - plot.clear(); - plot.add(f, find_end_point(f, 0.1, max_val, false), 6, ""); - plot.add(f, find_end_point(f, 0.1, -max_val, true, -1), find_end_point(f, -0.1, -max_val, false), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -2), find_end_point(f, -0.1, max_val, true, -1), ""); - plot.add(f, find_end_point(f, 0.1, -max_val, true, -3), find_end_point(f, -0.1, -max_val, false, -2), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -4), find_end_point(f, -0.1, max_val, true, -3), ""); - plot.plot("tgamma", "tgamma.svg", "z", "tgamma(z)"); + plot.clear(); + plot.add(f, -14, 0, ""); + plot.plot("Zeta Function Over [-14,0]", "zeta2.svg", "z", "zeta(z)"); - f = boost::math::lgamma; - max_val = f(10); - plot.clear(); - plot.add(f, find_end_point(f, 0.1, max_val, false), 10, ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -1), find_end_point(f, -0.1, max_val, true), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -2), find_end_point(f, -0.1, max_val, true, -1), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -3), find_end_point(f, -0.1, max_val, true, -2), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -4), find_end_point(f, -0.1, max_val, true, -3), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -5), find_end_point(f, -0.1, max_val, true, -4), ""); - plot.plot("lgamma", "lgamma.svg", "z", "lgamma(z)"); + f = boost::math::tgamma; + max_val = f(6); + plot.clear(); + plot.add(f, find_end_point(f, 0.1, max_val, false), 6, ""); + plot.add(f, find_end_point(f, 0.1, -max_val, true, -1), find_end_point(f, -0.1, -max_val, false), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -2), find_end_point(f, -0.1, max_val, true, -1), ""); + plot.add(f, find_end_point(f, 0.1, -max_val, true, -3), find_end_point(f, -0.1, -max_val, false, -2), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -4), find_end_point(f, -0.1, max_val, true, -3), ""); + plot.plot("tgamma", "tgamma.svg", "z", "tgamma(z)"); - f = boost::math::digamma; - max_val = 10; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, -max_val, true), 10, ""); - plot.add(f, find_end_point(f, 0.1, -max_val, true, -1), find_end_point(f, -0.1, max_val, true), ""); - plot.add(f, find_end_point(f, 0.1, -max_val, true, -2), find_end_point(f, -0.1, max_val, true, -1), ""); - plot.add(f, find_end_point(f, 0.1, -max_val, true, -3), find_end_point(f, -0.1, max_val, true, -2), ""); - plot.add(f, find_end_point(f, 0.1, -max_val, true, -4), find_end_point(f, -0.1, max_val, true, -3), ""); - plot.plot("digamma", "digamma.svg", "z", "digamma(z)"); + f = boost::math::lgamma; + max_val = f(10); + plot.clear(); + plot.add(f, find_end_point(f, 0.1, max_val, false), 10, ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -1), find_end_point(f, -0.1, max_val, true), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -2), find_end_point(f, -0.1, max_val, true, -1), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -3), find_end_point(f, -0.1, max_val, true, -2), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -4), find_end_point(f, -0.1, max_val, true, -3), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -5), find_end_point(f, -0.1, max_val, true, -4), ""); + plot.plot("lgamma", "lgamma.svg", "z", "lgamma(z)"); - f = boost::math::erf; - plot.clear(); - plot.add(f, -3, 3, ""); - plot.plot("erf", "erf.svg", "z", "erf(z)"); - f = boost::math::erfc; - plot.clear(); - plot.add(f, -3, 3, ""); - plot.plot("erfc", "erfc.svg", "z", "erfc(z)"); + f = boost::math::digamma; + max_val = 10; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, -max_val, true), 10, ""); + plot.add(f, find_end_point(f, 0.1, -max_val, true, -1), find_end_point(f, -0.1, max_val, true), ""); + plot.add(f, find_end_point(f, 0.1, -max_val, true, -2), find_end_point(f, -0.1, max_val, true, -1), ""); + plot.add(f, find_end_point(f, 0.1, -max_val, true, -3), find_end_point(f, -0.1, max_val, true, -2), ""); + plot.add(f, find_end_point(f, 0.1, -max_val, true, -4), find_end_point(f, -0.1, max_val, true, -3), ""); + plot.plot("digamma", "digamma.svg", "z", "digamma(z)"); + - f = boost::math::erf_inv; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, -3, true, -1), find_end_point(f, -0.1, 3, true, 1), ""); - plot.plot("erf_inv", "erf_inv.svg", "z", "erf_inv(z)"); - f = boost::math::erfc_inv; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, 3, false), find_end_point(f, -0.1, -3, false, 2), ""); - plot.plot("erfc_inv", "erfc_inv.svg", "z", "erfc_inv(z)"); + f = boost::math::erf; + plot.clear(); + plot.add(f, -3, 3, "erf"); + plot.plot("erf", "erf.svg", "z", "erf(z)"); - f = boost::math::log1p; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, -10, true, -1), 10, ""); - plot.plot("log1p", "log1p.svg", "z", "log1p(z)"); + f = boost::math::erfc; + plot.clear(); + plot.add(f, -3, 3, "erfc"); + plot.plot("erfc", "erfc.svg", "z", "erfc(z)"); - f = boost::math::expm1; - plot.clear(); - plot.add(f, -4, 2, ""); - plot.plot("expm1", "expm1.svg", "z", "expm1(z)"); + f = boost::math::erf_inv; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, -3, true, -1), find_end_point(f, -0.1, 3, true, 1), ""); + plot.plot("erf_inv", "erf_inv.svg", "z", "erf_inv(z)"); + f = boost::math::erfc_inv; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, 3, false), find_end_point(f, -0.1, -3, false, 2), ""); + plot.plot("erfc_inv", "erfc_inv.svg", "z", "erfc_inv(z)"); - f = boost::math::cbrt; - plot.clear(); - plot.add(f, -10, 10, ""); - plot.plot("cbrt", "cbrt.svg", "z", "cbrt(z)"); + f = boost::math::log1p; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, -10, true, -1), 10, ""); + plot.plot("log1p", "log1p.svg", "z", "log1p(z)"); - f = sqrt1pm1; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, -10, true, -1), 5, ""); - plot.plot("sqrt1pm1", "sqrt1pm1.svg", "z", "sqrt1pm1(z)"); + f = boost::math::expm1; + plot.clear(); + plot.add(f, -4, 2, ""); + plot.plot("expm1", "expm1.svg", "z", "expm1(z)"); - f2 = boost::math::powm1; - plot.clear(); - plot.add(boost::bind(f2, 0.0001, _1), find_end_point(boost::bind(f2, 0.0001, _1), -1, 10, false), 5, "a=0.0001"); - plot.add(boost::bind(f2, 0.001, _1), find_end_point(boost::bind(f2, 0.001, _1), -1, 10, false), 5, "a=0.001"); - plot.add(boost::bind(f2, 0.01, _1), find_end_point(boost::bind(f2, 0.01, _1), -1, 10, false), 5, "a=0.01"); - plot.add(boost::bind(f2, 0.1, _1), find_end_point(boost::bind(f2, 0.1, _1), -1, 10, false), 5, "a=0.1"); - plot.add(boost::bind(f2, 0.75, _1), -5, 5, "a=0.75"); - plot.add(boost::bind(f2, 1.25, _1), -5, 5, "a=1.25"); - plot.plot("powm1", "powm1.svg", "z", "powm1(a, z)"); + f = boost::math::cbrt; + plot.clear(); + plot.add(f, -10, 10, ""); + plot.plot("cbrt", "cbrt.svg", "z", "cbrt(z)"); - f = boost::math::sinc_pi; - plot.clear(); - plot.add(f, -10, 10, ""); - plot.plot("sinc_pi", "sinc_pi.svg", "z", "sinc_pi(z)"); + f = sqrt1pm1; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, -10, true, -1), 5, ""); + plot.plot("sqrt1pm1", "sqrt1pm1.svg", "z", "sqrt1pm1(z)"); - f = boost::math::sinhc_pi; - plot.clear(); - plot.add(f, -5, 5, ""); - plot.plot("sinhc_pi", "sinhc_pi.svg", "z", "sinhc_pi(z)"); + f2 = boost::math::powm1; + plot.clear(); + plot.add(boost::bind(f2, 0.0001, _1), find_end_point(boost::bind(f2, 0.0001, _1), -1, 10, false), 5, "a=0.0001"); + plot.add(boost::bind(f2, 0.001, _1), find_end_point(boost::bind(f2, 0.001, _1), -1, 10, false), 5, "a=0.001"); + plot.add(boost::bind(f2, 0.01, _1), find_end_point(boost::bind(f2, 0.01, _1), -1, 10, false), 5, "a=0.01"); + plot.add(boost::bind(f2, 0.1, _1), find_end_point(boost::bind(f2, 0.1, _1), -1, 10, false), 5, "a=0.1"); + plot.add(boost::bind(f2, 0.75, _1), -5, 5, "a=0.75"); + plot.add(boost::bind(f2, 1.25, _1), -5, 5, "a=1.25"); + plot.plot("powm1", "powm1.svg", "z", "powm1(a, z)"); - f = boost::math::acosh; - plot.clear(); - plot.add(f, 1, 10, ""); - plot.plot("acosh", "acosh.svg", "z", "acosh(z)"); + f = boost::math::sinc_pi; + plot.clear(); + plot.add(f, -10, 10, ""); + plot.plot("sinc_pi", "sinc_pi.svg", "z", "sinc_pi(z)"); - f = boost::math::asinh; - plot.clear(); - plot.add(f, -10, 10, ""); - plot.plot("asinh", "asinh.svg", "z", "asinh(z)"); + f = boost::math::sinhc_pi; + plot.clear(); + plot.add(f, -5, 5, ""); + plot.plot("sinhc_pi", "sinhc_pi.svg", "z", "sinhc_pi(z)"); - f = boost::math::atanh; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, -5, true, -1), find_end_point(f, -0.1, 5, true, 1), ""); - plot.plot("atanh", "atanh.svg", "z", "atanh(z)"); + f = boost::math::acosh; + plot.clear(); + plot.add(f, 1, 10, "acosh"); + plot.plot("acosh", "acosh.svg", "z", "acosh(z)"); - f2 = boost::math::tgamma_delta_ratio; - plot.clear(); - plot.add(boost::bind(f2, _1, -0.5), 1, 40, "delta = -0.5"); - plot.add(boost::bind(f2, _1, -0.2), 1, 40, "delta = -0.2"); - plot.add(boost::bind(f2, _1, -0.1), 1, 40, "delta = -0.1"); - plot.add(boost::bind(f2, _1, 0.1), 1, 40, "delta = 0.1"); - plot.add(boost::bind(f2, _1, 0.2), 1, 40, "delta = 0.2"); - plot.add(boost::bind(f2, _1, 0.5), 1, 40, "delta = 0.5"); - plot.add(boost::bind(f2, _1, 1.0), 1, 40, "delta = 1.0"); - plot.plot("tgamma_delta_ratio", "tgamma_delta_ratio.svg", "z", "tgamma_delta_ratio(delta, z)"); + f = boost::math::asinh; + plot.clear(); + plot.add(f, -10, 10, ""); + plot.plot("asinh", "asinh.svg", "z", "asinh(z)"); - f2 = boost::math::gamma_p; - plot.clear(); - plot.add(boost::bind(f2, 0.5, _1), 0, 20, "a = 0.5"); - plot.add(boost::bind(f2, 1.0, _1), 0, 20, "a = 1.0"); - plot.add(boost::bind(f2, 5.0, _1), 0, 20, "a = 5.0"); - plot.add(boost::bind(f2, 10.0, _1), 0, 20, "a = 10.0"); - plot.plot("gamma_p", "gamma_p.svg", "z", "gamma_p(a, z)"); + f = boost::math::atanh; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, -5, true, -1), find_end_point(f, -0.1, 5, true, 1), ""); + plot.plot("atanh", "atanh.svg", "z", "atanh(z)"); - f2 = boost::math::gamma_q; - plot.clear(); - plot.add(boost::bind(f2, 0.5, _1), 0, 20, "a = 0.5"); - plot.add(boost::bind(f2, 1.0, _1), 0, 20, "a = 1.0"); - plot.add(boost::bind(f2, 5.0, _1), 0, 20, "a = 5.0"); - plot.add(boost::bind(f2, 10.0, _1), 0, 20, "a = 10.0"); - plot.plot("gamma_q", "gamma_q.svg", "z", "gamma_q(a, z)"); + f2 = boost::math::tgamma_delta_ratio; + plot.clear(); + plot.add(boost::bind(f2, _1, -0.5), 1, 40, "delta = -0.5"); + plot.add(boost::bind(f2, _1, -0.2), 1, 40, "delta = -0.2"); + plot.add(boost::bind(f2, _1, -0.1), 1, 40, "delta = -0.1"); + plot.add(boost::bind(f2, _1, 0.1), 1, 40, "delta = 0.1"); + plot.add(boost::bind(f2, _1, 0.2), 1, 40, "delta = 0.2"); + plot.add(boost::bind(f2, _1, 0.5), 1, 40, "delta = 0.5"); + plot.add(boost::bind(f2, _1, 1.0), 1, 40, "delta = 1.0"); + plot.plot("tgamma_delta_ratio", "tgamma_delta_ratio.svg", "z", "tgamma_delta_ratio(delta, z)"); - f2 = lbeta; - plot.clear(); - plot.add(boost::bind(f2, 0.5, _1), 0.00001, 5, "a = 0.5"); - plot.add(boost::bind(f2, 1.0, _1), 0.00001, 5, "a = 1.0"); - plot.add(boost::bind(f2, 5.0, _1), 0.00001, 5, "a = 5.0"); - plot.add(boost::bind(f2, 10.0, _1), 0.00001, 5, "a = 10.0"); - plot.plot("beta", "beta.svg", "z", "log(beta(a, z))"); + f2 = boost::math::gamma_p; + plot.clear(); + plot.add(boost::bind(f2, 0.5, _1), 0, 20, "a = 0.5"); + plot.add(boost::bind(f2, 1.0, _1), 0, 20, "a = 1.0"); + plot.add(boost::bind(f2, 5.0, _1), 0, 20, "a = 5.0"); + plot.add(boost::bind(f2, 10.0, _1), 0, 20, "a = 10.0"); + plot.plot("gamma_p", "gamma_p.svg", "z", "gamma_p(a, z)"); - f = boost::math::expint; - max_val = f(4); - plot.clear(); - plot.add(f, find_end_point(f, 0.1, -max_val, true), 4, ""); - plot.add(f, -3, find_end_point(f, -0.1, -max_val, false), ""); - plot.plot("Exponential Integral Ei", "expint_i.svg", "z", "expint(z)"); + f2 = boost::math::gamma_q; + plot.clear(); + plot.add(boost::bind(f2, 0.5, _1), 0, 20, "a = 0.5"); + plot.add(boost::bind(f2, 1.0, _1), 0, 20, "a = 1.0"); + plot.add(boost::bind(f2, 5.0, _1), 0, 20, "a = 5.0"); + plot.add(boost::bind(f2, 10.0, _1), 0, 20, "a = 10.0"); + plot.plot("gamma_q", "gamma_q.svg", "z", "gamma_q(a, z)"); - f2u = boost::math::expint; - max_val = 1; - plot.clear(); - plot.add(boost::bind(f2u, 1, _1), find_end_point(boost::bind(f2u, 1, _1), 0.1, max_val, false), 2, "n = 1 "); - plot.add(boost::bind(f2u, 2, _1), find_end_point(boost::bind(f2u, 2, _1), 0.1, max_val, false), 2, "n = 2 "); - plot.add(boost::bind(f2u, 3, _1), 0, 2, "n = 3 "); - plot.add(boost::bind(f2u, 4, _1), 0, 2, "n = 4 "); - plot.plot("Exponential Integral En", "expint2.svg", "z", "expint(n, z)"); + f2 = lbeta; + plot.clear(); + plot.add(boost::bind(f2, 0.5, _1), 0.00001, 5, "a = 0.5"); + plot.add(boost::bind(f2, 1.0, _1), 0.00001, 5, "a = 1.0"); + plot.add(boost::bind(f2, 5.0, _1), 0.00001, 5, "a = 5.0"); + plot.add(boost::bind(f2, 10.0, _1), 0.00001, 5, "a = 10.0"); + plot.plot("beta", "beta.svg", "z", "log(beta(a, z))"); - f3 = boost::math::ibeta; - plot.clear(); - plot.add(boost::bind(f3, 9, 1, _1), 0, 1, "a = 9, b = 1"); - plot.add(boost::bind(f3, 7, 2, _1), 0, 1, "a = 7, b = 2"); - plot.add(boost::bind(f3, 5, 5, _1), 0, 1, "a = 5, b = 5"); - plot.add(boost::bind(f3, 2, 7, _1), 0, 1, "a = 2, b = 7"); - plot.add(boost::bind(f3, 1, 9, _1), 0, 1, "a = 1, b = 9"); - plot.plot("ibeta", "ibeta.svg", "z", "ibeta(a, b, z)"); + f = boost::math::expint; + max_val = f(4); + plot.clear(); + plot.add(f, find_end_point(f, 0.1, -max_val, true), 4, ""); + plot.add(f, -3, find_end_point(f, -0.1, -max_val, false), ""); + plot.plot("Exponential Integral Ei", "expint_i.svg", "z", "expint(z)"); - f2i = boost::math::legendre_p; - plot.clear(); - plot.add(boost::bind(f2i, 1, _1), -1, 1, "l = 1"); - plot.add(boost::bind(f2i, 2, _1), -1, 1, "l = 2"); - plot.add(boost::bind(f2i, 3, _1), -1, 1, "l = 3"); - plot.add(boost::bind(f2i, 4, _1), -1, 1, "l = 4"); - plot.add(boost::bind(f2i, 5, _1), -1, 1, "l = 5"); - plot.plot("Legendre Polynomials", "legendre_p.svg", "x", "legendre_p(l, x)"); + f2u = boost::math::expint; + max_val = 1; + plot.clear(); + plot.add(boost::bind(f2u, 1, _1), find_end_point(boost::bind(f2u, 1, _1), 0.1, max_val, false), 2, "n = 1 "); + plot.add(boost::bind(f2u, 2, _1), find_end_point(boost::bind(f2u, 2, _1), 0.1, max_val, false), 2, "n = 2 "); + plot.add(boost::bind(f2u, 3, _1), 0, 2, "n = 3 "); + plot.add(boost::bind(f2u, 4, _1), 0, 2, "n = 4 "); + plot.plot("Exponential Integral En", "expint2.svg", "z", "expint(n, z)"); - f2u = boost::math::legendre_q; - plot.clear(); - plot.add(boost::bind(f2u, 1, _1), -0.95, 0.95, "l = 1"); - plot.add(boost::bind(f2u, 2, _1), -0.95, 0.95, "l = 2"); - plot.add(boost::bind(f2u, 3, _1), -0.95, 0.95, "l = 3"); - plot.add(boost::bind(f2u, 4, _1), -0.95, 0.95, "l = 4"); - plot.add(boost::bind(f2u, 5, _1), -0.95, 0.95, "l = 5"); - plot.plot("Legendre Polynomials of the Second Kind", "legendre_q.svg", "x", "legendre_q(l, x)"); + f3 = boost::math::ibeta; + plot.clear(); + plot.add(boost::bind(f3, 9, 1, _1), 0, 1, "a = 9, b = 1"); + plot.add(boost::bind(f3, 7, 2, _1), 0, 1, "a = 7, b = 2"); + plot.add(boost::bind(f3, 5, 5, _1), 0, 1, "a = 5, b = 5"); + plot.add(boost::bind(f3, 2, 7, _1), 0, 1, "a = 2, b = 7"); + plot.add(boost::bind(f3, 1, 9, _1), 0, 1, "a = 1, b = 9"); + plot.plot("ibeta", "ibeta.svg", "z", "ibeta(a, b, z)"); - f2u = boost::math::laguerre; - plot.clear(); - plot.add(boost::bind(f2u, 0, _1), -5, 10, "n = 0"); - plot.add(boost::bind(f2u, 1, _1), -5, 10, "n = 1"); - plot.add(boost::bind(f2u, 2, _1), - find_end_point(boost::bind(f2u, 2, _1), -2, 20, false), - find_end_point(boost::bind(f2u, 2, _1), 4, 20, true), - "n = 2"); - plot.add(boost::bind(f2u, 3, _1), - find_end_point(boost::bind(f2u, 3, _1), -2, 20, false), - find_end_point(boost::bind(f2u, 3, _1), 1, 20, false, 8), - "n = 3"); - plot.add(boost::bind(f2u, 4, _1), - find_end_point(boost::bind(f2u, 4, _1), -2, 20, false), - find_end_point(boost::bind(f2u, 4, _1), 1, 20, true, 8), - "n = 4"); - plot.add(boost::bind(f2u, 5, _1), - find_end_point(boost::bind(f2u, 5, _1), -2, 20, false), - find_end_point(boost::bind(f2u, 5, _1), 1, 20, true, 8), - "n = 5"); - plot.plot("Laguerre Polynomials", "laguerre.svg", "x", "laguerre(n, x)"); + f2i = boost::math::legendre_p; + plot.clear(); + plot.add(boost::bind(f2i, 1, _1), -1, 1, "l = 1"); + plot.add(boost::bind(f2i, 2, _1), -1, 1, "l = 2"); + plot.add(boost::bind(f2i, 3, _1), -1, 1, "l = 3"); + plot.add(boost::bind(f2i, 4, _1), -1, 1, "l = 4"); + plot.add(boost::bind(f2i, 5, _1), -1, 1, "l = 5"); + plot.plot("Legendre Polynomials", "legendre_p.svg", "x", "legendre_p(l, x)"); - f2u = boost::math::hermite; - plot.clear(); - plot.add(boost::bind(f2u, 0, _1), -1.8, 1.8, "n = 0"); - plot.add(boost::bind(f2u, 1, _1), -1.8, 1.8, "n = 1"); - plot.add(boost::bind(f2u, 2, _1), -1.8, 1.8, "n = 2"); - plot.add(boost::bind(f2u, 3, _1), -1.8, 1.8, "n = 3"); - plot.add(boost::bind(f2u, 4, _1), -1.8, 1.8, "n = 4"); - plot.plot("Hermite Polynomials", "hermite.svg", "x", "hermite(n, x)"); + f2u = boost::math::legendre_q; + plot.clear(); + plot.add(boost::bind(f2u, 1, _1), -0.95, 0.95, "l = 1"); + plot.add(boost::bind(f2u, 2, _1), -0.95, 0.95, "l = 2"); + plot.add(boost::bind(f2u, 3, _1), -0.95, 0.95, "l = 3"); + plot.add(boost::bind(f2u, 4, _1), -0.95, 0.95, "l = 4"); + plot.add(boost::bind(f2u, 5, _1), -0.95, 0.95, "l = 5"); + plot.plot("Legendre Polynomials of the Second Kind", "legendre_q.svg", "x", "legendre_q(l, x)"); - f2 = boost::math::cyl_bessel_j; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -20, 20, "v = 0"); - plot.add(boost::bind(f2, 1, _1), -20, 20, "v = 1"); - plot.add(boost::bind(f2, 2, _1), -20, 20, "v = 2"); - plot.add(boost::bind(f2, 3, _1), -20, 20, "v = 3"); - plot.add(boost::bind(f2, 4, _1), -20, 20, "v = 4"); - plot.plot("Bessel J", "cyl_bessel_j.svg", "x", "cyl_bessel_j(v, x)"); + f2u = boost::math::laguerre; + plot.clear(); + plot.add(boost::bind(f2u, 0, _1), -5, 10, "n = 0"); + plot.add(boost::bind(f2u, 1, _1), -5, 10, "n = 1"); + plot.add(boost::bind(f2u, 2, _1), + find_end_point(boost::bind(f2u, 2, _1), -2, 20, false), + find_end_point(boost::bind(f2u, 2, _1), 4, 20, true), + "n = 2"); + plot.add(boost::bind(f2u, 3, _1), + find_end_point(boost::bind(f2u, 3, _1), -2, 20, false), + find_end_point(boost::bind(f2u, 3, _1), 1, 20, false, 8), + "n = 3"); + plot.add(boost::bind(f2u, 4, _1), + find_end_point(boost::bind(f2u, 4, _1), -2, 20, false), + find_end_point(boost::bind(f2u, 4, _1), 1, 20, true, 8), + "n = 4"); + plot.add(boost::bind(f2u, 5, _1), + find_end_point(boost::bind(f2u, 5, _1), -2, 20, false), + find_end_point(boost::bind(f2u, 5, _1), 1, 20, true, 8), + "n = 5"); + plot.plot("Laguerre Polynomials", "laguerre.svg", "x", "laguerre(n, x)"); - f2 = boost::math::cyl_neumann; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), find_end_point(boost::bind(f2, 0, _1), 0.1, -5, true), 20, "v = 0"); - plot.add(boost::bind(f2, 1, _1), find_end_point(boost::bind(f2, 1, _1), 0.1, -5, true), 20, "v = 1"); - plot.add(boost::bind(f2, 2, _1), find_end_point(boost::bind(f2, 2, _1), 0.1, -5, true), 20, "v = 2"); - plot.add(boost::bind(f2, 3, _1), find_end_point(boost::bind(f2, 3, _1), 0.1, -5, true), 20, "v = 3"); - plot.add(boost::bind(f2, 4, _1), find_end_point(boost::bind(f2, 4, _1), 0.1, -5, true), 20, "v = 4"); - plot.plot("Bessel Y", "cyl_neumann.svg", "x", "cyl_neumann(v, x)"); + f2u = boost::math::hermite; + plot.clear(); + plot.add(boost::bind(f2u, 0, _1), -1.8, 1.8, "n = 0"); + plot.add(boost::bind(f2u, 1, _1), -1.8, 1.8, "n = 1"); + plot.add(boost::bind(f2u, 2, _1), -1.8, 1.8, "n = 2"); + plot.add(boost::bind(f2u, 3, _1), -1.8, 1.8, "n = 3"); + plot.add(boost::bind(f2u, 4, _1), -1.8, 1.8, "n = 4"); + plot.plot("Hermite Polynomials", "hermite.svg", "x", "hermite(n, x)"); - f2 = boost::math::cyl_bessel_i; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), find_end_point(boost::bind(f2, 0, _1), -0.1, 20, false), find_end_point(boost::bind(f2, 0, _1), 0.1, 20, true), "v = 0"); - plot.add(boost::bind(f2, 2, _1), find_end_point(boost::bind(f2, 2, _1), -0.1, 20, false), find_end_point(boost::bind(f2, 2, _1), 0.1, 20, true), "v = 2"); - plot.add(boost::bind(f2, 5, _1), find_end_point(boost::bind(f2, 5, _1), -0.1, -20, true), find_end_point(boost::bind(f2, 5, _1), 0.1, 20, true), "v = 5"); - plot.add(boost::bind(f2, 7, _1), find_end_point(boost::bind(f2, 7, _1), -0.1, -20, true), find_end_point(boost::bind(f2, 7, _1), 0.1, 20, true), "v = 7"); - plot.add(boost::bind(f2, 10, _1), find_end_point(boost::bind(f2, 10, _1), -0.1, 20, false), find_end_point(boost::bind(f2, 10, _1), 0.1, 20, true), "v = 10"); - plot.plot("Bessel I", "cyl_bessel_i.svg", "x", "cyl_bessel_i(v, x)"); + f2 = boost::math::cyl_bessel_j; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -20, 20, "v = 0"); + plot.add(boost::bind(f2, 1, _1), -20, 20, "v = 1"); + plot.add(boost::bind(f2, 2, _1), -20, 20, "v = 2"); + plot.add(boost::bind(f2, 3, _1), -20, 20, "v = 3"); + plot.add(boost::bind(f2, 4, _1), -20, 20, "v = 4"); + plot.plot("Bessel J", "cyl_bessel_j.svg", "x", "cyl_bessel_j(v, x)"); - f2 = boost::math::cyl_bessel_k; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), find_end_point(boost::bind(f2, 0, _1), 0.1, 10, false), 10, "v = 0"); - plot.add(boost::bind(f2, 2, _1), find_end_point(boost::bind(f2, 2, _1), 0.1, 10, false), 10, "v = 2"); - plot.add(boost::bind(f2, 5, _1), find_end_point(boost::bind(f2, 5, _1), 0.1, 10, false), 10, "v = 5"); - plot.add(boost::bind(f2, 7, _1), find_end_point(boost::bind(f2, 7, _1), 0.1, 10, false), 10, "v = 7"); - plot.add(boost::bind(f2, 10, _1), find_end_point(boost::bind(f2, 10, _1), 0.1, 10, false), 10, "v = 10"); - plot.plot("Bessel K", "cyl_bessel_k.svg", "x", "cyl_bessel_k(v, x)"); + f2 = boost::math::cyl_neumann; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), find_end_point(boost::bind(f2, 0, _1), 0.1, -5, true), 20, "v = 0"); + plot.add(boost::bind(f2, 1, _1), find_end_point(boost::bind(f2, 1, _1), 0.1, -5, true), 20, "v = 1"); + plot.add(boost::bind(f2, 2, _1), find_end_point(boost::bind(f2, 2, _1), 0.1, -5, true), 20, "v = 2"); + plot.add(boost::bind(f2, 3, _1), find_end_point(boost::bind(f2, 3, _1), 0.1, -5, true), 20, "v = 3"); + plot.add(boost::bind(f2, 4, _1), find_end_point(boost::bind(f2, 4, _1), 0.1, -5, true), 20, "v = 4"); + plot.plot("Bessel Y", "cyl_neumann.svg", "x", "cyl_neumann(v, x)"); - f2u = boost::math::sph_bessel; - plot.clear(); - plot.add(boost::bind(f2u, 0, _1), 0, 20, "v = 0"); - plot.add(boost::bind(f2u, 2, _1), 0, 20, "v = 2"); - plot.add(boost::bind(f2u, 5, _1), 0, 20, "v = 5"); - plot.add(boost::bind(f2u, 7, _1), 0, 20, "v = 7"); - plot.add(boost::bind(f2u, 10, _1), 0, 20, "v = 10"); - plot.plot("Bessel j", "sph_bessel.svg", "x", "sph_bessel(v, x)"); + f2 = boost::math::cyl_bessel_i; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), find_end_point(boost::bind(f2, 0, _1), -0.1, 20, false), find_end_point(boost::bind(f2, 0, _1), 0.1, 20, true), "v = 0"); + plot.add(boost::bind(f2, 2, _1), find_end_point(boost::bind(f2, 2, _1), -0.1, 20, false), find_end_point(boost::bind(f2, 2, _1), 0.1, 20, true), "v = 2"); + plot.add(boost::bind(f2, 5, _1), find_end_point(boost::bind(f2, 5, _1), -0.1, -20, true), find_end_point(boost::bind(f2, 5, _1), 0.1, 20, true), "v = 5"); + plot.add(boost::bind(f2, 7, _1), find_end_point(boost::bind(f2, 7, _1), -0.1, -20, true), find_end_point(boost::bind(f2, 7, _1), 0.1, 20, true), "v = 7"); + plot.add(boost::bind(f2, 10, _1), find_end_point(boost::bind(f2, 10, _1), -0.1, 20, false), find_end_point(boost::bind(f2, 10, _1), 0.1, 20, true), "v = 10"); + plot.plot("Bessel I", "cyl_bessel_i.svg", "x", "cyl_bessel_i(v, x)"); - f2u = boost::math::sph_neumann; - plot.clear(); - plot.add(boost::bind(f2u, 0, _1), find_end_point(boost::bind(f2u, 0, _1), 0.1, -5, true), 20, "v = 0"); - plot.add(boost::bind(f2u, 2, _1), find_end_point(boost::bind(f2u, 2, _1), 0.1, -5, true), 20, "v = 2"); - plot.add(boost::bind(f2u, 5, _1), find_end_point(boost::bind(f2u, 5, _1), 0.1, -5, true), 20, "v = 5"); - plot.add(boost::bind(f2u, 7, _1), find_end_point(boost::bind(f2u, 7, _1), 0.1, -5, true), 20, "v = 7"); - plot.add(boost::bind(f2u, 10, _1), find_end_point(boost::bind(f2u, 10, _1), 0.1, -5, true), 20, "v = 10"); - plot.plot("Bessel y", "sph_neumann.svg", "x", "sph_neumann(v, x)"); + f2 = boost::math::cyl_bessel_k; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), find_end_point(boost::bind(f2, 0, _1), 0.1, 10, false), 10, "v = 0"); + plot.add(boost::bind(f2, 2, _1), find_end_point(boost::bind(f2, 2, _1), 0.1, 10, false), 10, "v = 2"); + plot.add(boost::bind(f2, 5, _1), find_end_point(boost::bind(f2, 5, _1), 0.1, 10, false), 10, "v = 5"); + plot.add(boost::bind(f2, 7, _1), find_end_point(boost::bind(f2, 7, _1), 0.1, 10, false), 10, "v = 7"); + plot.add(boost::bind(f2, 10, _1), find_end_point(boost::bind(f2, 10, _1), 0.1, 10, false), 10, "v = 10"); + plot.plot("Bessel K", "cyl_bessel_k.svg", "x", "cyl_bessel_k(v, x)"); - f4 = boost::math::ellint_rj; - plot.clear(); - plot.add(boost::bind(f4, _1, _1, _1, _1), find_end_point(boost::bind(f4, _1, _1, _1, _1), 0.1, 10, false), 4, "RJ"); - f3 = boost::math::ellint_rf; - plot.add(boost::bind(f3, _1, _1, _1), find_end_point(boost::bind(f3, _1, _1, _1), 0.1, 10, false), 4, "RF"); - plot.plot("Elliptic Integrals", "ellint_carlson.svg", "x", ""); + f2u = boost::math::sph_bessel; + plot.clear(); + plot.add(boost::bind(f2u, 0, _1), 0, 20, "v = 0"); + plot.add(boost::bind(f2u, 2, _1), 0, 20, "v = 2"); + plot.add(boost::bind(f2u, 5, _1), 0, 20, "v = 5"); + plot.add(boost::bind(f2u, 7, _1), 0, 20, "v = 7"); + plot.add(boost::bind(f2u, 10, _1), 0, 20, "v = 10"); + plot.plot("Bessel j", "sph_bessel.svg", "x", "sph_bessel(v, x)"); - f2 = boost::math::ellint_1; - plot.clear(); - plot.add(boost::bind(f2, _1, 0.5), -0.9, 0.9, "φ=0.5"); - plot.add(boost::bind(f2, _1, 0.75), -0.9, 0.9, "φ=0.75"); - plot.add(boost::bind(f2, _1, 1.25), -0.9, 0.9, "φ=1.25"); - plot.add(boost::bind(f2, _1, boost::math::constants::pi() / 2), -0.9, 0.9, "φ=π/2"); - plot.plot("Elliptic Of the First Kind", "ellint_1.svg", "k", "ellint_1(k, phi)"); + f2u = boost::math::sph_neumann; + plot.clear(); + plot.add(boost::bind(f2u, 0, _1), find_end_point(boost::bind(f2u, 0, _1), 0.1, -5, true), 20, "v = 0"); + plot.add(boost::bind(f2u, 2, _1), find_end_point(boost::bind(f2u, 2, _1), 0.1, -5, true), 20, "v = 2"); + plot.add(boost::bind(f2u, 5, _1), find_end_point(boost::bind(f2u, 5, _1), 0.1, -5, true), 20, "v = 5"); + plot.add(boost::bind(f2u, 7, _1), find_end_point(boost::bind(f2u, 7, _1), 0.1, -5, true), 20, "v = 7"); + plot.add(boost::bind(f2u, 10, _1), find_end_point(boost::bind(f2u, 10, _1), 0.1, -5, true), 20, "v = 10"); + plot.plot("Bessel y", "sph_neumann.svg", "x", "sph_neumann(v, x)"); - f2 = boost::math::ellint_2; - plot.clear(); - plot.add(boost::bind(f2, _1, 0.5), -1, 1, "φ=0.5"); - plot.add(boost::bind(f2, _1, 0.75), -1, 1, "φ=0.75"); - plot.add(boost::bind(f2, _1, 1.25), -1, 1, "φ=1.25"); - plot.add(boost::bind(f2, _1, boost::math::constants::pi() / 2), -1, 1, "φ=π/2"); - plot.plot("Elliptic Of the Second Kind", "ellint_2.svg", "k", "ellint_2(k, phi)"); + f4 = boost::math::ellint_rj; + plot.clear(); + plot.add(boost::bind(f4, _1, _1, _1, _1), find_end_point(boost::bind(f4, _1, _1, _1, _1), 0.1, 10, false), 4, "RJ"); + f3 = boost::math::ellint_rf; + plot.add(boost::bind(f3, _1, _1, _1), find_end_point(boost::bind(f3, _1, _1, _1), 0.1, 10, false), 4, "RF"); + plot.plot("Elliptic Integrals", "ellint_carlson.svg", "x", ""); - f3 = boost::math::ellint_3; - plot.clear(); - plot.add(boost::bind(f3, _1, 0, 1.25), -1, 1, "n=0 φ=1.25"); - plot.add(boost::bind(f3, _1, 0.5, 1.25), -1, 1, "n=0.5 φ=1.25"); - plot.add(boost::bind(f3, _1, 0.25, boost::math::constants::pi() / 2), - find_end_point( - boost::bind(f3, _1, 0.25, boost::math::constants::pi() / 2), - 0.5, 4, false, -1), - find_end_point( - boost::bind(f3, _1, 0.25, boost::math::constants::pi() / 2), - -0.5, 4, true, 1), "n=0.25 φ=π/2"); - plot.add(boost::bind(f3, _1, 0.75, boost::math::constants::pi() / 2), - find_end_point( - boost::bind(f3, _1, 0.75, boost::math::constants::pi() / 2), - 0.5, 4, false, -1), - find_end_point( - boost::bind(f3, _1, 0.75, boost::math::constants::pi() / 2), - -0.5, 4, true, 1), "n=0.75 φ=π/2"); - plot.plot("Elliptic Of the Third Kind", "ellint_3.svg", "k", "ellint_3(k, n, phi)"); + f2 = boost::math::ellint_1; + plot.clear(); + plot.add(boost::bind(f2, _1, 0.5), -0.9, 0.9, "φ=0.5"); + plot.add(boost::bind(f2, _1, 0.75), -0.9, 0.9, "φ=0.75"); + plot.add(boost::bind(f2, _1, 1.25), -0.9, 0.9, "φ=1.25"); + plot.add(boost::bind(f2, _1, boost::math::constants::pi() / 2), -0.9, 0.9, "φ=π/2"); + plot.plot("Elliptic Of the First Kind", "ellint_1.svg", "k", "ellint_1(k, phi)"); - f2 = boost::math::jacobi_sn; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); - plot.plot("Jacobi Elliptic sn", "jacobi_sn.svg", "k", "jacobi_sn(k, u)"); + f2 = boost::math::ellint_2; + plot.clear(); + plot.add(boost::bind(f2, _1, 0.5), -1, 1, "φ=0.5"); + plot.add(boost::bind(f2, _1, 0.75), -1, 1, "φ=0.75"); + plot.add(boost::bind(f2, _1, 1.25), -1, 1, "φ=1.25"); + plot.add(boost::bind(f2, _1, boost::math::constants::pi() / 2), -1, 1, "φ=π/2"); + plot.plot("Elliptic Of the Second Kind", "ellint_2.svg", "k", "ellint_2(k, phi)"); - f2 = boost::math::jacobi_cn; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); - plot.plot("Jacobi Elliptic cn", "jacobi_cn.svg", "k", "jacobi_cn(k, u)"); + f3 = boost::math::ellint_3; + plot.clear(); + plot.add(boost::bind(f3, _1, 0, 1.25), -1, 1, "n=0 φ=1.25"); + plot.add(boost::bind(f3, _1, 0.5, 1.25), -1, 1, "n=0.5 φ=1.25"); + plot.add(boost::bind(f3, _1, 0.25, boost::math::constants::pi() / 2), + find_end_point( + boost::bind(f3, _1, 0.25, boost::math::constants::pi() / 2), + 0.5, 4, false, -1), + find_end_point( + boost::bind(f3, _1, 0.25, boost::math::constants::pi() / 2), + -0.5, 4, true, 1), "n=0.25 φ=π/2"); + plot.add(boost::bind(f3, _1, 0.75, boost::math::constants::pi() / 2), + find_end_point( + boost::bind(f3, _1, 0.75, boost::math::constants::pi() / 2), + 0.5, 4, false, -1), + find_end_point( + boost::bind(f3, _1, 0.75, boost::math::constants::pi() / 2), + -0.5, 4, true, 1), "n=0.75 φ=π/2"); + plot.plot("Elliptic Of the Third Kind", "ellint_3.svg", "k", "ellint_3(k, n, phi)"); - f2 = boost::math::jacobi_dn; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); - plot.plot("Jacobi Elliptic dn", "jacobi_dn.svg", "k", "jacobi_dn(k, u)"); + f2 = boost::math::jacobi_sn; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); + plot.plot("Jacobi Elliptic sn", "jacobi_sn.svg", "k", "jacobi_sn(k, u)"); - f2 = boost::math::jacobi_cd; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); - plot.plot("Jacobi Elliptic cd", "jacobi_cd.svg", "k", "jacobi_cd(k, u)"); + f2 = boost::math::jacobi_cn; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); + plot.plot("Jacobi Elliptic cn", "jacobi_cn.svg", "k", "jacobi_cn(k, u)"); - f2 = boost::math::jacobi_cs; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), 0.1, 3, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), 0.1, 3, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), 0.1, 3, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), 0.1, 3, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), 0.1, 3, "k=1"); - plot.plot("Jacobi Elliptic cs", "jacobi_cs.svg", "k", "jacobi_cs(k, u)"); + f2 = boost::math::jacobi_dn; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); + plot.plot("Jacobi Elliptic dn", "jacobi_dn.svg", "k", "jacobi_dn(k, u)"); - f2 = boost::math::jacobi_dc; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); - plot.plot("Jacobi Elliptic dc", "jacobi_dc.svg", "k", "jacobi_dc(k, u)"); + f2 = boost::math::jacobi_cd; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); + plot.plot("Jacobi Elliptic cd", "jacobi_cd.svg", "k", "jacobi_cd(k, u)"); - f2 = boost::math::jacobi_ds; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), 0.1, 3, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), 0.1, 3, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), 0.1, 3, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), 0.1, 3, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), 0.1, 3, "k=1"); - plot.plot("Jacobi Elliptic ds", "jacobi_ds.svg", "k", "jacobi_ds(k, u)"); + f2 = boost::math::jacobi_cs; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), 0.1, 3, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), 0.1, 3, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), 0.1, 3, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), 0.1, 3, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), 0.1, 3, "k=1"); + plot.plot("Jacobi Elliptic cs", "jacobi_cs.svg", "k", "jacobi_cs(k, u)"); - f2 = boost::math::jacobi_nc; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -5, 5, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -5, 5, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -5, 5, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -5, 5, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -5, 5, "k=1"); - plot.plot("Jacobi Elliptic nc", "jacobi_nc.svg", "k", "jacobi_nc(k, u)"); + f2 = boost::math::jacobi_dc; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -10, 10, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -10, 10, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -10, 10, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -10, 10, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -10, 10, "k=1"); + plot.plot("Jacobi Elliptic dc", "jacobi_dc.svg", "k", "jacobi_dc(k, u)"); - f2 = boost::math::jacobi_ns; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), 0.1, 4, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), 0.1, 4, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), 0.1, 4, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), 0.1, 4, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), 0.1, 4, "k=1"); - plot.plot("Jacobi Elliptic ns", "jacobi_ns.svg", "k", "jacobi_ns(k, u)"); + f2 = boost::math::jacobi_ds; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), 0.1, 3, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), 0.1, 3, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), 0.1, 3, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), 0.1, 3, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), 0.1, 3, "k=1"); + plot.plot("Jacobi Elliptic ds", "jacobi_ds.svg", "k", "jacobi_ds(k, u)"); - f2 = boost::math::jacobi_nd; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -2, 2, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -2, 2, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -2, 2, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -2, 2, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -2, 2, "k=1"); - plot.plot("Jacobi Elliptic nd", "jacobi_nd.svg", "k", "jacobi_nd(k, u)"); + f2 = boost::math::jacobi_nc; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -5, 5, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -5, 5, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -5, 5, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -5, 5, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -5, 5, "k=1"); + plot.plot("Jacobi Elliptic nc", "jacobi_nc.svg", "k", "jacobi_nc(k, u)"); - f2 = boost::math::jacobi_sc; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -5, 5, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -5, 5, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -5, 5, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -5, 5, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -5, 5, "k=1"); - plot.plot("Jacobi Elliptic sc", "jacobi_sc.svg", "k", "jacobi_sc(k, u)"); + f2 = boost::math::jacobi_ns; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), 0.1, 4, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), 0.1, 4, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), 0.1, 4, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), 0.1, 4, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), 0.1, 4, "k=1"); + plot.plot("Jacobi Elliptic ns", "jacobi_ns.svg", "k", "jacobi_ns(k, u)"); - f2 = boost::math::jacobi_sd; - plot.clear(); - plot.add(boost::bind(f2, 0, _1), -2.5, 2.5, "k=0"); - plot.add(boost::bind(f2, 0.5, _1), -2.5, 2.5, "k=0.5"); - plot.add(boost::bind(f2, 0.75, _1), -2.5, 2.5, "k=0.75"); - plot.add(boost::bind(f2, 0.95, _1), -2.5, 2.5, "k=0.95"); - plot.add(boost::bind(f2, 1, _1), -2.5, 2.5, "k=1"); - plot.plot("Jacobi Elliptic sd", "jacobi_sd.svg", "k", "jacobi_sd(k, u)"); + f2 = boost::math::jacobi_nd; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -2, 2, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -2, 2, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -2, 2, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -2, 2, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -2, 2, "k=1"); + plot.plot("Jacobi Elliptic nd", "jacobi_nd.svg", "k", "jacobi_nd(k, u)"); - f = boost::math::airy_ai; - plot.clear(); - plot.add(f, -20, 20, ""); - plot.plot("Ai", "airy_ai.svg", "z", "airy_ai(z)"); + f2 = boost::math::jacobi_sc; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -5, 5, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -5, 5, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -5, 5, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -5, 5, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -5, 5, "k=1"); + plot.plot("Jacobi Elliptic sc", "jacobi_sc.svg", "k", "jacobi_sc(k, u)"); - f = boost::math::airy_bi; - plot.clear(); - plot.add(f, -20, 3, ""); - plot.plot("Bi", "airy_bi.svg", "z", "airy_bi(z)"); + f2 = boost::math::jacobi_sd; + plot.clear(); + plot.add(boost::bind(f2, 0, _1), -2.5, 2.5, "k=0"); + plot.add(boost::bind(f2, 0.5, _1), -2.5, 2.5, "k=0.5"); + plot.add(boost::bind(f2, 0.75, _1), -2.5, 2.5, "k=0.75"); + plot.add(boost::bind(f2, 0.95, _1), -2.5, 2.5, "k=0.95"); + plot.add(boost::bind(f2, 1, _1), -2.5, 2.5, "k=1"); + plot.plot("Jacobi Elliptic sd", "jacobi_sd.svg", "k", "jacobi_sd(k, u)"); - f = boost::math::airy_ai_prime; - plot.clear(); - plot.add(f, -20, 20, ""); - plot.plot("Ai'", "airy_aip.svg", "z", "airy_ai_prime(z)"); + f = boost::math::airy_ai; + plot.clear(); + plot.add(f, -20, 20, ""); + plot.plot("Ai", "airy_ai.svg", "z", "airy_ai(z)"); - f = boost::math::airy_bi_prime; - plot.clear(); - plot.add(f, -20, 3, ""); - plot.plot("Bi'", "airy_bip.svg", "z", "airy_bi_prime(z)"); + f = boost::math::airy_bi; + plot.clear(); + plot.add(f, -20, 3, ""); + plot.plot("Bi", "airy_bi.svg", "z", "airy_bi(z)"); - f = boost::math::trigamma; - max_val = 30; - plot.clear(); - plot.add(f, find_end_point(f, 0.1, max_val, false), 5, ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -1), find_end_point(f, -0.1, max_val, true), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -2), find_end_point(f, -0.1, max_val, true, -1), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -3), find_end_point(f, -0.1, max_val, true, -2), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -4), find_end_point(f, -0.1, max_val, true, -3), ""); - plot.add(f, find_end_point(f, 0.1, max_val, false, -5), find_end_point(f, -0.1, max_val, true, -4), ""); - plot.plot("Trigamma", "trigamma.svg", "x", "trigamma(x)"); + f = boost::math::airy_ai_prime; + plot.clear(); + plot.add(f, -20, 20, ""); + plot.plot("Ai'", "airy_aip.svg", "z", "airy_ai_prime(z)"); - f2i = boost::math::polygamma; - max_val = -50; - plot.clear(); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true), 5, ""); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -1), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true), ""); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -2), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -1), ""); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -3), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -2), ""); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -4), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -3), ""); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -5), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -4), ""); - plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -6), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -5), ""); - plot.plot("Polygamma", "polygamma2.svg", "x", "polygamma(2, x)"); + f = boost::math::airy_bi_prime; + plot.clear(); + plot.add(f, -20, 3, ""); + plot.plot("Bi'", "airy_bip.svg", "z", "airy_bi_prime(z)"); - max_val = 800; - plot.clear(); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false), 5, ""); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -1), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true), ""); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -2), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -1), ""); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -3), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -2), ""); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -4), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -3), ""); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -5), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -4), ""); - plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -6), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -5), ""); - plot.plot("Polygamma", "polygamma3.svg", "x", "polygamma(3, x)"); + f = boost::math::trigamma; + max_val = 30; + plot.clear(); + plot.add(f, find_end_point(f, 0.1, max_val, false), 5, ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -1), find_end_point(f, -0.1, max_val, true), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -2), find_end_point(f, -0.1, max_val, true, -1), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -3), find_end_point(f, -0.1, max_val, true, -2), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -4), find_end_point(f, -0.1, max_val, true, -3), ""); + plot.add(f, find_end_point(f, 0.1, max_val, false, -5), find_end_point(f, -0.1, max_val, true, -4), ""); + plot.plot("Trigamma", "trigamma.svg", "x", "trigamma(x)"); + + f2i = boost::math::polygamma; + max_val = -50; + plot.clear(); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true), 5, ""); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -1), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true), ""); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -2), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -1), ""); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -3), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -2), ""); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -4), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -3), ""); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -5), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -4), ""); + plot.add(boost::bind(f2i, 2, _1), find_end_point(boost::bind(f2i, 2, _1), 0.1, max_val, true, -6), find_end_point(boost::bind(f2i, 2, _1), -0.1, -max_val, true, -5), ""); + plot.plot("Polygamma", "polygamma2.svg", "x", "polygamma(2, x)"); + + max_val = 800; + plot.clear(); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false), 5, ""); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -1), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true), ""); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -2), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -1), ""); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -3), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -2), ""); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -4), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -3), ""); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -5), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -4), ""); + plot.add(boost::bind(f2i, 3, _1), find_end_point(boost::bind(f2i, 3, _1), 0.1, max_val, false, -6), find_end_point(boost::bind(f2i, 3, _1), -0.1, max_val, true, -5), ""); + plot.plot("Polygamma", "polygamma3.svg", "x", "polygamma(3, x)"); + + + } + catch (const std::exception& ex) + { + std::cout << ex.what() << std::endl; + } return 0; } diff --git a/doc/html/index.html b/doc/html/index.html index f7cb4076c..4f6cfd12d 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -122,7 +122,7 @@ This manual is also available in -

Last revised: August 20, 2018 at 18:25:34 GMT

+

Last revised: August 30, 2018 at 17:17:24 GMT


diff --git a/doc/html/indexes/s01.html b/doc/html/indexes/s01.html index 184d2bce1..99773cfbf 100644 --- a/doc/html/indexes/s01.html +++ b/doc/html/indexes/s01.html @@ -24,7 +24,7 @@

-Function Index

+Function Index

1 2 4 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

@@ -134,6 +134,10 @@
  • +

    al

    + +
  • +
  • area

  • +

    get_epsilon

    + +
  • +
  • get_from_string

  • @@ -1487,8 +1499,11 @@

    -Class Index

    +Class Index

    A B C D E F G H I L M N O P Q R S T U W

    diff --git a/doc/html/indexes/s03.html b/doc/html/indexes/s03.html index e1e2844d8..caeec333b 100644 --- a/doc/html/indexes/s03.html +++ b/doc/html/indexes/s03.html @@ -24,7 +24,7 @@

    -Typedef Index

    +Typedef Index

    A B C D E F G H I L N O P R S T U V W

    @@ -212,6 +212,10 @@

    lognormal

    +
  • +

    lookup_t

    + +
  • N diff --git a/doc/html/indexes/s04.html b/doc/html/indexes/s04.html index adb10b7e5..5a772e760 100644 --- a/doc/html/indexes/s04.html +++ b/doc/html/indexes/s04.html @@ -24,7 +24,7 @@

    -Macro Index

    +Macro Index

    B F

    @@ -298,6 +298,10 @@
  • +

    BOOST_MATH_TEST_VALUE

    + +
  • +
  • BOOST_MATH_UNDERFLOW_ERROR_POLICY

  • diff --git a/doc/html/indexes/s05.html b/doc/html/indexes/s05.html index f21ec4910..ec6060b24 100644 --- a/doc/html/indexes/s05.html +++ b/doc/html/indexes/s05.html @@ -23,7 +23,7 @@

    -Index

    +Index

    1 2 4 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

  • +

    al

    + +
  • +
  • Algorithm TOMS 748: Alefeld, Potra and Shi: Enclosing zeros of continuous functions

  • @@ -503,6 +509,7 @@ @@ -962,6 +970,10 @@
  • +

    BOOST_MATH_TEST_VALUE

    + +
  • +
  • BOOST_MATH_UNDERFLOW_ERROR_POLICY

  • @@ -1431,6 +1443,10 @@
  • +

    Calculation of the Type of the Result

    + +
  • +
  • called

  • @@ -1592,6 +1608,7 @@
  • n

  • operator

  • prime

  • +
  • quad

  • @@ -1914,6 +1931,7 @@
  • Hypergeometric Distribution

  • Implementation

  • Introduction

  • +
  • Lambert W function

  • Log Gamma

  • Mathematical Constants

  • Minimax Approximations and the Remez Algorithm

  • @@ -2527,6 +2545,7 @@
  • D

  • expression

  • J

  • +
  • k

  • R

  • @@ -2888,20 +2907,20 @@
  • Error rates for expint (Ei)

  • Error rates for expint (En)

  • Error rates for expm1

    - +
  • Error rates for gamma_p

    @@ -3002,7 +3021,7 @@
  • Error rates for log1p

    - +
  • Error rates for non central beta CDF

    @@ -3036,7 +3055,7 @@
  • Error rates for owens_t

    - +
  • Error rates for polygamma

    @@ -3087,7 +3106,7 @@
  • Error rates for zeta

    - +
  • Estimating Sample Sizes for a Binomial Distribution.

    @@ -3180,8 +3199,8 @@
  • C99 and C++ TR1 C-style Functions

  • C99 and TR1 C Functions Overview

  • Error Logs For Error Rate Tables

  • -
  • Error rates for expint (Ei)

  • -
  • Error rates for expint (En)

  • +
  • Error rates for expint (Ei)

  • +
  • Error rates for expint (En)

  • Exponential Integral Ei

  • Exponential Integral En

  • TR1 C Functions Quick Reference

  • @@ -3219,6 +3238,7 @@
  • C99 and TR1 C Functions Overview

  • C99 C Functions

  • expm1

  • +
  • small

  • @@ -3302,6 +3322,7 @@
  • Introduction

  • Inverse Chi-Squared Distribution Bayes Example

  • Jacobi Elliptic SN, CN and DN

  • +
  • Lambert W function

  • Locating Function Minima using Brent's algorithm

  • Mathematically Undefined Function Policies

  • Negative Binomial Sales Quota Example.

  • @@ -3566,6 +3587,10 @@
  • +

    float

    + +
  • +
  • Floating-Point Classification: Infinities and NaNs

  • +

    Floating-Point Representation Distance (ULP), and Finding Adjacent Floating-Point Values

    + +
  • +
  • float_advance

  • @@ -3745,6 +3774,7 @@
  • accuracy

  • Cephes

  • distribution

  • +
  • more

  • P

  • performance

  • @@ -3950,6 +3980,7 @@
  • BOOST_MATH_DISCRETE_QUANTILE_POLICY

  • BOOST_MATH_OVERFLOW_ERROR_POLICY

  • expression

  • +
  • more

  • normal

  • @@ -3966,6 +3997,10 @@
  • +

    get_epsilon

    + +
  • +
  • get_from_string

  • @@ -4023,8 +4058,8 @@
  • Error rates for ellint_rj

  • Error rates for erf

  • Error rates for erfc

  • -
  • Error rates for expint (Ei)

  • -
  • Error rates for expint (En)

  • +
  • Error rates for expint (Ei)

  • +
  • Error rates for expint (En)

  • Error rates for gamma_p

  • Error rates for gamma_q

  • Error rates for ibeta

  • @@ -4044,7 +4079,7 @@
  • Error rates for tgamma (incomplete)

  • Error rates for tgamma_lower

  • Error rates for trigamma

  • -
  • Error rates for zeta

  • +
  • Error rates for zeta

  • Exponential Integral Ei

  • Incomplete Beta Functions

  • Incomplete Gamma Functions

  • @@ -4186,6 +4221,7 @@
  • expression

  • hypergeometric

  • hypergeometric_distribution

  • +
  • k

  • Lanczos approximation

  • policy_type

  • value_type

  • @@ -4239,8 +4275,11 @@
    @@ -4462,7 +4502,10 @@
  • interval

    - +
  • Introduction

    @@ -4500,8 +4543,10 @@
  • @@ -4654,6 +4699,7 @@
  • accuracy

  • expression

  • jacobi_elliptic

  • +
  • k

  • @@ -4726,10 +4772,13 @@
    • -

      K

      +

      k

    • @@ -4817,6 +4866,49 @@
    • +

      Lambert W function

      + +
    • +
    • +

      lambert_w0

      + +
    • +
    • +

      lambert_w0_prime

      + +
    • +
    • +

      lambert_wm1

      + +
    • +
    • +

      lambert_wm1_prime

      + +
    • +
    • Lanczos approximation

      • accuracy

      • @@ -5017,6 +5109,13 @@
      • +

        ln

        + +
      • +
      • Locating Function Minima using Brent's algorithm

      • +

        required

        + +
      • +
      • Riemann Zeta Function

        - +

        This documentation aims to use of the following naming and formatting conventions. diff --git a/doc/html/math_toolkit/expint/expint_i.html b/doc/html/math_toolkit/expint/expint_i.html index 480fad603..baf71c47f 100644 --- a/doc/html/math_toolkit/expint/expint_i.html +++ b/doc/html/math_toolkit/expint/expint_i.html @@ -85,7 +85,7 @@ zero error.

        -

        Table 6.75. Error rates for expint (Ei)

        +

        Table 6.78. Error rates for expint (Ei)

        diff --git a/doc/html/math_toolkit/expint/expint_n.html b/doc/html/math_toolkit/expint/expint_n.html index 41e447012..41f945e2f 100644 --- a/doc/html/math_toolkit/expint/expint_n.html +++ b/doc/html/math_toolkit/expint/expint_n.html @@ -84,7 +84,7 @@ zero error.

        -

        Table 6.74. Error rates for expint (En)

        +

        Table 6.77. Error rates for expint (En)

        diff --git a/doc/html/math_toolkit/jacobi/jacobi_sn.html b/doc/html/math_toolkit/jacobi/jacobi_sn.html index 562dd547d..1af05798e 100644 --- a/doc/html/math_toolkit/jacobi/jacobi_sn.html +++ b/doc/html/math_toolkit/jacobi/jacobi_sn.html @@ -7,7 +7,7 @@ - +
        @@ -20,7 +20,7 @@

        -PrevUpHomeNext +PrevUpHomeNext

        @@ -77,7 +77,7 @@
        -PrevUpHomeNext +PrevUpHomeNext
        diff --git a/doc/html/math_toolkit/navigation.html b/doc/html/math_toolkit/navigation.html index bed81120d..941717454 100644 --- a/doc/html/math_toolkit/navigation.html +++ b/doc/html/math_toolkit/navigation.html @@ -27,7 +27,7 @@ Navigation

        - +

        Boost.Math documentation is provided in both HTML and PDF formats. diff --git a/doc/html/math_toolkit/next_float.html b/doc/html/math_toolkit/next_float.html index 0b98650b8..ab7debb17 100644 --- a/doc/html/math_toolkit/next_float.html +++ b/doc/html/math_toolkit/next_float.html @@ -68,6 +68,34 @@ for example XRC eXact Real in C.

        +

        + The accuracy of mathematical functions can be assessed and displayed in terms + of Unit in + the Last Place, often as a ulps plot or by binning the differences + as a histogram. Samples are evaluated using the implementation under test and + compared with 'known good' representation obtained using a more accurate method. + Other implementations, often using arbitrary precision arithmetic, for example + Wolfram Alpha are one source + of references values. The other method, used widely in Boost.Math special functions, + it to carry out the same algorithm, but using a higher precision type, typically + using Boost.Multiprecision types like cpp_bin_float_quad + for 128-bit (about 35 decimal digit precision), or cpp_bin_float_50 + (for 50 decimal digit precision). +

        +

        + When converted to a particular machine representation, say double, + say using a static_cast, the value + is the nearest representation possible for the double + type. This value cannot be 'wrong' by more than half a Unit + in the last place (ULP), and can be obtained using the Boost.Math function + ulp. (Unless the algorithm + is fundamentally flawed, something that should be revealed by 'sanity' checks + using some independent sources). +

        +

        + See some discussion and example plots by Cleve Moler of Mathworks ulps + plots reveal math-function accuracy. +

        diff --git a/doc/html/math_toolkit/next_float/ulp.html b/doc/html/math_toolkit/next_float/ulp.html index 54f831778..f326c7ae4 100644 --- a/doc/html/math_toolkit/next_float/ulp.html +++ b/doc/html/math_toolkit/next_float/ulp.html @@ -57,7 +57,7 @@ in the last place of x.

        - Corner cases are handled as followes: + Corner cases are handled as follows:

        • @@ -70,7 +70,7 @@ If the argument is zero then returns the smallest representable value: for example for type double this would be either std::numeric_limits<double>::min() or std::numeric_limits<double>::denorm_min() depending whether denormals are supported - (which have the values 2.2250738585072014e-308 + (which have the values 2.2250738585072014e-308 and 4.9406564584124654e-324 respectively).
        • diff --git a/doc/html/math_toolkit/owens_t.html b/doc/html/math_toolkit/owens_t.html index 60b7dc7b8..e058be522 100644 --- a/doc/html/math_toolkit/owens_t.html +++ b/doc/html/math_toolkit/owens_t.html @@ -120,7 +120,7 @@ Over the built-in types and range tested, errors are less than 10 * std::numeric_limits<RealType>::epsilon().

          -

          Table 6.83. Error rates for owens_t

          +

          Table 6.86. Error rates for owens_t

        diff --git a/doc/html/math_toolkit/pol_tutorial/changing_policy_defaults.html b/doc/html/math_toolkit/pol_tutorial/changing_policy_defaults.html index 5a948aa72..97b64b956 100644 --- a/doc/html/math_toolkit/pol_tutorial/changing_policy_defaults.html +++ b/doc/html/math_toolkit/pol_tutorial/changing_policy_defaults.html @@ -55,9 +55,9 @@

        #define BOOST_MATH_ASSERT_UNDEFINED_POLICY false
         #define BOOST_MATH_OVERFLOW_ERROR_POLICY errno_on_error
        -

        - may be ignored. -

        +
        • + may be ignored*. +

        The compiler command line shows:

        diff --git a/doc/html/math_toolkit/pol_tutorial/namespace_policies.html b/doc/html/math_toolkit/pol_tutorial/namespace_policies.html index 5d5204be7..c2a143eea 100644 --- a/doc/html/math_toolkit/pol_tutorial/namespace_policies.html +++ b/doc/html/math_toolkit/pol_tutorial/namespace_policies.html @@ -176,12 +176,12 @@ errno = 33 that now the macro BOOST_MATH_DECLARE_DISTRIBUTIONS accepts two parameters: the floating point type to use, and the policy type to apply. For example:

        -
        BOOST_MATH_DECLARE_DISTRIBUTIONS(double, mypolicy)
        +
        BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy)
         

        Results a set of typedefs being defined like this:

        -
        typedef boost::math::normal_distribution<double, mypolicy> normal;
        +
        typedef boost::math::normal_distribution<double, my_policy> normal;
         

        The name of each typedef is the same as the name of the distribution class diff --git a/doc/html/math_toolkit/pol_tutorial/policy_tut_defaults.html b/doc/html/math_toolkit/pol_tutorial/policy_tut_defaults.html index 92065a352..97c45a1cd 100644 --- a/doc/html/math_toolkit/pol_tutorial/policy_tut_defaults.html +++ b/doc/html/math_toolkit/pol_tutorial/policy_tut_defaults.html @@ -113,14 +113,31 @@ // // Define a policy that sets ::errno on overflow, and does // not promote double to long double internally: -// -typedef policy<domain_error<errno_on_error>, promote_double<false> > mypolicy; + +typedef policy +< + domain_error<errno_on_error>, + promote_double<false> +> my_policy;

        - then mypolicy defines a policy - where only the overflow error handling and double-promotion + then my_policy defines a + policy where only the overflow error handling and double-promotion policies differ from the defaults.

        +

        + We can also add a desired precision, for example, 9 bits or 3 decimal digits, + to an error-handling policy, usually to trade precision for speed: +

        +
        typedef policy<domain_error<errno_on_error>, digit2<9> > my_policy;
        +
        +

        + Or if you want to further modify an existing user policy, use normalise: +

        +
        using boost::math::policies::normalise;
        +
        +typedef normalise<my_policy, digits2<9>>::type my_policy_9; // errno on error, and limited precision.
        +
        diff --git a/doc/html/math_toolkit/pol_tutorial/policy_usage.html b/doc/html/math_toolkit/pol_tutorial/policy_usage.html index da0f254b3..47522df41 100644 --- a/doc/html/math_toolkit/pol_tutorial/policy_usage.html +++ b/doc/html/math_toolkit/pol_tutorial/policy_usage.html @@ -36,8 +36,8 @@ is the recommended method for setting installation-wide policies.
      • - By instantiating a distribution object with an explicit policy: this - is mainly reserved for ad hoc policy changes. + By instantiating a statistical distribution object with an explicit policy: + this is mainly reserved for ad hoc policy changes.
      • By passing a policy to a special function as an optional final argument: diff --git a/doc/html/math_toolkit/pol_tutorial/what_is_a_policy.html b/doc/html/math_toolkit/pol_tutorial/what_is_a_policy.html index 5820cbab5..91673c164 100644 --- a/doc/html/math_toolkit/pol_tutorial/what_is_a_policy.html +++ b/doc/html/math_toolkit/pol_tutorial/what_is_a_policy.html @@ -52,21 +52,34 @@
      • - Some of these policies could arguably be runtime variables, but then we couldn't - use compile-time dispatch internally to select the best evaluation method - for the given policies. + Some of these policies could arguably be run-time variables, but then we + couldn't use compile-time dispatch internally to select the best evaluation + method for the given policies.

        For this reason a Policy is a type: in fact it's an instance of the class template boost::math::policies::policy<>. This class is just a compile-time-container - of user-selected policies (sometimes called a type-list): + of user-selected policies (sometimes called a type-list). +

        +

        + Over a dozen policy + defaults are provided, so most of the time you can ignore the policy + framework, but you can overwrite the defaults with your own policies to give + detailed control, for example:

        using namespace boost::math::policies;
        -//
        -// Define a policy that sets ::errno on overflow, and does
        -// not promote double to long double internally:
        -//
        -typedef policy<domain_error<errno_on_error>, promote_double<false> > mypolicy;
        +
        +// Define a policy that sets ::errno on overflow,
        +// and does not promote double to long double internally,
        +// and only aims for precision of only 3 decimal digits,
        +// to an error-handling policy, usually to trade precision for speed:
        +
        +typedef policy
        +<
        +  domain_error<errno_on_error>,
        +  promote_double<false>,
        +  digits10<3>
        +> my_policy;
         
        diff --git a/doc/html/math_toolkit/powers/cbrt.html b/doc/html/math_toolkit/powers/cbrt.html index 8b53b0403..81bfc08dc 100644 --- a/doc/html/math_toolkit/powers/cbrt.html +++ b/doc/html/math_toolkit/powers/cbrt.html @@ -70,7 +70,7 @@ should have approximately 2 epsilon accuracy.

        -

        Table 6.80. Error rates for cbrt

        +

        Table 6.83. Error rates for cbrt

        diff --git a/doc/html/math_toolkit/powers/cos_pi.html b/doc/html/math_toolkit/powers/cos_pi.html index 262d52f1a..fefa60d15 100644 --- a/doc/html/math_toolkit/powers/cos_pi.html +++ b/doc/html/math_toolkit/powers/cos_pi.html @@ -57,7 +57,7 @@ computing the cosine of πx.

        -

        Table 6.77. Error rates for cos_pi

        +

        Table 6.80. Error rates for cos_pi

        diff --git a/doc/html/math_toolkit/powers/expm1.html b/doc/html/math_toolkit/powers/expm1.html index b2713c998..f2451d9d8 100644 --- a/doc/html/math_toolkit/powers/expm1.html +++ b/doc/html/math_toolkit/powers/expm1.html @@ -79,7 +79,7 @@ should have approximately 1 epsilon accuracy.

        -

        Table 6.79. Error rates for expm1

        +

        Table 6.82. Error rates for expm1

        diff --git a/doc/html/math_toolkit/powers/log1p.html b/doc/html/math_toolkit/powers/log1p.html index d0e83b7d7..cfc6e1836 100644 --- a/doc/html/math_toolkit/powers/log1p.html +++ b/doc/html/math_toolkit/powers/log1p.html @@ -93,7 +93,7 @@ should have approximately 1 epsilon accuracy.

        -

        Table 6.78. Error rates for log1p

        +

        Table 6.81. Error rates for log1p

        diff --git a/doc/html/math_toolkit/powers/powm1.html b/doc/html/math_toolkit/powers/powm1.html index 55db14e28..d35ebd57b 100644 --- a/doc/html/math_toolkit/powers/powm1.html +++ b/doc/html/math_toolkit/powers/powm1.html @@ -72,7 +72,7 @@ Should have approximately 2-3 epsilon accuracy.

        -

        Table 6.82. Error rates for powm1

        +

        Table 6.85. Error rates for powm1

        diff --git a/doc/html/math_toolkit/powers/sin_pi.html b/doc/html/math_toolkit/powers/sin_pi.html index 683158c47..7afd286a8 100644 --- a/doc/html/math_toolkit/powers/sin_pi.html +++ b/doc/html/math_toolkit/powers/sin_pi.html @@ -57,7 +57,7 @@ computing the sine of πx.

        -

        Table 6.76. Error rates for sin_pi

        +

        Table 6.79. Error rates for sin_pi

        diff --git a/doc/html/math_toolkit/powers/sqrt1pm1.html b/doc/html/math_toolkit/powers/sqrt1pm1.html index ef806798f..1d84158e2 100644 --- a/doc/html/math_toolkit/powers/sqrt1pm1.html +++ b/doc/html/math_toolkit/powers/sqrt1pm1.html @@ -75,7 +75,7 @@ should have approximately 3 epsilon accuracy.

        -

        Table 6.81. Error rates for sqrt1pm1

        +

        Table 6.84. Error rates for sqrt1pm1

        diff --git a/doc/html/math_toolkit/real_concepts.html b/doc/html/math_toolkit/real_concepts.html index da73d43c3..dcd4ede60 100644 --- a/doc/html/math_toolkit/real_concepts.html +++ b/doc/html/math_toolkit/real_concepts.html @@ -32,7 +32,7 @@ any type RealType that meets the conceptual requirements given below. All the built-in floating-point types like double will meet these requirements. (Built-in types are also called fundamental - types). + (built-in) types).

        User-defined types that meet the conceptual requirements can also be used. diff --git a/doc/html/math_toolkit/remez.html b/doc/html/math_toolkit/remez.html index d7cdb4c54..83edb4dd0 100644 --- a/doc/html/math_toolkit/remez.html +++ b/doc/html/math_toolkit/remez.html @@ -297,7 +297,7 @@

        - Practical + Practical Considerations

        diff --git a/doc/html/math_toolkit/root_comparison/cbrt_comparison.html b/doc/html/math_toolkit/root_comparison/cbrt_comparison.html index be1e5e25c..8a2fc6c62 100644 --- a/doc/html/math_toolkit/root_comparison/cbrt_comparison.html +++ b/doc/html/math_toolkit/root_comparison/cbrt_comparison.html @@ -29,7 +29,7 @@

        In the table below, the cube root of 28 was computed for three fundamental - types floating-point types, and one Boost.Multiprecision + (built-in) types floating-point types, and one Boost.Multiprecision type cpp_bin_float using 50 decimal digit precision, using four algorithms.

        @@ -202,8 +202,8 @@
      • For fundamental - types, there was little to choose between the three derivative - methods, but for cpp_bin_float, + (built-in) types, there was little to choose between the three + derivative methods, but for cpp_bin_float, Newton-Raphson iteration was twice as fast. Note that the cube-root is an extreme test case as the cost of calling the functor is so cheap that the runtimes are largely diff --git a/doc/html/math_toolkit/root_comparison/root_n_comparison.html b/doc/html/math_toolkit/root_comparison/root_n_comparison.html index ee2f365c7..fbcd8a101 100644 --- a/doc/html/math_toolkit/root_comparison/root_n_comparison.html +++ b/doc/html/math_toolkit/root_comparison/root_n_comparison.html @@ -5202,7 +5202,8 @@
      • Perhaps surprisingly, there is little difference between the various algorithms for fundamental - types floating-point types. Using the first derivatives (Newton-Raphson iteration) + (built-in) types floating-point types. Using the first derivatives + (Newton-Raphson iteration) is usually the best, but while the improvement over the no-derivative TOMS Algorithm 748: enclosing zeros of continuous functions is considerable diff --git a/doc/html/math_toolkit/roots/root_comparison/cbrt_comparison.html b/doc/html/math_toolkit/roots/root_comparison/cbrt_comparison.html new file mode 100644 index 000000000..f3066043f --- /dev/null +++ b/doc/html/math_toolkit/roots/root_comparison/cbrt_comparison.html @@ -0,0 +1,1589 @@ + + + +Comparison of Cube Root Finding Algorithms + + + + + + + + +
      • + + + + + + +
        Boost C++ LibrariesHomeLibrariesPeopleFAQMore
        +
        +
        +PrevUpHomeNext +
        +
        + +

        + In the table below, the cube root of 28 was computed for three fundamental + (built-in) types floating-point types, and one Boost.Multiprecision + type cpp_bin_float + using 50 decimal digit precision, using four algorithms. +

        +

        + The 'exact' answer was computed using a 100 decimal digit type: +

        +
        cpp_bin_float_100 full_answer ("3.036588971875662519420809578505669635581453977248111123242141654169177268411884961770250390838097895");
        +
        +

        + Times were measured using Boost.Timer + using class cpu_timer. +

        +
          +
        • + Its is the number of iterations taken to find + the root. +
        • +
        • + Times is the CPU time-taken in arbitrary units. +
        • +
        • + Norm is a normalized time, in comparison to the + quickest algorithm (with value 1.00). +
        • +
        • + Dis is the distance from the nearest representation + of the 'exact' root in bits. Distance from the 'exact' answer is measured + by using function Boost.Math + float_distance. One or two bits distance means that all results + are effectively 'correct'. Zero means 'exact' - the nearest representable + value for the floating-point type. +
        • +
        +

        + The cube-root function is a simple function, and is a contrived example + for root-finding. It does allow us to investigate some of the factors controlling + efficiency that may be extrapolated to more complex functions. +

        +

        + The program used was root_finding_algorithms.cpp. + 100000 evaluations of each floating-point type and algorithm were used + and the CPU times were judged from repeat runs to have an uncertainty of + 10 %. Comparing MSVC for double + and long double + (which are identical on this patform) may give a guide to uncertainty of + timing. +

        +

        + The requested precision was set as follows: +

        +
        ++++ + + + + + + + + + + + + + + + + + + + + + + +
        +

        + Function +

        +
        +

        + Precision Requested +

        +
        +

        + TOMS748 +

        +
        +

        + numeric_limits<T>::digits - 2 +

        +
        +

        + Newton +

        +
        +

        + floor(numeric_limits<T>::digits * 0.6) +

        +
        +

        + Halley +

        +
        +

        + floor(numeric_limits<T>::digits * 0.4) +

        +
        +

        + Schröder +

        +
        +

        + floor(numeric_limits<T>::digits * 0.4) +

        +
        +
          +
        • + The C++ Standard cube root function std::cbrt + is only defined for built-in or fundamental types, so cannot be used + with any User-Defined floating-point types like Boost.Multiprecision. + This, and that the cube function is so impeccably-behaved, allows the + implementer to use many tricks to achieve a fast computation. On some + platforms,std::cbrt appeared several times as quick + as the more general boost::math::cbrt, + on other platforms / compiler options boost::math::cbrt + is noticeably faster. In general, the results are highly dependent + on the code-generation / processor architecture selection compiler + options used. One can assume that the standard library will have been + compiled with options nearly optimal for the platform + it was installed on, where as the user has more choice over the options + used for Boost.Math. Pick something too general/conservative and performance + suffers, while selecting options that make use of the latest instruction + set opcodes speed's things up noticeably. +
        • +
        • + Two compilers in optimise mode were compared: GCC 4.9.1 using Netbeans + IDS and Microsoft Visual Studio 2013 (Update 1) on the same hardware. + The number of iterations seemed consistent, but the relative run-times + surprisingly different. +
        • +
        • + boost::math::cbrt allows use with any + user-defined floating-point type, conveniently Boost.Multiprecision. + It too can take some advantage of the good-behaviour of the cube function, + compared to the more general implementation in the nth root-finding + examples. For example, it uses a polynomial approximation to generate + a better guess than dividing the exponent by three, and can avoid the + complex checks in Newton-Raphson + iteration required to prevent the search going wildly off-track. + For a known precision, it may also be possible to fix the number of + iterations, allowing inlining and loop unrolling. It also algebraically + simplifies the Halley steps leading to a big reduction in the number + of floating point operations required compared to a "black box" + implementation that calculates the derivatives seperately and then + combines them in the Halley code. Typically, it was found that computation + using type double took + a few times longer when using the various root-finding algorithms directly + rather than the hand coded/optimized cbrt + routine. +
        • +
        • + The importance of getting a good guess can be seen by the iteration + count for the multiprecision case: here we "cheat" a little + and use the cube-root calculated to double precision as the initial + guess. The limitation of this tactic is that the range of possible + (exponent) values may be less than the multiprecision type. +
        • +
        • + For fundamental + (built-in) types, there was little to choose between the three + derivative methods, but for cpp_bin_float, + Newton-Raphson + iteration was twice as fast. Note that the cube-root is an extreme + test case as the cost of calling the functor is so cheap that the runtimes + are largely dominated by the complexity of the iteration code. +
        • +
        • + Compiling with optimisation halved computation times, and any differences + between algorithms became nearly negligible. The optimisation speed-up + of the TOMS + Algorithm 748: enclosing zeros of continuous functions was + especially noticable. +
        • +
        • + Using a multiprecision type like cpp_bin_float_50 + for a precision of 50 decimal digits took a lot longer, as expected + because most computation uses software rather than 64-bit floating-point + hardware. Speeds are often more than 50 times slower. +
        • +
        • + Using cpp_bin_float_50, + TOMS Algorithm + 748: enclosing zeros of continuous functions was much slower + showing the benefit of using derivatives. Newton-Raphson + iteration was found to be twice as quick as either of the second-derivative + methods: this is an extreme case though, the function and its derivatives + are so cheap to compute that we're really measuring the complexity + of the boilerplate root-finding code. +
        • +
        • + For multiprecision types only one or two extra iterations + are needed to get the remaining 35 digits, whatever the algorithm used. + (The time taken was of course much greater for these types). +
        • +
        • + Using a 100 decimal-digit type only doubled the time and required only + a very few more iterations, so the cost of extra precision is mainly + the underlying cost of computing more digits, not in the way the algorithm + works. This confirms previous observations using NTL + A Library for doing Number Theory high-precision types. +
        • +
        +
        + + Program + root_finding_algorithms.cpp, Clang version 6.0.0 (tags/RELEASE_600/final), + Dinkumware standard library version 650, Win32, x64
        1000000 evaluations + of each of 5 root_finding algorithms.
        +
        +
        +

        Table 12.1. Cube root(28) for float, double, long double and cpp_bin_float_50

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algorithm +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + cbrt +

        +
        +

        + 0 +

        +
        +

        + 62500 +

        +
        +

        + 1.0 +

        +
        +

        + 0 +

        +
        + +

        + 0 +

        +
        +

        + 46875 +

        +
        +

        + 1.0 +

        +
        +

        + 1 +

        +
        + +

        + 0 +

        +
        +

        + 62500 +

        +
        +

        + 1.0 +

        +
        +

        + 1 +

        +
        + +

        + 0 +

        +
        +

        + 5750000 +

        +
        +

        + 1.1 +

        +
        +

        + 0 +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 8 +

        +
        +

        + 343750 +

        +
        +

        + 5.5 +

        +
        +

        + -1 +

        +
        + +

        + 11 +

        +
        +

        + 531250 +

        +
        +

        + 11. +

        +
        +

        + 2 +

        +
        + +

        + 11 +

        +
        +

        + 531250 +

        +
        +

        + 8.5 +

        +
        +

        + 2 +

        +
        + +

        + 6 +

        +
        +

        + 40031250 +

        +
        +

        + 7.4 +

        +
        +

        + -2 +

        +
        +
        +

        + Newton +

        +
        +

        + 5 +

        +
        +

        + 171875 +

        +
        +

        + 2.8 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 187500 +

        +
        +

        + 4.0 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 187500 +

        +
        +

        + 3.0 +

        +
        +

        + 0 +

        +
        + +

        + 2 +

        +
        +

        + 5406250 +

        +
        +

        + 1.0 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 3 +

        +
        +

        + 187500 +

        +
        +

        + 3.0 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 218750 +

        +
        +

        + 4.7 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 203125 +

        +
        +

        + 3.3 +

        +
        +

        + 0 +

        +
        + +

        + 2 +

        +
        +

        + 12968750 +

        +
        +

        + 2.4 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 4 +

        +
        +

        + 203125 +

        +
        +

        + 3.3 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 250000 +

        +
        +

        + 5.3 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 218750 +

        +
        +

        + 3.5 +

        +
        +

        + 0 +

        +
        + +

        + 2 +

        +
        +

        + 15984375 +

        +
        +

        + 3.0 +

        +
        +

        + 0 +

        +
        +
        +
        +
        + + Program + root_finding_algorithms.cpp, GNU C++ version 7.2.0, GNU libstdc++ version + 20170814, Win32, x64
        1000000 evaluations of each of 5 root_finding + algorithms.
        +
        +
        +

        Table 12.2. Cube root(28) for float, double, long double and cpp_bin_float_50

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algorithm +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + cbrt +

        +
        +

        + 0 +

        +
        +

        + 46875 +

        +
        +

        + 1.0 +

        +
        +

        + 0 +

        +
        + +

        + 0 +

        +
        +

        + 125000 +

        +
        +

        + 1.0 +

        +
        +

        + 0 +

        +
        + +

        + 0 +

        +
        +

        + 109375 +

        +
        +

        + 1.0 +

        +
        +

        + 0 +

        +
        + +

        + 0 +

        +
        +

        + 3875000 +

        +
        +

        + 1.0 +

        +
        +

        + 0 +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 8 +

        +
        +

        + 281250 +

        +
        +

        + 6.0 +

        +
        +

        + -1 +

        +
        + +

        + 11 +

        +
        +

        + 453125 +

        +
        +

        + 3.6 +

        +
        +

        + 2 +

        +
        + +

        + 10 +

        +
        +

        + 1750000 +

        +
        +

        + 16. +

        +
        +

        + -1 +

        +
        + +

        + 6 +

        +
        +

        + 30312500 +

        +
        +

        + 7.8 +

        +
        +

        + -2 +

        +
        +
        +

        + Newton +

        +
        +

        + 5 +

        +
        +

        + 109375 +

        +
        +

        + 2.3 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 140625 +

        +
        +

        + 1.1 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 281250 +

        +
        +

        + 2.6 +

        +
        +

        + 0 +

        +
        + +

        + 2 +

        +
        +

        + 4125000 +

        +
        +

        + 1.1 +

        +
        +

        + -1 +

        +
        +
        +

        + Halley +

        +
        +

        + 3 +

        +
        +

        + 125000 +

        +
        +

        + 2.7 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 140625 +

        +
        +

        + 1.1 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 515625 +

        +
        +

        + 4.7 +

        +
        +

        + 0 +

        +
        + +

        + 2 +

        +
        +

        + 9171875 +

        +
        +

        + 2.4 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 4 +

        +
        +

        + 140625 +

        +
        +

        + 3.0 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 171875 +

        +
        +

        + 1.4 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 671875 +

        +
        +

        + 6.1 +

        +
        +

        + 0 +

        +
        + +

        + 2 +

        +
        +

        + 10578125 +

        +
        +

        + 2.7 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +
        + + + +
        +
        +
        +PrevUpHomeNext +
        + + diff --git a/doc/html/math_toolkit/roots/root_comparison/elliptic_comparison.html b/doc/html/math_toolkit/roots/root_comparison/elliptic_comparison.html new file mode 100644 index 000000000..44771f9f7 --- /dev/null +++ b/doc/html/math_toolkit/roots/root_comparison/elliptic_comparison.html @@ -0,0 +1,1871 @@ + + + +Comparison of Elliptic Integral Root Finding Algoritghms + + + + + + + + + + + + + + + +
        Boost C++ LibrariesHomeLibrariesPeopleFAQMore
        +
        +
        +PrevUpHomeNext +
        +
        + +

        + A second example compares four root finding algorithms for locating the + second radius of an ellipse with first radius 28 and arc length 300, for + four floating-point types, float, + double, long + double and a Boost.Multiprecision + type cpp_bin_float_50. +

        +

        + Which is to say we're solving: +

        +
        4xE(sqrt(1 - 282 / x2)) - 300 = 0
        +

        + In each case the target accuracy was set using our "recomended" + accuracy limits (or at least limits that make a good starting point - which + is likely to give close to full accuracy without resorting to unnecessary + iterations). +

        +
        ++++ + + + + + + + + + + + + + + + + + + + + + + +
        +

        + Function +

        +
        +

        + Precision Requested +

        +
        +

        + TOMS748 +

        +
        +

        + numeric_limits<T>::digits - 2 +

        +
        +

        + Newton +

        +
        +

        + floor(numeric_limits<T>::digits * 0.6) +

        +
        +

        + Halley +

        +
        +

        + floor(numeric_limits<T>::digits * 0.4) +

        +
        +

        + Schröder +

        +
        +

        + floor(numeric_limits<T>::digits * 0.4) +

        +
        +

        + Tests used Microsoft Visual Studio 2013 (Update 1) and GCC 4.9.1 using + source code root_elliptic_finding.cpp. +

        +

        + The timing uncertainty (especially using MSVC) is at least 5% of normalized + time 'Norm'. +

        +

        + To pick out the 'best' and 'worst' algorithms are highlighted in blue and + red. More than one result can be 'best' when normalized times are indistinguishable + within the uncertainty. +

        +
        + + Program + root_elliptic_finding.cpp, + Microsoft Visual C++ version 14.0, Dinkumware standard library version + 650, Win32 Compiled in optimise mode., _X86_SSE2 +
        +
        +

        Table 12.12. root with radius 28 and arc length 300) for float, double, long + double and cpp_bin_float_50 types, using _X86_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 5 +

        +
        +

        + 828 +

        +
        +

        + 1.15 +

        +
        +

        + -1 +

        +
        + +

        + 9 +

        +
        +

        + 1453 +

        +
        +

        + 1.52 +

        +
        +

        + 1 +

        +
        + +

        + 9 +

        +
        +

        + 1437 +

        +
        +

        + 1.56 +

        +
        +

        + 1 +

        +
        + +

        + 11 +

        +
        +

        + 1326562 +

        +
        +

        + 1.46 +

        +
        +

        + -3 +

        +
        +
        +

        + Newton +

        +
        +

        + 3 +

        +
        +

        + 890 +

        +
        +

        + 1.24 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 1156 +

        +
        +

        + 1.21 +

        +
        +

        + 1 +

        +
        + +

        + 4 +

        +
        +

        + 1171 +

        +
        +

        + 1.27 +

        +
        +

        + 1 +

        +
        + +

        + 5 +

        +
        +

        + 1089062 +

        +
        +

        + 1.20 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 2 +

        +
        +

        + 718 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 953 +

        +
        +

        + 1.00 +

        +
        +

        + 3 +

        +
        + +

        + 3 +

        +
        +

        + 921 +

        +
        +

        + 1.00 +

        +
        +

        + 3 +

        +
        + +

        + 4 +

        +
        +

        + 909375 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 3 +

        +
        +

        + 906 +

        +
        +

        + 1.26 +

        +
        +

        + -1 +

        +
        + +

        + 6 +

        +
        +

        + 1703 +

        +
        +

        + 1.79 +

        +
        +

        + 1 +

        +
        + +

        + 6 +

        +
        +

        + 1687 +

        +
        +

        + 1.83 +

        +
        +

        + 1 +

        +
        + +

        + 5 +

        +
        +

        + 1131250 +

        +
        +

        + 1.24 +

        +
        +

        + -2 +

        +
        +
        +
        +
        + + Program + root_elliptic_finding.cpp, + Microsoft Visual C++ version 12.0, Dinkumware standard library version + 610, Win32 Compiled in optimise mode., _X64_AVX +
        +
        +

        Table 12.13. root with radius 28 and arc length 300) for float, double, long + double and cpp_bin_float_50 types, using _X64_AVX

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 5 +

        +
        +

        + 500 +

        +
        +

        + 1.33 +

        +
        +

        + -1 +

        +
        + +

        + 9 +

        +
        +

        + 1046 +

        +
        +

        + 1.72 +

        +
        +

        + 1 +

        +
        + +

        + 9 +

        +
        +

        + 1062 +

        +
        +

        + 1.70 +

        +
        +

        + 1 +

        +
        + +

        + 11 +

        +
        +

        + 698437 +

        +
        +

        + 1.54 +

        +
        +

        + -3 +

        +
        +
        +

        + Newton +

        +
        +

        + 3 +

        +
        +

        + 484 +

        +
        +

        + 1.29 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 734 +

        +
        +

        + 1.21 +

        +
        +

        + 1 +

        +
        + +

        + 4 +

        +
        +

        + 687 +

        +
        +

        + 1.10 +

        +
        +

        + 1 +

        +
        + +

        + 5 +

        +
        +

        + 545312 +

        +
        +

        + 1.20 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 2 +

        +
        +

        + 375 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 609 +

        +
        +

        + 1.00 +

        +
        +

        + 3 +

        +
        + +

        + 3 +

        +
        +

        + 625 +

        +
        +

        + 1.00 +

        +
        +

        + 3 +

        +
        + +

        + 4 +

        +
        +

        + 453125 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 3 +

        +
        +

        + 546 +

        +
        +

        + 1.46 +

        +
        +

        + -1 +

        +
        + +

        + 6 +

        +
        +

        + 1109 +

        +
        +

        + 1.82 +

        +
        +

        + 1 +

        +
        + +

        + 6 +

        +
        +

        + 1187 +

        +
        +

        + 1.90 +

        +
        +

        + 1 +

        +
        + +

        + 5 +

        +
        +

        + 564062 +

        +
        +

        + 1.24 +

        +
        +

        + -2 +

        +
        +
        +
        +
        + + Program + root_elliptic_finding.cpp, + GNU C++ version 7.2.0, GNU libstdc++ version 20170814, Win32 Compiled in + optimise mode., _X64_SSE2 +
        +
        +

        Table 12.14. root with radius 28 and arc length 300) for float, double, long + double and cpp_bin_float_50 types, using _X64_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 5 +

        +
        +

        + 437 +

        +
        +

        + 1.17 +

        +
        +

        + -1 +

        +
        + +

        + 8 +

        +
        +

        + 1234 +

        +
        +

        + 1.08 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 2562 +

        +
        +

        + 1.48 +

        +
        +

        + 4 +

        +
        + +

        + 11 +

        +
        +

        + 657812 +

        +
        +

        + 1.49 +

        +
        +

        + -3 +

        +
        +
        +

        + Newton +

        +
        +

        + 3 +

        +
        +

        + 437 +

        +
        +

        + 1.17 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 1421 +

        +
        +

        + 1.25 +

        +
        +

        + 1 +

        +
        + +

        + 4 +

        +
        +

        + 2031 +

        +
        +

        + 1.17 +

        +
        +

        + 1 +

        +
        + +

        + 5 +

        +
        +

        + 550000 +

        +
        +

        + 1.25 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 2 +

        +
        +

        + 375 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 1140 +

        +
        +

        + 1.00 +

        +
        +

        + 1 +

        +
        + +

        + 3 +

        +
        +

        + 1734 +

        +
        +

        + 1.00 +

        +
        +

        + 7 +

        +
        + +

        + 4 +

        +
        +

        + 440625 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 3 +

        +
        +

        + 500 +

        +
        +

        + 1.33 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 1484 +

        +
        +

        + 1.30 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 2156 +

        +
        +

        + 1.24 +

        +
        +

        + 3 +

        +
        + +

        + 5 +

        +
        +

        + 535937 +

        +
        +

        + 1.22 +

        +
        +

        + -2 +

        +
        +
        +
        +

        + Remarks: +

        +
          +
        • + The function being solved is now moderately expensive to call, and + twice as expensive to call when obtaining the derivative than when + not. Consequently there is very little improvement in moving from a + derivative free method, to Newton iteration. However, once you've calculated + the first derivative the second comes almost for free, consequently + the third order methods (Halley) does much the best. +
        • +
        • + Of the two second order methods, Halley does best as would be expected: + the Schroder method offers better guarantees of quadratic + convergence, while Halley relies on a smooth function with a single + root to give cubic convergence. It's not entirely + clear why Schroder iteration often does worse than Newton. +
        • +
        +
        + + + +
        +
        +
        +PrevUpHomeNext +
        + + diff --git a/doc/html/math_toolkit/roots/root_comparison/root_n_comparison.html b/doc/html/math_toolkit/roots/root_comparison/root_n_comparison.html new file mode 100644 index 000000000..d0c2cd770 --- /dev/null +++ b/doc/html/math_toolkit/roots/root_comparison/root_n_comparison.html @@ -0,0 +1,5289 @@ + + + +Comparison of Nth-root Finding Algorithms + + + + + + + + + + + + + + + +
        Boost C++ LibrariesHomeLibrariesPeopleFAQMore
        +
        +
        +PrevUpHomeNext +
        +
        + +

        + A second example compares four generalized nth-root finding algorithms + for various n-th roots (5, 7 and 13) of a single value 28.0, for four floating-point + types, float, double, long + double and a Boost.Multiprecision + type cpp_bin_float_50. + In each case the target accuracy was set using our "recomended" + accuracy limits (or at least limits that make a good starting point - which + is likely to give close to full accuracy without resorting to unnecessary + iterations). +

        +
        ++++ + + + + + + + + + + + + + + + + + + + + + + +
        +

        + Function +

        +
        +

        + Precision Requested +

        +
        +

        + TOMS748 +

        +
        +

        + numeric_limits<T>::digits - 2 +

        +
        +

        + Newton +

        +
        +

        + floor(numeric_limits<T>::digits * 0.6) +

        +
        +

        + Halley +

        +
        +

        + floor(numeric_limits<T>::digits * 0.4) +

        +
        +

        + Schröder +

        +
        +

        + floor(numeric_limits<T>::digits * 0.4) +

        +
        +

        + Tests used Microsoft Visual Studio 2013 (Update 1) and GCC 4.9.1 using + source code root_n_finding_algorithms.cpp. +

        +

        + The timing uncertainty (especially using MSVC) is at least 5% of normalized + time 'Norm'. +

        +

        + To pick out the 'best' and 'worst' algorithms are highlighted in blue and + red. More than one result can be 'best' when normalized times are indistinguishable + within the uncertainty. +

        +
        + + Program + ..\example\root_n_finding_algorithms.cpp, Microsoft Visual C++ version + 14.0, Dinkumware standard library version 650, Win32 Compiled in optimise + mode., _X86_SSE2 +
        +

        + Fraction of full accuracy 1 +

        +
        +

        Table 12.3. 5th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X86_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 7 +

        +
        +

        + 795 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 11 +

        +
        +

        + 1106 +

        +
        +

        + 1.26 +

        +
        +

        + 1 +

        +
        + +

        + 11 +

        +
        +

        + 1135 +

        +
        +

        + 1.43 +

        +
        +

        + 1 +

        +
        + +

        + 12 +

        +
        +

        + 148750 +

        +
        +

        + 7.50 +

        +
        +

        + 0 +

        +
        +
        +

        + Newton +

        +
        +

        + 3 +

        +
        +

        + 839 +

        +
        +

        + 1.06 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 879 +

        +
        +

        + 1.00 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 871 +

        +
        +

        + 1.09 +

        +
        +

        + -1 +

        +
        + +

        + 6 +

        +
        +

        + 19843 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 2 +

        +
        +

        + 843 +

        +
        +

        + 1.06 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 903 +

        +
        +

        + 1.03 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 859 +

        +
        +

        + 1.08 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 37968 +

        +
        +

        + 1.91 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 2 +

        +
        +

        + 867 +

        +
        +

        + 1.09 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 920 +

        +
        +

        + 1.05 +

        +
        +

        + -1 +

        +
        + +

        + 3 +

        +
        +

        + 796 +

        +
        +

        + 1.00 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 47187 +

        +
        +

        + 2.38 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +

        Table 12.4. 7th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X86_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 12 +

        +
        +

        + 959 +

        +
        +

        + 1.26 +

        +
        +

        + 1 +

        +
        + +

        + 15 +

        +
        +

        + 1214 +

        +
        +

        + 1.56 +

        +
        +

        + 2 +

        +
        + +

        + 15 +

        +
        +

        + 1209 +

        +
        +

        + 1.52 +

        +
        +

        + 2 +

        +
        + +

        + 14 +

        +
        +

        + 184531 +

        +
        +

        + 6.79 +

        +
        +

        + 0 +

        +
        +
        +

        + Newton +

        +
        +

        + 5 +

        +
        +

        + 764 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 776 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 796 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 27187 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 4 +

        +
        +

        + 795 +

        +
        +

        + 1.04 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 826 +

        +
        +

        + 1.06 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 865 +

        +
        +

        + 1.09 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 55625 +

        +
        +

        + 2.05 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 5 +

        +
        +

        + 826 +

        +
        +

        + 1.08 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 831 +

        +
        +

        + 1.07 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 832 +

        +
        +

        + 1.05 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 78750 +

        +
        +

        + 2.90 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +

        Table 12.5. 11th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X86_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 12 +

        +
        +

        + 1059 +

        +
        +

        + 1.37 +

        +
        +

        + -2 +

        +
        + +

        + 14 +

        +
        +

        + 1253 +

        +
        +

        + 1.60 +

        +
        +

        + 2 +

        +
        + +

        + 14 +

        +
        +

        + 1251 +

        +
        +

        + 1.60 +

        +
        +

        + 2 +

        +
        + +

        + 17 +

        +
        +

        + 275468 +

        +
        +

        + 8.81 +

        +
        +

        + 2 +

        +
        +
        +

        + Newton +

        +
        +

        + 6 +

        +
        +

        + 771 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 784 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 784 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 9 +

        +
        +

        + 31250 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 4 +

        +
        +

        + 828 +

        +
        +

        + 1.07 +

        +
        +

        + -1 +

        +
        + +

        + 5 +

        +
        +

        + 831 +

        +
        +

        + 1.06 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 826 +

        +
        +

        + 1.05 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 56718 +

        +
        +

        + 1.81 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 6 +

        +
        +

        + 875 +

        +
        +

        + 1.13 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 881 +

        +
        +

        + 1.12 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 881 +

        +
        +

        + 1.12 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 97031 +

        +
        +

        + 3.10 +

        +
        +

        + 0 +

        +
        +
        +
        +
        + + Program + root_n_finding_algorithms.cpp, Microsoft Visual C++ version 12.0, Dinkumware + standard library version 610, Win32 Compiled in optimise mode., _X64_AVX +
        +

        + Fraction of full accuracy 1 +

        +
        +

        Table 12.6. 5th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X64_AVX

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 7 +

        +
        +

        + 239 +

        +
        +

        + 1.50 +

        +
        +

        + 0 +

        +
        + +

        + 11 +

        +
        +

        + 451 +

        +
        +

        + 2.53 +

        +
        +

        + 1 +

        +
        + +

        + 11 +

        +
        +

        + 439 +

        +
        +

        + 2.49 +

        +
        +

        + 1 +

        +
        + +

        + 12 +

        +
        +

        + 90312 +

        +
        +

        + 7.51 +

        +
        +

        + 0 +

        +
        +
        +

        + Newton +

        +
        +

        + 3 +

        +
        +

        + 159 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 178 +

        +
        +

        + 1.00 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 176 +

        +
        +

        + 1.00 +

        +
        +

        + -1 +

        +
        + +

        + 6 +

        +
        +

        + 12031 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 2 +

        +
        +

        + 168 +

        +
        +

        + 1.06 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 203 +

        +
        +

        + 1.14 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 198 +

        +
        +

        + 1.13 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 20937 +

        +
        +

        + 1.74 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 2 +

        +
        +

        + 173 +

        +
        +

        + 1.09 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 206 +

        +
        +

        + 1.16 +

        +
        +

        + -1 +

        +
        + +

        + 3 +

        +
        +

        + 203 +

        +
        +

        + 1.15 +

        +
        +

        + -1 +

        +
        + +

        + 4 +

        +
        +

        + 26250 +

        +
        +

        + 2.18 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +

        Table 12.7. 7th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X64_AVX

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 12 +

        +
        +

        + 385 +

        +
        +

        + 2.19 +

        +
        +

        + 1 +

        +
        + +

        + 15 +

        +
        +

        + 635 +

        +
        +

        + 3.13 +

        +
        +

        + 2 +

        +
        + +

        + 15 +

        +
        +

        + 621 +

        +
        +

        + 3.17 +

        +
        +

        + 2 +

        +
        + +

        + 14 +

        +
        +

        + 114843 +

        +
        +

        + 6.81 +

        +
        +

        + 0 +

        +
        +
        +

        + Newton +

        +
        +

        + 5 +

        +
        +

        + 176 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 203 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 196 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 16875 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 4 +

        +
        +

        + 209 +

        +
        +

        + 1.19 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 254 +

        +
        +

        + 1.25 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 246 +

        +
        +

        + 1.26 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 32343 +

        +
        +

        + 1.92 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 5 +

        +
        +

        + 223 +

        +
        +

        + 1.27 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 273 +

        +
        +

        + 1.34 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 275 +

        +
        +

        + 1.40 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 45156 +

        +
        +

        + 2.68 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +

        Table 12.8. 11th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X64_AVX

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 12 +

        +
        +

        + 467 +

        +
        +

        + 2.42 +

        +
        +

        + -2 +

        +
        + +

        + 14 +

        +
        +

        + 648 +

        +
        +

        + 3.06 +

        +
        +

        + 2 +

        +
        + +

        + 14 +

        +
        +

        + 640 +

        +
        +

        + 2.99 +

        +
        +

        + 2 +

        +
        + +

        + 17 +

        +
        +

        + 170000 +

        +
        +

        + 8.85 +

        +
        +

        + 2 +

        +
        +
        +

        + Newton +

        +
        +

        + 6 +

        +
        +

        + 193 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 212 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 214 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 9 +

        +
        +

        + 19218 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 4 +

        +
        +

        + 209 +

        +
        +

        + 1.08 +

        +
        +

        + -1 +

        +
        + +

        + 5 +

        +
        +

        + 256 +

        +
        +

        + 1.21 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 250 +

        +
        +

        + 1.17 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 32656 +

        +
        +

        + 1.70 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 6 +

        +
        +

        + 248 +

        +
        +

        + 1.28 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 306 +

        +
        +

        + 1.44 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 298 +

        +
        +

        + 1.39 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 53437 +

        +
        +

        + 2.78 +

        +
        +

        + 0 +

        +
        +
        +
        +
        + + Program + ..\example\root_n_finding_algorithms.cpp, GNU C++ version 7.2.0, GNU libstdc++ + version 20170814, Win32 Compiled in optimise mode., _X64_SSE2 +
        +

        + Fraction of full accuracy 1 +

        +
        +

        Table 12.9. 5th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X64_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 7 +

        +
        +

        + 260 +

        +
        +

        + 2.26 +

        +
        +

        + 0 +

        +
        + +

        + 11 +

        +
        +

        + 481 +

        +
        +

        + 3.51 +

        +
        +

        + 1 +

        +
        + +

        + 9 +

        +
        +

        + 1526 +

        +
        +

        + 6.78 +

        +
        +

        + 0 +

        +
        + +

        + 12 +

        +
        +

        + 70937 +

        +
        +

        + 8.11 +

        +
        +

        + 0 +

        +
        +
        +

        + Newton +

        +
        +

        + 3 +

        +
        +

        + 115 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 137 +

        +
        +

        + 1.00 +

        +
        +

        + -1 +

        +
        + +

        + 5 +

        +
        +

        + 225 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 8750 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 2 +

        +
        +

        + 135 +

        +
        +

        + 1.17 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 162 +

        +
        +

        + 1.18 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 412 +

        +
        +

        + 1.83 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 14687 +

        +
        +

        + 1.68 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 2 +

        +
        +

        + 135 +

        +
        +

        + 1.17 +

        +
        +

        + 0 +

        +
        + +

        + 3 +

        +
        +

        + 170 +

        +
        +

        + 1.24 +

        +
        +

        + -1 +

        +
        + +

        + 3 +

        +
        +

        + 425 +

        +
        +

        + 1.89 +

        +
        +

        + 0 +

        +
        + +

        + 4 +

        +
        +

        + 18906 +

        +
        +

        + 2.16 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +

        Table 12.10. 7th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X64_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 12 +

        +
        +

        + 465 +

        +
        +

        + 3.39 +

        +
        +

        + 1 +

        +
        + +

        + 15 +

        +
        +

        + 662 +

        +
        +

        + 4.24 +

        +
        +

        + 2 +

        +
        + +

        + 13 +

        +
        +

        + 2304 +

        +
        +

        + 8.17 +

        +
        +

        + 0 +

        +
        + +

        + 14 +

        +
        +

        + 91093 +

        +
        +

        + 7.29 +

        +
        +

        + 0 +

        +
        +
        +

        + Newton +

        +
        +

        + 5 +

        +
        +

        + 137 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 156 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 282 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 12500 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 4 +

        +
        +

        + 181 +

        +
        +

        + 1.32 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 210 +

        +
        +

        + 1.35 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 540 +

        +
        +

        + 1.91 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 24062 +

        +
        +

        + 1.92 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 5 +

        +
        +

        + 201 +

        +
        +

        + 1.47 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 242 +

        +
        +

        + 1.55 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 660 +

        +
        +

        + 2.34 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 35625 +

        +
        +

        + 2.85 +

        +
        +

        + 0 +

        +
        +
        +
        +
        +

        Table 12.11. 11th root(28) for float, double, long double and cpp_bin_float_50 + types, using _X64_SSE2

        +
        +++++++++++++++++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +

        + float +

        +
        + + + + +

        + double +

        +
        + + + + +

        + long d +

        +
        + + + + +

        + cpp50 +

        +
        + +   
        +

        + Algo +

        +
        +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        + +

        + Its +

        +
        +

        + Times +

        +
        +

        + Norm +

        +
        +

        + Dis +

        +
        +
        +

        + TOMS748 +

        +
        +

        + 12 +

        +
        +

        + 562 +

        +
        +

        + 3.53 +

        +
        +

        + -2 +

        +
        + +

        + 14 +

        +
        +

        + 701 +

        +
        +

        + 3.85 +

        +
        +

        + 2 +

        +
        + +

        + 14 +

        +
        +

        + 2665 +

        +
        +

        + 9.10 +

        +
        +

        + 1 +

        +
        + +

        + 17 +

        +
        +

        + 137968 +

        +
        +

        + 9.60 +

        +
        +

        + 2 +

        +
        +
        +

        + Newton +

        +
        +

        + 6 +

        +
        +

        + 159 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 182 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 293 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        + +

        + 9 +

        +
        +

        + 14375 +

        +
        +

        + 1.00 +

        +
        +

        + 0 +

        +
        +
        +

        + Halley +

        +
        +

        + 4 +

        +
        +

        + 185 +

        +
        +

        + 1.16 +

        +
        +

        + -1 +

        +
        + +

        + 5 +

        +
        +

        + 220 +

        +
        +

        + 1.21 +

        +
        +

        + 0 +

        +
        + +

        + 5 +

        +
        +

        + 757 +

        +
        +

        + 2.58 +

        +
        +

        + 0 +

        +
        + +

        + 6 +

        +
        +

        + 26093 +

        +
        +

        + 1.82 +

        +
        +

        + 0 +

        +
        +
        +

        + Schröder +

        +
        +

        + 6 +

        +
        +

        + 246 +

        +
        +

        + 1.55 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 279 +

        +
        +

        + 1.53 +

        +
        +

        + 0 +

        +
        + +

        + 7 +

        +
        +

        + 1135 +

        +
        +

        + 3.87 +

        +
        +

        + 0 +

        +
        + +

        + 8 +

        +
        +

        + 41093 +

        +
        +

        + 2.86 +

        +
        +

        + 0 +

        +
        +
        +
        +

        + Some tentative conclusions can be drawn from this limited exercise. +

        +
          +
        • + Perhaps surprisingly, there is little difference between the various + algorithms for fundamental + (built-in) types floating-point types. Using the first derivatives + (Newton-Raphson + iteration) is usually the best, but while the improvement over + the no-derivative TOMS + Algorithm 748: enclosing zeros of continuous functions is considerable + in number of iterations, but little in execution time. This reflects + the fact that the function we are finding the root for is trivial to + evaluate, so runtimetimes are dominated by the time taken by the boilerplate + code in each method. +
        • +
        • + The extra cost of evaluating the second derivatives (Halley + or Schröder) + is usually too much for any net benefit: as with the cube root, these + functors are so cheap to evaluate that the runtime is largely dominated + by the complexity of the root finding method. +
        • +
        • + For a Boost.Multiprecision + floating-point type, the Newton-Raphson + iteration is a clear winner with a several-fold gain over TOMS Algorithm 748: + enclosing zeros of continuous functions, and again no improvement + from the second-derivative algorithms. +
        • +
        • + The run-time of 50 decimal-digit Boost.Multiprecision + is about 30-fold greater than double. +
        • +
        • + The column 'dis' showing the number of bits distance from the correct + result. The Newton-Raphson algorithm shows a bit or two better accuracy + than TOMS + Algorithm 748: enclosing zeros of continuous functions. +
        • +
        • + The goodness of the 'guess' is especially crucial for Boost.Multiprecision. + Separate experiments show that evaluating the 'guess' using double allows convergence to the final + exact result in one or two iterations. So in this contrived example, + crudely dividing the exponent by N for a 'guess', it would be far better + to use a pow<double> + or , if more precise pow<long double>, + function to estimate a 'guess'. The limitation of this tactic is that + the range of possible (exponent) values may be less than the multiprecision + type. +
        • +
        • + Using floating-point extension SSE2 + instructions made a modest ten-percent speedup. +
        • +
        • + Using MSVC, there was some improvement using 64-bit, markedly for + Boost.Multiprecision. +
        • +
        • + The GCC compiler 4.9.1 using 64-bit was at least five-folder faster + that 32-bit, apparently reflecting better optimization. +
        • +
        +

        + Clearly, your mileage will vary, but in + summary, Newton-Raphson + iteration seems the first choice of algorithm, and effort to find + a good 'guess' the first speed-up target, especially for Boost.Multiprecision. + And of course, compiler optimisation is crucial for speed. +

        +
        + + + +
        +
        +
        +PrevUpHomeNext +
        + + diff --git a/doc/html/math_toolkit/tuning.html b/doc/html/math_toolkit/tuning.html index b70f50b87..cf7628a41 100644 --- a/doc/html/math_toolkit/tuning.html +++ b/doc/html/math_toolkit/tuning.html @@ -109,15 +109,15 @@ be stored as tables of floating point values instead. If mixed arithmetic is slow then add:

        -

        - #define BOOST_MATH_INT_TABLE_TYPE(RT, IT) RT -

        +
        1. + define BOOST_MATH_INT_TABLE_TYPE(RT, IT) RT +

        to boost/math/tools/user.hpp, otherwise the default of:

        -

        - #define BOOST_MATH_INT_TABLE_TYPE(RT, IT) IT -

        +
        1. + define BOOST_MATH_INT_TABLE_TYPE(RT, IT) IT +

        Set in boost/math/config.hpp is fine, and may well result in smaller code. diff --git a/doc/html/math_toolkit/zetas.html b/doc/html/math_toolkit/zetas.html index 95aac8025..c30297d35 100644 --- a/doc/html/math_toolkit/zetas.html +++ b/doc/html/math_toolkit/zetas.html @@ -6,7 +6,7 @@ - + @@ -20,7 +20,7 @@


        -PrevUpHomeNext +PrevUpHomeNext

        @@ -42,7 +42,7 @@
        -PrevUpHomeNext +PrevUpHomeNext
        diff --git a/doc/html/math_toolkit/zetas/zeta.html b/doc/html/math_toolkit/zetas/zeta.html index c575b8e65..b9af300dc 100644 --- a/doc/html/math_toolkit/zetas/zeta.html +++ b/doc/html/math_toolkit/zetas/zeta.html @@ -89,7 +89,7 @@ zero error.

        -

        Table 6.73. Error rates for zeta

        +

        Table 6.76. Error rates for zeta

        diff --git a/doc/html/special.html b/doc/html/special.html index bda6e9f24..bafe68a82 100644 --- a/doc/html/special.html +++ b/doc/html/special.html @@ -159,6 +159,7 @@
        Jacobi Elliptic Function sn
        +
        Lambert W function
        Zeta Functions
        Riemann Zeta Function
        Exponential Integrals
        diff --git a/doc/html/standalone_HTML.manifest b/doc/html/standalone_HTML.manifest index d33863b51..62dacee6e 100644 --- a/doc/html/standalone_HTML.manifest +++ b/doc/html/standalone_HTML.manifest @@ -223,6 +223,7 @@ math_toolkit/jacobi/jacobi_ns.html math_toolkit/jacobi/jacobi_sc.html math_toolkit/jacobi/jacobi_sd.html math_toolkit/jacobi/jacobi_sn.html +math_toolkit/lambert_w.html math_toolkit/zetas.html math_toolkit/zetas/zeta.html math_toolkit/expint.html diff --git a/doc/html4_symbols.qbk b/doc/html4_symbols.qbk index 1b329410d..3c0ae907b 100644 --- a/doc/html4_symbols.qbk +++ b/doc/html4_symbols.qbk @@ -9,6 +9,7 @@ [/ See also Latin-1 aka Western (ISO-8859-1) in latin1_symbols.qbk] [/ http://www.htmlhelp.com/reference/html40/entities/latin1.html] [/Unicode Latin extended http://www.unicode.org/charts/U0080.pdf] +[/https://en.wikipedia.org/wiki/Template:Unicode_chart_Arrows chart arrows] [/ Also some miscellaneous math characters added to this list - see the end.] [/ For others see also math_toolkit.symbols.qbk] @@ -117,7 +118,8 @@ [template int[]'''∫'''] [/ ? integral] [template there4[]'''∴'''] [/ ? therefore] [template sim[]'''∼'''] [/ ~ tilde operator = varies with = similar to] -[template cong[]'''≅'''] [/ ? approximately equal to] +[template simeq[]'''≃'''] [/ ~_asymptotic equality] +[template cong[]'''≅'''] [/ =~ approximately equal to] [template approx[]'''≈'''] [/ ? ~~ very approximately equal to] [template asymp[]'''≈'''] [/ ˜ almost equal to = asymptotic to] [template ne[]'''≠'''] [/ ? not equal to] @@ -149,6 +151,9 @@ [template rchev[]'''⟩'''] [/ right chevron] [template rflat[]'''⟮'''] [/ right flat bracket Misc Math Symbol A] [template lflat[]'''⟮'''] [/ left flat bracket] +[template mapsto[]'''↦'''] [/ function maps to https://en.wikipedia.org/wiki/Maplet ] + [template obelus[]'''÷'''] [/ division dot over and under dash divide symbol] + [/ U2000.pdf punctuation] [template endash[]'''–'''] [/ em width dash] [template emdash[]'''—'''] [/ en width dash] @@ -212,6 +217,9 @@ [template plusmn[]'''±'''] [/ plus-minus sign = plus-or-minus sign ] [template micro[]'''µ'''] [/ micro sign ] [template cedil[]'''¸'''] [/ cedilla = spacing cedilla ] +[template ccedil[]'''ç'''] [/ small c with cedilla ] +[template Ccedil[]'''ç'''] [/ capial C with cedilla ] + [template ordm[]'''º'''] [/ masculine ordinal indicator ] [template ordf[]'''ª'''] [/ feminine ordinal indicator ] [template laquo[]'''«'''] [/ left-pointing double angle quotation mark = left pointing guillemet ] diff --git a/doc/index.html b/doc/index.html deleted file mode 100644 index e9ea26091..000000000 --- a/doc/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - Automatic redirection failed, please go to - html/index.html -

        Copyright Daryle Walker, Hubert Holin and John Maddock 2013

        -

        Distributed under the Boost Software License, Version 1.0. (See accompanying file - LICENSE_1_0.txt or copy at www.boost.org/LICENSE_1_0.txt).

        - - - - - - - - diff --git a/doc/math.qbk b/doc/math.qbk index c8b2c5389..97241c702 100644 --- a/doc/math.qbk +++ b/doc/math.qbk @@ -1,5 +1,5 @@ [book Math Toolkit - [quickbook 1.6] + [quickbook 1.7] [copyright 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2017 Nikhar Agrawal, Anton Bikineev, Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno Lalande, John Maddock, Jeremy Murphy, Johan RÃ¥de, Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle Walker and Xiaogang Zhang] [/purpose ISBN 0-9504833-2-X 978-0-9504833-2-0, Classification 519.2-dc22] [license @@ -216,6 +216,15 @@ and use the function's name as the link text.] [def __sph_hankel_1 [link math_toolkit.hankel.sph_hankel sph_hankel_1]] [def __sph_hankel_2 [link math_toolkit.hankel.sph_hankel sph_hankel_2]] +[/Lambert W function] +[def __lambert_w [link math_toolkit.sf_lambert_w.lambert_w_function lambert_w]] +[def __lambert_w_implementation [link math_toolkit.lambert_w.implementation implementation]] +[def __lambert_w_precision[link math_toolkit.lambert_w.precision precision]] +[def __lambert_w_examples [link math_toolkit.lambert_w.examples examples]] +[def __lambert_w_wm1_near_zero [link math_toolkit.lambert_w.wm1_near_zero wm1_near_zero]] +[def __lambert_w_references [link math_toolkit.lambert_w.references references]] + + [/Airy Functions] [def __airy_ai [link math_toolkit.airy.ai airy_ai]] [def __airy_bi [link math_toolkit.airy.bi airy_bi]] @@ -379,6 +388,8 @@ and use the function's name as the link text.] [def __real_concept [link math_toolkit.real_concepts real concept]] [def __next_float [link math_toolkit.next_float Adjacent Floating-Point Values]] +[def __remez_practical [link math_toolkit.remez.practical remez_practical]] + [/ External links] [def __gsl [@http://www.gnu.org/software/gsl/ GSL-1.9]] @@ -406,6 +417,8 @@ and use the function's name as the link text.] [def __errno [@http://en.wikipedia.org/wiki/Errno `::errno`]] [def __Mathworld [@http://mathworld.wolfram.com Wolfram MathWorld]] [def __Mathematica [@http://www.wolfram.com/products/mathematica/index.html Wolfram Mathematica]] +[def __Maple [@https://www.maplesoft.com Maple]] + [def __WolframAlpha [@http://www.wolframalpha.com/ Wolfram Alpha]] [def __TOMS748 [@http://portal.acm.org/citation.cfm?id=210111 TOMS Algorithm 748: enclosing zeros of continuous functions]] [def __TOMS910 [@http://portal.acm.org/citation.cfm?id=1916469 TOMS Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations]] @@ -420,7 +433,7 @@ and use the function's name as the link text.] [def __epsilon [@http://en.wikipedia.org/wiki/Machine_epsilon machine epsilon]] [def __ADL [@http://en.cppreference.com/w/cpp/language/adl Argument Dependent Lookup (ADL)]] [def __function_template_instantiation [@http://en.cppreference.com/w/cpp/language/function_template Function template instantiation]] -[def __fundamental_types [@http://en.cppreference.com/w/cpp/language/types fundamental types]] +[def __fundamental_types [@http://en.cppreference.com/w/cpp/language/types fundamental (built-in) types]] [def __guard_digits [@http://en.wikipedia.org/wiki/Guard_digit guard digits]] [def __SSE2 [@http://en.wikipedia.org/wiki/SSE2 SSE2 instructions]] [def __representable [@http://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding representable]] @@ -429,6 +442,12 @@ and use the function's name as the link text.] [def __random_variable [@http://en.wikipedia.org/wiki/Random_variable random variable]] [def __probability_distribution [@http://en.wikipedia.org/wiki/Probability_distribution probability_distribution]] [def __C_math [@http://www.cplusplus.com/reference/cmath/ C math functions]] +[def __Lambert_W [@http://en.wikipedia.org/wiki/Lambert_W_function Lambert W function]] +[def __CUDA [@https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-cplusplus-language-support CUDA NVidia GPU C/C++ language support]] +[def __Luu_thesis [@http://discovery.ucl.ac.uk/1482128/1/Luu_thesis.pdf Thomas Luu, Thesis, University College London (2015)]] +[def __rational_function [@https://en.wikipedia.org/wiki/Rational_function rational function]] +[def __remez [@http://en.wikipedia.org/wiki/Remez_algorithm Remez algorithm]] +[def __ULP [@https://en.wikipedia.org/wiki/Unit_in_the_last_place Unit in the Last Place]] [/ Some composite templates] [template super[x]''''''[x]''''''] @@ -594,6 +613,9 @@ and as a CD ISBN 0-9504833-2-X 978-0-9504833-2-0, Classification 519.2-dc22. [include sf/jacobi_elliptic.qbk] +[/ Lambert W function] +[include sf/lambert_w.qbk] + [section:zetas Zeta Functions] [include sf/zeta.qbk] [endsect] diff --git a/doc/policies/policy_tutorial.qbk b/doc/policies/policy_tutorial.qbk index ff0d92fe0..537734496 100644 --- a/doc/policies/policy_tutorial.qbk +++ b/doc/policies/policy_tutorial.qbk @@ -15,20 +15,30 @@ control: and `double` to `long double` in order to improve precision. * What precision to use when calculating the result. -Some of these policies could arguably be runtime variables, but then we couldn't +Some of these policies could arguably be run-time variables, but then we couldn't use compile-time dispatch internally to select the best evaluation method for the given policies. For this reason a Policy is a /type/: in fact it's an instance of the class template `boost::math::policies::policy<>`. This class is just a -compile-time-container of user-selected policies (sometimes called a type-list): +compile-time-container of user-selected policies (sometimes called a type-list). + +Over a dozen __policy_defaults are provided, so most of the time you can ignore the policy framework, +but you can overwrite the defaults with your own policies to give detailed control, for example: using namespace boost::math::policies; - // - // Define a policy that sets ::errno on overflow, and does - // not promote double to long double internally: - // - typedef policy, promote_double > mypolicy; + + // Define a policy that sets ::errno on overflow, + // and does not promote double to long double internally, + // and only aims for precision of only 3 decimal digits, + // to an error-handling policy, usually to trade precision for speed: + + typedef policy + < + domain_error, + promote_double, + digits10<3> + > my_policy; [endsect] [/section:what_is_a_policy So Just What is a Policy Anyway?] @@ -83,13 +93,28 @@ inherits the defaults for any policies not explicitly set, so given: // // Define a policy that sets ::errno on overflow, and does // not promote double to long double internally: - // - typedef policy, promote_double > mypolicy; -then `mypolicy` defines a policy where only the overflow error handling and + typedef policy + < + domain_error, + promote_double + > my_policy; + +then `my_policy` defines a policy where only the overflow error handling and `double`-promotion policies differ from the defaults. -[endsect][/section:policy_tut_defaults Policies Have Sensible Defaults] +We can also add a desired precision, for example, 9 bits or 3 decimal digits, +to an error-handling policy, usually to trade precision for speed: + + typedef policy, digit2<9> > my_policy; + +Or if you want to further modify an existing user policy, use `normalise`: + + using boost::math::policies::normalise; + + typedef normalise>::type my_policy_9; // errno on error, and limited precision. + +[endsect] [/section:policy_tut_defaults Policies Have Sensible Defaults] [section:policy_usage So How are Policies Used Anyway?] @@ -97,7 +122,7 @@ The details follow later, but basically policies can be set by either: * Defining some macros that change the default behaviour: [*this is the recommended method for setting installation-wide policies]. -* By instantiating a distribution object with an explicit policy: +* By instantiating a statistical distribution object with an explicit policy: this is mainly reserved for ad hoc policy changes. * By passing a policy to a special function as an optional final argument: this is mainly reserved for ad hoc policy changes. @@ -297,11 +322,11 @@ Handling policies for the statistical distributions is very similar except that the macro BOOST_MATH_DECLARE_DISTRIBUTIONS accepts two parameters: the floating point type to use, and the policy type to apply. For example: - BOOST_MATH_DECLARE_DISTRIBUTIONS(double, mypolicy) + BOOST_MATH_DECLARE_DISTRIBUTIONS(double, my_policy) Results a set of typedefs being defined like this: - typedef boost::math::normal_distribution normal; + typedef boost::math::normal_distribution normal; The name of each typedef is the same as the name of the distribution class template, but without the "_distribution" suffix. diff --git a/doc/sf/lambert_w.qbk b/doc/sf/lambert_w.qbk new file mode 100644 index 000000000..c390df87b --- /dev/null +++ b/doc/sf/lambert_w.qbk @@ -0,0 +1,825 @@ +[section:lambert_w Lambert W function] + +[h4:synopsis Synopsis] + +`` +#include +`` + + namespace boost { namespace math { + + template + ``__sf_result`` lambert_w0(T z); // W0 branch, default policy. + template + ``__sf_result`` lambert_wm1(T z); // W-1 branch, default policy. + template + ``__sf_result`` lambert_w0_prime(T z); // W0 branch 1st derivative. + template + ``__sf_result`` lambert_wm1_prime(T z); // W-1 branch 1st derivative. + + template + ``__sf_result`` lambert_w0(T z, const ``__Policy``&); // W0 with policy. + template + ``__sf_result`` lambert_wm1(T z, const ``__Policy``&); // W-1 with policy. + template + ``__sf_result`` lambert_w0_prime(T z, const ``__Policy``&); // W0 derivative with policy. + template + ``__sf_result`` lambert_wm1_prime(T z, const ``__Policy``&); // W-1 derivative with policy. + + } // namespace boost + } // namespace math + + +[h4:description Description] + +The __Lambert_W is the solution of the equation ['W[dot]e[super W] = z]. +It is also called the Omega function, the inverse function of ['f(W) = W e[super W]]. + +On the z-interval \[0, [infin]\] there is just one real solution. +On the interval (-1/e, 0) there are two real solutions on two branches called variously W0, W+, W-1, Wp, Wm, W-. +Two principal branches functions called `lambert_w0` and `lambert_wm1`, and their 1st derivative, +are provided by Boost's real-only implementation. + +In the graphs below, the red line is the W0 branch, and the blue line the W-1 branch. + +[import ../../example/lambert_w_graph.cpp] + +[graph lambert_w_graph] +[graph lambert_w_graph_big_w] +[graph lambert_w0_prime_graph] +[graph lambert_wm1_prime_graph] + +There is a singularity where the branches meet at argument [^z = -1/e] [cong] [^ -0.367879...] and [^W = -1]. + +This implementation computes the two real (not complex) branches W0 and W-1 +with the functions `lambert_w0` and `lambert_wm1`, +and their 1st derivatives, `lambert_w0_prime` and `lambert_wm1_prime`, +from the first [^z] argument. + +For C++ types `float` the maximum z argument `(std::numeric_limits<>::max)()` +Lambert_w0(3.2e+38) = 84.29, +and for `double`, lambert_w0(1.797e+308) = 703.3. + +The final __Policy argument is optional and can be used to control the behaviour of the function: +how it handles errors. + +Refer to __policy_section for more details and see examples below. + +[h5:applications Applications of the Lambert W function] + +The Lambert W function has a myriad of applications. +Corless et al. provide a comprehensive description of the range of applications, +from the mathematical, like iterated exponentiation and asymptotic roots of trinomials, +to the real-world, such as the range of a jet plane, enzyme kinetics, water movement in soil, +epidemics, and diode current (an example replicated [@../../example/lambert_w_diode.cpp here]). +Since the publication of their landmark paper, there have been many more applications, and +also many new implementations of the function, upon which this implementation builds. + +[h4:examples Examples] + +[import ../../example/lambert_w_simple_examples.cpp] + +You will usually need the following `#include` and `using` statements: + +[lambert_w_simple_examples_includes] + +Some examples follow, starting with the trivial: + +[lambert_w_simple_examples_0] + +Other floating-point types can be used too, here `float`, +including user defined types like __multiprecision. +It is convenient to use a function like `show_value` +to display all (and only) potentially significant decimal digits, including any significant trailing zeros, +(`std::numeric_limits::max_digits10`) for the type `T`. + +[lambert_w_simple_examples_1] + +Example of an integer argument to `lambert_w0`, +showing that an `int` literal is correctly promoted to a `double`. + +[lambert_w_simple_examples_2] + +Using __multiprecision types to get much higher precision is painless. + +[lambert_w_simple_examples_3] + +[warning When using multiprecision, take very great care not to +construct or assign non-integers from `double`, `float` ... silently losing precision. +Use `"1.2345678901234567890123456789"` rather than `1.2345678901234567890123456789`.] + +Using multiprecision types, it is all too easy to get multiprecision precision wrong! + +[lambert_w_simple_examples_4] + +[note See spurious non-seven decimal digits appearing after digit #17 in the argument 0.7777777777777777...!] + +And similarly constructing from a literal `double 0.9`, with more random digits after digit number 17. + +[lambert_w_simple_examples_4a] + +Note how the `cpp_float_dec_50` result is only as correct as from a `double = 0.9`. + +Now see the correct result for all 50 decimal digits constructing from a decimal digit string "0.9": + +[lambert_w_simple_examples_4b] + +Note the expected zeros for all places up to 50 - and the correct Lambert W result! + +(It is just as easy to compute even higher precisions, +at least to thousands of decimal digits, but not shown here for brevity. +See [@../../example/lambert_w_simple_examples.cpp lambert_w_simple_examples.cpp] +for comparison of an evaluation at 1000 decimal digit precision with __WolframAlpha). + +Policies can be used to control what action to take on errors: + +[lambert_w_simple_examples_error_policies] + +[tip Always use [^try'n'catch] blocks to ensure you get an error message like this:] + +[lambert_w_simple_examples_error_message_1] + +Show error reporting if a value is passed to `lambert_w0` that is out of range, +(and was probably meant to be passed to `lambert_wm1` instead). + +[lambert_w_simple_examples_out_of_range] + +The full source of these examples is at [@../../example/lambert_w_simple_examples.cpp lambert_w_simple_examples.cpp] + +[h5:diode_resistance Diode Resistance Example] + +[import ../../example/lambert_w_diode_graph.cpp] + +A typical example of a practical application is estimating the current flow +through a diode with series resistance from a paper by Banwell and Jayakumar. + +Having the Lambert W function available makes it simple to reproduce the plot +in their paper (Fig 2) comparing estimates using with Lambert W function +and some actual measurements. +The colored curves show the effect of various series resistance on the current +compared to an extrapolated line in grey with no internal (or external) resistance. + +Two formulae relating the diode current and effect of series resistance can be combined, +but yield an otherwise intractable equation relating the current versus voltage +with a varying series resistance. This was reformulated as a +generalized equation in terms of the Lambert W function: + +Banwell and Jakaumar equation 5 + +[sixemspace][space] I(V) = [mu] V[sub T]/ R [sub S] [dot] W[sub 0](I[sub 0] R[sub S] / ([mu] V[sub T])) + +Using these variables + +[lambert_w_diode_graph_1] + +the formula can be rendered in C++ + +[lambert_w_diode_graph_2] + +to reproduce their Fig 2: + +[graph diode_iv_plot] + +The plotted points for no external series resistance +(derived from their published plot as the raw data are not publicly available) +are used to extrapolate back to estimate the intrinsic emitter resistance as 0.3 ohm. +Its effect is visible when the straight line from low voltages starts to curve. + +See [@../../example/lambert_w_diode.cpp lambert_w_diode.cpp] and +[@../../example/lambert_w_diode_graph.cpp lambert_w_diode_graph.cpp] +for details of the calculation. + +[h5:implementations Existing implementations] + +The function is described in Wolfram Mathworld as +[@http://mathworld.wolfram.com/LambertW-Function.html [^Lambert W function]]. +The principal value of the Lambert W function is implemented in the Wolfram Language as ['ProductLog\[k, z\]] +where k is the branch, by default zero, but also the negative branch when k = -1. + +__WolframAlpha has provided many reference values for testing. +For example, the output from [@https://www.wolframalpha.com/input/?i=productlog(1)] +is 0.56714329040978387299996866221035554975381578718651... + +Also the [@https://www.wolframalpha.com Wolfram language] command: [^N\[ProductLog\[-1\], 50\]] + +produces the same output to the specified 50 decimal digit precision. + +The symbolic algebra program __Maple also computes Lambert W to an arbitrary precision. + +[h4:precision Controlling the compromise between Precision and Speed] + +[h5:small_floats Floating-point types `double` and `float`] +This implementation provides good precision and excellent speed for __fundamental types `float` and `double`. + +All the functions usually return values within a few __ulp +for the floating-point type, except for very small arguments very near zero, +and for arguments very close to the singularity at the branch point. + +By default, this implementation provides the best possible speed. +Very slightly average higher precision and less bias might be obtained +by adding a __halley step refinement, +but at the cost of more than doubling the run time. + +[h5:big_floats Floating-point types larger than double] + +For floating-point types with precision greater than `double` and `float` __fundamental_types, +a `double` evaluation is used as a first approximation followed by Halley refinement, +using a single step where it can be predicted that this will be sufficient, +and only using __halley iteration when necessary. +Higher precision types are always going to be [*very, very much slower]. + +The 'best' evaluation (the nearest __representable) can be achieved by `static_cast`ing from a +higher precision type, typically a __multiprecision type like `cpp_bin_float_50`, +but at the cost of increasing run-time >100-fold; +this has been used here to provide our reference values for testing. + +[import ../../example/lambert_w_precision_example.cpp] + +For example, we get a reference value using a high precision type, for example; + + using boost::multiprecision::cpp_bin_float_50; + +that uses Halley iteration to refine until it is as precise as possible for this `cpp_bin_float_50` type. + +As a further check we can compare this with a __WolframAlpha computation +using command [^N\[ProductLog\[10.\], 50\]] to get 50 decimal digits +and similarly [^N\[ProductLog\[10.\], 17\]] to get the nearest representable for 64-bit `double` precision. + +[lambert_w_precision_reference_w] + +giving us the same nearest representable using 64-bit `double` as `1.7455280027406994`. + +However, the rational polynomial and Fukushima Schroder approximations are so good for type `float` and `double` +that negligible improvement is gained from a `double` Halley step. + +This is shown with [@../../example/lambert_w_precision_example.cpp lambert_w_precision_example.cpp] +for Lambert W0: + +[lambert_w_precision_1] + +with this output: + +[lambert_w_precision_output_1] + +and then for W-1: + +[lambert_w_precision_2] + +with this output: + +[lambert_w_precision_output_2] + +[h5:differences_distribution Distribution of differences from 'best' `double` evaluations] + +The distribution of differences from 'best' are shown in these graphs comparing `double` precision evaluations +with reference 'best' z50 evaluations using `cpp_bin_float_50` type reduced to `double` with `static_cast::max)()` returns W for the floating_point type T, (for example, for `double`, 703.22703310477018L) +* `z == std::numeric_limits::infinity()` throws a `overflow_error`. + +(An argument z passed as infinity probably indicates that something has already gone wrong, +but if it is desired to return infinity, +this case should be handled before calling `lambert_w0`). + +[h5:wm1_edges w-1 Branch] + +The range mathematically is -1/e <= z <= 0, but numerically, + +* `z == -1/e` (for the type T) returns exactly -1. +* `z == 0` returns -[infin] (or the nearest equivalent if `std::has_infinity == false`). +* `z == std::numeric_limits::min()` returns the maximum (most negative) possible value of Lambert W for the type T. [br] +For example, for `double`: lambert_wm1(-2.2250738585072014e-308) = -714.96865723796634 [br] +and for `float`: lambert_wm1(-1.17549435e-38) = -91.8567734 [br] + +* `z < std::numeric_limits::min()`, means that z is zero or denormalized (if `std::numeric_limits::has_denorm_min == true`), +for example: `r = lambert_wm1(std::numeric_limits::denorm_min());` and an overflow_error exception is thrown, +and will give a message like: + + Error in function boost::math::lambert_wm1(): + Argument z = -4.9406564584124654e-324 is too small + (z < -std::numeric_limits::min so denormalized) for Lambert W-1 branch! + +Denormalized values of z are not supported for Lambert W-1 (because not all floating-point types denormalize), +and anyway it only covers a tiny fraction of the range of possible z arguments values. + +[h4:compilers Compilers] + +The code has been tested on VS 15.7.5, Clang 6.0.0 and GCC 8.1.0; it is expected to work on all C++ compilers. +As expected, debug mode is very much slower. The release version optimisation also varied. + +[h5:diagnostics Diagnostics Macros] + +Several macros are provided to output diagnostic information (potentially [*much] output). +These can be statements, for example: + +`#define BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS` + +placed [*before] the `lambert_w` include statement + +`#include `, + +or defined on the project compile command-line: `/DBOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS`, + +or defined in a jamfile.v2: `BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS` + +[import ../../include/boost/math/special_functions/lambert_w.hpp] + +[boost_math_instrument_lambert_w_macros] + +[h4:implementation Implementation] + +There are many previous implementations, each with increasing accuracy and/or speed. +See __lambert_w_references below. + +For most of the range of ['z] arguments, some initial approximation followed by a single refinement, +often using Halley or similar method, gives a useful precision. +For speed, several implementations avoid evaluation of a iteration test using the exponential function, +estimating that a single refinement step will suffice, +but these rarely get to the best result possible. +To get a better precision, additional refinements, probably iterative, are needed +for example, using __halley or __schroder methods. + +For C++, the most precise results possible, closest to the nearest __representable for the C++ type being used, +it is usually necessary to use a higher precision type for intermediate computation, +finally static-casting back to the smaller desired result type. +This strategy is used by __Maple and WolframAlpha, for example, using arbitrary precision arithmetic, +and some of their high-precision values are used for testing this library. +This method is also used to provide __boost_test values using __multiprecision, +typically, a 50 decimal digit type like `cpp_bin_float_50` +`static_cast` to a `float`, `double` or `long double` type. + +For ['z] argument values near the singularity and near zero, other approximations may be used, +possibly followed by refinement or increasing number of series terms until a desired precision is achieved. +At extreme arguments near to zero or the singularity at the branch point, +even this fails and the only method to achieve a really close result is to cast from a higher precision type. + +In practical applications, the increased computation required +(often towards a thousand-fold slower and requiring much additional code for __multiprecision) +is not justified and the algorithms here do not implement this. +But because the Boost.Lambert_W algorithms has been tested using __multiprecision, +users who require this can always easily achieve the nearest representation for the fundamental_types +- if the application justifies the very large extra computation cost. + +[h5 Evolution of this implementation] + +One compact real-only implementation was based on an algorithm by __Luu_thesis, +(see routine 11 on page 98 for his Lambert W algorithm) +and his Halley refinement is used iteratively when required. +A first implementation was based on Thomas Luu's code posted at +[@https://svn.boost.org/trac/boost/ticket/11027 Boost Trac \#11027]. +It has been implemented from Luu's algorithm but templated on `RealType` parameter and result +and handles both __fundamental_types (`float, double, long double`), __multiprecision, +and also has been tested successfully with a proposed fixed_point type. + +A first approximation was computed using the method of Barry et al (see references 5 & 6 below). +This was extended to the widely used [@https://people.sc.fsu.edu/~jburkardt/f_src/toms443/toms443.html TOMS443] +FORTRAN and C++ versions by John Burkardt using Schroeder refinement(s). +(For users only requiring an accuracy of relative accuracy of 0.02%, Barry's function alone might suffice, +but a better __rational_function approximation method has since been developed for this implementation). + +We also considered using __newton method. +`` + f(w) = w e^w -z = 0 // Luu equation 6.37 + f'(w) = e^w (1 + w), Wolfram alpha (d)/(dw)(f(w) = w exp(w) - z) = e^w (w + 1) + if (f(w) / f'(w) -1 < tolerance + w1 = w0 - (expw0 * (w0 + 1)); // Refine new Newton/Raphson estimate. +`` +but concluded that since the Newton-Raphson method takes typically 6 iterations to converge within tolerance, +whereas Halley usually takes only 1 to 3 iterations to achieve an result within 1 __ulp, +so the Newton-Raphson method is unlikely to be quicker +than the additional cost of computing the 2nd derivative for Halley's method. + +Halley refinement uses the simplified formulae obtained from +[@http://www.wolframalpha.com/input/?i=%5B2(z+exp(z)-w)+d%2Fdx+(z+exp(z)-w)%5D+%2F+%5B2+(d%2Fdx+(z+exp(z)-w))%5E2+-+(z+exp(z)-w)+d%5E2%2Fdx%5E2+(z+exp(z)-w)%5D Wolfram Alpha] + + [2(z exp(z)-w) d/dx (z exp(z)-w)] / [2 (d/dx (z exp(z)-w))^2 - (z exp(z)-w) d^2/dx^2 (z exp(z)-w)] + +[h4:compact_implementation Implementing Compact Algorithms] + +The most compact algorithm can probably be implemented using the log approximation of Corless et al. +followed by Halley iteration (but is also slowest and least precise near zero and near the branch singularity). + +[h4:faster_implementation Implementing Faster Algorithms] + +More recently, the Tosio Fukushima has developed an even faster algorithm, +avoiding any transcendental function calls as these are necessarily expensive. +The current implementation of Lambert W-1 is based on his algorithm +starting with a translation from Fukushima's FORTRAN into C++ by Darko Veberic. + +Many applications of the Lambert W function make many repeated evaluations for Monte Carlo methods; +for these applications speed is very important. +Luu, and Chapeau-Blondeau and Monir provide typical usage examples. + +Fukushima improves the important observation that much of the execution time of all +previous iterative algorithms was spent evaluating transcendental functions, usually `exp`. +He has put a lot of work into avoiding any slow transcendental functions by using lookup tables and +bisection, finishing with a single Schroeder refinement, +without any check on the final precision of the result (necessarily evaluating an expensive exponential). + +Theoretical and practical tests confirm that Fukushima's algorithm gives Lambert W estimates +with a known small error bound (several __ulp) over nearly all the range of ['z] argument. + +A mean difference was computed to express the typical error and is often about 0.5 epsilon, +the theoretical minimum. Using the __float_distance, we can also express this as the number of +bits that are different from the nearest representable or 'exact' or 'best' value. +The number and distribution of these few bits differences was studied by binning, including their sign. +Bins for (signed) 0, 1, 2 and 3 and 4 bits proved suitable. + +However, though these give results within a few __epsilon of the nearest representable result, +they do not get as close as is very often possible with further refinement, +nrealy always to within one or two __epsilon. + +More significantly, the evaluations of the sum of all signed differences using the Fukshima algorithm +show a slight bias, being more likely to be a bit or few below the nearest representation than above; +bias might have unwanted effects on some statistical computations. + +Fukushima's method also does not cover the full range of z arguments of 'float' precision and above. + +For this implementation of Lambert W0, John Maddock used the Boost.Math `minimax.exe` __remez method program +to devise a __rational_function for several ranges of argument z for the W0 branch of Lambert W0 function. +These minimax rational approximations are combined for an algorithm that is both smaller and faster. + +Sadly it has not proved practical to use the same __remez method for Lambert W-1 branch +and so the Fukushima algorithm is retained for this branch. + +An advantage of both minimax rational __remez approximations +is that the [*distribution] from the reference values is reasonably random and insignificantly biased. + +For example, table below a test of Lambert W0 10000 values of argument z covering the main range of possible values, +10000 comparisons from z = 0.0501 to 703, in 0.001 step factor 1.05 when module 7 == 0 + +[table:lambert_w0_Fukushima Fukushima Lambert W0 and typical improvement from a single Halley step. +[[Method] [Exact] [One_bit] [Two_bits] [Few_bits] [inexact] [bias]] +[[Schroeder W0] [8804 ] [ 1154 ] [ 37 ] [ 5 ] [1243 ] [-1193]] +[[after Halley step] [ 9710 ] [ 288 ] [ 2 ] [0] [ 292 ] [22]] +] [/table:lambert_w0_Fukushima W0] + +Lambert W0 values computed using the Fukushima method with Schroeder refinement gave about 1/6 lambert_w0 values that are +one bit different from the 'best', and < 1% that are a few bits 'wrong'. +If a Halley refinement step is added, only 1 in 30 are even one bit different, and only 2 two bits 'wrong'. + +[table:lambert_w0_plus_halley Rational polynomial Lambert W0 and typical improvement from a single Halley step. +[[Method] [Exact] [One_bit] [Two_bits] [Few_bits] [inexact] [bias]] +[[rational/polynomial] [7135] [ 2863 ] [ 2 ] [0] [ 2867 ] [ -59] ] +[[after Halley step ] [ 9724 ] [273] [ 3 ] [0] [ 279 ] [5] ] +] [/table:lambert_w0_plus_halley] + +With the rational polynomial approximation method, there are a third one bit from the best and none more than two bits. +Adding a Halley step (or iteration) reduces the number that are one bit different from about a third down to one in 30; +this is unavoidable 'computational noise'. +An extra Halley step would double the runtime for a tiny gain and so is not chosen for this implementation, +but remains a option, as detailed above. + +For the Lambert W-1 branch, the Fukushima algorithm is used. + +[table:lambert_wm1_fukushima Lambert W-1 using Fukushima algorithm. +[[Method] [Exact] [One_bit] [Two_bits] [Few_bits] [inexact] [bias]] +[[Fukushima W-1] [ 7167] [2704 ] [ 129 ] [0] [2962 ] [-160]] +[[plus Halley step] [ 7379 ] [ 2529 ] [92 ] [0] [ 2713 ] [ 549]] +] [/table:lambert_wm1_fukushima] + +[/ generated using PAB j:\Cpp\Misc\lambert_w_compare_jm2\lambert_w_compare_jm2.cpp] + +[h5:lookup_tables Lookup tables] + +For speed during the bisection, Fukushima's algorithm computes lookup tables of powers of e and z for integral Lambert W. +There are 64 elements in these tables. The FORTRAN version (and the C++ translation by Veberic) +computed these (once) as `static` data. This is slower, may cause trouble with multithreading, and +is slightly inaccurate because of rounding errors from repeated(64) multiplications. + +In this implementation the array values have been computed using __multiprecision 50 decimal digit +and output as C++ arrays 37 decimal digit `long double` literals using `max_digits10` precision + + std::cout.precision(std::numeric_limits::max_digits10); + +The arrays are as `const` and `constexpr` and `static` as possible (for the compiler version), +using BOOST_STATIC_CONSTEXPR macro. +(See [@../../test/lambert_w_lookup_table_generator.cpp lambert_w_lookup_table_generator.cpp] +The precision was chosen to ensure that if used as `long double` arrays, +then the values output to +[@ ../../include/boost/math/special_functions/lambert_w_lookup_table.ipp lambert_w_lookup_table.ipp] +will be the nearest representable value for the type chose by a `typedef` in +[@../../include/boost/math/special_functions/lambert_w.hpp lambert_w.hpp]. + + typedef double lookup_t; // Type for lookup table (`double` or `float`, or even `long double`?) + +This is to allow for future use at higher precision, up to platforms that use 128-bit +(hardware or software) for their `long double` type. + +The accuracy of the tables was confirmed using __WolframAlpha and agrees at the 37th decimal place, +so ensuring that the value is exactly read into even 128-bit `long double` to the nearest representation. + +[h5:higher_precision Higher precision] + +For types more precise than `double`, Fukushima reported that it was best to use the `double` estimate +as a starting point, followed by refinement using __halley iterations or other methods; +our experience confirms this. + +Using __multiprecision it is simple to compute very high precision values of Lambert +W at least to thousands of decimal digits over most of the range of z arguments. + +For this reason, the lookup tables and bisection are only carried out at low precision, +usually `double`, chosen by the `typedef double lookup_t`. Unlike the FORTRAN version, +the lookup tables of Lambert_W of integral values are precomputed as C++ static arrays of floating-point literals. +The default is a `typedef` setting the type to `double`. +To allow users to vary the precision from `float` to `long double` these are computed to +128-bit precision to ensure that even platforms with `long double` do not lose precision. + +The FORTRAN version and translation only permits the z argument to be the largest +items in these lookup arrays, `wm0s[64] = 3.99049`, producing an error message and returning `NaN`. +So 64 is the largest possible value ever returned from the `lambert_w0` function. +This is far from the `std::numeric_limits<>::max()` for even `float`s. +Therefore this implementation uses an approximation or 'guess' and Halley's method to refine the result. +Logarithmic approximation is discussed at length by R.M.Corless et al. (page 349). +Here we use the first two terms of equation 4.19: + + T lz = log(z); + T llz = log(lz); + guess = lz - llz + (llz / lz); + +This gives a useful precision suitable for Halley refinement. + +Similarly, for Lambert W-1 branch, tiny values very near zero, +W > 64 cannot be computed using the lookup table. +For this region, an approximation followed by a few (usually 3) Halley refinements. +See __lambert_w_wm1_near_zero. + +For the less well-behaved regions for Lambert W0 ['z] arguments near zero, +and near the branch singularity at ['-1/e], some series functions are used. + +[h5:small_z Small values of argument z near zero] + +When argument ['z] is small and near zero, there is an efficient and accurate series evaluation method available +(implemented in `lambert_w0_small_z`). +There is no equivalent for the W-1 branch as this only covers argument `z < -1/e`. +The cutoff used `abs(z) < 0.05` is as found by trial and error by Fukushima. + +Coefficients of the inverted series expansion of the Lambert W function around `z = 0` +are computed following Fukushima using 17 terms of a Taylor series +computed using __Mathematica with + + InverseSeries[Series[z Exp[z],{z,0,17}]] + +See Tosio Fukushima, Journal of Computational and Applied Mathematics 244 (2013), page 86. + +To provide higher precision constants (34 decimal digits) for types larger than `long double`, + + InverseSeries[Series[z Exp[z],{z,0,34}]] + +were also computed, but for current hardware it was found that evaluating a `double` precision +and then refining with Halley's method was quicker and more accurate. + +Decimal values of specifications for built-in floating-point types below +are 21 digits precision == `std::numeric_limits::max_digits10` for `long double`. + +Specializations for `lambert_w0_small_z` are provided for +`float`, `double`, `long double`, `float128` and for __multiprecision types. + +The `tag_type` selection is based on the value `std::numeric_limits::max_digits10` +(and [*not] on the floating-point type T). +This distinguishes between `long double` types that commonly vary between 64 and 80-bits, +and also compilers that have a `float` type using 64 bits and/or `long double` using 128-bits. + +As noted in the __lambert_w_implementation section above, +it is only possible to ensure the nearest representable value by casting from a higher precision type, +computed at very, very much greater cost. + +For multiprecision types, first several terms of the series are tabulated and evaluated as a polynomial: +(this will save us a bunch of expensive calls to `pow`). +Then our series functor is initialized "as if" it had already reached term 18, +enough evaluation of built-in 64-bit double and float (and 80-bit `long double`) types. +Finally the functor is called repeatedly to compute as many additional series terms +as necessary to achive the desired precision, set from `get_epsilon` +(or terminated by `evaluation_error` on reaching the set iteration limit `max_series_iterations`). + +A little more than one decimal digit of precision is gained by each additional series term. +This allows computation of Lambert W near zero to at least 1000 decimal digit precision, +given sufficient compute time. + +[h4:near_singularity Argument z near the singularity at -1/e between branches W0 and W-1] + +Variants of Function `lambert_w_singularity_series` are used to handle ['z] arguments +which are near to the singularity at `z = -exp(-1) = -3.6787944` where the branches W0 and W-1 join. + +T. Fukushima / Journal of Computational and Applied Mathematics 244 (2013) 77-89 +describes using __Mathematica + + InverseSeries\[Series\[sqrt\[2(p Exp\[1 + p\] + 1)\], {p,-1, 20}\]\] + +to provide his Table 3, page 85. + +This implementation used __Mathematica to obtain 40 series terms at 50 decimal digit precision +`` + N\[InverseSeries\[Series\[Sqrt\[2(p Exp\[1 + p\] + 1)\], { p,-1,40 }\]\], 50\] + + -1+p-p^2/3+(11 p^3)/72-(43 p^4)/540+(769 p^5)/17280-(221 p^6)/8505+(680863 p^7)/43545600 ... +`` +These constants are computed at compile time for the full precision for any `RealType T` +using the original rationals from Fukushima Table 3. + +Longer decimal digits strings are rationals pre-evaluated using __Mathematica. +Some integer constants overflow, so largest size available is used, suffixed by `uLL`. + +Above the 14th term, the rationals exceed the range of `unsigned long long` and are replaced +by pre-computed decimal values at least 21 digits precision == `max_digits10` for `long double`. + +A macro `BOOST_MATH_TEST_VALUE` (defined in [@../../test/test_value.hpp test_value.hpp]) +taking a decimal floating-point literal was used +to allow testing with both built-in floating-point types like `double` +which have contructors taking literal decimal values like `3.14`, +[*and] also multiprecision and other User-defined Types that only provide full-precision construction +from decimal digit strings like `"3.14"`. +(Construction of multiprecision types from built-in floating-point types +only provides the precision of the built-in type, like `double`, only 17 decimal digits). + +[tip Be exceeding careful not to silently lose precision by constructing multiprecision types from literal decimal types, usually [^double]. Use decimal digit strings like "3.1459" instead. See examples.] + +Fukushima's implementation used 20 series terms; it was confirmed that using more terms does not usefully increase accuracy. + +[h5:wm1_near_zero Lambert W-1 arguments values very near zero.] + +The lookup tables of Fukushima have only 64 elements, +so that the z argument nearest zero is -1.0264389699511303e-26, +that corresponds to a maximum Lambert W-1 value of 64.0. +Fukushima's implementation did not cater for z argument values that are smaller (nearer to zero), +but this implementation adds code to accept smaller (but not denormalised) values of z. +A crude approximation for these very small values is to take the exponent and multiply by ln[10] ~= 2.3. +We also tried the approximation first proposed by Corless et al. using ln(-z), (equation 4.19 page 349) +and then tried improving by a 2nd term -ln(ln(-z)), and finally the ratio term -ln(ln(-z))/ln(-z). + +For a z very close to z = -1.0264389699511303e-26 when W = 64, when effect of ln(ln(-z) term, and ratio L1/L2 is greatest, +the possible 'guesses' are + + z = -1.e-26, w = -64.02, guess = -64.0277, ln(-z) = -59.8672, ln(-ln(-z) = 4.0921, llz/lz = -0.0684 + +whereas at the minimum (unnormalized) z + + z = -2.2250e-308, w = -714.9, guess = -714.9687, ln(-z) = -708.3964, ln(-ln(-z) = 6.5630, llz/lz = -0.0092 + +Although the addition of the 3rd ratio term did not reduce the number of Halley iterations needed, +it might allow return of a better low precision estimate [*without any Halley iterations]. +For the worst case near w = 64, the error in the 'guess' is 0.008, ratio 0.0001 or 1 in 10,000 digits 10 ~= 4. +Two log evalutations are still needed, but is probably over an order of magnitude faster. + +Halley's method was then used to refine the estimate of Lambert W-1 from this guess. +Experiments showed that although all approximations reached with __ulp of the closest representable value, +the computational cost of the log functions was easily paid by far fewer iterations +(typically from 8 down to 4 iterations for double or float). +[h5:halley Halley refinement] + +After obtaining a double approximation, for `double`, `long double` and `quad` 128-bit precision, +a single iteration should suffice because +Halley iteration should triple the precision with each step +(as long as the function is well behaved - and it is), +and since we have at least half of the bits correct already, +one Halley step is ample to get to 128-bit precision. + + +[h5:lambert_w_derivatives Lambert W Derivatives] + +The derivatives are computed using the formulae in [@https://en.wikipedia.org/wiki/Lambert_W_function#Derivative Wikipedia]. + +[h4:testing Testing] + +Initial testing of the algorithm was done using a small number of spot tests. + +After it was established that the underlying algorithm (including unlimited Halley refinements with a tight terminating criterion) was correct, +some tables of Lambert W values were computed using a 100 decimal digit precision __multiprecision `cpp_dec_float_100` type and saved as +a C++ program that will initialise arrays of values of z arguments and lambert_W0 (`lambert_w_mp_high_values.ipp` and `lambert_w_mp_low_values.ipp` ). + +(A few of these pairs were checked against values computed by Wolfram Alpha to try to guard against mistakes; +all those tested agreed to the penultimate decimal place, so they can be considered reliable to at least 98 decimal digits precision). + +A macro `BOOST_MATH_TEST_VALUE` was used to allow tests with any real type, both __fundamental_types and __multiprecision. +(This is necessary because __fundamental_types have a constructor from floating-point literals like 3.1459F, 3.1459 or 3.1459L +whereas __multiprecision types may lose precision unless constructed from decimal digits strings like "3.1459"). + +The 100-decimal digits precision pairs were then used to assess the precision of less-precise types, including +__multiprecision `cpp_bin_float_quad` and `cpp_bin_float_50`. `static_cast`ing from the high precision types should +give the closest representable value of the less-precise type; this is then be used to assess the precision of +the Lambert W algorithm. + +Tests using +confirm that over nearly all the range of z arguments, +nearly all estimates are the nearest __representable value, a minority are within 1 __ulp and only a very few 2 ULP. + +[graph lambert_w0_errors_graph] +[graph lambert_wm1_errors_graph] + +For the range of z arguments over the range -0.35 to 0.5, a different algorithm is used, but the same +technique of evaluating reference values using a __multiprecision `cpp_dec_float_100` was used. +For extremely small z arguments, near zero, and those extremely near the singularity at the branch point, +precision can be much lower, as might be expected. + +See source at: +[@../../example/lambert_w_simple_examples.cpp lambert_w_simple_examples.cpp] +[@../../test/test_lambert_w.cpp test_lambert_w.cpp] contains routine tests using __boost_test. +[@../sf/lambert_w_errors_graph.cpp lambert_w_errors_graph.cpp] generating error graphs. + +[h5:quadrature_testing Testing with quadrature] + +A further method of testing over a wide range of argument z values was devised by Nick Thompson +(cunningly also to test the recently written quadrature routines!). +These are definite integral formulas involving the W function that are exactly known constants, +for example, LambertW0(1/(z[sup2]) == [sqrt](2[pi]), +see [@https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals Definite Integrals]. +Some care was needed to avoid overflow and underflow as the integral function must evaluate to a finite result over the entire range. + +[h5 Other implementations] + +The Lambert W has also been discussed in a [@http://lists.boost.org/Archives/boost/2016/09/230819.php Boost thread]. + +This also gives link to a prototype version by which also gives complex results [^(x < -exp(-1)], about -0.367879). +[@https://github.com/CzB404/lambert_w/ Balazs Cziraki 2016] +Physicist, PhD student at Eotvos Lorand University, ELTE TTK Institute of Physics, Budapest. +has also produced a prototype C++ library that can compute the Lambert W function for floating point +[*and complex number types]. +This is not implemented here but might be completed in the future. + +[h4:acknowledgements Acknowledgements] + +* Thanks to Wolfram for use of their invaluable online Wolfram Alpha service. +* Thanks for Mark Chapman for performing offline Wolfram computations. + +[h4:references References] + +# NIST Digital Library of Mathematical Functions. [@http://dlmf.nist.gov/4.13.F1]. + +# [@http://www.orcca.on.ca/LambertW/ Lambert W Poster], +R. M. Corless, G. H. Gonnet, D. E. G. Hare, D. J. Jeffery and D. E. Knuth, +On the Lambert W function Advances in Computational Mathematics, Vol 5, (1996) pp 329-359. + +# [@https://people.sc.fsu.edu/~jburkardt/f_src/toms443/toms443.html TOMS443], +Andrew Barry, S. J. Barry, Patricia Culligan-Hensley, +Algorithm 743: WAPR - A Fortran routine for calculating real values of the W-function,[br] +ACM Transactions on Mathematical Software, Volume 21, Number 2, June 1995, pages 172-181.[br] +BISECT approximates the W function using bisection (GNU licence). +Original FORTRAN77 version by Andrew Barry, S. J. Barry, Patricia Culligan-Hensley, +this version by C++ version by John Burkardt. + +# [@https://people.sc.fsu.edu/~jburkardt/f_src/toms743/toms743.html TOMS743] Fortran 90 (updated 2014). + +Initial guesses based on: + +# R.M.Corless, G.H.Gonnet, D.E.G.Hare, D.J.Jeffrey, and D.E.Knuth, +On the Lambert W function, Adv.Comput.Math., vol. 5, pp. 329 to 359, (1996). + +# D.A. Barry, J.-Y. Parlange, L. Li, H. Prommer, C.J. Cunningham, and +F. Stagnitti. Analytical approximations for real values of the Lambert +W-function. Mathematics and Computers in Simulation, 53(1), 95-103 (2000). + +# D.A. Barry, J.-Y. Parlange, L. Li, H. Prommer, C.J. Cunningham, and +F. Stagnitti. Erratum to analytical approximations for real values of the +Lambert W-function. Mathematics and Computers in Simulation, 59(6):543-543, 2002. + +# C++ __CUDA version of Luu algorithm, [@https://github.com/thomasluu/plog/blob/master/plog.cu plog]. + +# __Luu_thesis, see routine 11, page 98 for Lambert W algorithm. + +# Having Fun with Lambert W(x) Function, Darko Veberic +University of Nova Gorica, Slovenia IK, Forschungszentrum Karlsruhe, Germany, J. Stefan Institute, Ljubljana, Slovenia. + +# Fran[ccedil]ois Chapeau-Blondeau and Abdelilah Monir, Numerical Evaluation of the Lambert W Function and Application to Generation of Generalized +Gaussian Noise With Exponent 1/2, IEEE Transactions on Signal Processing, 50(9) (2002) 2160 - 2165. + +# Toshio Fukushima, Precise and fast computation of Lambert W-functions without transcendental function evaluations, Journal of Computational and Applied +Mathematics, 244 (2013) 77-89. + +# T.C. Banwell and A. Jayakumar, Electronic Letter, Feb 2000, 36(4), pages 291-2. +Exact analytical solution for current flow through diode with series resistance. +[@http://dx.doi.org/10.1049/el:20000301] + +# Princeton Companion to Applied Mathematics, +'The Lambert-W function', Section 1.3: Series and Generating Functions. + +# Cleve Moler, Mathworks blog [@https://blogs.mathworks.com/cleve/2013/09/02/the-lambert-w-function/#bfba4e2d-e049-45a6-8285-fe8b51d69ce7 The Lambert W Function] + +# Digital Library of Mathematical Function, [@https://dlmf.nist.gov/4.13 Lambert W function]. + +[endsect] [/section:lambert_w Lambert W function] + +[/ + Copyright 2016 John Maddock, Paul A. Bristow, Thomas Luu, Nicholas Thompson. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt + or copy at http://www.boost.org/LICENSE_1_0.txt). +] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 747f417b4..b9579378c 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -33,6 +33,9 @@ project msvc:/wd4456 # declaration of hides previous local declaration. #-msvc:/Za # nonfinite Serialization examples fail link if disable MS extensions, # because serialization library is built with MS extensions enabled (default). + clang:-Wno-unknown-pragmas + clang:-Wno-language-extension-token + ../../.. ../include_private off:../test//no_eh diff --git a/example/lambert_w_diode.cpp b/example/lambert_w_diode.cpp new file mode 100644 index 000000000..a62506850 --- /dev/null +++ b/example/lambert_w_diode.cpp @@ -0,0 +1,165 @@ +// Copyright Paul A. Bristow 2016 +// Copyright John Z. Maddock 2016 + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +/*! brief Example of using Lambert W function to compute current through a diode connected transistor with preset series resistance. + \details T. C. Banwell and A. Jayakumar, + Exact analytical solution of current flow through diode with series resistance, + Electron Letters, 36(4):291-2 (2000) + DOI: dx.doi.org/10.1049/el:20000301 + + The current through a diode connected NPN bipolar junction transistor (BJT) + type 2N2222 (See https://en.wikipedia.org/wiki/2N2222 and + https://www.fairchildsemi.com/datasheets/PN/PN2222.pdf Datasheet) + was measured, for a voltage between 0.3 to 1 volt, see Fig 2 for a log plot, + showing a knee visible at about 0.6 V. + + The transistor parameter isat was estimated to be 25 fA and the ideality factor = 1.0. + The intrinsic emitter resistance re was estimated from the rsat = 0 data to be 0.3 ohm. + + The solid curves in Figure 2 are calculated using equation 5 with rsat included with re. + + http://www3.imperial.ac.uk/pls/portallive/docs/1/7292572.PDF +*/ + +#include +using boost::math::lambert_w0; + +#include +// using std::cout; +// using std::endl; +#include +#include +#include +#include +#include + +/*! +Compute thermal voltage as a function of temperature, +about 25 mV at room temperature. +https://en.wikipedia.org/wiki/Boltzmann_constant#Role_in_semiconductor_physics:_the_thermal_voltage + +\param temperature Temperature (degrees centigrade). +*/ +const double v_thermal(double temperature) +{ + constexpr const double boltzmann_k = 1.38e-23; // joules/kelvin. + const double charge_q = 1.6021766208e-19; // Charge of an electron (columb). + double temp =+ 273; // Degrees C to K. + return boltzmann_k * temp / charge_q; +} // v_thermal + +/*! +Banwell & Jayakumar, equation 2 +*/ +double i(double isat, double vd, double vt, double nu) +{ + double i = isat * (exp(vd / (nu * vt)) - 1); + return i; +} // + +/*! + +Banwell & Jayakumar, Equation 4. +i current flow = isat +v voltage source. +isat reverse saturation current in equation 4. +(might implement equation 4 instead of simpler equation 5?). +vd voltage drop = v - i* rs (equation 1). +vt thermal voltage, 0.0257025 = 25 mV. +nu junction ideality factor (default = unity), also known as the emission coefficient. +re intrinsic emitter resistance, estimated to be 0.3 ohm from low current. +rsat reverse saturation current + +\param v Voltage V to compute current I(V). +\param vt Thermal voltage, for example 0.0257025 = 25 mV, computed from boltzmann_k * temp / charge_q; +\param rsat Resistance in series with the diode. +\param re Instrinsic emitter resistance (estimated to be 0.3 ohm from the Rs = 0 data) +\param isat Reverse saturation current (See equation 2). +\param nu Ideality factor (default = unity). + +\returns I amp as function of V volt. + +*/ +double iv(double v, double vt, double rsat, double re, double isat, double nu = 1.) +{ + // V thermal 0.0257025 = 25 mV + // was double i = (nu * vt/r) * lambert_w((i0 * r) / (nu * vt)); equ 5. + + rsat = rsat + re; + double i = nu * vt / rsat; + std::cout << "nu * vt / rsat = " << i << std::endl; // 0.000103223 + + double x = isat * rsat / (nu * vt); + std::cout << "isat * rsat / (nu * vt) = " << x << std::endl; + + double eterm = (v + isat * rsat) / (nu * vt); + std::cout << "(v + isat * rsat) / (nu * vt) = " << eterm << std::endl; + + double e = exp(eterm); + std::cout << "exp(eterm) = " << e << std::endl; + + double w0 = lambert_w0(x * e); + std::cout << "w0 = " << w0 << std::endl; + return i * w0 - isat; + +} // double iv + +std::array rss = {0., 2.18, 10., 51., 249}; // series resistance (ohm). +std::array vds = { 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 }; // Diode voltage. + +int main() +{ + try + { + std::cout << "Lambert W diode current example." << std::endl; + + //[lambert_w_diode_example_1 + double x = 0.01; + //std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.00990147 + + double nu = 1.0; // Assumed ideal. + double vt = v_thermal(25); // v thermal, Shockley equation, expect about 25 mV at room temperature. + double boltzmann_k = 1.38e-23; // joules/kelvin + double temp = 273 + 25; + double charge_q = 1.6e-19; // column + vt = boltzmann_k * temp / charge_q; + std::cout << "V thermal " + << vt << std::endl; // V thermal 0.0257025 = 25 mV + double rsat = 0.; + double isat = 25.e-15; // 25 fA; + std::cout << "Isat = " << isat << std::endl; + + double re = 0.3; // Estimated from slope of straight section of graph (equation 6). + + double v = 0.9; + double icalc = iv(v, vt, 249., re, isat); + + std::cout << "voltage = " << v << ", current = " << icalc << ", " << log(icalc) << std::endl; // voltage = 0.9, current = 0.00108485, -6.82631 + //] [/lambert_w_diode_example_1] + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} // int main() + +/* + Output: +//[lambert_w_output_1 + Lambert W diode current example. + V thermal 0.0257025 + Isat = 2.5e-14 + nu * vt / rsat = 0.000103099 + isat * rsat / (nu * vt) = 2.42486e-10 + (v + isat * rsat) / (nu * vt) = 35.016 + exp(eterm) = 1.61167e+15 + w0 = 10.5225 + voltage = 0.9, current = 0.00108485, -6.82631 + +//] [/lambert_w_output_1] +*/ + diff --git a/example/lambert_w_diode_graph.cpp b/example/lambert_w_diode_graph.cpp new file mode 100644 index 000000000..961fe7878 --- /dev/null +++ b/example/lambert_w_diode_graph.cpp @@ -0,0 +1,280 @@ +// Copyright Paul A. Bristow 2016 +// Copyright John Z. Maddock 2016 + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +/*! \brief Graph showing use of Lambert W function to compute current +through a diode-connected transistor with preset series resistance. + +\details T. C. Banwell and A. Jayakumar, +Exact analytical solution of current flow through diode with series resistance, +Electron Letters, 36(4):291-2 (2000). +DOI: dx.doi.org/10.1049/el:20000301 + +The current through a diode connected NPN bipolar junction transistor (BJT) +type 2N2222 (See https://en.wikipedia.org/wiki/2N2222 and +https://www.fairchildsemi.com/datasheets/PN/PN2222.pdf Datasheet) +was measured, for a voltage between 0.3 to 1 volt, see Fig 2 for a log plot, showing a knee visible at about 0.6 V. + +The transistor parameter I sat was estimated to be 25 fA and the ideality factor = 1.0. +The intrinsic emitter resistance re was estimated from the rsat = 0 data to be 0.3 ohm. + +The solid curves in Figure 2 are calculated using equation 5 with rsat included with re. + +http://www3.imperial.ac.uk/pls/portallive/docs/1/7292572.PDF + +*/ + +#include +using boost::math::lambert_w0; +#include +using boost::math::isfinite; +#include +using namespace boost::svg; + +#include +// using std::cout; +// using std::endl; +#include +#include +#include +#include +#include +#include +using std::pair; +#include +using std::map; +#include +using std::multiset; +#include +using std::numeric_limits; +#include // + +/*! +Compute thermal voltage as a function of temperature, +about 25 mV at room temperature. +https://en.wikipedia.org/wiki/Boltzmann_constant#Role_in_semiconductor_physics:_the_thermal_voltage + +\param temperature Temperature (degrees Celsius). +*/ +const double v_thermal(double temperature) +{ + BOOST_CONSTEXPR const double boltzmann_k = 1.38e-23; // joules/kelvin. + BOOST_CONSTEXPR double charge_q = 1.6021766208e-19; // Charge of an electron (columb). + double temp = +273; // Degrees C to K. + return boltzmann_k * temp / charge_q; +} // v_thermal + + /*! + Banwell & Jayakumar, equation 2, page 291. + */ +double i(double isat, double vd, double vt, double nu) +{ + double i = isat * (exp(vd / (nu * vt)) - 1); + return i; +} // + + /*! + Banwell & Jayakumar, Equation 4, page 291. + i current flow = isat + v voltage source. + isat reverse saturation current in equation 4. + (might implement equation 4 instead of simpler equation 5?). + vd voltage drop = v - i* rs (equation 1). + vt thermal voltage, 0.0257025 = 25 mV. + nu junction ideality factor (default = unity), also known as the emission coefficient. + re intrinsic emitter resistance, estimated to be 0.3 ohm from low current. + rsat reverse saturation current + + \param v Voltage V to compute current I(V). + \param vt Thermal voltage, for example 0.0257025 = 25 mV, computed from boltzmann_k * temp / charge_q; + \param rsat Resistance in series with the diode. + \param re Instrinsic emitter resistance (estimated to be 0.3 ohm from the Rs = 0 data) + \param isat Reverse saturation current (See equation 2). + \param nu Ideality factor (default = unity). + + \returns I amp as function of V volt. + */ + +//[lambert_w_diode_graph_2 +double iv(double v, double vt, double rsat, double re, double isat, double nu = 1.) +{ + // V thermal 0.0257025 = 25 mV + // was double i = (nu * vt/r) * lambert_w((i0 * r) / (nu * vt)); equ 5. + + rsat = rsat + re; + double i = nu * vt / rsat; + // std::cout << "nu * vt / rsat = " << i << std::endl; // 0.000103223 + + double x = isat * rsat / (nu * vt); +// std::cout << "isat * rsat / (nu * vt) = " << x << std::endl; + + double eterm = (v + isat * rsat) / (nu * vt); + // std::cout << "(v + isat * rsat) / (nu * vt) = " << eterm << std::endl; + + double e = exp(eterm); +// std::cout << "exp(eterm) = " << e << std::endl; + + double w0 = lambert_w0(x * e); +// std::cout << "w0 = " << w0 << std::endl; + return i * w0 - isat; +} // double iv + +//] [\lambert_w_diode_graph_2] + + +std::array rss = { 0., 2.18, 10., 51., 249 }; // series resistance (ohm). +std::array vds = { 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 }; // Diode voltage. +std::array lni = { -19.65, -15.75, -11.86, -7.97, -4.08, -0.0195, 3.6 }; // ln(current). + +int main() +{ + try + { + std::cout << "Lambert W diode current example." << std::endl; + +//[lambert_w_diode_graph_1 + double nu = 1.0; // Assumed ideal. + double vt = v_thermal(25); // v thermal, Shockley equation, expect about 25 mV at room temperature. + double boltzmann_k = 1.38e-23; // joules/kelvin + double temp = 273 + 25; + double charge_q = 1.6e-19; // column + vt = boltzmann_k * temp / charge_q; + std::cout << "V thermal " << vt << std::endl; // V thermal 0.0257025 = 25 mV + double rsat = 0.; + double isat = 25.e-15; // 25 fA; + std::cout << "Isat = " << isat << std::endl; + double re = 0.3; // Estimated from slope of straight section of graph (equation 6). + double v = 0.9; + double icalc = iv(v, vt, 249., re, isat); + std::cout << "voltage = " << v << ", current = " << icalc << ", " << log(icalc) << std::endl; // voltage = 0.9, current = 0.00108485, -6.82631 +//] [/lambert_w_diode_graph_1] + + // Plot a few measured data points. + std::map zero_data; // Extrapolated from slope of measurements with no external resistor. + zero_data[0.3] = -19.65; + zero_data[0.4] = -15.75; + zero_data[0.5] = -11.86; + zero_data[0.6] = -7.97; + zero_data[0.7] = -4.08; + zero_data[0.8] = -0.0195; + zero_data[0.9] = 3.9; + + std::map measured_zero_data; // No external series resistor. + measured_zero_data[0.3] = -19.65; + measured_zero_data[0.4] = -15.75; + measured_zero_data[0.5] = -11.86; + measured_zero_data[0.6] = -7.97; + measured_zero_data[0.7] = -4.2; + measured_zero_data[0.72] = -3.5; + measured_zero_data[0.74] = -2.8; + measured_zero_data[0.76] = -2.3; + measured_zero_data[0.78] = -2.0; + // Measured from Fig 2 as raw data not available. + + double step = 0.1; + for (int i = 0; i < vds.size(); i++) + { + zero_data[vds[i]] = lni[i]; + std::cout << lni[i] << " " << vds[i] << std::endl; + } + step = 0.01; + + std::map data_2; + for (double v = 0.3; v < 1.; v += step) + { + double current = iv(v, vt, 2., re, isat); + data_2[v] = log(current); + // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; + } + std::map data_10; + for (double v = 0.3; v < 1.; v += step) + { + double current = iv(v, vt, 10., re, isat); + data_10[v] = log(current); + // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; + } + std::map data_51; + for (double v = 0.3; v < 1.; v += step) + { + double current = iv(v, vt, 51., re, isat); + data_51[v] = log(current); + // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; + } + std::map data_249; + for (double v = 0.3; v < 1.; v += step) + { + double current = iv(v, vt, 249., re, isat); + data_249[v] = log(current); + // std::cout << "v " << v << ", current = " << current << " log current = " << log(current) << std::endl; + } + + svg_2d_plot data_plot; + + data_plot.title("Diode current versus voltage") + .x_size(400) + .y_size(300) + .legend_on(true) + .legend_lines(true) + .x_label("voltage (V)") + .y_label("log(current) (A)") + //.x_label_on(true) + //.y_label_on(true) + //.xy_values_on(false) + .x_range(0.25, 1.) + .y_range(-20., +4.) + .x_major_interval(0.1) + .y_major_interval(4) + .x_major_grid_on(true) + .y_major_grid_on(true) + //.x_values_on(true) + //.y_values_on(true) + .y_values_rotation(horizontal) + //.plot_window_on(true) + .x_values_precision(3) + .y_values_precision(3) + .coord_precision(4) // Needed to avoid stepping on curves. + .copyright_holder("Paul A. Bristow") + .copyright_date("2016") + //.background_border_color(black); + ; + + // ₀ = subscript zero. + data_plot.plot(zero_data, "I₀(V)").fill_color(lightgray).shape(none).size(3).line_on(true).line_width(0.5); + data_plot.plot(measured_zero_data, "Rs=0 Ω").fill_color(lightgray).shape(square).size(3).line_on(true).line_width(0.5); + data_plot.plot(data_2, "Rs=2 Ω").line_color(blue).shape(none).line_on(true).bezier_on(false).line_width(1); + data_plot.plot(data_10, "Rs=10 Ω").line_color(purple).shape(none).line_on(true).bezier_on(false).line_width(1); + data_plot.plot(data_51, "Rs=51 Ω").line_color(green).shape(none).line_on(true).line_width(1); + data_plot.plot(data_249, "Rs=249 Ω").line_color(red).shape(none).line_on(true).line_width(1); + data_plot.write("./diode_iv_plot"); + + // bezier_on(true); + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } + + +} // int main() + + /* + + //[lambert_w_output_1 + Output: + Lambert W diode current example. + V thermal 0.0257025 + Isat = 2.5e-14 + voltage = 0.9, current = 0.00108485, -6.82631 + -19.65 0.3 + -15.75 0.4 + -11.86 0.5 + -7.97 0.6 + -4.08 0.7 + -0.0195 0.8 + 3.6 0.9 + + //] [/lambert_w_output_1] + */ diff --git a/example/lambert_w_example.cpp b/example/lambert_w_example.cpp new file mode 100644 index 000000000..88a1fb13a --- /dev/null +++ b/example/lambert_w_example.cpp @@ -0,0 +1,239 @@ +// Copyright Paul A. Bristow 2016. + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +// Test that can build and run a simple example of Lambert W function, +// using algorithm of Thomas Luu. +// https://svn.boost.org/trac/boost/ticket/11027 + +#include // for BOOST_PLATFORM, BOOST_COMPILER, BOOST_STDLIB ... +#include // for BOOST_MSVC versions. +#include +#include // boost::exception +#include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. + +#define BOOST_MATH_INSTRUMENT_LAMBERT_W // #define only for diagnostic output. + +// For lambert_w function. +#include + +#include +// using std::cout; +// using std::endl; +#include +#include +#include +#include // For std::numeric_limits. + +//! Show information about build, architecture, address model, platform, ... +std::string show_versions() +{ + std::ostringstream message; + + message << "Program: " << __FILE__ << "\n"; +#ifdef __TIMESTAMP__ + message << __TIMESTAMP__; +#endif + message << "\nBuildInfo:\n" " Platform " << BOOST_PLATFORM; + // http://stackoverflow.com/questions/1505582/determining-32-vs-64-bit-in-c +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define IS64BIT 1 + message << ", 64-bit."; +#else +#define IS32BIT 1 + message << ", 32-bit."; +#endif + + message << "\n Compiler " BOOST_COMPILER; +#ifdef BOOST_MSC_VER +#ifdef _MSC_FULL_VER + message << "\n MSVC version " << BOOST_STRINGIZE(_MSC_FULL_VER) << "."; +#endif +#ifdef __WIN64 + mess age << "\n WIN64" << std::endl; +#endif // __WIN64 +#ifdef _WIN32 + message << "\n WIN32" << std::endl; +#endif // __WIN32 +#endif +#ifdef __GNUC__ + //PRINT_MACRO(__GNUC__); + //PRINT_MACRO(__GNUC_MINOR__); + //PRINT_MACRO(__GNUC_PATCH__); + std::cout << "GCC " << __VERSION__ << std::endl; + //PRINT_MACRO(LONG_MAX); +#endif // __GNUC__ + + message << "\n STL " << BOOST_STDLIB; + + message << "\n Boost version " << BOOST_VERSION / 100000 << "." << BOOST_VERSION / 100 % 1000 << "." << BOOST_VERSION % 100; + +#ifdef BOOST_HAS_FLOAT128 + message << ", BOOST_HAS_FLOAT128" << std::endl; +#endif + message << std::endl; + return message.str(); +} // std::string versions() + +int main() +{ + try + { + //std::cout << "Lambert W example basic!" << std::endl; + //std::cout << show_versions() << std::endl; + + //std::cout << exp(1) << std::endl; // 2.71828 + //std::cout << exp(-1) << std::endl; // 0.367879 + //std::cout << std::numeric_limits::epsilon() / 2 << std::endl; // 1.11022e-16 + + using namespace boost::math; + using boost::math::constants::exp_minus_one; + double x = 1.; + + double W1 = lambert_w(1.); + // Note, NOT integer X, for example: lambert_w(1); or will get message like + // error C2338: Must be floating-point, not integer type, for example W(1.), not W(1)! + // + + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.567143 + // This 'golden ratio' for exponentials is http://mathworld.wolfram.com/OmegaConstant.html + // since exp[-W(1)] = W(1) + // A030178 Decimal expansion of LambertW(1): the solution to x*exp(x) + // = 0.5671432904097838729999686622103555497538157871865125081351310792230457930866 + // http://oeis.org/A030178 + + double expplogone = exp(-lambert_w(1.)); + if (expplogone != W1) + { + std::cout << expplogone << " " << W1 << std::endl; // + } + + +//[lambert_w_example_1 + + x = 0.01; + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.00990147 +//] [/lambert_w_example_1] + x = -0.01; + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // -0.0101015 + x = -0.1; + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // + /**/ + + for (double xd = 1.; xd < 1e20; xd *= 10) + { + + // 1. 0.56714329040978387 + // 0.56714329040978384 + + // 10 1.7455280027406994 + // 1.7455280027406994 + + // 100 3.3856301402900502 + // 3.3856301402900502 + // 1000 5.2496028524015959 + // 5.249602852401596227126056319697306282521472386059592844451465483991362228320942832739693150854347718 + + // 1e19 40.058769161984308 + // 40.05876916198431163898797971203180915622644925765346546858291325452428038208071849105889199253335063 + std::cout << "Lambert W (" << xd << ") = " << lambert_w(xd) << std::endl; // + } + // + // Test near singularity. + + // http://www.wolframalpha.com/input/?i=N%5Blambert_w%5B-0.367879%5D,17%5D test value N[lambert_w[-0.367879],17] + // -0.367879441171442321595523770161460867445811131031767834 + x = -0.367879; // < -exp(1) = -0.367879 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // Lambert W (-0.36787900000000001) = -0.99845210378080340 + // -0.99845210378080340 + // -0.99845210378072726 N[lambert_w[-0.367879],17] wolfram so very close. + + x = -0.3678794; // expect -0.99952696660756813 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + x = -0.36787944; // expect -0.99992019848408340 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + x = -0.367879441; // -0.99996947070054883 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + x = -0.36787944117; // -0.99999719977527159 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + x = -0.367879441171; // -0.99999844928821992 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + + x = -exp_minus_one() + std::numeric_limits::epsilon(); + // Lambert W (-0.36787944117144211) = -0.99999996349975895 + // N[lambert_w[-0.36787944117144211],17] == -0.99999996608315303 + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + std::cout << " 1 - sqrt(eps) = " << static_cast(1) - sqrt(std::numeric_limits::epsilon()) << std::endl; + x = -exp_minus_one(); + // N[lambert_w[-0.36787944117144233],17] == -1.000000000000000 + 6.7595465843924897×10^-9 i + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.0 + // At Singularity - 0.36787944117144233 == -0.36787944117144233 returned - 1.0000000000000000 + // Lambert W(-0.36787944117144233) = -1.0000000000000000 + + + x = (std::numeric_limits::max)()/4; + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // OK 702.023799146706 + x = (std::numeric_limits::max)()/2; + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // + x = (std::numeric_limits::max)(); + std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // + // Error in function boost::math::log1p(double): numeric overflow + /* */ + + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } + + +} // int main() + + /* + +//[lambert_w_output_1 + Output: + + 1> example_basic.cpp +1> Generating code +1> All 237 functions were compiled because no usable IPDB/IOBJ from previous compilation was found. +1> Finished generating code +1> LambertW.vcxproj -> J:\Cpp\Misc\x64\Release\LambertW.exe +1> LambertW.vcxproj -> J:\Cpp\Misc\x64\Release\LambertW.pdb (Full PDB) +1> Lambert W example basic! +1> Platform: Win32 +1> Compiler: Microsoft Visual C++ version 14.0 +1> STL : Dinkumware standard library version 650 +1> Boost : 1.63.0 +1> _MSC_FULL_VER = 190024123 +1> Win32 +1> x64 +1> (x64) +1> Iteration #0, w0 0.577547206058041, w1 = 0.567143616915443, difference = 0.0289944962755619, relative 0.018343835374856 +1> Iteration #1, w0 0.567143616915443, w1 = 0.567143290409784, difference = 9.02208135089566e-07, relative 5.75702234328901e-07 +1> Final 0.567143290409784 after 2 iterations, difference = 0 +1> Iteration #0, w0 0.577547206058041, w1 = 0.567143616915443, difference = 0.0289944962755619, relative 0.018343835374856 +1> Iteration #1, w0 0.567143616915443, w1 = 0.567143290409784, difference = 9.02208135089566e-07, relative 5.75702234328901e-07 +1> Final 0.567143290409784 after 2 iterations, difference = 0 +1> Lambert W (1) = 0.567143290409784 +1> Iteration #0, w0 0.577547206058041, w1 = 0.567143616915443, difference = 0.0289944962755619, relative 0.018343835374856 +1> Iteration #1, w0 0.567143616915443, w1 = 0.567143290409784, difference = 9.02208135089566e-07, relative 5.75702234328901e-07 +1> Final 0.567143290409784 after 2 iterations, difference = 0 +1> Iteration #0, w0 0.0099072820916067, w1 = 0.00990147384359511, difference = 5.92416060777624e-06, relative 0.000586604388734591 +1> Final 0.00990147384359511 after 1 iterations, difference = 0 +1> Lambert W (0.01) = 0.00990147384359511 +1> Iteration #0, w0 -0.0101016472705154, w1 = -0.0101015271985388, difference = -1.17664437923951e-07, relative 1.18865171889748e-05 +1> Final -0.0101015271985388 after 1 iterations, difference = 0 +1> Lambert W (-0.01) = -0.0101015271985388 +1> Iteration #0, w0 -0.111843322610692, w1 = -0.111832559158964, difference = -8.54817065376601e-06, relative 9.62461362694622e-05 +1> Iteration #1, w0 -0.111832559158964, w1 = -0.111832559158963, difference = -5.68989300120393e-16, relative 6.43929354282591e-15 +1> Final -0.111832559158963 after 2 iterations, difference = 0 +1> Lambert W (-0.1) = -0.111832559158963 +1> Iteration #0, w0 -0.998452103785573, w1 = -0.998452103780803, difference = -2.72004641033163e-15, relative 4.77662354114727e-12 +1> Final -0.998452103780803 after 1 iterations, difference = 0 +1> Lambert W (-0.367879) = -0.998452103780803 + +//] [/lambert_w_output_1] + */ diff --git a/example/lambert_w_graph.cpp b/example/lambert_w_graph.cpp new file mode 100644 index 000000000..3eb43f75a --- /dev/null +++ b/example/lambert_w_graph.cpp @@ -0,0 +1,286 @@ +// Copyright Paul A. Bristow 2017 +// Copyright John Z. Maddock 2017 + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +/*! \brief Graph showing use of Lambert W function. + +\details + +Both Lambert W0 and W-1 branches can be shown on one graph. +But useful to have another graph for larger values of argument z. +Need two separate graphs for Lambert W0 and -1 prime because +the sensible ranges and axes are too different. + +One would get too small LambertW0 in top right and W-1 in bottom left. + +*/ + +#include +using boost::math::lambert_w0; +using boost::math::lambert_wm1; +using boost::math::lambert_w0_prime; +using boost::math::lambert_wm1_prime; + +#include +using boost::math::isfinite; +#include +using namespace boost::svg; +#include +using boost::svg::show_2d_plot_settings; + +#include +// using std::cout; +// using std::endl; +#include +#include +#include +#include +#include +#include +using std::pair; +#include +using std::map; +#include +using std::multiset; +#include +using std::numeric_limits; +#include // + + /*! + */ +int main() +{ + try + { + std::cout << "Lambert W graph example." << std::endl; + +//[lambert_w_graph_1 +//] [/lambert_w_graph_1] + { + std::map wm1s; // Lambert W-1 branch values. + std::map w0s; // Lambert W0 branch values. + + std::cout.precision(std::numeric_limits::max_digits10); + + int count = 0; + for (double z = -0.36787944117144232159552377016146086744581113103176804; z < 2.8; z += 0.001) + { + double w0 = lambert_w0(z); + w0s[z] = w0; + // std::cout << "z " << z << ", w = " << w0 << std::endl; + count++; + } + std::cout << "points " << count << std::endl; + + count = 0; + for (double z = -0.3678794411714423215955237701614608727; z < -0.001; z += 0.001) + { + double wm1 = lambert_wm1(z); + wm1s[z] = wm1; + count++; + } + std::cout << "points " << count << std::endl; + + svg_2d_plot data_plot; + data_plot.title("Lambert W function.") + .x_size(400) + .y_size(300) + .legend_on(true) + .legend_lines(true) + .x_label("z") + .y_label("W") + .x_range(-1, 3.) + .y_range(-4., +1.) + .x_major_interval(1.) + .y_major_interval(1.) + .x_major_grid_on(true) + .y_major_grid_on(true) + //.x_values_on(true) + //.y_values_on(true) + .y_values_rotation(horizontal) + //.plot_window_on(true) + .x_values_precision(3) + .y_values_precision(3) + .coord_precision(4) // Needed to avoid stepping on curves. + .copyright_holder("Paul A. Bristow") + .copyright_date("2018") + //.background_border_color(black); + ; + data_plot.plot(w0s, "W0 branch").line_color(red).shape(none).line_on(true).bezier_on(false).line_width(1); + data_plot.plot(wm1s, "W-1 branch").line_color(blue).shape(none).line_on(true).bezier_on(false).line_width(1); + data_plot.write("./lambert_w_graph"); + + show_2d_plot_settings(data_plot); // For plot diagnosis only. + + } // small z Lambert W + + { // bigger argument z Lambert W + + std::map w0s_big; // Lambert W0 branch values for large z and W. + std::map wm1s_big; // Lambert W-1 branch values for small z and large -W. + int count = 0; + for (double z = -0.3678794411714423215955237701614608727; z < 10000.; z += 50.) + { + double w0 = lambert_w0(z); + w0s_big[z] = w0; + count++; + } + std::cout << "points " << count << std::endl; + + count = 0; + for (double z = -0.3678794411714423215955237701614608727; z < -0.001; z += 0.001) + { + double wm1 = lambert_wm1(z); + wm1s_big[z] = wm1; + count++; + } + std::cout << "Lambert W0 large z argument points = " << count << std::endl; + + svg_2d_plot data_plot2; + data_plot2.title("Lambert W0 function for larger z.") + .x_size(400) + .y_size(300) + .legend_on(false) + .x_label("z") + .y_label("W") + //.x_label_on(true) + //.y_label_on(true) + //.xy_values_on(false) + .x_range(-1, 10000.) + .y_range(-1., +8.) + .x_major_interval(2000.) + .y_major_interval(1.) + .x_major_grid_on(true) + .y_major_grid_on(true) + //.x_values_on(true) + //.y_values_on(true) + .y_values_rotation(horizontal) + //.plot_window_on(true) + .x_values_precision(3) + .y_values_precision(3) + .coord_precision(4) // Needed to avoid stepping on curves. + .copyright_holder("Paul A. Bristow") + .copyright_date("2018") + //.background_border_color(black); + ; + + data_plot2.plot(w0s_big, "W0 branch").line_color(red).shape(none).line_on(true).bezier_on(false).line_width(1); + // data_plot2.plot(wm1s_big, "W-1 branch").line_color(blue).shape(none).line_on(true).bezier_on(false).line_width(1); + // This wouldn't show anything useful. + data_plot2.write("./lambert_w_graph_big_w"); + } // Big argument z Lambert W + + { // Lambert W0 Derivative plots + + // std::map wm1ps; // Lambert W-1 prime branch values. + std::map w0ps; // Lambert W0 prime branch values. + + std::cout.precision(std::numeric_limits::max_digits10); + + int count = 0; + for (double z = -0.36; z < 3.; z += 0.001) + { + double w0p = lambert_w0_prime(z); + w0ps[z] = w0p; + // std::cout << "z " << z << ", w0 = " << w0 << std::endl; + count++; + } + std::cout << "points " << count << std::endl; + + //count = 0; + //for (double z = -0.36; z < -0.1; z += 0.001) + //{ + // double wm1p = lambert_wm1_prime(z); + // std::cout << "z " << z << ", w-1 = " << wm1p << std::endl; + // wm1ps[z] = wm1p; + // count++; + //} + //std::cout << "points " << count << std::endl; + + svg_2d_plot data_plotp; + data_plotp.title("Lambert W0 prime function.") + .x_size(400) + .y_size(300) + .legend_on(false) + .x_label("z") + .y_label("W0'") + .x_range(-0.3, +1.) + .y_range(0., +5.) + .x_major_interval(0.2) + .y_major_interval(2.) + .x_major_grid_on(true) + .y_major_grid_on(true) + .y_values_rotation(horizontal) + .x_values_precision(3) + .y_values_precision(3) + .coord_precision(4) // Needed to avoid stepping on curves. + .copyright_holder("Paul A. Bristow") + .copyright_date("2018") + ; + + // derivative of N[productlog(0, x), 55] at x=0 to 10 + // Plot[D[N[ProductLog[0, x], 55], x], {x, 0, 10}] + // Plot[ProductLog[x]/(x + x ProductLog[x]), {x, 0, 10}] + data_plotp.plot(w0ps, "W0 prime branch").line_color(red).shape(none).line_on(true).bezier_on(false).line_width(1); + data_plotp.write("./lambert_w0_prime_graph"); + } // Lambert W0 Derivative plots + + { // Lambert Wm1 Derivative plots + + std::map wm1ps; // Lambert W-1 prime branch values. + + std::cout.precision(std::numeric_limits::max_digits10); + + int count = 0; + for (double z = -0.3678; z < -0.00001; z += 0.001) + { + double wm1p = lambert_wm1_prime(z); + // std::cout << "z " << z << ", w-1 = " << wm1p << std::endl; + wm1ps[z] = wm1p; + count++; + } + std::cout << "Lambert W-1 prime points = " << count << std::endl; + + svg_2d_plot data_plotp; + data_plotp.title("Lambert W-1 prime function.") + .x_size(400) + .y_size(300) + .legend_on(false) + .x_label("z") + .y_label("W-1'") + .x_range(-0.4, +0.01) + .x_major_interval(0.1) + .y_range(-20., -5.) + .y_major_interval(5.) + .x_major_grid_on(true) + .y_major_grid_on(true) + .y_values_rotation(horizontal) + .x_values_precision(3) + .y_values_precision(3) + .coord_precision(4) // Needed to avoid stepping on curves. + .copyright_holder("Paul A. Bristow") + .copyright_date("2018") + ; + + // derivative of N[productlog(0, x), 55] at x=0 to 10 + // Plot[D[N[ProductLog[0, x], 55], x], {x, 0, 10}] + // Plot[ProductLog[x]/(x + x ProductLog[x]), {x, 0, 10}] + data_plotp.plot(wm1ps, "W-1 prime branch").line_color(blue).shape(none).line_on(true).bezier_on(false).line_width(1); + data_plotp.write("./lambert_wm1_prime_graph"); + } // Lambert W-1 prime graph + } // try + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} // int main() + + /* + + //[lambert_w_graph_1_output + + //] [/lambert_w_graph_1_output] + */ diff --git a/example/lambert_w_precision_example.cpp b/example/lambert_w_precision_example.cpp new file mode 100644 index 000000000..3d81c934d --- /dev/null +++ b/example/lambert_w_precision_example.cpp @@ -0,0 +1,269 @@ +// Copyright Paul A. Bristow 2016, 2018. + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +//! Lambert W examples of controlling precision + +// #define BOOST_MATH_INSTRUMENT_LAMBERT_W // #define only for (much) diagnostic output. + +#include // for BOOST_PLATFORM, BOOST_COMPILER, BOOST_STDLIB ... +#include // for BOOST_MSVC versions. +#include +#include // boost::exception +#include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. +#include +#include // for float_distance. +#include // for relative and epsilon difference. + +// Built-in/fundamental GCC float128 or Intel Quad 128-bit type, if available. +#ifdef __GNUC__ +#include // Not available for MSVC. +// sets BOOST_MP_USE_FLOAT128 for GCC +using boost::multiprecision::float128; +#endif //# NOT _MSC_VER + +#include // boost::multiprecision::cpp_dec_float_50 +using boost::multiprecision::cpp_dec_float_50; // 50 decimal digits type. +using boost::multiprecision::cpp_dec_float_100; // 100 decimal digits type. + +#include +using boost::multiprecision::cpp_bin_float_double_extended; +using boost::multiprecision::cpp_bin_float_double; +using boost::multiprecision::cpp_bin_float_quad; +// For lambert_w function. +#include +// using boost::math::lambert_w0; +// using boost::math::lambert_wm1; + +#include +#include +#include +#include +#include // For std::numeric_limits. + +int main() +{ + try + { + std::cout << "Lambert W examples of precision control." << std::endl; + std::cout.precision(std::numeric_limits::max_digits10); + std::cout << std::showpoint << std::endl; // Show any trailing zeros. + + using boost::math::constants::exp_minus_one; + + using boost::math::lambert_w0; + using boost::math::lambert_wm1; + + // Error handling policy examples. + using namespace boost::math::policies; + using boost::math::policies::make_policy; + using boost::math::policies::policy; + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::domain_error; + using boost::math::policies::throw_on_error; + +//[lambert_w_precision_reference_w + + using boost::multiprecision::cpp_bin_float_50; + using boost::math::float_distance; + + cpp_bin_float_50 z("10."); // Note use a decimal digit string, not a double 10. + cpp_bin_float_50 r; + std::cout.precision(std::numeric_limits::digits10); + + r = lambert_w0(z); // Default policy. + std::cout << "lambert_w0(z) cpp_bin_float_50 = " << r << std::endl; + //lambert_w0(z) cpp_bin_float_50 = 1.7455280027406993830743012648753899115352881290809 + // [N[productlog[10], 50]] == 1.7455280027406993830743012648753899115352881290809 + std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "lambert_w0(z) static_cast from cpp_bin_float_50 = " + << static_cast(r) << std::endl; + // double lambert_w0(z) static_cast from cpp_bin_float_50 = 1.7455280027406994 + // [N[productlog[10], 17]] == 1.7455280027406994 + std::cout << "bits different from Wolfram = " + << static_cast(float_distance(static_cast(r), 1.7455280027406994)) + << std::endl; // 0 + + +//] [/lambert_w_precision_reference_w] + +//[lambert_w_precision_0 + std::cout.precision(std::numeric_limits::max_digits10); // Show all potentially significant decimal digits, + std::cout << std::showpoint << std::endl; // and show any significant trailing zeros too. + + float x = 10.; + std::cout << "Lambert W (" << x << ") = " << lambert_w0(x) << std::endl; +//] [/lambert_w_precision_0] + +/* +//[lambert_w_precision_output_0 +Lambert W (10.0000000) = 1.74552800 +//] [/lambert_w_precision_output_0] +*/ + { // Lambert W0 Halley step example +//[lambert_w_precision_1 + using boost::math::lambert_w_detail::lambert_w_halley_step; + using boost::math::epsilon_difference; + using boost::math::relative_difference; + + std::cout << std::showpoint << std::endl; // and show any significant trailing zeros too. + std::cout.precision(std::numeric_limits::max_digits10); // 17 decimal digits for double. + + cpp_bin_float_50 z50("1.23"); // Note: use a decimal digit string, not a double 1.23! + double z = static_cast(z50); + cpp_bin_float_50 w50; + w50 = lambert_w0(z50); + std::cout.precision(std::numeric_limits::max_digits10); // 50 decimal digits. + std::cout << "Reference Lambert W (" << z << ") =\n " + << w50 << std::endl; + std::cout.precision(std::numeric_limits::max_digits10); // 17 decimal digits for double. + double wr = static_cast(w50); + std::cout << "Reference Lambert W (" << z << ") = " << wr << std::endl; + + double w = lambert_w0(z); + std::cout << "Rat/poly Lambert W (" << z << ") = " << lambert_w0(z) << std::endl; + // Add a Halley step to the value obtained from rational polynomial approximation. + double ww = lambert_w_halley_step(lambert_w0(z), z); + std::cout << "Halley Step Lambert W (" << z << ") = " << lambert_w_halley_step(lambert_w0(z), z) << std::endl; + + std::cout << "absolute difference from Halley step = " << w - ww << std::endl; + std::cout << "relative difference from Halley step = " << relative_difference(w, ww) << std::endl; + std::cout << "epsilon difference from Halley step = " << epsilon_difference(w, ww) << std::endl; + std::cout << "epsilon for float = " << std::numeric_limits::epsilon() << std::endl; + std::cout << "bits different from Halley step = " << static_cast(float_distance(w, ww)) << std::endl; +//] [/lambert_w_precision_1] + + +/* +//[lambert_w_precision_output_1 + Reference Lambert W (1.2299999999999999822364316059974953532218933105468750) = + 0.64520356959320237759035605255334853830173300262666480 + Reference Lambert W (1.2300000000000000) = 0.64520356959320235 + Rat/poly Lambert W (1.2300000000000000) = 0.64520356959320224 + Halley Step Lambert W (1.2300000000000000) = 0.64520356959320235 + absolute difference from Halley step = -1.1102230246251565e-16 + relative difference from Halley step = 1.7207329236029286e-16 + epsilon difference from Halley step = 0.77494921535422934 + epsilon for float = 2.2204460492503131e-16 + bits different from Halley step = 1 +//] [/lambert_w_precision_output_1] +*/ + + } // Lambert W0 Halley step example + + { // Lambert W-1 Halley step example + //[lambert_w_precision_2 + using boost::math::lambert_w_detail::lambert_w_halley_step; + using boost::math::epsilon_difference; + using boost::math::relative_difference; + + std::cout << std::showpoint << std::endl; // and show any significant trailing zeros too. + std::cout.precision(std::numeric_limits::max_digits10); // 17 decimal digits for double. + + cpp_bin_float_50 z50("-0.123"); // Note: use a decimal digit string, not a double -1.234! + double z = static_cast(z50); + cpp_bin_float_50 wm1_50; + wm1_50 = lambert_wm1(z50); + std::cout.precision(std::numeric_limits::max_digits10); // 50 decimal digits. + std::cout << "Reference Lambert W-1 (" << z << ") =\n " + << wm1_50 << std::endl; + std::cout.precision(std::numeric_limits::max_digits10); // 17 decimal digits for double. + double wr = static_cast(wm1_50); + std::cout << "Reference Lambert W-1 (" << z << ") = " << wr << std::endl; + + double w = lambert_wm1(z); + std::cout << "Rat/poly Lambert W-1 (" << z << ") = " << lambert_wm1(z) << std::endl; + // Add a Halley step to the value obtained from rational polynomial approximation. + double ww = lambert_w_halley_step(lambert_wm1(z), z); + std::cout << "Halley Step Lambert W (" << z << ") = " << lambert_w_halley_step(lambert_wm1(z), z) << std::endl; + + std::cout << "absolute difference from Halley step = " << w - ww << std::endl; + std::cout << "relative difference from Halley step = " << relative_difference(w, ww) << std::endl; + std::cout << "epsilon difference from Halley step = " << epsilon_difference(w, ww) << std::endl; + std::cout << "epsilon for float = " << std::numeric_limits::epsilon() << std::endl; + std::cout << "bits different from Halley step = " << static_cast(float_distance(w, ww)) << std::endl; + //] [/lambert_w_precision_2] + } + /* + //[lambert_w_precision_output_2 + Reference Lambert W-1 (-0.12299999999999999822364316059974953532218933105468750) = + -3.2849102557740360179084675531714935199110302996513384 + Reference Lambert W-1 (-0.12300000000000000) = -3.2849102557740362 + Rat/poly Lambert W-1 (-0.12300000000000000) = -3.2849102557740357 + Halley Step Lambert W (-0.12300000000000000) = -3.2849102557740362 + absolute difference from Halley step = 4.4408920985006262e-16 + relative difference from Halley step = 1.3519066740696092e-16 + epsilon difference from Halley step = 0.60884463935795785 + epsilon for float = 2.2204460492503131e-16 + bits different from Halley step = -1 + //] [/lambert_w_precision_output_2] + */ + + + + // Similar example using cpp_bin_float_quad (128-bit floating-point types). + + cpp_bin_float_quad zq = 10.; + std::cout << "\nTest evaluation of cpp_bin_float_quad Lambert W(" << zq << ")" + << std::endl; + std::cout << std::setprecision(3) << "std::numeric_limits::digits = " << std::numeric_limits::digits << std::endl; + std::cout << std::setprecision(3) << "std::numeric_limits::epsilon() = " << std::numeric_limits::epsilon() << std::endl; + std::cout << std::setprecision(3) << "std::numeric_limits::max_digits10 = " << std::numeric_limits::max_digits10 << std::endl; + std::cout << std::setprecision(3) << "std::numeric_limits::digits10 = " << std::numeric_limits::digits10 << std::endl; + std::cout.precision(std::numeric_limits::max_digits10); + // All are same precision because double precision first approximation used before Halley. + + /* + + */ + + { // Reference value for lambert_w0(10) + cpp_dec_float_50 z("10"); + cpp_dec_float_50 r; + std::cout.precision(std::numeric_limits::digits10); + + r = lambert_w0(z); // Default policy. + std::cout << "lambert_w0(z) cpp_dec_float_50 = " << r << std::endl; // 0.56714329040978387299996866221035554975381578718651 + std::cout.precision(std::numeric_limits::max_digits10); + + std::cout << "lambert_w0(z) cpp_dec_float_50 cast to quad (max_digits10(" << std::numeric_limits::max_digits10 << + " ) = " << static_cast(r) << std::endl; // 1.7455280027406993830743012648753899115352881290809 + std::cout.precision(std::numeric_limits::digits10); // 1.745528002740699383074301264875389837 + std::cout << "lambert_w0(z) cpp_dec_float_50 cast to quad (digits10(" << std::numeric_limits::digits10 << + " ) = " << static_cast(r) << std::endl; // 1.74552800274069938307430126487539 + std::cout.precision(std::numeric_limits::digits10 + 1); // + + std::cout << "lambert_w0(z) cpp_dec_float_50 cast to quad (digits10(" << std::numeric_limits::digits10 << + " ) = " << static_cast(r) << std::endl; // 1.74552800274069938307430126487539 + + // [N[productlog[10], 50]] == 1.7455280027406993830743012648753899115352881290809 + + // [N[productlog[10], 37]] == 1.745528002740699383074301264875389912 + // [N[productlog[10], 34]] == 1.745528002740699383074301264875390 + // [N[productlog[10], 33]] == 1.74552800274069938307430126487539 + + // lambert_w0(z) cpp_dec_float_50 cast to quad = 1.745528002740699383074301264875389837 + + // lambert_w0(z) cpp_dec_float_50 = 1.7455280027406993830743012648753899115352881290809 + // lambert_w0(z) cpp_dec_float_50 cast to quad = 1.745528002740699383074301264875389837 + // lambert_w0(z) cpp_dec_float_50 cast to quad = 1.74552800274069938307430126487539 + } + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} // int main() + + /* + + + + + */ + + diff --git a/example/lambert_w_simple_examples.cpp b/example/lambert_w_simple_examples.cpp new file mode 100644 index 000000000..9e32b4971 --- /dev/null +++ b/example/lambert_w_simple_examples.cpp @@ -0,0 +1,256 @@ +// Copyright Paul A. Bristow 2016, 2017. + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +// Build and run a simple examples of Lambert W function. + +// Some macros that will show some(or much) diagnostic values if #defined. +//#define-able macros +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W0 // W0 branch diagnostics. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_Wm1 // W1 branch diagnostics. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY // Halley refinement diagnostics. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER // Schroeder refinement diagnostics. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS // Number of terms used for near-singularity series. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN // higher than built-in precision types approximation and refinement. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES // Show evaluation of series near branch singularity. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS // Show evaluation of series for small z. +//#define BOOST_MATH_INSTRUMENT_LAMBERT_W0_LOOKUP // Show results from lookup table. + +#include // for BOOST_PLATFORM, BOOST_COMPILER, BOOST_STDLIB ... +#include // for BOOST_MSVC versions. +#include +#include // boost::exception +#include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. +#include + +#include // boost::multiprecision::cpp_dec_float_50 +using boost::multiprecision::cpp_dec_float_50; // 50 decimal digits type. +using boost::multiprecision::cpp_dec_float_100; // 100 decimal digits type. +using boost::multiprecision::backends::cpp_dec_float; +using boost::multiprecision::number; +typedef number > cpp_dec_float_1000; // 1000 decimal digit types + +#include +using boost::multiprecision::cpp_bin_float_double; // == double +using boost::multiprecision::cpp_bin_float_double_extended; // 80-bit long double emulation. +using boost::multiprecision::cpp_bin_float_quad; // 128-bit quad precision. + +//[lambert_w_simple_examples_includes +#include // For lambert_w function. + +using boost::math::lambert_w0; +using boost::math::lambert_wm1; +//] //[/lambert_w_simple_examples_includes] + +#include +// using std::cout; +// using std::endl; +#include +#include +#include +#include // For std::numeric_limits. + +//! Show value of z to the full possibly-significant max_digits10 precision of type T. +template +void show_value(T z) +{ + std::streamsize precision = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout.precision(std::numeric_limits::max_digits10); // Show all posssibly significant digits. + std::ios::fmtflags flags(std::cout.flags()); + std::cout.setf(std::ios_base::showpoint); // Include any trailing zeros. + std::cout << z; + // Restore: + std::cout.precision(precision); + std::cout.flags(flags); +} // template void show_value(T z) + +int main() +{ + try + { + std::cout << "Lambert W simple examples." << std::endl; + + using boost::math::constants::exp_minus_one; //-1/e, the branch point, a singularity ~= -0.367879. + + // using statements needed for changing error handling policy. + using boost::math::policies::policy; + using boost::math::policies::make_policy; + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::ignore_error; + using boost::math::policies::throw_on_error; + + { +//[lambert_w_simple_examples_0 + std::cout.precision(std::numeric_limits::max_digits10); + // Show all potentially significant decimal digits, + std::cout << std::showpoint << std::endl; + // and show significant trailing zeros too. + + double z = 10.; + double r = lambert_w0(z); // Default policy for double. + std::cout << "lambert_w0(z) = " << r << std::endl; + // lambert_w0(z) = 1.7455280027406994 +//] [/lambert_w_simple_examples_0] + } + { + // Other floating-point types can be used too, here float. + // It is convenient to use a function like `show_value` + // to display all potentially significant decimal digits + // for the type, including any significant trailing zeros. + //[lambert_w_simple_examples_1 + float z = 10.F; + float r; + r = lambert_w0(z); // Default policy digits10 = 7, digits2 = 24 + std::cout << "lambert_w0("; + show_value(z); + std::cout << ") = "; + show_value(r); + std::cout << std::endl; // lambert_w0(10.0000000) = 1.74552798 + //] //[/lambert_w_simple_examples_1] + } + { + // Example of an integer argument to lambert_w, + // showing that an integer is correctly promoted to a double. +//[lambert_w_simple_examples_2 + std::cout.precision(std::numeric_limits::max_digits10); + double r = lambert_w0(10); // Pass an int argument "10" that should be promoted to double argument. + std::cout << "lambert_w0(10) = " << r << std::endl; // lambert_w0(10) = 1.7455280027406994 + double rp = lambert_w0(10); + std::cout << "lambert_w0(10) = " << rp << std::endl; + // lambert_w0(10) = 1.7455280027406994 + auto rr = lambert_w0(10); // C++11 needed. + std::cout << "lambert_w0(10) = " << rr << std::endl; + // lambert_w0(10) = 1.7455280027406994 too, showing that rr has been promoted to double. +//] //[/lambert_w_simple_examples_2] + } + { + // Using multiprecision types to get much higher precision is painless. + //[lambert_w_simple_examples_3 + cpp_dec_float_50 z("10"); + // Note construction using a decimal digit string "10", + // NOT a floating-point double literal 10. + cpp_dec_float_50 r; + r = lambert_w0(z); + std::cout << "lambert_w0("; show_value(z); std::cout << ") = "; + show_value(r); + std::cout << std::endl; + // lambert_w0(10.000000000000000000000000000000000000000000000000000000000000000000000000000000) = + // 1.7455280027406993830743012648753899115352881290809413313533156980404446940000000 + //] //[/lambert_w_simple_examples_3] + } + // Using multiprecision types to get multiprecision precision wrong! + { + //[lambert_w_simple_examples_4 + cpp_dec_float_50 z(0.7777777777777777777777777777777777777777777777777777777777777777777777777); + // Compiler evaluates the nearest double-precision binary representation, + // from the max_digits10 of the floating_point literal double 0.7777777777777777777777777777..., + // so any extra digits in the multiprecision type + // beyond max_digits10 (usually 17) are random and meaningless. + cpp_dec_float_50 r; + r = lambert_w0(z); + std::cout << "lambert_w0("; + show_value(z); + std::cout << ") = "; show_value(r); + std::cout << std::endl; + // lambert_w0(0.77777777777777779011358916250173933804035186767578125000000000000000000000000000) + // = 0.48086152073210493501934682309060873341910109230469724725005039758139532631901386 + //] //[/lambert_w_simple_examples_4] + } + { + //[lambert_w_simple_examples_4a + cpp_dec_float_50 z(0.9); // Construct from floating_point literal double 0.9. + cpp_dec_float_50 r; + r = lambert_w0(0.9); + std::cout << "lambert_w0("; + show_value(z); + std::cout << ") = "; show_value(r); + std::cout << std::endl; + // lambert_w0(0.90000000000000002220446049250313080847263336181640625000000000000000000000000000) + // = 0.52983296563343440510607251781038939952850341796875000000000000000000000000000000 + std::cout << "lambert_w0(0.9) = " << lambert_w0(static_cast(0.9)) + // lambert_w0(0.9) + // = 0.52983296563343441 + << std::endl; + //] //[/lambert_w_simple_examples_4a] + } + { + // Using multiprecision types to get multiprecision precision right! + //[lambert_w_simple_examples_4b + cpp_dec_float_50 z("0.9"); // Construct from decimal digit string. + cpp_dec_float_50 r; + r = lambert_w0(z); + std::cout << "lambert_w0("; + show_value(z); + std::cout << ") = "; show_value(r); + std::cout << std::endl; + // 0.90000000000000000000000000000000000000000000000000000000000000000000000000000000) + // = 0.52983296563343441213336643954546304857788132269804249284012528304239956413801252 + //] //[/lambert_w_simple_examples_4b] + } + // Getting extreme precision (1000 decimal digits) Lambert W values. + { + std::cout.precision(std::numeric_limits::digits10); + cpp_dec_float_1000 z("2.0"); + cpp_dec_float_1000 r; + r = lambert_w0(z); + std::cout << "lambert_w0(z) = " << r << std::endl; + // 0.8526055020137254913464724146953174668984533001514035087721073946525150656742630448965773783502494847334503972691804119834761668851953598826198984364998343940330324849743119327028383008883133161249045727544669202220292076639777316648311871183719040610274221013237163543451621208284315007250267190731048119566857455987975973474411544571619699938899354169616378479326962044241495398851839432070255805880208619490399218130868317114428351234208216131218024303904457925834743326836272959669122797896855064630871955955318383064292191644322931561534814178034773896739684452724587331245831001449498844495771266728242975586931792421997636537572767708722190588748148949667744956650966402600446780664924889043543203483210769017254907808218556111831854276511280553252641907484685164978750601216344998778097446525021666473925144772131644151718261199915247932015387685261438125313159125475113124470774926288823525823567568542843625471594347837868505309329628014463491611881381186810879712667681285740515197493390563 + // Wolfram alpha command N[productlog[0, 2.0],1000] gives the identical result: + // 0.8526055020137254913464724146953174668984533001514035087721073946525150656742630448965773783502494847334503972691804119834761668851953598826198984364998343940330324849743119327028383008883133161249045727544669202220292076639777316648311871183719040610274221013237163543451621208284315007250267190731048119566857455987975973474411544571619699938899354169616378479326962044241495398851839432070255805880208619490399218130868317114428351234208216131218024303904457925834743326836272959669122797896855064630871955955318383064292191644322931561534814178034773896739684452724587331245831001449498844495771266728242975586931792421997636537572767708722190588748148949667744956650966402600446780664924889043543203483210769017254907808218556111831854276511280553252641907484685164978750601216344998778097446525021666473925144772131644151718261199915247932015387685261438125313159125475113124470774926288823525823567568542843625471594347837868505309329628014463491611881381186810879712667681285740515197493390563 + } + { +//[lambert_w_simple_examples_error_policies + // Define an error handling policy: + typedef policy< + domain_error, + overflow_error // possibly unwise? + > my_throw_policy; + + std::cout.precision(std::numeric_limits::max_digits10); + // Show all potentially significant decimal digits, + std::cout << std::showpoint << std::endl; + // and show significant trailing zeros too. + double z = +1; + std::cout << "Lambert W (" << z << ") = " << lambert_w0(z) << std::endl; + // Lambert W (1.0000000000000000) = 0.56714329040978384 + std::cout << "\nLambert W (" << z << ", my_throw_policy()) = " + << lambert_w0(z, my_throw_policy()) << std::endl; + // Lambert W (1.0000000000000000, my_throw_policy()) = 0.56714329040978384 + //] //[/lambert_w_simple_example_error_policies] + } + { + // Show error reporting from passing a value to lambert_wm1 that is out of range, + // (and probably was meant to be passed to lambert_0 instead). +//[lambert_w_simple_examples_out_of_range + double z = +1.; + double r = lambert_wm1(z); + std::cout << "lambert_wm1(+1.) = " << r << std::endl; + //] [/lambert_w_simple_examples_out_of_range] + // Error in function boost::math::lambert_wm1(): + // Argument z = 1 is out of range (z <= 0) for Lambert W-1 branch! (Try Lambert W0 branch?) + } + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} // int main() + + /* + + Output: +//[lambert_w_simple_examples_error_message_1 +Error in function boost::math::lambert_wm1(): +Argument z = 1 is out of range (z <= 0) for Lambert W-1 branch! (Try Lambert W0 branch?) +//] [/lambert_w_simple_examples_error_message_1] + + /* + + + */ + + diff --git a/include/boost/math/constants/calculate_constants.hpp b/include/boost/math/constants/calculate_constants.hpp index 2dcdb9a02..71487885b 100644 --- a/include/boost/math/constants/calculate_constants.hpp +++ b/include/boost/math/constants/calculate_constants.hpp @@ -296,6 +296,15 @@ inline T constant_three_quarters::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_S return static_cast(3) / static_cast(4); } +template +template +inline T constant_sixth::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_)) +{ + BOOST_MATH_STD_USING + return static_cast(1) / static_cast(6); +} + +// Pi and related constants. template template inline T constant_pi_minus_three::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_)) @@ -326,7 +335,14 @@ inline T constant_exp_minus_half::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_S return exp(static_cast(-0.5)); } -// Pi +template +template +inline T constant_exp_minus_one::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_)) +{ + BOOST_MATH_STD_USING + return exp(static_cast(-1.)); +} + template template inline T constant_one_div_root_two::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_)) @@ -356,7 +372,6 @@ inline T constant_root_one_div_pi::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_ return sqrt(static_cast(1) / pi > >()); } - template template inline T constant_four_thirds_pi::compute(BOOST_MATH_EXPLICIT_TEMPLATE_TYPE_SPEC(mpl::int_)) diff --git a/include/boost/math/constants/constants.hpp b/include/boost/math/constants/constants.hpp index 8c5c4105d..d35aa0271 100644 --- a/include/boost/math/constants/constants.hpp +++ b/include/boost/math/constants/constants.hpp @@ -65,8 +65,7 @@ namespace boost{ namespace math struct dummy_size{}; // - // Max number of binary digits in the string representations - // of our constants: + // Max number of binary digits in the string representations of our constants: // BOOST_STATIC_CONSTANT(int, max_string_digits = (101 * 1000L) / 301L); @@ -266,7 +265,7 @@ namespace boost{ namespace math BOOST_DEFINE_MATH_CONSTANT(third, 3.333333333333333333333333333333333333e-01, "3.33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333e-01") BOOST_DEFINE_MATH_CONSTANT(twothirds, 6.666666666666666666666666666666666666e-01, "6.66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667e-01") BOOST_DEFINE_MATH_CONSTANT(two_thirds, 6.666666666666666666666666666666666666e-01, "6.66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667e-01") - BOOST_DEFINE_MATH_CONSTANT(sixth, 1.66666666666666666666666666666666666666666e-01, "1.66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667e-01") + BOOST_DEFINE_MATH_CONSTANT(sixth, 1.666666666666666666666666666666666666e-01, "1.66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667e-01"); BOOST_DEFINE_MATH_CONSTANT(three_quarters, 7.500000000000000000000000000000000000e-01, "7.50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-01") BOOST_DEFINE_MATH_CONSTANT(root_two, 1.414213562373095048801688724209698078e+00, "1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157273501384623e+00") BOOST_DEFINE_MATH_CONSTANT(root_three, 1.732050807568877293527446341505872366e+00, "1.73205080756887729352744634150587236694280525381038062805580697945193301690880003708114618675724857567562614142e+00") @@ -302,6 +301,7 @@ namespace boost{ namespace math BOOST_DEFINE_MATH_CONSTANT(one_div_cbrt_pi, 6.827840632552956814670208331581645981e-01, "6.82784063255295681467020833158164598108367515632448804042681583118899226433403918237673501922595519865685577274e-01") BOOST_DEFINE_MATH_CONSTANT(e, 2.718281828459045235360287471352662497e+00, "2.71828182845904523536028747135266249775724709369995957496696762772407663035354759457138217852516642742746639193e+00") BOOST_DEFINE_MATH_CONSTANT(exp_minus_half, 6.065306597126334236037995349911804534e-01, "6.06530659712633423603799534991180453441918135487186955682892158735056519413748423998647611507989456026423789794e-01") + BOOST_DEFINE_MATH_CONSTANT(exp_minus_one, 3.678794411714423215955237701614608674e-01, "3.67879441171442321595523770161460867445811131031767834507836801697461495744899803357147274345919643746627325277e-01"); BOOST_DEFINE_MATH_CONSTANT(e_pow_pi, 2.314069263277926900572908636794854738e+01, "2.31406926327792690057290863679485473802661062426002119934450464095243423506904527835169719970675492196759527048e+01") BOOST_DEFINE_MATH_CONSTANT(root_e, 1.648721270700128146848650787814163571e+00, "1.64872127070012814684865078781416357165377610071014801157507931164066102119421560863277652005636664300286663776e+00") BOOST_DEFINE_MATH_CONSTANT(log10_e, 4.342944819032518276511289189166050822e-01, "4.34294481903251827651128918916605082294397005803666566114453783165864649208870774729224949338431748318706106745e-01") @@ -344,3 +344,5 @@ namespace boost{ namespace math #include #endif // BOOST_MATH_CONSTANTS_CONSTANTS_INCLUDED + + diff --git a/include/boost/math/cstdfloat/cstdfloat_cmath.hpp b/include/boost/math/cstdfloat/cstdfloat_cmath.hpp index 6e23302d8..83fb480dc 100644 --- a/include/boost/math/cstdfloat/cstdfloat_cmath.hpp +++ b/include/boost/math/cstdfloat/cstdfloat_cmath.hpp @@ -182,7 +182,7 @@ #define BOOST_CSTDFLOAT_FLOAT128_ATANH atanhq_patch #define BOOST_CSTDFLOAT_FLOAT128_TGAMMA tgammaq_patch #endif // BOOST_CSTDFLOAT_BROKEN_FLOAT128_MATH_FUNCTIONS - #endif + #endif // Implement quadruple-precision functions in the namespace // boost::math::cstdfloat::detail. Subsequently inject these into the @@ -536,9 +536,11 @@ using boost::math::cstdfloat::detail::ldexp; using boost::math::cstdfloat::detail::frexp; using boost::math::cstdfloat::detail::fabs; + #if !(defined(_GLIBCXX_USE_FLOAT128) && defined(__GNUC__) && (__GNUC__ >= 7)) using boost::math::cstdfloat::detail::abs; #endif + using boost::math::cstdfloat::detail::floor; using boost::math::cstdfloat::detail::ceil; using boost::math::cstdfloat::detail::sqrt; diff --git a/include/boost/math/special_functions.hpp b/include/boost/math/special_functions.hpp index c30a26c5f..0f2a66467 100644 --- a/include/boost/math/special_functions.hpp +++ b/include/boost/math/special_functions.hpp @@ -70,5 +70,6 @@ #include #include #include +#include #endif // BOOST_MATH_SPECIAL_FUNCTIONS_HPP diff --git a/include/boost/math/special_functions/lambert_w.hpp b/include/boost/math/special_functions/lambert_w.hpp new file mode 100644 index 000000000..435a703f3 --- /dev/null +++ b/include/boost/math/special_functions/lambert_w.hpp @@ -0,0 +1,2176 @@ +// Copyright John Maddock 2017. +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright Nicholas Thompson 2018 + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +#ifndef BOOST_MATH_SF_LAMBERT_W_HPP +#define BOOST_MATH_SF_LAMBERT_W_HPP + +#ifdef _MSC_VER +#pragma warning(disable : 4127) +#endif + +/* +Implementation of an algorithm for the Lambert W0 and W-1 real-only functions. + +This code is based in part on the algorithm by +Toshio Fukushima, +"Precise and fast computation of Lambert W-functions without transcendental function evaluations", +J.Comp.Appl.Math. 244 (2013) 77-89, +and on a C/C++ version by Darko Veberic, darko.veberic@ijs.si +based on the Fukushima algorithm and Toshio Fukushima's FORTRAN version of his algorithm. + +First derivative of Lambert_w is derived from +Princeton Companion to Applied Mathematics, 'The Lambert-W function', Section 1.3: Series and Generating Functions. + +*/ + +/* +TODO revise this list of macros. +Some macros that will show some (or much) diagnostic values if #defined. +//[boost_math_instrument_lambert_w_macros + +// #define-able macros +BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY // Halley refinement diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION // Precision. +BOOST_MATH_INSTRUMENT_LAMBERT_WM1 // W1 branch diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_WM1_HALLEY // Halley refinement diagnostics only for W-1 branch. +BOOST_MATH_INSTRUMENT_LAMBERT_WM1_TINY // K > 64, z > -1.0264389699511303e-26 +BOOST_MATH_INSTRUMENT_LAMBERT_WM1_LOOKUP // Show results from W-1 lookup table. +BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER // Schroeder refinement diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS // Number of terms used for near-singularity series. +BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES // Show evaluation of series near branch singularity. +BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES +BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS // Show evaluation of series for small z. +//] [/boost_math_instrument_lambert_w_macros] +*/ + +#include +#include +#include +#include +#include // for log (1 + x) +#include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. +#include // powers with compile time exponent, used in arbitrary precision code. +#include // series functor. +//#include // polynomial. +#include // evaluate_polynomial. +#include +#include +#include // boost::math::tools::max_value(). +#include // For create_test_value and macro BOOST_MATH_TEST_VALUE. + +#include +#include +#include +#include + +// Needed for testing and diagnostics only. +#include +#include +#include // For float_distance. + +typedef double lookup_t; // Type for lookup table (double or float, or even long double?) + +//#include "J:\Cpp\Misc\lambert_w_lookup_table_generator\lambert_w_lookup_table.ipp" +// #include "lambert_w_lookup_table.ipp" // Boost.Math version. +#include + +namespace boost { +namespace math { +namespace lambert_w_detail { + +//! \brief Applies a single Halley step to make a better estimate of Lambert W. +//! \details Used the simplified formulae obtained from +//! http://www.wolframalpha.com/input/?i=%5B2(z+exp(z)-w)+d%2Fdx+(z+exp(z)-w)%5D+%2F+%5B2+(d%2Fdx+(z+exp(z)-w))%5E2+-+(z+exp(z)-w)+d%5E2%2Fdx%5E2+(z+exp(z)-w)%5D +//! [2(z exp(z)-w) d/dx (z exp(z)-w)] / [2 (d/dx (z exp(z)-w))^2 - (z exp(z)-w) d^2/dx^2 (z exp(z)-w)] + +//! \tparam T floating-point (or fixed-point) type. +//! \param w_est Lambert W estimate. +//! \param z Argument z for Lambert_w function. +//! \returns New estimate of Lambert W, hopefully improved. +//! +template +inline T lambert_w_halley_step(T w_est, const T z) +{ + BOOST_MATH_STD_USING + T e = exp(w_est); + w_est -= 2 * (w_est + 1) * (e * w_est - z) / (z * (w_est + 2) + e * (w_est * (w_est + 2) + 2)); + return w_est; +} // template lambert_w_halley_step(T w_est, T z) + +//! \brief Halley iterate to refine Lambert_w estimate, +//! taking at least one Halley_step. +//! Repeat Halley steps until the *last step* had fewer than half the digits wrong, +//! the step we've just taken should have been sufficient to have completed the iteration. + +//! \tparam T floating-point (or fixed-point) type. +//! \param z Argument z for Lambert_w function. +//! \param w_est Lambert w estimate. +template +inline + T lambert_w_halley_iterate(T w_est, const T z) +{ + BOOST_MATH_STD_USING + static const T max_diff = boost::math::tools::root_epsilon() * fabs(w_est); + + T w_new = lambert_w_halley_step(w_est, z); + T diff = fabs(w_est - w_new); + while (diff > max_diff) + { + w_est = w_new; + w_new = lambert_w_halley_step(w_est, z); + diff = fabs(w_est - w_new); + } + return w_new; +} // template lambert_w_halley_iterate(T w_est, T z) + +// Two Halley function versions that either +// single step (if mpl::false_) or iterate (if mpl::true_). +// Selected at compile-time using parameter 3. +template +inline +T lambert_w_maybe_halley_iterate(T z, T w, mpl::false_ const&) +{ + return lambert_w_halley_step(z, w); // Single step. +} + +template +inline +T lambert_w_maybe_halley_iterate(T z, T w, mpl::true_ const&) +{ + return lambert_w_halley_iterate(z, w); // Iterate steps. +} + +//! maybe_reduce_to_double function, +//! Two versions that have a compile-time option to +//! reduce argument z to double precision (if mpl::true_). +//! Version is selected at compile-time using parameter 2. + +template +inline +double maybe_reduce_to_double(const T& z, const mpl::true_&) +{ + return static_cast(z); // Reduce to double precision. +} + +template +inline +T maybe_reduce_to_double(const T& z, const mpl::false_&) +{ // Don't reduce to double. + return z; +} + +template +inline +double must_reduce_to_double(const T& z, const mpl::true_&) +{ + return static_cast(z); // Reduce to double precision. +} + +template +inline +double must_reduce_to_double(const T& z, const mpl::false_&) +{ // try a lexical_cast and hope for the best: + return boost::lexical_cast(z); +} + +//! \brief Schroeder method, fifth-order update formula, +//! \details See T. Fukushima page 80-81, and +//! A. Householder, The Numerical Treatment of a Single Nonlinear Equation, +//! McGraw-Hill, New York, 1970, section 4.4. +//! Fukushima algorithm switches to @c schroeder_update after pre-computed bisections, +//! chosen to ensure that the result will be achieve the +/- 10 epsilon target. +//! \param w Lambert w estimate from bisection or series. +//! \param y bracketing value from bisection. +//! \returns Refined estimate of Lambert w. + +// Schroeder refinement, called unless NOT required by precision policy. +template +inline +T schroeder_update(const T w, const T y) +{ + // Compute derivatives using 5th order Schroeder refinement. + // Since this is the final step, it will always use the highest precision type T. + // Example of Call: + // result = schroeder_update(w, y); + //where + // w is estimate of Lambert W (from bisection or series). + // y is z * e^-w. + + BOOST_MATH_STD_USING // Aid argument dependent lookup of abs. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + using boost::math::float_distance; + T fd = float_distance(w, y); + std::cout << "Schroder "; + if (abs(fd) < 214748000.) + { + std::cout << " Distance = "<< static_cast(fd); + } + else + { + std::cout << "Difference w - y = " << (w - y) << "."; + } + std::cout << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + // Fukushima equation 18, page 6. + const T f0 = w - y; // f0 = w - y. + const T f1 = 1 + y; // f1 = df/dW + const T f00 = f0 * f0; + const T f11 = f1 * f1; + const T f0y = f0 * y; + const T result = + w - 4 * f0 * (6 * f1 * (f11 + f0y) + f00 * y) / + (f11 * (24 * f11 + 36 * f0y) + + f00 * (6 * y * y + 8 * f1 * y + f0y)); // Fukushima Page 81, equation 21 from equation 20. + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + std::cout << "Schroeder refined " << w << " " << y << ", difference " << w-y << ", change " << w - result << ", to result " << result << std::endl; + std::cout.precision(saved_precision); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + + return result; +} // template T schroeder_update(const T w, const T y) + + //! \brief Series expansion used near the singularity/branch point z = -exp(-1) = -3.6787944. + //! Wolfram InverseSeries[Series[sqrt[2(p Exp[1 + p] + 1)], {p,-1, 20}]] + //! Wolfram command used to obtain 40 series terms at 50 decimal digit precision was + //! N[InverseSeries[Series[Sqrt[2(p Exp[1 + p] + 1)], { p,-1,40 }]], 50] + //! -1+p-p^2/3+(11 p^3)/72-(43 p^4)/540+(769 p^5)/17280-(221 p^6)/8505+(680863 p^7)/43545600 ... + //! Decimal values of specifications for built-in floating-point types below + //! are at least 21 digits precision == max_digits10 for long double. + //! Longer decimal digits strings are rationals evaluated using Wolfram. + +template +T lambert_w_singularity_series(const T p) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES + std::size_t saved_precision = std::cout.precision(3); + std::cout << "Singularity_series Lambert_w p argument = " << p << std::endl; + std::cout + //<< "Argument Type = " << typeid(T).name() + //<< ", max_digits10 = " << std::numeric_limits::max_digits10 + //<< ", epsilon = " << std::numeric_limits::epsilon() + << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES + + static const T q[] = + { + -static_cast(1), // j0 + +T(1), // j1 + -T(1) / 3, // 1/3 j2 + +T(11) / 72, // 0.152777777777777778, // 11/72 j3 + -T(43) / 540, // 0.0796296296296296296, // 43/540 j4 + +T(769) / 17280, // 0.0445023148148148148, j5 + -T(221) / 8505, // 0.0259847148736037625, j6 + //+T(0.0156356325323339212L), // j7 + //+T(0.015635632532333921222810111699000587889476778365667L), // j7 from Wolfram N[680863/43545600, 50] + +T(680863uLL) / 43545600uLL, // +0.0156356325323339212, j7 + //-T(0.00961689202429943171L), // j8 + -T(1963uLL) / 204120uLL, // 0.00961689202429943171, j8 + //-T(0.0096168920242994317068391142465216539290613364687439L), // j8 from Wolfram N[1963/204120, 50] + +T(226287557uLL) / 37623398400uLL, // 0.00601454325295611786, j9 + -T(5776369uLL) / 1515591000uLL, // 0.00381129803489199923, j10 + //+T(0.00244087799114398267L), j11 0.0024408779911439826658968585286437530215699919795550 + +T(169709463197uLL) / 69528040243200uLL, // j11 + // -T(0.00157693034468678425L), // j12 -0.0015769303446867842539234095399314115973161850314723 + -T(1118511313uLL) / 709296588000uLL, // j12 + +T(667874164916771uLL) / 650782456676352000uLL, // j13 + //+T(0.00102626332050760715L), // j13 0.0010262633205076071544375481533906861056468041465973 + -T(500525573uLL) / 744761417400uLL, // j14 + // -T(0.000672061631156136204L), j14 + //+T(1003663334225097487uLL) / 234281684403486720000uLL, // j15 0.00044247306181462090993020760858473726479232802068800 error C2177: constant too big + //+T(0.000442473061814620910L, // j15 + BOOST_MATH_TEST_VALUE(T, +0.000442473061814620910), // j15 + // -T(0.000292677224729627445L), // j16 + BOOST_MATH_TEST_VALUE(T, -0.000292677224729627445), // j16 + //+T(0.000194387276054539318L), // j17 + BOOST_MATH_TEST_VALUE(T, 0.000194387276054539318), // j17 + //-T(0.000129574266852748819L), // j18 + BOOST_MATH_TEST_VALUE(T, -0.000129574266852748819), // j18 + //+T(0.0000866503580520812717L), // j19 N[+1150497127780071399782389/13277465363600276402995200000, 50] 0.000086650358052081271660451590462390293190597827783288 + BOOST_MATH_TEST_VALUE(T, +0.0000866503580520812717), // j19 + //-T(0.0000581136075044138168L) // j20 N[2853534237182741069/49102686267859224000000, 50] 0.000058113607504413816772205464778828177256611844221913 + // -T(2853534237182741069uLL) / 49102686267859224000000uLL // j20 // error C2177: constant too big, + // so must use BOOST_MATH_TEST_VALUE(T, ) format in hope of using suffix Q for quad or decimal digits string for others. + //-T(0.000058113607504413816772205464778828177256611844221913L), // j20 N[2853534237182741069/49102686267859224000000, 50] 0.000058113607504413816772205464778828177256611844221913 + BOOST_MATH_TEST_VALUE(T, -0.000058113607504413816772205464778828177256611844221913) // j20 - last used by Fukushima + // More terms don't seem to give any improvement (worse in fact) and are not use for many z values. + //BOOST_MATH_TEST_VALUE(T, +0.000039076684867439051635395583044527492132109160553593), // j21 + //BOOST_MATH_TEST_VALUE(T, -0.000026338064747231098738584082718649443078703982217219), // j22 + //BOOST_MATH_TEST_VALUE(T, +0.000017790345805079585400736282075184540383274460464169), // j23 + //BOOST_MATH_TEST_VALUE(T, -0.000012040352739559976942274116578992585158113153190354), // j24 + //BOOST_MATH_TEST_VALUE(T, +8.1635319824966121713827512573558687050675701559448E-6), // j25 + //BOOST_MATH_TEST_VALUE(T, -5.5442032085673591366657251660804575198155559225316E-6) // j26 + // -T(5.5442032085673591366657251660804575198155559225316E-6L) // j26 + // 21 to 26 Added for long double. + }; // static const T q[] + + /* + // Temporary copy of original double values for comparison; these are reproduced well. + static const T q[] = + { + -1L, // j0 + +1L, // j1 + -0.333333333333333333L, // 1/3 j2 + +0.152777777777777778L, // 11/72 j3 + -0.0796296296296296296L, // 43/540 + +0.0445023148148148148L, + -0.0259847148736037625L, + +0.0156356325323339212L, + -0.00961689202429943171L, + +0.00601454325295611786L, + -0.00381129803489199923L, + +0.00244087799114398267L, + -0.00157693034468678425L, + +0.00102626332050760715L, + -0.000672061631156136204L, + +0.000442473061814620910L, + -0.000292677224729627445L, + +0.000194387276054539318L, + -0.000129574266852748819L, + +0.0000866503580520812717L, + -0.0000581136075044138168L // j20 + }; + */ + + // Decide how many series terms to use, increasing as z approaches the singularity, + // balancing run-time versus computational noise from round-off. + // In practice, we truncate the series expansion at a certain order. + // If the order is too large, not only does the amount of computation increase, + // but also the round-off errors accumulate. + // See Fukushima equation 35, page 85 for logic of choice of number of series terms. + + BOOST_MATH_STD_USING // Aid argument dependent lookup (ADL) of abs. + + const T absp = abs(p); + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS + { + int terms = 20; // Default to using all terms. + if (absp < 0.001150) + { // Very near singularity. + terms = 6; + } + else if (absp < 0.0766) + { // Near singularity. + terms = 10; + } + std::streamsize saved_precision = std::cout.precision(3); + std::cout << "abs(p) = " << absp << ", terms = " << terms << std::endl; + std::cout.precision(saved_precision); + } +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS + + if (absp < 0.01159) + { // Only 6 near-singularity series terms are useful. + return + -1 + + p * (1 + + p * (q[2] + + p * (q[3] + + p * (q[4] + + p * (q[5] + + p * q[6] + ))))); + } + else if (absp < 0.0766) // Use 10 near-singularity series terms. + { // Use 10 near-singularity series terms. + return + -1 + + p * (1 + + p * (q[2] + + p * (q[3] + + p * (q[4] + + p * (q[5] + + p * (q[6] + + p * (q[7] + + p * (q[8] + + p * (q[9] + + p * q[10] + ))))))))); + } + else + { // Use all 20 near-singularity series terms. + return + -1 + + p * (1 + + p * (q[2] + + p * (q[3] + + p * (q[4] + + p * (q[5] + + p * (q[6] + + p * (q[7] + + p * (q[8] + + p * (q[9] + + p * (q[10] + + p * (q[11] + + p * (q[12] + + p * (q[13] + + p * (q[14] + + p * (q[15] + + p * (q[16] + + p * (q[17] + + p * (q[18] + + p * (q[19] + + p * q[20] // Last Fukushima term. + ))))))))))))))))))); + // + // more terms for more precise T: long double ... + //// but makes almost no difference, so don't use more terms? + // p*q[21] + + // p*q[22] + + // p*q[23] + + // p*q[24] + + // p*q[25] + // ))))))))))))))))))); + } +} // template T lambert_w_singularity_series(const T p) + + + ///////////////////////////////////////////////////////////////////////////////////////////// + + //! \brief Series expansion used near zero (abs(z) < 0.05). + //! \details + //! Coefficients of the inverted series expansion of the Lambert W function around z = 0. + //! Tosio Fukushima always uses all 17 terms of a Taylor series computed using Wolfram with + //! InverseSeries[Series[z Exp[z],{z,0,17}]] + //! Tosio Fukushima / Journal of Computational and Applied Mathematics 244 (2013) page 86. + + //! Decimal values of specifications for built-in floating-point types below + //! are 21 digits precision == max_digits10 for long double. + //! Care! Some coefficients might overflow some fixed_point types. + + //! This version is intended to allow use by user-defined types + //! like Boost.Multiprecision quad and cpp_dec_float types. + //! The three specializations below for built-in float, double + //! (and perhaps long double) will be chosen in preference for these types. + + //! This version uses rationals computed by Wolfram as far as possible, + //! limited by maximum size of uLL integers. + //! For higher term, uses decimal digit strings computed by Wolfram up to the maximum possible using uLL rationals, + //! and then higher coefficients are computed as necessary using function lambert_w0_small_z_series_term + //! until the precision required by the policy is achieved. + //! InverseSeries[Series[z Exp[z],{z,0,34}]] also computed. + + // Series evaluation for LambertW(z) as z -> 0. + // See http://functions.wolfram.com/ElementaryFunctions/ProductLog/06/01/01/0003/ + // http://functions.wolfram.com/ElementaryFunctions/ProductLog/06/01/01/0003/MainEq1.L.gif + + //! \brief lambert_w0_small_z uses a tag_type to select a variant depending on the size of the type. + //! The Lambert W is computed by lambert_w0_small_z for small z. + //! The cutoff for z smallness determined by Tosio Fukushima by trial and error is (abs(z) < 0.05), + //! but the optimum might be a function of the size of the type of z. + + //! \details + //! The tag_type selection is based on the value @c std::numeric_limits::max_digits10. + //! This allows distinguishing between long double types that commonly vary between 64 and 80-bits, + //! and also compilers that have a float type using 64 bits and/or long double using 128-bits. + //! It assumes that max_digits10 is defined correctly or this might fail to make the correct selection. + //! causing very small differences in computing lambert_w that would be very difficult to detect and diagnose. + //! Cannot switch on @c std::numeric_limits<>::max() because comparison values may overflow the compiler limit. + //! Cannot switch on @c std::numeric_limits::max_exponent10() + //! because both 80 and 128 bit floating-point implementations use 11 bits for the exponent. + //! So must rely on @c std::numeric_limits::max_digits10. + + //! Specialization of float zero series expansion used for small z (abs(z) < 0.05). + //! Specializations of lambert_w0_small_z for built-in types. + //! These specializations should be chosen in preference to T version. + //! For example: lambert_w0_small_z(0.001F) should use the float version. + //! (Parameter Policy is not used by built-in types when all terms are used during an inline computation, + //! but for the tag_type selection to work, they all must include Policy in their signature. + + // Forward declaration of variants of lambert_w0_small_z. +template +T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<0> const&); // for float (32-bit) type. + +template +T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<1> const&); // for double (64-bit) type. + +template +T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<2> const&); // for long double (double extended 80-bit) type. + +template +T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<3> const&); // for long double (128-bit) type. + +template +T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<4> const&); // for float128 quadmath Q type. + +template +T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<5> const&); // Generic multiprecision T. + // Set tag_type depending on max_digits10. +template +T lambert_w0_small_z(T x, const Policy& pol) +{ //std::numeric_limits::max_digits10 == 36 ? 3 : // 128-bit long double. + typedef boost::mpl::int_ + < + std::numeric_limits::is_specialized == 0 ? 5 : +#ifndef BOOST_NO_CXX11_NUMERIC_LIMITS + std::numeric_limits::max_digits10 <= 9 ? 0 : // for float 32-bit. + std::numeric_limits::max_digits10 <= 17 ? 1 : // for double 64-bit. + std::numeric_limits::max_digits10 <= 22 ? 2 : // for 80-bit double extended. + std::numeric_limits::max_digits10 < 37 ? 4 // for both 128-bit long double (3) and 128-bit quad suffix Q type (4). +#else + std::numeric_limits::radix != 2 ? 5 : + std::numeric_limits::digits <= 24 ? 0 : // for float 32-bit. + std::numeric_limits::digits <= 53 ? 1 : // for double 64-bit. + std::numeric_limits::digits <= 64 ? 2 : // for 80-bit double extended. + std::numeric_limits::digits <= 113 ? 4 // for both 128-bit long double (3) and 128-bit quad suffix Q type (4). +#endif + : 5 // All Generic multiprecision types. + > tag_type; + // std::cout << "\ntag type = " << tag_type << std::endl; // error C2275: 'tag_type': illegal use of this type as an expression. + return lambert_w0_small_z(x, pol, tag_type()); +} // template T lambert_w0_small_z(T x) + + //! Specialization of float (32-bit) series expansion used for small z (abs(z) < 0.05). + // Only 9 Coefficients are computed to 21 decimal digits precision, ample for 32-bit float used by most platforms. + // Taylor series coefficients used are computed by Wolfram to 50 decimal digits using instruction + // N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], + // as proposed by Tosio Fukushima and implemented by Darko Veberic. + +template +T lambert_w0_small_z(T z, const Policy&, boost::mpl::int_<0> const&) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::streamsize prec = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout << "\ntag_type 0 float lambert_w0_small_z called with z = " << z << " using " << 9 << " terms of precision " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z * (1 - // j1 z^1 term = 1 + z * (1 - // j2 z^2 term = -1 + z * (static_cast(3uLL) / 2uLL - // 3/2 // j3 z^3 term = 1.5. + z * (2.6666666666666666667F - // 8/3 // j4 + z * (5.2083333333333333333F - // -125/24 // j5 + z * (10.8F - // j6 + z * (23.343055555555555556F - // j7 + z * (52.012698412698412698F - // j8 + z * 118.62522321428571429F)))))))); // j9 + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "return w = " << result << std::endl; + std::cout.precision(prec); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + return result; +} // template T lambert_w0_small_z(T x, mpl::int_<0> const&) + + //! Specialization of double (64-bit double) series expansion used for small z (abs(z) < 0.05). + // 17 Coefficients are computed to 21 decimal digits precision suitable for 64-bit double used by most platforms. + // Taylor series coefficients used are computed by Wolfram to 50 decimal digits using instruction + // N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], as proposed by Tosio Fukushima and implemented by Veberic. + +template +T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<1> const&) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::streamsize prec = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout << "\ntag_type 1 double lambert_w0_small_z called with z = " << z << " using " << 17 << " terms of precision, " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z * (1. - // j1 z^1 + z * (1. - // j2 z^2 + z * (1.5 - // 3/2 // j3 z^3 + z * (2.6666666666666666667 - // 8/3 // j4 + z * (5.2083333333333333333 - // -125/24 // j5 + z * (10.8 - // j6 + z * (23.343055555555555556 - // j7 + z * (52.012698412698412698 - // j8 + z * (118.62522321428571429 - // j9 + z * (275.57319223985890653 - // j10 + z * (649.78717234347442681 - // j11 + z * (1551.1605194805194805 - // j12 + z * (3741.4497029592385495 - // j13 + z * (9104.5002411580189358 - // j14 + z * (22324.308512706601434 - // j15 + z * (55103.621972903835338 - // j16 + z * 136808.86090394293563)))))))))))))))); // j17 z^17 + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "return w = " << result << std::endl; + std::cout.precision(prec); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + return result; +} // T lambert_w0_small_z(const T z, boost::mpl::int_<1> const&) + + //! Specialization of long double (80-bit double extended) series expansion used for small z (abs(z) < 0.05). + // 21 Coefficients are computed to 21 decimal digits precision suitable for 80-bit long double used by some + // platforms including GCC and Clang when generating for Intel X86 floating-point processors with 80-bit operations enabled (the default). + // (This is NOT used by Microsoft Visual Studio where double and long always both use only 64-bit type. + // Nor used for 128-bit float128.) +template +T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<2> const&) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::streamsize precision = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout << "\ntag_type 2 long double (80-bit double extended) lambert_w0_small_z called with z = " << z << " using " << 21 << " terms of precision, " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES +// T result = +// z * (1.L - // j1 z^1 +// z * (1.L - // j2 z^2 +// z * (1.5L - // 3/2 // j3 +// z * (2.6666666666666666667L - // 8/3 // j4 +// z * (5.2083333333333333333L - // -125/24 // j5 +// z * (10.800000000000000000L - // j6 +// z * (23.343055555555555556L - // j7 +// z * (52.012698412698412698L - // j8 +// z * (118.62522321428571429L - // j9 +// z * (275.57319223985890653L - // j10 +// z * (649.78717234347442681L - // j11 +// z * (1551.1605194805194805L - // j12 +// z * (3741.4497029592385495L - // j13 +// z * (9104.5002411580189358L - // j14 +// z * (22324.308512706601434L - // j15 +// z * (55103.621972903835338L - // j16 +// z * (136808.86090394293563L - // j17 z^17 last term used by Fukushima double. +// z * (341422.050665838363317L - // z^18 +// z * (855992.9659966075514633L - // z^19 +// z * (2.154990206091088289321e6L - // z^20 +// z * 5.4455529223144624316423e6L // z^21 +// )))))))))))))))))))); +// + + T result = +z * (1.L - // z j1 +z * (1.L - // z^2 +z * (1.500000000000000000000000000000000L - // z^3 +z * (2.666666666666666666666666666666666L - // z ^ 4 +z * (5.208333333333333333333333333333333L - // z ^ 5 +z * (10.80000000000000000000000000000000L - // z ^ 6 +z * (23.34305555555555555555555555555555L - // z ^ 7 +z * (52.01269841269841269841269841269841L - // z ^ 8 +z * (118.6252232142857142857142857142857L - // z ^ 9 +z * (275.5731922398589065255731922398589L - // z ^ 10 +z * (649.7871723434744268077601410934744L - // z ^ 11 +z * (1551.160519480519480519480519480519L - // z ^ 12 +z * (3741.449702959238549516327294105071L - //z ^ 13 +z * (9104.500241158018935796713574491352L - // z ^ 14 +z * (22324.308512706601434280005708577137L - // z ^ 15 +z * (55103.621972903835337697771560205422L - // z ^ 16 +z * (136808.86090394293563342215789305736L - // z ^ 17 +z * (341422.05066583836331735491399356945L - // z^18 +z * (855992.9659966075514633630250633224L - // z^19 +z * (2.154990206091088289321708745358647e6L // z^20 distance -5 without term 20 +)))))))))))))))))))); + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "return w = " << result << std::endl; + std::cout.precision(precision); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + return result; +} // long double lambert_w0_small_z(const T z, boost::mpl::int_<1> const&) + +//! Specialization of 128-bit long double series expansion used for small z (abs(z) < 0.05). +// 34 Taylor series coefficients used are computed by Wolfram to 50 decimal digits using instruction +// N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], +// and are suffixed by L as they are assumed of type long double. +// (This is NOT used for 128-bit quad boost::multiprecision::float128 type which required a suffix Q +// nor multiprecision type cpp_bin_float_quad that can only be initialised at full precision of the type +// constructed with a decimal digit string like "2.6666666666666666666666666666666666666666666666667".) + +template +T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<3> const&) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::streamsize precision = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout << "\ntag_type 3 long double (128-bit) lambert_w0_small_z called with z = " << z << " using " << 17 << " terms of precision, " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z * (1.L - // j1 + z * (1.L - // j2 + z * (1.5L - // 3/2 // j3 + z * (2.6666666666666666666666666666666666L - // 8/3 // j4 + z * (5.2052083333333333333333333333333333L - // -125/24 // j5 + z * (10.800000000000000000000000000000000L - // j6 + z * (23.343055555555555555555555555555555L - // j7 + z * (52.0126984126984126984126984126984126L - // j8 + z * (118.625223214285714285714285714285714L - // j9 + z * (275.57319223985890652557319223985890L - // * z ^ 10 - // j10 + z * (649.78717234347442680776014109347442680776014109347L - // j11 + z * (1551.1605194805194805194805194805194805194805194805L - // j12 + z * (3741.4497029592385495163272941050718828496606274384L - // j13 + z * (9104.5002411580189357967135744913522691300469078247L - // j14 + z * (22324.308512706601434280005708577137148565719994291L - // j15 + z * (55103.621972903835337697771560205422639285073147507L - // j16 + z * 136808.86090394293563342215789305736395683485630576L // j17 + )))))))))))))))); + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "return w = " << result << std::endl; + std::cout.precision(precision); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + return result; +} // T lambert_w0_small_z(const T z, boost::mpl::int_<3> const&) + +//! Specialization of 128-bit quad series expansion used for small z (abs(z) < 0.05). +// 34 Taylor series coefficients used were computed by Wolfram to 50 decimal digits using instruction +// N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], +// and are suffixed by Q as they are assumed of type quad. +// This could be used for 128-bit quad (which requires a suffix Q for full precision). +// But experiments with GCC 7.2.0 show that while this gives full 128-bit precision +// when the -f-ext-numeric-literals option is in force and the libquadmath library available, +// over the range -0.049 to +0.049, +// it is slightly slower than getting a double approximation followed by a single Halley step. + +#ifdef BOOST_HAS_FLOAT128 +template +T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<4> const&) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::streamsize precision = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout << "\ntag_type 4 128-bit quad float128 lambert_w0_small_z called with z = " << z << " using " << 34 << " terms of precision, " + << std::numeric_limits::max_digits10 << " max decimal digits." << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z * (1.Q - // z j1 + z * (1.Q - // z^2 + z * (1.500000000000000000000000000000000Q - // z^3 + z * (2.666666666666666666666666666666666Q - // z ^ 4 + z * (5.208333333333333333333333333333333Q - // z ^ 5 + z * (10.80000000000000000000000000000000Q - // z ^ 6 + z * (23.34305555555555555555555555555555Q - // z ^ 7 + z * (52.01269841269841269841269841269841Q - // z ^ 8 + z * (118.6252232142857142857142857142857Q - // z ^ 9 + z * (275.5731922398589065255731922398589Q - // z ^ 10 + z * (649.7871723434744268077601410934744Q - // z ^ 11 + z * (1551.160519480519480519480519480519Q - // z ^ 12 + z * (3741.449702959238549516327294105071Q - //z ^ 13 + z * (9104.500241158018935796713574491352Q - // z ^ 14 + z * (22324.308512706601434280005708577137Q - // z ^ 15 + z * (55103.621972903835337697771560205422Q - // z ^ 16 + z * (136808.86090394293563342215789305736Q - // z ^ 17 + z * (341422.05066583836331735491399356945Q - // z^18 + z * (855992.9659966075514633630250633224Q - // z^19 + z * (2.154990206091088289321708745358647e6Q - // 20 + z * (5.445552922314462431642316420035073e6Q - // 21 + z * (1.380733000216662949061923813184508e7Q - // 22 + z * (3.511704498513923292853869855945334e7Q - // 23 + z * (8.956800256102797693072819557780090e7Q - // 24 + z * (2.290416846187949813964782641734774e8Q - // 25 + z * (5.871035041171798492020292225245235e8Q - // 26 + z * (1.508256053857792919641317138812957e9Q - // 27 + z * (3.882630161293188940385873468413841e9Q - // 28 + z * (1.001394313665482968013913601565723e10Q - // 29 + z * (2.587356736265760638992878359024929e10Q - // 30 + z * (6.696209709358073856946120522333454e10Q - // 31 + z * (1.735711659599198077777078238043644e11Q - // 32 + z * (4.505680465642353886756098108484670e11Q - // 33 + z * (1.171223178256487391904047636564823e12Q //z^34 + )))))))))))))))))))))))))))))))))); + + + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "return w = " << result << std::endl; + std::cout.precision(precision); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + return result; +} // T lambert_w0_small_z(const T z, boost::mpl::int_<4> const&) float128 + +#else + +template +inline T lambert_w0_small_z(const T z, const Policy& pol, boost::mpl::int_<4> const&) +{ + return lambert_w0_small_z(z, pol, boost::mpl::int_<5>()); +} + +#endif // BOOST_HAS_FLOAT128 + +//! Series functor to compute series term using pow and factorial. +//! \details Functor is called after evaluating polynomial with the coefficients as rationals below. +template +struct lambert_w0_small_z_series_term +{ + typedef T result_type; + //! \param _z Lambert W argument z. + //! \param -term -pow<18>(z) / 6402373705728000uLL + //! \param _k number of terms == initially 18 + + // Note *after* evaluating N terms, its internal state has k = N and term = (-1)^N z^N. + + lambert_w0_small_z_series_term(T _z, T _term, int _k) + : k(_k), z(_z), term(_term) { } + + T operator()() + { // Called by sum_series until needs precision set by factor (policy::get_epsilon). + using std::pow; + ++k; + term *= -z / k; + //T t = pow(z, k) * pow(T(k), -1 + k) / factorial(k); // (z^k * k(k-1)^k) / k! + T result = term * pow(T(k), -1 + k); // term * k^(k-1) + // std::cout << " k = " << k << ", term = " << term << ", result = " << result << std::endl; + return result; // + } +private: + int k; + T z; + T term; +}; // template struct lambert_w0_small_z_series_term + + //! Generic variant for T a User-defined types like Boost.Multiprecision. +template +inline T lambert_w0_small_z(T z, const Policy& pol, boost::mpl::int_<5> const&) +{ +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::streamsize precision = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout << "Generic lambert_w0_small_z called with z = " << z << " using as many terms needed for precision." << std::endl; + std::cout << "Argument z is of type " << typeid(T).name() << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + // First several terms of the series are tabulated and evaluated as a polynomial: + // this will save us a bunch of expensive calls to pow. + // Then our series functor is initialized "as if" it had already reached term 18, + // enough evaluation of built-in 64-bit double and float (and 80-bit long double?) types. + + // Coefficients should be stored such that the coefficients for the x^i terms are in poly[i]. + static const T coeff[] = + { + 0, // z^0 Care: zeroth term needed by tools::evaluate_polynomial, but not in the Wolfram equation, so indexes are one different! + 1, // z^1 term. + -1, // z^2 term + static_cast(3uLL) / 2uLL, // z^3 term. + -static_cast(8uLL) / 3uLL, // z^4 + static_cast(125uLL) / 24uLL, // z^5 + -static_cast(54uLL) / 5uLL, // z^6 + static_cast(16807uLL) / 720uLL, // z^7 + -static_cast(16384uLL) / 315uLL, // z^8 + static_cast(531441uLL) / 4480uLL, // z^9 + -static_cast(156250uLL) / 567uLL, // z^10 + static_cast(2357947691uLL) / 3628800uLL, // z^11 + -static_cast(2985984uLL) / 1925uLL, // z^12 + static_cast(1792160394037uLL) / 479001600uLL, // z^13 + -static_cast(7909306972uLL) / 868725uLL, // z^14 + static_cast(320361328125uLL) / 14350336uLL, // z^15 + -static_cast(35184372088832uLL) / 638512875uLL, // z^16 + static_cast(2862423051509815793uLL) / 20922789888000uLL, // z^17 term + -static_cast(5083731656658uLL) / 14889875uLL, + // z^18 term. = 136808.86090394293563342215789305735851647769682393 + + // z^18 is biggest that can be computed as rational using the largest possible uLL integers, + // so higher terms cannot be potentially compiler-computed as uLL rationals. + // Wolfram (5083731656658 z ^ 18) / 14889875 or + // -341422.05066583836331735491399356945575432970390954 z^18 + + // See note below calling the functor to compute another term, + // sufficient for 80-bit long double precision. + // Wolfram -341422.05066583836331735491399356945575432970390954 z^19 term. + // (5480386857784802185939 z^19)/6402373705728000 + // But now this variant is not used to compute long double + // as specializations are provided above. + }; // static const T coeff[] + + /* + Table of 19 computed coefficients: + + #0 0 + #1 1 + #2 -1 + #3 1.5 + #4 -2.6666666666666666666666666666666665382713370408509 + #5 5.2083333333333333333333333333333330765426740817019 + #6 -10.800000000000000000000000000000000616297582203915 + #7 23.343055555555555555555555555555555076212991619177 + #8 -52.012698412698412698412698412698412659282693193402 + #9 118.62522321428571428571428571428571146835390992496 + #10 -275.57319223985890652557319223985891400375196748314 + #11 649.7871723434744268077601410934743969785223845882 + #12 -1551.1605194805194805194805194805194947599566007429 + #13 3741.4497029592385495163272941050719510009019331763 + #14 -9104.5002411580189357967135744913524243896052869184 + #15 22324.308512706601434280005708577137322392070452582 + #16 -55103.621972903835337697771560205423203318720697224 + #17 136808.86090394293563342215789305735851647769682393 + 136808.86090394293563342215789305735851647769682393 == Exactly same as Wolfram computed value. + #18 -341422.05066583836331735491399356947486381600607416 + 341422.05066583836331735491399356945575432970390954 z^19 Wolfram value differs at 36 decimal digit, as expected. + */ + + using boost::math::policies::get_epsilon; // for type T. + using boost::math::tools::sum_series; + using boost::math::tools::evaluate_polynomial; + // http://www.boost.org/doc/libs/release/libs/math/doc/html/math_toolkit/roots/rational.html + + // std::streamsize prec = std::cout.precision(std::numeric_limits ::max_digits10); + + T result = evaluate_polynomial(coeff, z); + // template + // V evaluate_polynomial(const T(&poly)[N], const V& val); + // Size of coeff found from N + //std::cout << "evaluate_polynomial(coeff, z); == " << result << std::endl; + //std::cout << "result = " << result << std::endl; + // It's an artefact of the way I wrote the functor: *after* evaluating N + // terms, its internal state has k = N and term = (-1)^N z^N. So after + // evaluating 18 terms, we initialize the functor to the term we've just + // evaluated, and then when it's called, it increments itself to the next term. + // So 18!is 6402373705728000, which is where that comes from. + + // The 19th coefficient of the polynomial is actually, 19 ^ 18 / 19!= + // 104127350297911241532841 / 121645100408832000 which after removing GCDs + // reduces down to Wolfram rational 5480386857784802185939 / 6402373705728000. + // Wolfram z^19 term +(5480386857784802185939 z^19) /6402373705728000 + // +855992.96599660755146336302506332246623424823099755 z^19 + + //! Evaluate Functor. + lambert_w0_small_z_series_term s(z, -pow<18>(z) / 6402373705728000uLL, 18); + + // Temporary to list the coefficients. + //std::cout << " Table of coefficients" << std::endl; + //std::streamsize saved_precision = std::cout.precision(50); + //for (size_t i = 0; i != 19; i++) + //{ + // std::cout << "#" << i << " " << coeff[i] << std::endl; + //} + //std::cout.precision(saved_precision); + + boost::uintmax_t max_iter = policies::get_max_series_iterations(); // Max iterations from policy. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "max iter from policy = " << max_iter << std::endl; + // // max iter from policy = 1000000 is default. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + result = sum_series(s, get_epsilon(), max_iter, result); + // result == evaluate_polynomial. + //sum_series(Functor& func, int bits, boost::uintmax_t& max_terms, const U& init_value) + // std::cout << "sum_series(s, get_epsilon(), max_iter, result); = " << result << std::endl; + + //T epsilon = get_epsilon(); + //std::cout << "epilson from policy = " << epsilon << std::endl; + // epilson from policy = 1.93e-34 for T == quad + // 5.35e-51 for t = cpp_bin_float_50 + + // std::cout << " get eps = " << get_epsilon() << std::endl; // quad eps = 1.93e-34, bin_float_50 eps = 5.35e-51 + policies::check_series_iterations("boost::math::lambert_w0_small_z<%1%>(%1%)", max_iter, pol); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS + std::cout << "z = " << z << " needed " << max_iter << " iterations." << std::endl; + std::cout.precision(prec); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS + return result; +} // template inline T lambert_w0_small_z_series(T z, const Policy& pol) + +// Approximate lambert_w0 (used for z values that are outside range of lookup table or rational functions) +// Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. +template +inline +T lambert_w0_approx(T z) +{ + BOOST_MATH_STD_USING + T lz = log(z); + T llz = log(lz); + T w = lz - llz + (llz / lz); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + return w; + // std::cout << "w max " << max_w << std::endl; // double 703.227 +} + + ////////////////////////////////////////////////////////////////////////////////////////// + +//! \brief Lambert_w0 implementations for float, double and higher precisions. +//! 3rd parameter used to select which version is used. + +//! /details Rational polynomials are provided for several range of argument z. +//! For very small values of z, and for z very near the branch singularity at -e^-1 (~= -0.367879), +//! two other series functions are used. + +//! float precision polynomials are used for 32-bit (usually float) precision (for speed) +//! double precision polynomials are used for 64-bit (usually double) precision. +//! For higher precisions, a 64-bit double approximation is computed first, +//! and then refined using Halley interations. + +template +inline T get_near_singularity_param(T z) +{ + BOOST_MATH_STD_USING + const T p2 = 2 * (boost::math::constants::e() * z + 1); + const T p = sqrt(p2); + return p; +} +inline float get_near_singularity_param(float z) +{ + return static_cast(get_near_singularity_param((double)z)); +} +inline double get_near_singularity_param(double z) +{ + return static_cast(get_near_singularity_param((long double)z)); +} + +// Forward declarations: + +//template T lambert_w0_small_z(T z, const Policy& pol); +//template +//T lambert_w0_imp(T w, const Policy& pol, const mpl::int_<0>&); // 32 bit usually float. +//template +//T lambert_w0_imp(T w, const Policy& pol, const mpl::int_<1>&); // 64 bit usually double. +//template +//T lambert_w0_imp(T w, const Policy& pol, const mpl::int_<2>&); // 80-bit long double. + +template +T lambert_w_positive_rational_float(T z) +{ + BOOST_MATH_STD_USING + if (z < 2) + { + if (z < 0.5) + { // 0.05 < z < 0.5 + // Maximum Deviation Found: 2.993e-08 + // Expected Error Term : 2.993e-08 + // Maximum Relative Change in Control Points : 7.555e-04 Y offset : -8.196592331e-01 + static const T Y = 8.196592331e-01f; + static const T P[] = { + 1.803388345e-01f, + -4.820256838e-01f, + -1.068349741e+00f, + -3.506624319e-02f, + }; + static const T Q[] = { + 1.000000000e+00f, + 2.871703469e+00f, + 1.690949264e+00f, + }; + return z * (Y + boost::math::tools::evaluate_polynomial(P, z) / boost::math::tools::evaluate_polynomial(Q, z)); + } + else + { // 0.5 < z < 2 + // Max error in interpolated form: 1.018e-08 + static const T Y = 5.503368378e-01f; + static const T P[] = { + 4.493332766e-01f, + 2.543432707e-01f, + -4.808788799e-01f, + -1.244425316e-01f, + }; + static const T Q[] = { + 1.000000000e+00f, + 2.780661241e+00f, + 1.830840318e+00f, + 2.407221031e-01f, + }; + return z * (Y + boost::math::tools::evaluate_rational(P, Q, z)); + } + } + else if (z < 6) + { + // 2 < z < 6 + // Max error in interpolated form: 2.944e-08 + static const T Y = 1.162393570e+00f; + static const T P[] = { + -1.144183394e+00f, + -4.712732855e-01f, + 1.563162512e-01f, + 1.434010911e-02f, + }; + static const T Q[] = { + 1.000000000e+00f, + 1.192626340e+00f, + 2.295580708e-01f, + 5.477869455e-03f, + }; + return Y + boost::math::tools::evaluate_rational(P, Q, z); + } + else if (z < 18) + { + // 6 < z < 18 + // Max error in interpolated form: 5.893e-08 + static const T Y = 1.809371948e+00f; + static const T P[] = { + -1.689291769e+00f, + -3.337812742e-01f, + 3.151434873e-02f, + 1.134178734e-03f, + }; + static const T Q[] = { + 1.000000000e+00f, + 5.716915685e-01f, + 4.489521292e-02f, + 4.076716763e-04f, + }; + return Y + boost::math::tools::evaluate_rational(P, Q, z); + } + else if (z < 9897.12905874) // 2.8 < log(z) < 9.2 + { + // Max error in interpolated form: 1.771e-08 + static const T Y = -1.402973175e+00f; + static const T P[] = { + 1.966174312e+00f, + 2.350864728e-01f, + -5.098074353e-02f, + -1.054818339e-02f, + }; + static const T Q[] = { + 1.000000000e+00f, + 4.388208264e-01f, + 8.316639634e-02f, + 3.397187918e-03f, + -1.321489743e-05f, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_polynomial(P, log_w) / boost::math::tools::evaluate_polynomial(Q, log_w); + } + else if (z < 7.896296e+13) // 9.2 < log(z) <= 32 + { + // Max error in interpolated form: 5.821e-08 + static const T Y = -2.735729218e+00f; + static const T P[] = { + 3.424903470e+00f, + 7.525631787e-02f, + -1.427309584e-02f, + -1.435974178e-05f, + }; + static const T Q[] = { + 1.000000000e+00f, + 2.514005579e-01f, + 6.118994652e-03f, + -1.357889535e-05f, + 7.312865624e-08f, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_polynomial(P, log_w) / boost::math::tools::evaluate_polynomial(Q, log_w); + } + else // 32 < log(z) < 100 + { + // Max error in interpolated form: 1.491e-08 + static const T Y = -4.012863159e+00f; + static const T P[] = { + 4.431629226e+00f, + 2.756690487e-01f, + -2.992956930e-03f, + -4.912259384e-05f, + }; + static const T Q[] = { + 1.000000000e+00f, + 2.015434591e-01f, + 4.949426142e-03f, + 1.609659944e-05f, + -5.111523436e-09f, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_polynomial(P, log_w) / boost::math::tools::evaluate_polynomial(Q, log_w); + } +} + +template +T lambert_w_negative_rational_float(T z, const Policy& pol) +{ + BOOST_MATH_STD_USING + if (z > -0.27) + { + if (z < -0.051) + { + // -0.27 < z < -0.051 + // Max error in interpolated form: 5.080e-08 + static const T Y = 1.255809784e+00f; + static const T P[] = { + -2.558083412e-01f, + -2.306524098e+00f, + -5.630887033e+00f, + -3.803974556e+00f, + }; + static const T Q[] = { + 1.000000000e+00f, + 5.107680783e+00f, + 7.914062868e+00f, + 3.501498501e+00f, + }; + return z * (Y + boost::math::tools::evaluate_rational(P, Q, z)); + } + else + { + // Very small z so use a series function. + return lambert_w0_small_z(z, pol); + } + } + else if (z > -0.3578794411714423215955237701) + { // Very close to branch singularity. + // Max error in interpolated form: 5.269e-08 + static const T Y = 1.220928431e-01f; + static const T P[] = { + -1.221787446e-01f, + -6.816155875e+00f, + 7.144582035e+01f, + 1.128444390e+03f, + }; + static const T Q[] = { + 1.000000000e+00f, + 6.480326790e+01f, + 1.869145243e+02f, + -1.361804274e+03f, + 1.117826726e+03f, + }; + T d = z + 0.367879441171442321595523770161460867445811f; + return -d / (Y + boost::math::tools::evaluate_polynomial(P, d) / boost::math::tools::evaluate_polynomial(Q, d)); + } + else + { + // z is very close (within 0.01) of the singularity at e^-1. + return lambert_w_singularity_series(get_near_singularity_param(z)); + } +} + +//! Lambert_w0 @b 'float' implementation, selected when T is 32-bit precision. +template +inline T lambert_w0_imp(T z, const Policy& pol, const mpl::int_<1>&) +{ + static const char* function = "boost::math::lambert_w0<%1%>"; // For error messages. + BOOST_MATH_STD_USING // Aid ADL of std functions. + + if ((boost::math::isnan)(z)) + { + return boost::math::policies::raise_domain_error(function, "Expected a value > -e^-1 (-0.367879...) but got %1%.", z, pol); + } + if ((boost::math::isinf)(z)) + { + return boost::math::policies::raise_overflow_error(function, "Expected a finite value but got %1%.", z, pol); + } + + if (z >= 0.05) // Fukushima switch point. + // if (z >= 0.045) // 34 terms makes 128-bit 'exact' below 0.045. + { // Normal ranges using several rational polynomials. + return lambert_w_positive_rational_float(z); + } + else if (z <= -0.3678794411714423215955237701614608674458111310f) + { + if (z < -0.3678794411714423215955237701614608674458111310f) + return boost::math::policies::raise_domain_error(function, "Expected z >= -e^-1 (-0.367879...) but got %1%.", z, pol); + return -1; + } + else // z < 0.05 + { + return lambert_w_negative_rational_float(z, pol); + } +} // T lambert_w0_imp(T z, const Policy& pol, const mpl::int_<1>&) for 32-bit usually float. + +template +T lambert_w_positive_rational_double(T z) +{ + BOOST_MATH_STD_USING + if (z < 2) + { + if (z < 0.5) + { + // Max error in interpolated form: 2.255e-17 + static const T offset = 8.19659233093261719e-01; + static const T P[] = { + 1.80340766906685177e-01, + 3.28178241493119307e-01, + -2.19153620687139706e+00, + -7.24750929074563990e+00, + -7.28395876262524204e+00, + -2.57417169492512916e+00, + -2.31606948888704503e-01 + }; + static const T Q[] = { + 1.00000000000000000e+00, + 7.36482529307436604e+00, + 2.03686007856430677e+01, + 2.62864592096657307e+01, + 1.59742041380858333e+01, + 4.03760534788374589e+00, + 2.91327346750475362e-01 + }; + return z * (offset + boost::math::tools::evaluate_polynomial(P, z) / boost::math::tools::evaluate_polynomial(Q, z)); + } + else + { + // Max error in interpolated form: 3.806e-18 + static const T offset = 5.50335884094238281e-01; + static const T P[] = { + 4.49664083944098322e-01, + 1.90417666196776909e+00, + 1.99951368798255994e+00, + -6.91217310299270265e-01, + -1.88533935998617058e+00, + -7.96743968047750836e-01, + -1.02891726031055254e-01, + -3.09156013592636568e-03 + }; + static const T Q[] = { + 1.00000000000000000e+00, + 6.45854489419584014e+00, + 1.54739232422116048e+01, + 1.72606164253337843e+01, + 9.29427055609544096e+00, + 2.29040824649748117e+00, + 2.21610620995418981e-01, + 5.70597669908194213e-03 + }; + return z * (offset + boost::math::tools::evaluate_rational(P, Q, z)); + } + } + else if (z < 6) + { + // 2 < z < 6 + // Max error in interpolated form: 1.216e-17 + static const T Y = 1.16239356994628906e+00; + static const T P[] = { + -1.16230494982099475e+00, + -3.38528144432561136e+00, + -2.55653717293161565e+00, + -3.06755172989214189e-01, + 1.73149743765268289e-01, + 3.76906042860014206e-02, + 1.84552217624706666e-03, + 1.69434126904822116e-05, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 3.77187616711220819e+00, + 4.58799960260143701e+00, + 2.24101228462292447e+00, + 4.54794195426212385e-01, + 3.60761772095963982e-02, + 9.25176499518388571e-04, + 4.43611344705509378e-06, + }; + return Y + boost::math::tools::evaluate_rational(P, Q, z); + } + else if (z < 18) + { + // 6 < z < 18 + // Max error in interpolated form: 1.985e-19 + static const T offset = 1.80937194824218750e+00; + static const T P[] = + { + -1.80690935424793635e+00, + -3.66995929380314602e+00, + -1.93842957940149781e+00, + -2.94269984375794040e-01, + 1.81224710627677778e-03, + 2.48166798603547447e-03, + 1.15806592415397245e-04, + 1.43105573216815533e-06, + 3.47281483428369604e-09 + }; + static const T Q[] = { + 1.00000000000000000e+00, + 2.57319080723908597e+00, + 1.96724528442680658e+00, + 5.84501352882650722e-01, + 7.37152837939206240e-02, + 3.97368430940416778e-03, + 8.54941838187085088e-05, + 6.05713225608426678e-07, + 8.17517283816615732e-10 + }; + return offset + boost::math::tools::evaluate_rational(P, Q, z); + } + else if (z < 9897.12905874) // 2.8 < log(z) < 9.2 + { + // Max error in interpolated form: 1.195e-18 + static const T Y = -1.40297317504882812e+00; + static const T P[] = { + 1.97011826279311924e+00, + 1.05639945701546704e+00, + 3.33434529073196304e-01, + 3.34619153200386816e-02, + -5.36238353781326675e-03, + -2.43901294871308604e-03, + -2.13762095619085404e-04, + -4.85531936495542274e-06, + -2.02473518491905386e-08, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 8.60107275833921618e-01, + 4.10420467985504373e-01, + 1.18444884081994841e-01, + 2.16966505556021046e-02, + 2.24529766630769097e-03, + 9.82045090226437614e-05, + 1.36363515125489502e-06, + 3.44200749053237945e-09, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_rational(P, Q, log_w); + } + else if (z < 7.896296e+13) // 9.2 < log(z) <= 32 + { + // Max error in interpolated form: 6.529e-18 + static const T Y = -2.73572921752929688e+00; + static const T P[] = { + 3.30547638424076217e+00, + 1.64050071277550167e+00, + 4.57149576470736039e-01, + 4.03821227745424840e-02, + -4.99664976882514362e-04, + -1.28527893803052956e-04, + -2.95470325373338738e-06, + -1.76662025550202762e-08, + -1.98721972463709290e-11, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 6.91472559412458759e-01, + 2.48154578891676774e-01, + 4.60893578284335263e-02, + 3.60207838982301946e-03, + 1.13001153242430471e-04, + 1.33690948263488455e-06, + 4.97253225968548872e-09, + 3.39460723731970550e-12, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_rational(P, Q, log_w); + } + else if (z < 2.6881171e+43) // 32 < log(z) < 100 + { + // Max error in interpolated form: 2.015e-18 + static const T Y = -4.01286315917968750e+00; + static const T P[] = { + 5.07714858354309672e+00, + -3.32994414518701458e+00, + -8.61170416909864451e-01, + -4.01139705309486142e-02, + -1.85374201771834585e-04, + 1.08824145844270666e-05, + 1.17216905810452396e-07, + 2.97998248101385990e-10, + 1.42294856434176682e-13, + }; + static const T Q[] = { + 1.00000000000000000e+00, + -4.85840770639861485e-01, + -3.18714850604827580e-01, + -3.20966129264610534e-02, + -1.06276178044267895e-03, + -1.33597828642644955e-05, + -6.27900905346219472e-08, + -9.35271498075378319e-11, + -2.60648331090076845e-14, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_rational(P, Q, log_w); + } + else // 100 < log(z) < 710 + { + // Max error in interpolated form: 5.277e-18 + static const T Y = -5.70115661621093750e+00; + static const T P[] = { + 6.42275660145116698e+00, + 1.33047964073367945e+00, + 6.72008923401652816e-02, + 1.16444069958125895e-03, + 7.06966760237470501e-06, + 5.48974896149039165e-09, + -7.00379652018853621e-11, + -1.89247635913659556e-13, + -1.55898770790170598e-16, + -4.06109208815303157e-20, + -2.21552699006496737e-24, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 3.34498588416632854e-01, + 2.51519862456384983e-02, + 6.81223810622416254e-04, + 7.94450897106903537e-06, + 4.30675039872881342e-08, + 1.10667669458467617e-10, + 1.31012240694192289e-13, + 6.53282047177727125e-17, + 1.11775518708172009e-20, + 3.78250395617836059e-25, + }; + T log_w = log(z); + return log_w + Y + boost::math::tools::evaluate_rational(P, Q, log_w); + } +} + +template +T lambert_w_negative_rational_double(T z, const Policy& pol) +{ + BOOST_MATH_STD_USING + if (z > -0.1) + { + if (z < -0.051) + { + // -0.1 < z < -0.051 + // Maximum Deviation Found: 4.402e-22 + // Expected Error Term : 4.240e-22 + // Maximum Relative Change in Control Points : 4.115e-03 + static const T Y = 1.08633995056152344e+00; + static const T P[] = { + -8.63399505615014331e-02, + -1.64303871814816464e+00, + -7.71247913918273738e+00, + -1.41014495545382454e+01, + -1.02269079949257616e+01, + -2.17236002836306691e+00, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 7.44775406945739243e+00, + 2.04392643087266541e+01, + 2.51001961077774193e+01, + 1.31256080849023319e+01, + 2.11640324843601588e+00, + }; + return z * (Y + boost::math::tools::evaluate_rational(P, Q, z)); + } + else + { + // Very small z > 0.051: + return lambert_w0_small_z(z, pol); + } + } + else if (z > -0.2) + { + // -0.2 < z < -0.1 + // Maximum Deviation Found: 2.898e-20 + // Expected Error Term : 2.873e-20 + // Maximum Relative Change in Control Points : 3.779e-04 + static const T Y = 1.20359611511230469e+00; + static const T P[] = { + -2.03596115108465635e-01, + -2.95029082937201859e+00, + -1.54287922188671648e+01, + -3.81185809571116965e+01, + -4.66384358235575985e+01, + -2.59282069989642468e+01, + -4.70140451266553279e+00, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 9.57921436074599929e+00, + 3.60988119290234377e+01, + 6.73977699505546007e+01, + 6.41104992068148823e+01, + 2.82060127225153607e+01, + 4.10677610657724330e+00, + }; + return z * (Y + boost::math::tools::evaluate_rational(P, Q, z)); + } + else if (z > -0.3178794411714423215955237) + { + // Max error in interpolated form: 6.996e-18 + static const T Y = 3.49680423736572266e-01; + static const T P[] = { + -3.49729841718749014e-01, + -6.28207407760709028e+01, + -2.57226178029669171e+03, + -2.50271008623093747e+04, + 1.11949239154711388e+05, + 1.85684566607844318e+06, + 4.80802490427638643e+06, + 2.76624752134636406e+06, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 1.82717661215113000e+02, + 8.00121119810280100e+03, + 1.06073266717010129e+05, + 3.22848993926057721e+05, + -8.05684814514171256e+05, + -2.59223192927265737e+06, + -5.61719645211570871e+05, + 6.27765369292636844e+04, + }; + T d = z + 0.367879441171442321595523770161460867445811; + return -d / (Y + boost::math::tools::evaluate_polynomial(P, d) / boost::math::tools::evaluate_polynomial(Q, d)); + } + else if (z > -0.3578794411714423215955237701) + { + // Max error in interpolated form: 1.404e-17 + static const T Y = 5.00126481056213379e-02; + static const T P[] = { + -5.00173570682372162e-02, + -4.44242461870072044e+01, + -9.51185533619946042e+03, + -5.88605699015429386e+05, + -1.90760843597427751e+06, + 5.79797663818311404e+08, + 1.11383352508459134e+10, + 5.67791253678716467e+10, + 6.32694500716584572e+10, + }; + static const T Q[] = { + 1.00000000000000000e+00, + 9.08910517489981551e+02, + 2.10170163753340133e+05, + 1.67858612416470327e+07, + 4.90435561733227953e+08, + 4.54978142622939917e+09, + 2.87716585708739168e+09, + -4.59414247951143131e+10, + -1.72845216404874299e+10, + }; + T d = z + 0.36787944117144232159552377016146086744581113103176804; + return -d / (Y + boost::math::tools::evaluate_polynomial(P, d) / boost::math::tools::evaluate_polynomial(Q, d)); + } + else + { // z is very close (within 0.01) of the singularity at -e^-1, + // so use a series expansion from R. M. Corless et al. + const T p2 = 2 * (boost::math::constants::e() * z + 1); + const T p = sqrt(p2); + return lambert_w_detail::lambert_w_singularity_series(p); + } +} + +//! Lambert_w0 @b 'double' implementation, selected when T is 64-bit precision. +template +inline T lambert_w0_imp(T z, const Policy& pol, const mpl::int_<2>&) +{ + static const char* function = "boost::math::lambert_w0<%1%>"; + BOOST_MATH_STD_USING // Aid ADL of std functions. + + // Detect unusual case of 32-bit double with a wider/64-bit long double + BOOST_STATIC_ASSERT_MSG(std::numeric_limits::digits >= 53, + "Our double precision coefficients will be truncated, " + "please file a bug report with details of your platform's floating point types " + "- or possibly edit the coefficients to have " + "an appropriate size-suffix for 64-bit floats on your platform - L?"); + + if ((boost::math::isnan)(z)) + { + return boost::math::policies::raise_domain_error(function, "Expected a value > -e^-1 (-0.367879...) but got %1%.", z, pol); + } + if ((boost::math::isinf)(z)) + { + return boost::math::policies::raise_overflow_error(function, "Expected a finite value but got %1%.", z, pol); + } + + if (z >= 0.05) + { + return lambert_w_positive_rational_double(z); + } + else if (z <= -0.36787944117144232159552377016146086744581113103176804) // Precision is max_digits10(cpp_bin_float_50). + { + if (z < -0.36787944117144232159552377016146086744581113103176804) + { + return boost::math::policies::raise_domain_error(function, "Expected z >= -e^-1 (-0.367879...) but got %1%.", z, pol); + } + return -1; + } + else + { + return lambert_w_negative_rational_double(z, pol); + } +} // T lambert_w0_imp(T z, const Policy& pol, const mpl::int_<2>&) 64-bit precision, usually double. + +//! lambert_W0 implementation for extended precision types including +//! long double (80-bit and 128-bit), ??? +//! quad float128, Boost.Multiprecision types like cpp_bin_float_quad, cpp_bin_float_50... + +template +inline T lambert_w0_imp(T z, const Policy& pol, const mpl::int_<0>&) +{ + static const char* function = "boost::math::lambert_w0<%1%>"; + BOOST_MATH_STD_USING // Aid ADL of std functions. + + // Filter out special cases first: + if ((boost::math::isnan)(z)) + { + return boost::math::policies::raise_domain_error(function, "Expected z >= -e^-1 (-0.367879...) but got %1%.", z, pol); + } + if (fabs(z) <= 0.05f) + { + // Very small z: + return lambert_w0_small_z(z, pol); + } + if (z > (std::numeric_limits::max)()) + { + if ((boost::math::isinf)(z)) + { + return policies::raise_overflow_error(function, 0, pol); + // Or might return infinity if available else max_value, + // but other Boost.Math special functions raise overflow. + } + // z is larger than the largest double, so cannot use the polynomial to get an approximation, + // so use the asymptotic approximation and Halley iterate: + + T w = lambert_w0_approx(z); // Make an inline function as also used elsewhere. + //T lz = log(z); + //T llz = log(lz); + //T w = lz - llz + (llz / lz); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + return lambert_w_halley_iterate(w, z); + } + if (z < -0.3578794411714423215955237701) + { // Very close to branch point so rational polynomials are not usable. + if (z <= -boost::math::constants::exp_minus_one()) + { + if (z == -boost::math::constants::exp_minus_one()) + { // Exactly at the branch point singularity. + return -1; + } + return boost::math::policies::raise_domain_error(function, "Expected z >= -e^-1 (-0.367879...) but got %1%.", z, pol); + } + // z is very close (within 0.01) of the branch singularity at -e^-1 + // so use a series approximation proposed by Corless et al. + const T p2 = 2 * (boost::math::constants::e() * z + 1); + const T p = sqrt(p2); + T w = lambert_w_detail::lambert_w_singularity_series(p); + return lambert_w_halley_iterate(w, z); + } + + // Phew! If we get here we are in the normal range of the function, + // so get a double precision approximation first, then iterate to full precision of T. + // We define a tag_type that is: + // mpl::true_ if there are so many digits precision wanted that iteration is necessary. + // mpl::false_ if a single Halley step is sufficient. + + typedef typename policies::precision::type precision_type; + typedef mpl::bool_< + (precision_type::value == 0) || (precision_type::value > 113) ? + true // Unknown at compile-time, variable/arbitrary, or more than float128 or cpp_bin_quad 128-bit precision. + : false // float, double, float128, cpp_bin_quad 128-bit, so single Halley step. + > tag_type; + + // For speed, we also cast z to type double when that is possible + // if (boost::is_constructible() == true). + T w = lambert_w0_imp(maybe_reduce_to_double(z, boost::is_constructible()), pol, mpl::int_<2>()); + + return lambert_w_maybe_halley_iterate(w, z, tag_type()); + +} // T lambert_w0_imp(T z, const Policy& pol, const mpl::int_<0>&) all extended precision types. + + // Lambert w-1 implementation +// ============================================================================================== + + //! Lambert W for W-1 branch, -max(z) < z <= -1/e. + // TODO is -max(z) allowed? +template +T lambert_wm1_imp(const T z, const Policy& pol) +{ + // Catch providing an integer value as parameter x to lambert_w, for example, lambert_w(1). + // Need to ensure it is a floating-point type (of the desired type, float 1.F, double 1., or long double 1.L), + // or static_casted integer, for example: static_cast(1) or static_cast(1). + // Want to allow fixed_point types too, so do not just test for floating-point. + // Integral types should be promoted to double by user Lambert w functions. + // If integral type provided to user function lambert_w0 or lambert_wm1, + // then should already have been promoted to double. + BOOST_STATIC_ASSERT_MSG(!boost::is_integral::value, + "Must be floating-point or fixed type (not integer type), for example: lambert_wm1(1.), not lambert_wm1(1)!"); + + BOOST_MATH_STD_USING // Aid argument dependent lookup (ADL) of abs. + + const char* function = "boost::math::lambert_wm1()"; // Used for error messages. + + // Check for edge and corner cases first: + if ((boost::math::isnan)(z)) + { + return policies::raise_domain_error(function, + "Argument z is NaN!", + z, pol); + } // isnan + + if ((boost::math::isinf)(z)) + { + return policies::raise_domain_error(function, + "Argument z is infinite!", + z, pol); + } // isinf + + if (z == static_cast(0)) + { // z is exactly zero so return -std::numeric_limits::infinity(); + if (std::numeric_limits::has_infinity) + { + return -std::numeric_limits::infinity(); + } + else + { + return -tools::max_value(); + } + } + if (std::numeric_limits::has_denorm) + { // All real types except arbitrary precision. + if (!(boost::math::isnormal)(z)) + { // Almost zero - might also just return infinity like z == 0 or max_value? + return policies::raise_overflow_error(function, + "Argument z = %1% is denormalized! (must be z > (std::numeric_limits::min)() or z == 0)", + z, pol); + } + } + + if (z > static_cast(0)) + { // + return policies::raise_domain_error(function, + "Argument z = %1% is out of range (z <= 0) for Lambert W-1 branch! (Try Lambert W0 branch?)", + z, pol); + } + if (z > -boost::math::tools::min_value()) + { // z is denormalized, so cannot be computed. + // -std::numeric_limits::min() is smallest for type T, + // for example, for double: lambert_wm1(-2.2250738585072014e-308) = -714.96865723796634 + return policies::raise_overflow_error(function, + "Argument z = %1% is too small (z < -std::numeric_limits::min so denormalized) for Lambert W-1 branch!", + z, pol); + } + if (z == -boost::math::constants::exp_minus_one()) // == singularity/branch point z = -exp(-1) = -3.6787944. + { // At singularity, so return exactly -1. + return -static_cast(1); + } + // z is too negative for the W-1 (or W0) branch. + if (z < -boost::math::constants::exp_minus_one()) // > singularity/branch point z = -exp(-1) = -3.6787944. + { + return policies::raise_domain_error(function, + "Argument z = %1% is out of range (z < -exp(-1) = -3.6787944... <= 0) for Lambert W-1 (or W0) branch!", + z, pol); + } + if (z < static_cast(-0.35)) + { // Close to singularity/branch point z = -0.3678794411714423215955237701614608727 but on W-1 branch. + const T p2 = 2 * (boost::math::constants::e() * z + 1); + if (p2 == 0) + { // At the singularity at branch point. + return -1; + } + if (p2 > 0) + { + T w_series = lambert_w_singularity_series(T(-sqrt(p2))); + if (boost::math::tools::digits() > 53) + { // Multiprecision, so try a Halley refinement. + w_series = lambert_w_detail::lambert_w_halley_iterate(w_series, z); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_NOT_BUILTIN + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Lambert W-1 Halley updated to " << w_series << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_NOT_BUILTIN + } + return w_series; + } + // Should not get here. + return policies::raise_domain_error(function, + "Argument z = %1% is out of range for Lambert W-1 branch. (Should not get here - please report!)", + z, pol); + } // if (z < -0.35) + + using lambert_w_lookup::wm1es; + using lambert_w_lookup::wm1zs; + using lambert_w_lookup::noof_wm1zs; // size == 64 + + // std::cout <<" Wm1zs[63] (== G[64]) = " << " " << wm1zs[63] << std::endl; // Wm1zs[63] (== G[64]) = -1.0264389699511283e-26 + // Check that z argument value is not smaller than lookup_table G[64] + // std::cout << "(z > wm1zs[63]) = " << std::boolalpha << (z > wm1zs[63]) << std::endl; + + if (z >= wm1zs[63]) // wm1zs[63] = -1.0264389699511282259046957018510946438e-26L W = 64.00000000000000000 + { // z >= -1.0264389699511303e-26 (but z != 0 and z >= std::numeric_limits::min() and so NOT denormalized). + + // Some info on Lambert W-1 values for extreme values of z. + // std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + // std::cout << "-std::numeric_limits::min() = " << -(std::numeric_limits::min)() << std::endl; + // std::cout << "-std::numeric_limits::min() = " << -(std::numeric_limits::min)() << std::endl; + // -std::numeric_limits::min() = -1.1754943508222875e-38 + // -std::numeric_limits::min() = -2.2250738585072014e-308 + // N[productlog(-1, -1.1754943508222875 * 10^-38 ), 50] = -91.856775324595479509567756730093823993834155027858 + // N[productlog(-1, -2.2250738585072014e-308 * 10^-308 ), 50] = -1424.8544521230553853558132180518404363617968042942 + // N[productlog(-1, -1.4325445274604020119111357113179868158* 10^-27), 37] = -65.99999999999999999999999999999999955 + + // R.M.Corless, G.H.Gonnet, D.E.G.Hare, D.J.Jeffrey, and D.E.Knuth, + // “On the Lambert W function, ” Adv.Comput.Math., vol. 5, pp. 329–359, 1996. + // Francois Chapeau-Blondeau and Abdelilah Monir + // Numerical Evaluation of the Lambert W Function + // IEEE Transactions On Signal Processing, VOL. 50, NO. 9, Sep 2002 + // https://pdfs.semanticscholar.org/7a5a/76a9369586dd0dd34dda156d8f2779d1fd59.pdf + // Estimate Lambert W using ln(-z) ... + // This is roughly the power of ten * ln(10) ~= 2.3. n ~= 10^n + // and improve by adding a second term -ln(ln(-z)) + T guess; // bisect lowest possible Gk[=64] (for lookup_t type) + T lz = log(-z); + T llz = log(-lz); + guess = lz - llz + (llz / lz); // Chapeau-Blondeau equation 20, page 2162. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_TINY + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "z = " << z << ", guess = " << guess << ", ln(-z) = " << lz << ", ln(-ln(-z) = " << llz << ", llz/lz = " << (llz / lz) << std::endl; + // z = -1.0000000000000001e-30, guess = -73.312782616731482, ln(-z) = -69.077552789821368, ln(-ln(-z) = 4.2352298269101114, llz/lz = -0.061311231447304194 + // z = -9.9999999999999999e-91, guess = -212.56650048504233, ln(-z) = -207.23265836946410, ln(-ln(-z) = 5.3338421155782205, llz/lz = -0.025738424423764311 + // >z = -2.2250738585072014e-308, guess = -714.95942238244606, ln(-z) = -708.39641853226408, ln(-ln(-z) = 6.5630038501819854, llz/lz = -0.0092645920821846622 + int d10 = policies::digits_base10(); // policy template parameter digits10 + int d2 = policies::digits(); // digits base 2 from policy. + std::cout << "digits10 = " << d10 << ", digits2 = " << d2 // For example: digits10 = 1, digits2 = 5 + << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_TINY + if (policies::digits() < 12) + { // For the worst case near w = 64, the error in the 'guess' is ~0.008, ratio ~ 0.0001 or 1 in 10,000 digits 10 ~= 4, or digits2 ~= 12. + return guess; + } + T result = lambert_w_detail::lambert_w_halley_iterate(guess, z); + return result; + + // Was Fukushima + // G[k=64] == g[63] == -1.02643897e-26 + //return policies::raise_domain_error(function, + // "Argument z = %1% is too small (< -1.02643897e-26) ! (Should not occur, please report.", + // z, pol); + } // Z too small so use approximation and Halley. + // Else Use a lookup table to find the nearest integer part of Lambert W-1 as starting point for Bisection. + + if (boost::math::tools::digits() > 53) + { // T is more precise than 64-bit double (or long double, or ?), + // so compute an approximate value using only one Schroeder refinement, + // (avoiding any double-precision Halley refinement from policy double_digits2<50> 53 - 3 = 50 + // because are next going to use Halley refinement at full/high precision using this as an approximation). + using boost::math::policies::precision; + using boost::math::policies::digits10; + using boost::math::policies::digits2; + using boost::math::policies::policy; + // Compute a 50-bit precision approximate W0 in a double (no Halley refinement). + T double_approx(static_cast(lambert_wm1_imp(must_reduce_to_double(z, boost::is_constructible()), policy >()))); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_NOT_BUILTIN + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Lambert_wm1 Argument Type " << typeid(T).name() << " approximation double = " << double_approx << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + // Perform additional Halley refinement(s) to ensure that + // get a near as possible to correct result (usually +/- one epsilon). + T result = lambert_w_halley_iterate(double_approx, z); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Result " << typeid(T).name() << " precision Halley refinement = " << result << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + return result; + } // digits > 53 - higher precision than double. + else // T is double or less precision. + { // Use a lookup table to find the nearest integer part of Lambert W as starting point for Bisection. + using namespace boost::math::lambert_w_detail::lambert_w_lookup; + // Bracketing sequence n = (2, 4, 8, 16, 32, 64) for W-1 branch. (0 is -infinity) + // Since z is probably quite small, start with lowest n (=2). + int n = 2; + if (wm1zs[n - 1] > z) + { + goto bisect; + } + for (int j = 1; j <= 5; ++j) + { + n *= 2; + if (wm1zs[n - 1] > z) + { + goto overshot; + } + } + // else z < g[63] == -1.0264389699511303e-26, so Lambert W-1 integer part > 64. + // This should not now occur (should be caught by test and code above) so should be a logic_error? + return policies::raise_domain_error(function, + "Argument z = %1% is too small (< -1.026439e-26) (logic error - please report!)", + z, pol); + overshot: + { + int nh = n / 2; + for (int j = 1; j <= 5; ++j) + { + nh /= 2; // halve step size. + if (nh <= 0) + { + break; // goto bisect; + } + if (wm1zs[n - nh - 1] > z) + { + n -= nh; + } + } + } + bisect: + --n; + // g[n] now holds lambert W of floor integer n and g[n+1] the ceil part; + // these are used as initial values for bisection. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_LOOKUP + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Result lookup W-1(" << z << ") bisection between wm1zs[" << n - 1 << "] = " << wm1zs[n - 1] << " and wm1zs[" << n << "] = " << wm1zs[n] + << ", bisect mean = " << (wm1zs[n - 1] + wm1zs[n]) / 2 << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_LOOKUP + + // Compute bisections is the number of bisections computed from n, + // such that a single application of the fifth-order Schroeder update formula + // after the bisections is enough to evaluate Lambert W-1 with (near?) 53-bit accuracy. + // Fukushima established these by trial and error? + int bisections = 11; // Assume maximum number of bisections will be needed (most common case). + if (n >= 8) + { + bisections = 8; + } + else if (n >= 3) + { + bisections = 9; + } + else if (n >= 2) + { + bisections = 10; + } + // Bracketing, Fukushima section 2.3, page 82: + // (Avoiding using exponential function for speed). + // Only use @c lookup_t precision, default double, for bisection (again for speed), + // and use later Halley refinement for higher precisions. + using lambert_w_lookup::halves; + using lambert_w_lookup::sqrtwm1s; + + typedef typename mpl::if_c::value, lookup_t, T>::type calc_type; + + calc_type w = -static_cast(n); // Equation 25, + calc_type y = static_cast(z * wm1es[n - 1]); // Equation 26, + // Perform the bisections fractional bisections for necessary precision. + for (int j = 0; j < bisections; ++j) + { // Equation 27. + calc_type wj = w - halves[j]; // Subtract 1/2, 1/4, 1/8 ... + calc_type yj = y * sqrtwm1s[j]; // Multiply by sqrt(1/e), ... + if (wj < yj) + { + w = wj; + y = yj; + } + } // for j + return static_cast(schroeder_update(w, y)); // Schroeder 5th order method refinement. + +// else // Perform additional Halley refinement(s) to ensure that +// // get a near as possible to correct result (usually +/- epsilon). +// { +// // result = lambert_w_halley_iterate(result, z); +// result = lambert_w_halley_step(result, z); // Just one Halley step should be enough. +//#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_HALLEY +// std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); +// std::cout << "Halley refinement estimate = " << result << std::endl; +// std::cout.precision(saved_precision); +//#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W1_HALLEY +// return result; // Halley +// } // Schroeder or Schroeder and Halley. + } + } // template T lambert_wm1_imp(const T z) +} // namespace lambert_w_detail + +///////////////////////////// User Lambert w functions. ////////////////////////////// + +//! Lambert W0 using User-defined policy. + template + inline + typename boost::math::tools::promote_args::type + lambert_w0(T z, const Policy& pol) + { + // Promote integer or expression template arguments to double, + // without doing any other internal promotion like float to double. + typedef typename tools::promote_args::type result_type; + + // Work out what precision has been selected, + // based on the Policy and the number type. + typedef typename policies::precision::type precision_type; + // and then select the correct implementation based on that precision (not the type T): + typedef mpl::int_< + (precision_type::value == 0) || (precision_type::value > 53) ? + 0 // either variable precision (0), or greater than 64-bit precision. + : (precision_type::value <= 24) ? 1 // 32-bit (probably float) precision. + : 2 // 64-bit (probably double) precision. + > tag_type; + + return lambert_w_detail::lambert_w0_imp(result_type(z), pol, tag_type()); // + } // lambert_w0(T z, const Policy& pol) + + //! Lambert W0 using default policy. + template + inline + typename tools::promote_args::type + lambert_w0(T z) + { + // Promote integer or expression template arguments to double, + // without doing any other internal promotion like float to double. + typedef typename tools::promote_args::type result_type; + + // Work out what precision has been selected, based on the Policy and the number type. + // For the default policy version, we want the *default policy* precision for T. + typedef typename policies::precision >::type precision_type; + // and then select the correct implementation based on that (not the type T): + typedef mpl::int_< + (precision_type::value == 0) || (precision_type::value > 53) ? + 0 // either variable precision (0), or greater than 64-bit precision. + : (precision_type::value <= 24) ? 1 // 32-bit (probably float) precision. + : 2 // 64-bit (probably double) precision. + > tag_type; + return lambert_w_detail::lambert_w0_imp(result_type(z), policies::policy<>(), tag_type()); + } // lambert_w0(T z) using default policy. + + //! W-1 branch (-max(z) < z <= -1/e). + + //! Lambert W-1 using User-defined policy. + template + inline + typename tools::promote_args::type + lambert_wm1(T z, const Policy& pol) + { + // Promote integer or expression template arguments to double, + // without doing any other internal promotion like float to double. + typedef typename tools::promote_args::type result_type; + return lambert_w_detail::lambert_wm1_imp(result_type(z), pol); // + } + + //! Lambert W-1 using default policy. + template + inline + typename tools::promote_args::type + lambert_wm1(T z) + { + typedef typename tools::promote_args::type result_type; + return lambert_w_detail::lambert_wm1_imp(result_type(z), policies::policy<>()); + } // lambert_wm1(T z) + + // First derivative of Lambert W0 and W-1. + template + inline typename tools::promote_args::type + lambert_w0_prime(T z, const Policy& pol) + { + typedef typename tools::promote_args::type result_type; + using std::numeric_limits; + if (z == 0) + { + return static_cast(1); + } + // This is the sensible choice if we regard the Lambert-W function as complex analytic. + // Of course on the real line, it's just undefined. + if (z == - boost::math::constants::exp_minus_one()) + { + return numeric_limits::has_infinity ? numeric_limits::infinity() : boost::math::tools::max_value(); + } + // if z < -1/e, we'll let lambert_w0 do the error handling: + result_type w = lambert_w0(result_type(z), pol); + // If w ~ -1, then presumably this can get inaccurate. + // Is there an accurate way to evaluate 1 + W(-1/e + eps)? + // Yes: This is discussed in the Princeton Companion to Applied Mathematics, + // 'The Lambert-W function', Section 1.3: Series and Generating Functions. + // 1 + W(-1/e + x) ~ sqrt(2ex). + // Nick is not convinced this formula is more accurate than the naive one. + // However, for z != -1/e, we never get rounded to w = -1 in any precision I've tested (up to cpp_bin_float_100). + return w / (z * (1 + w)); + } // lambert_w0_prime(T z) + + template + inline typename tools::promote_args::type + lambert_w0_prime(T z) + { + return lambert_w0_prime(z, policies::policy<>()); + } + + template + inline typename tools::promote_args::type + lambert_wm1_prime(T z, const Policy& pol) + { + using std::numeric_limits; + typedef typename tools::promote_args::type result_type; + //if (z == 0) + //{ + // return static_cast(1); + //} + //if (z == - boost::math::constants::exp_minus_one()) + if (z == 0 || z == - boost::math::constants::exp_minus_one()) + { + return numeric_limits::has_infinity ? -numeric_limits::infinity() : -boost::math::tools::max_value(); + } + + result_type w = lambert_wm1(z, pol); + return w/(z*(1+w)); + } // lambert_wm1_prime(T z) + + template + inline typename tools::promote_args::type + lambert_wm1_prime(T z) + { + return lambert_wm1_prime(z, policies::policy<>()); + } + +}} //boost::math namespaces + +#endif // #ifdef BOOST_MATH_SF_LAMBERT_W_HPP + diff --git a/include/boost/math/special_functions/lambert_w_lookup_table.ipp b/include/boost/math/special_functions/lambert_w_lookup_table.ipp new file mode 100644 index 000000000..4c48cdff5 --- /dev/null +++ b/include/boost/math/special_functions/lambert_w_lookup_table.ipp @@ -0,0 +1,134 @@ +// Copyright Paul A. Bristow 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// I:/modular-boost/libs/math/include/boost/math/special_functions/lambert_w_lookup_table.ipp + +// A collection of 128-bit precision integral z argument Lambert W values computed using 37 decimal digits precision. +// C++ floating-point precision is 128-bit long double. +// Output as 53 decimal digits, suffixed L. + +// C++ floating-point type is provided by lambert_w.hpp typedef. +// For example: typedef lookup_t double; (or float or long double) + +// Written by I:\modular-boost\libs\math\test\lambert_w_lookup_table_generator.cpp Thu Jan 25 16:52:07 2018 + +// Sizes of arrays of z values for Lambert W[0], W[1] ... W[64]" and W[-1], W[-2] ... W[-64]. + +namespace boost { +namespace math { +namespace lambert_w_detail { +namespace lambert_w_lookup +{ +BOOST_STATIC_CONSTEXPR std::size_t noof_sqrts = 12; +BOOST_STATIC_CONSTEXPR std::size_t noof_halves = 12; +BOOST_STATIC_CONSTEXPR std::size_t noof_w0es = 65; +BOOST_STATIC_CONSTEXPR std::size_t noof_w0zs = 65; +BOOST_STATIC_CONSTEXPR std::size_t noof_wm1es = 64; +BOOST_STATIC_CONSTEXPR std::size_t noof_wm1zs = 64; + +BOOST_STATIC_CONSTEXPR lookup_t halves[noof_halves] = +{ // Common to Lambert W0 and W-1 (and exactly representable). + 0.5L, 0.25L, 0.125L, 0.0625L, 0.03125L, 0.015625L, 0.0078125L, 0.00390625L, 0.001953125L, 0.0009765625L, 0.00048828125L, 0.000244140625L +}; // halves, 0.5, 0.25, ... 0.000244140625, common to W0 and W-1. + +BOOST_STATIC_CONSTEXPR lookup_t sqrtw0s[noof_sqrts] = +{ // For Lambert W0 only. + 0.6065306597126334242631173765403218567L, 0.77880078307140486866846070009071995L, 0.882496902584595403104717592968701829L, 0.9394130628134757862473572557999761753L, 0.9692332344763440819139583751755278177L, 0.9844964370054084060204319075254540376L, 0.9922179382602435121227899136829802692L, 0.996101369470117490071323985506950379L, 0.9980487811074754727142805899944244847L, 0.9990239141819756622368328253791383317L, 0.9995118379398893653889967919448497792L, 0.9997558891748972165136242351259789505L +}; // sqrtw0s + +BOOST_STATIC_CONSTEXPR lookup_t sqrtwm1s[noof_sqrts] = +{ // For Lambert W-1 only. + 1.648721270700128146848650787814163572L, 1.284025416687741484073420568062436458L, 1.133148453066826316829007227811793873L, 1.064494458917859429563390594642889673L, 1.031743407499102670938747815281507144L, 1.015747708586685747458535072082351749L, 1.007843097206447977693453559760123579L, 1.003913889338347573443609603903460282L, 1.001955033591002812046518898047477216L, 1.000977039492416535242845292611606506L, 1.000488400478694473126173623807163354L, 1.000244170429747854937005233924135774L +}; // sqrtwm1s + +BOOST_STATIC_CONSTEXPR lookup_t w0es[noof_w0zs] = +{ // Fukushima e powers array e[0] = 2.718, 1., e[2] = e^-1 = 0.135, e[3] = e^-2 = 0.133 ... e[64] = 4.3596100000630809736231248158884615452e-28. + 2.7182818284590452353602874713526624978e+00L, + 1.0000000000000000000000000000000000000e+00L, 3.6787944117144232159552377016146086745e-01L, 1.3533528323661269189399949497248440341e-01L, 4.9787068367863942979342415650061776632e-02L, + 1.8315638888734180293718021273241242212e-02L, 6.7379469990854670966360484231484242488e-03L, 2.4787521766663584230451674308166678915e-03L, 9.1188196555451620800313608440928262647e-04L, + 3.3546262790251183882138912578086101931e-04L, 1.2340980408667954949763669073003382607e-04L, 4.5399929762484851535591515560550610238e-05L, 1.6701700790245659312635517360580879078e-05L, + 6.1442123533282097586823081788055323112e-06L, 2.2603294069810543257852772905386894694e-06L, 8.3152871910356788406398514256526229461e-07L, 3.0590232050182578837147949770228963937e-07L, + 1.1253517471925911451377517906012719164e-07L, 4.1399377187851666596510277189552806229e-08L, 1.5229979744712628436136629233517431862e-08L, 5.6027964375372675400129828162064630798e-09L, + 2.0611536224385578279659403801558209764e-09L, 7.5825604279119067279417432412681264430e-10L, 2.7894680928689248077189130306442932077e-10L, 1.0261879631701890303927527840612497760e-10L, + 3.7751345442790977516449695475234067792e-11L, 1.3887943864964020594661763746086856910e-11L, 5.1090890280633247198744001934792157666e-12L, 1.8795288165390832947582704184221926212e-12L, + 6.9144001069402030094125846587414092712e-13L, 2.5436656473769229103033856148576816666e-13L, 9.3576229688401746049158322233787067450e-14L, 3.4424771084699764583923893328515572846e-14L, + 1.2664165549094175723120904155965096382e-14L, 4.6588861451033973641842455436101684114e-15L, 1.7139084315420129663027203425760492412e-15L, 6.3051167601469893856390211922465427614e-16L, + 2.3195228302435693883122636097380800411e-16L, 8.5330476257440657942780498229412441658e-17L, 3.1391327920480296287089646522319196491e-17L, 1.1548224173015785986262442063323868655e-17L, + 4.2483542552915889953292347828586580179e-18L, 1.5628821893349887680908829951058341550e-18L, 5.7495222642935598066643808805734234249e-19L, 2.1151310375910804866314010070226514702e-19L, + 7.7811322411337965157133167292798981918e-20L, 2.8625185805493936444701216291839372068e-20L, 1.0530617357553812378763324449428108806e-20L, 3.8739976286871871129314774972691278293e-21L, + 1.4251640827409351062853210280340602263e-21L, 5.2428856633634639371718053028323436716e-22L, 1.9287498479639177830173428165270125748e-22L, 7.0954741622847041389832693878080734877e-23L, + 2.6102790696677048047026953153318648093e-23L, 9.6026800545086760302307696700074909076e-24L, 3.5326285722008070297353928101772088374e-24L, 1.2995814250075030736007134060714855303e-24L, + 4.7808928838854690812771770423179628939e-25L, 1.7587922024243116489558751288034363178e-25L, 6.4702349256454603261540395529264893765e-26L, 2.3802664086944006058943245888024963309e-26L, + 8.7565107626965203384887328007391660366e-27L, 3.2213402859925160890012477758489437534e-27L, 1.1850648642339810062850307390972809891e-27L, 4.3596100000630809736231248158884596428e-28L, + +}; // w0es + +BOOST_STATIC_CONSTEXPR lookup_t w0zs[noof_w0zs] = +{ // z values for W[0], W[1], W[2] ... W[64] (Fukushima array Fk). + 0.0000000000000000000000000000000000000e+00L, + 2.7182818284590452353602874713526624978e+00L, 1.4778112197861300454460854921150015626e+01L, 6.0256610769563003222785588963745153691e+01L, 2.1839260013257695631244104481144351361e+02L, + 7.4206579551288301710557790020276139812e+02L, 2.4205727609564107356503230832603296776e+03L, 7.6764321089992101948460416680168500271e+03L, 2.3847663896333826197948736795623109390e+04L, + 7.2927755348178456069389970204894839685e+04L, 2.2026465794806716516957900645284244366e+05L, 6.5861555886717600300859134371483559776e+05L, 1.9530574970280470496960624587818413980e+06L, + 5.7513740961159665432393360873381476632e+06L, 1.6836459978306874888489314790750032292e+07L, 4.9035260587081659589527825691375819733e+07L, 1.4217776832812596218820837985250320561e+08L, + 4.1063419681078006965118239806655900596e+08L, 1.1818794444719492004981570586630806042e+09L, 3.3911637183005579560532906419857313738e+09L, 9.7033039081958055593821366108308111737e+09L, + 2.7695130424147508641409976558651358487e+10L, 7.8868082614895014356985518811525255163e+10L, 2.2413047926372475980079655175092843139e+11L, 6.3573893111624333505933989166748517618e+11L, + 1.8001224834346468131040337866531539479e+12L, 5.0889698451498078710141863447784789126e+12L, 1.4365302496248562650461177217211790925e+13L, 4.0495197800161304862957327843914007993e+13L, + 1.1400869461717722015726999684446230289e+14L, 3.2059423744573386440971405952224204950e+14L, 9.0051433962267018216365614546207459567e+14L, 2.5268147258457822451512967243234631750e+15L, + 7.0832381329352301326018261305316090522e+15L, 1.9837699245933465967698692976753294646e+16L, 5.5510470830970075484537561902113104381e+16L, 1.5520433569614702817608320254284931407e+17L, + 4.3360826779369661842459877227403719730e+17L, 1.2105254067703227363724895246493485480e+18L, 3.3771426165357561311906703760513324357e+18L, 9.4154106734807994163159964299613921804e+18L, + 2.6233583234732252918129199544138403574e+19L, 7.3049547543861043990576614751671879498e+19L, 2.0329709713386190214340167519800405595e+20L, 5.6547040503180956413560918381429636734e+20L, + 1.5720421975868292906615658755032744790e+21L, 4.3682149334771264822761478593874428627e+21L, 1.2132170565093316762294432610117848880e+22L, 3.3680332378068632345542636794533635462e+22L, + 9.3459982052259884835729892206738573922e+22L, 2.5923527642935362320437266614667426924e+23L, 7.1876803203773878618909930893087860822e+23L, 1.9921241603726199616378561653688236827e+24L, + 5.5192924995054165325072406547517121131e+24L, 1.5286067837683347062387143159276002521e+25L, 4.2321318958281094260005100745711666956e+25L, 1.1713293177672778461879598480402173158e+26L, + 3.2408603996214813669049988277609543829e+26L, 8.9641258264226027960478448084812796397e+26L, 2.4787141382364034104243901241243054434e+27L, 6.8520443388941057019777430988685937812e+27L, + 1.8936217407781711443114787060753312270e+28L, 5.2317811346197017832254642778313331353e+28L, 1.4450833904658542238325922893692265683e+29L, 3.9904954117194348050619127737142206367e+29L + +}; // w0zs + +BOOST_STATIC_CONSTEXPR lookup_t wm1es[noof_wm1es] = +{ // Fukushima e array e[0] = e^1 = 2.718, e[1] = e^2 = 7.39 ... e[64] = 4.60718e+28. + 2.7182818284590452353602874713526624978e+00L, + 7.3890560989306502272304274605750078132e+00L, 2.0085536923187667740928529654581717897e+01L, 5.4598150033144239078110261202860878403e+01L, 1.4841315910257660342111558004055227962e+02L, + 4.0342879349273512260838718054338827961e+02L, 1.0966331584284585992637202382881214324e+03L, 2.9809579870417282747435920994528886738e+03L, 8.1030839275753840077099966894327599650e+03L, + 2.2026465794806716516957900645284244366e+04L, 5.9874141715197818455326485792257781614e+04L, 1.6275479141900392080800520489848678317e+05L, 4.4241339200892050332610277594908828178e+05L, + 1.2026042841647767777492367707678594494e+06L, 3.2690173724721106393018550460917213155e+06L, 8.8861105205078726367630237407814503508e+06L, 2.4154952753575298214775435180385823880e+07L, + 6.5659969137330511138786503259060033569e+07L, 1.7848230096318726084491003378872270388e+08L, 4.8516519540979027796910683054154055868e+08L, 1.3188157344832146972099988837453027851e+09L, + 3.5849128461315915616811599459784206892e+09L, 9.7448034462489026000346326848229752776e+09L, 2.6489122129843472294139162152811882341e+10L, 7.2004899337385872524161351466126157915e+10L, + 1.9572960942883876426977639787609534279e+11L, 5.3204824060179861668374730434117744166e+11L, 1.4462570642914751736770474229969288569e+12L, 3.9313342971440420743886205808435276858e+12L, + 1.0686474581524462146990468650741401650e+13L, 2.9048849665247425231085682111679825667e+13L, 7.8962960182680695160978022635108224220e+13L, 2.1464357978591606462429776153126088037e+14L, + 5.8346174252745488140290273461039101900e+14L, 1.5860134523134307281296446257746601252e+15L, 4.3112315471151952271134222928569253908e+15L, 1.1719142372802611308772939791190194522e+16L, + 3.1855931757113756220328671701298646000e+16L, 8.6593400423993746953606932719264934250e+16L, 2.3538526683701998540789991074903480451e+17L, 6.3984349353005494922266340351557081888e+17L, + 1.7392749415205010473946813036112352261e+18L, 4.7278394682293465614744575627442803708e+18L, 1.2851600114359308275809299632143099258e+19L, 3.4934271057485095348034797233406099533e+19L, + 9.4961194206024488745133649117118323102e+19L, 2.5813128861900673962328580021527338043e+20L, 7.0167359120976317386547159988611740546e+20L, 1.9073465724950996905250998409538484474e+21L, + 5.1847055285870724640874533229334853848e+21L, 1.4093490824269387964492143312370168789e+22L, 3.8310080007165768493035695487861993899e+22L, 1.0413759433029087797183472933493796440e+23L, + 2.8307533032746939004420635480140745409e+23L, 7.6947852651420171381827455901293939921e+23L, 2.0916594960129961539070711572146737782e+24L, 5.6857199993359322226403488206332533034e+24L, + 1.5455389355901039303530766911174620068e+25L, 4.2012104037905142549565934307191617684e+25L, 1.1420073898156842836629571831447656302e+26L, 3.1042979357019199087073421411071003721e+26L, + 8.4383566687414544890733294803731179601e+26L, 2.2937831594696098790993528402686136005e+27L, 6.2351490808116168829092387089284697448e+27L +}; // wm1es + +BOOST_STATIC_CONSTEXPR lookup_t wm1zs[noof_wm1zs] = +{ // Fukushima G array of z values for integral K, (Fukushima Gk) g[0] (k = -1) = 1 ... g[64] = -1.0264389699511303e-26. + -3.6787944117144232159552377016146086745e-01L, + -2.7067056647322538378799898994496880682e-01L, -1.4936120510359182893802724695018532990e-01L, -7.3262555554936721174872085092964968848e-02L, -3.3689734995427335483180242115742121244e-02L, + -1.4872513059998150538271004584900007349e-02L, -6.3831737588816134560219525908649783853e-03L, -2.6837010232200947105711130062468881545e-03L, -1.1106882367801159454787302165703044346e-03L, + -4.5399929762484851535591515560550610238e-04L, -1.8371870869270225243899069096638966986e-04L, -7.3730548239938517104187698145666387735e-05L, -2.9384282290753706235208604777002963102e-05L, + -1.1641402067449950376895791995913672125e-05L, -4.5885348075273868255721924655343445906e-06L, -1.8005627955081458322204028649620350662e-06L, -7.0378941219347833214067471222239770590e-07L, + -2.7413963540482731185045932620331377352e-07L, -1.0645313231320808326024667350792279852e-07L, -4.1223072448771156559318807603116419528e-08L, -1.5923376898615004128677660806663065530e-08L, + -6.1368298043116345769816086674174450569e-09L, -2.3602323152914347699033314033408744848e-09L, -9.0603229062698346039479269140561762700e-10L, -3.4719859662410051486654409365217142276e-10L, + -1.3283631472964644271673440503045960993e-10L, -5.0747278046555248958473301297399200773e-11L, -1.9360320299432568426355237044475945959e-11L, -7.3766303773930764398798182830872768331e-12L, + -2.8072868906520523814747496670136120235e-12L, -1.0671679036256927021016406931839827582e-12L, -4.0525329757101362313986893299088308423e-13L, -1.5374324278841211301808010293913555758e-13L, + -5.8272886672428440854292491647585674200e-14L, -2.2067908660514462849736574172862899665e-14L, -8.3502821888768497979241489950570881481e-15L, -3.1572276215253043438828784344882603413e-15L, + -1.1928704609782512589094065678481294667e-15L, -4.5038074274761565346423524046963087756e-16L, -1.6993417021166355981316939131434632072e-16L, -6.4078169762734539491726202799339200356e-17L, + -2.4147993510032951187990399698408378385e-17L, -9.0950634616416460925150243301974013218e-18L, -3.4236981860988704669138593608831552044e-18L, -1.2881333612472271400115547331327717431e-18L, + -4.8440839844747536942311292467369300505e-19L, -1.8207788854829779430777944237164900798e-19L, -6.8407875971564885101695409345634890862e-20L, -2.5690139750480973292141845983878483991e-20L, + -9.6437492398195889150867140826350628738e-21L, -3.6186918227651991108814673877821174787e-21L, -1.3573451162272064984454015639725697008e-21L, -5.0894204288895982960223079251039701810e-22L, + -1.9076194289884357960571121174956927722e-22L, -7.1476978375412669048039237333931704166e-23L, -2.6773000149758626855152191436980592206e-23L, -1.0025115553818576399048488234179587012e-23L, + -3.7527362568743669891693429406973638384e-24L, -1.4043571811296963574776515073934728352e-24L, -5.2539064576179122030932396804434996219e-25L, -1.9650175744554348142907611432678556896e-25L, + -7.3474021582506822389671905824031421322e-26L, -2.7465543000397410133825686340097295749e-26L, -1.0264389699511282259046957018510946438e-26L +}; // wm1zs +} // namespace lambert_w_lookup +} // namespace detail +} // namespace math +} // namespace boost diff --git a/include/boost/math/special_functions/lambert_w_pb.hpp b/include/boost/math/special_functions/lambert_w_pb.hpp new file mode 100644 index 000000000..ed82f3259 --- /dev/null +++ b/include/boost/math/special_functions/lambert_w_pb.hpp @@ -0,0 +1,1671 @@ +// Copyright Paul A. Bristow 2016, 2017. + +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or +// copy at http ://www.boost.org/LICENSE_1_0.txt). + +/* +Implementation of the Fukushima algorithm for the Lambert W0 and W-1 real-only functions. + +This code is based on the algorithm by +Toshio Fukushima, +"Precise and fast computation of Lambert W-functions without transcendental function evaluations", +J. Comp. Appl. Math. 244 (2013) 77-89, +and its author's FORTRAN code, +and on a C/C++ version by Darko Veberic, darko.veberic@ijs.si +based on the algorithm and a FORTRAN version of Toshio Fukushima. + +Some macros that will show some (or much) diagnostic values if #defined. +//[boost_math_instrument_lambert_w_macros + +// #define-able macros +BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES +BOOST_MATH_INSTRUMENT_LAMBERT_W0 // W0 branch diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_W0 // W1 branch diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY // Halley refinement diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION // Precision. +BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY_W0 // Halley refinement diagnostics only for W0 branch. +BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY_WM1 // Halley refinement diagnostics only for W-1 branch. +BOOST_MATH_INSTRUMENT_LAMBERT_WM1_TINY // K > 64, z > -1.0264389699511303e-26 +BOOST_MATH_INSTRUMENT_LAMBERT_W0_HUGE // K > 64, z > 3.9904954117194348e+29 +BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER // Schroeder refinement diagnostics. +BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS // Number of terms used for near-singularity series. +BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN // higher than built-in precision types approximation and refinement. +BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION // Show bisection only estimate. +BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES // Show evaluation of series near branch singularity. +BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS // Show evaluation of series for small z. +BOOST_MATH_INSTRUMENT_LAMBERT_W0_LOOKUP // Show results from W0 lookup table. +BOOST_MATH_INSTRUMENT_LAMBERT_WM1_LOOKUP // Show results from W-1 lookup table. +//] [/boost_math_instrument_lambert_w_macros] +*/ + +#ifndef BOOST_MATH_FKDVPB_LAMBERT_W_HPP +#define BOOST_MATH_FKDVPB_LAMBERT_W_HPP + +#ifdef _MSC_VER +# pragma warning (disable: 4127) // warning C4127: conditional expression is constant +#endif // _MSC_VER + +#include +#include +#include +#include +#include // for log (1 + x) +#include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. +#include // for float_distance +#include // Link fails for pow without this, even though uses std::pow. +#include // series functor. +#include // polynomial. +#include // evaluate_polynomial. +#include +#include +#include // boost::math::tools::max_value(). +#include // Macro BOOST_MATH_TEST_VALUE. + +#include +#include +#include +#include + +// Needed for testing and diagnostics only. +#include +#include +#include // For float_distance. +//#include // For create_test_value and macro BOOST_MATH_TEST_VALUE. + +typedef double lookup_t; // Type for lookup table (double or float, or even long double?) + +#include "J:\Cpp\Misc\lambert_w_lookup_table_generator\lambert_w_lookup_table.ipp" // BOTH w0 and wm1 version in namespace detail. +// #include "lambert_w_lookup_table.ipp" // Boost.Math version. Only Wm1 needed using namespace lambert_w_detail + +namespace boost +{ +namespace math +{ + namespace detail + { + + + //! \brief Series expansion used near the singularity/branch point z = -exp(-1) = -3.6787944. + //! Wolfram InverseSeries[Series[sqrt[2(p Exp[1 + p] + 1)], {p,-1, 20}]] + //! Wolfram command used to obtain 40 series terms at 50 decimal digit precision was + //! N[InverseSeries[Series[Sqrt[2(p Exp[1 + p] + 1)], { p,-1,40 }]], 50] + //! -1+p-p^2/3+(11 p^3)/72-(43 p^4)/540+(769 p^5)/17280-(221 p^6)/8505+(680863 p^7)/43545600 ... + //! Decimal values of specifications for built-in floating-point types below + //! are at least 21 digits precision == max_digits10 for long double. + //! Longer decimal digits strings are rationals evaluated using Wolfram. + + template + T lambert_w_singularity_series(const T p) + { +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES + std::size_t saved_precision = std::cout.precision(3); + std::cout << "Singularity_series Lambert_w p argument = " << p << std::endl; + std::cout + //<< "Argument Type = " << typeid(T).name() + //<< ", max_digits10 = " << std::numeric_limits::max_digits10 + //<< ", epsilon = " << std::numeric_limits::epsilon() + << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES + + static const T q[] = + { + -static_cast(1), // j0 + +T(1), // j1 + -T(1) / 3, // 1/3 j2 + +T(11) / 72, // 0.152777777777777778, // 11/72 j3 + -T(43) / 540, // 0.0796296296296296296, // 43/540 j4 + +T(769) / 17280, // 0.0445023148148148148, j5 + -T(221) / 8505, // 0.0259847148736037625, j6 + //+T(0.0156356325323339212L), // j7 + //+T(0.015635632532333921222810111699000587889476778365667L), // j7 from Wolfram N[680863/43545600, 50] + +T(680863uLL) / 43545600uLL, // +0.0156356325323339212, j7 + //-T(0.00961689202429943171L), // j8 + -T(1963uLL) / 204120uLL, // 0.00961689202429943171, j8 + //-T(0.0096168920242994317068391142465216539290613364687439L), // j8 from Wolfram N[1963/204120, 50] + +T(226287557uLL) / 37623398400uLL, // 0.00601454325295611786, j9 + -T(5776369uLL) / 1515591000uLL, // 0.00381129803489199923, j10 + //+T(0.00244087799114398267L), j11 0.0024408779911439826658968585286437530215699919795550 + +T(169709463197uLL) / 69528040243200uLL, // j11 + // -T(0.00157693034468678425L), // j12 -0.0015769303446867842539234095399314115973161850314723 + -T(1118511313uLL) / 709296588000uLL, // j12 + +T(667874164916771uLL) / 650782456676352000uLL, // j13 + //+T(0.00102626332050760715L), // j13 0.0010262633205076071544375481533906861056468041465973 + -T(500525573uLL) / 744761417400uLL, // j14 + // -T(0.000672061631156136204L), j14 + //+T(1003663334225097487uLL) / 234281684403486720000uLL, // j15 0.00044247306181462090993020760858473726479232802068800 error C2177: constant too big + //+T(0.000442473061814620910L, // j15 + BOOST_MATH_TEST_VALUE(T, +0.000442473061814620910), // j15 + // -T(0.000292677224729627445L), // j16 + BOOST_MATH_TEST_VALUE(T, -0.000292677224729627445), // j16 + //+T(0.000194387276054539318L), // j17 + BOOST_MATH_TEST_VALUE(T, 0.000194387276054539318), // j17 + //-T(0.000129574266852748819L), // j18 + BOOST_MATH_TEST_VALUE(T, -0.000129574266852748819), // j18 + //+T(0.0000866503580520812717L), // j19 N[+1150497127780071399782389/13277465363600276402995200000, 50] 0.000086650358052081271660451590462390293190597827783288 + BOOST_MATH_TEST_VALUE(T, +0.0000866503580520812717), // j19 + //-T(0.0000581136075044138168L) // j20 N[2853534237182741069/49102686267859224000000, 50] 0.000058113607504413816772205464778828177256611844221913 + // -T(2853534237182741069uLL) / 49102686267859224000000uLL // j20 // error C2177: constant too big, + // so must use BOOST_MATH_TEST_VALUE(T, ) format in hope of using suffix Q for quad or decimal digits string for others. + //-T(0.000058113607504413816772205464778828177256611844221913L), // j20 N[2853534237182741069/49102686267859224000000, 50] 0.000058113607504413816772205464778828177256611844221913 + BOOST_MATH_TEST_VALUE(T, -0.000058113607504413816772205464778828177256611844221913) // j20 - last used by Fukushima + // More terms don't seem to give any improvement (worse in fact) and are not use for many z values. + //BOOST_MATH_TEST_VALUE(T, +0.000039076684867439051635395583044527492132109160553593), // j21 + //BOOST_MATH_TEST_VALUE(T, -0.000026338064747231098738584082718649443078703982217219), // j22 + //BOOST_MATH_TEST_VALUE(T, +0.000017790345805079585400736282075184540383274460464169), // j23 + //BOOST_MATH_TEST_VALUE(T, -0.000012040352739559976942274116578992585158113153190354), // j24 + //BOOST_MATH_TEST_VALUE(T, +8.1635319824966121713827512573558687050675701559448E-6), // j25 + //BOOST_MATH_TEST_VALUE(T, -5.5442032085673591366657251660804575198155559225316E-6) // j26 + // -T(5.5442032085673591366657251660804575198155559225316E-6L) // j26 + // 21 to 26 Added for long double. + }; // static const T q[] + + /* + // Temporary copy of original double values for comparison; these are reproduced well. + static const T q[] = + { + -1L, // j0 + +1L, // j1 + -0.333333333333333333L, // 1/3 j2 + +0.152777777777777778L, // 11/72 j3 + -0.0796296296296296296L, // 43/540 + +0.0445023148148148148L, + -0.0259847148736037625L, + +0.0156356325323339212L, + -0.00961689202429943171L, + +0.00601454325295611786L, + -0.00381129803489199923L, + +0.00244087799114398267L, + -0.00157693034468678425L, + +0.00102626332050760715L, + -0.000672061631156136204L, + +0.000442473061814620910L, + -0.000292677224729627445L, + +0.000194387276054539318L, + -0.000129574266852748819L, + +0.0000866503580520812717L, + -0.0000581136075044138168L // j20 + }; + */ + + // Decide how many series terms to use, increasing as z approaches the singularity, + // balancing run-time versus computational noise from round-off. + // In practice, we truncate the series expansion at a certain order. + // If the order is too large, not only does the amount of computation increase, + // but also the round-off errors accumulate. + // See Fukushima equation 35, page 85 for logic of choice of number of series terms. + + BOOST_MATH_STD_USING // Aid argument dependent lookup (ADL) of abs. + + const T absp = abs(p); + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS + { + int terms = 20; // Default to using all terms. + if (absp < 0.001150) + { // Very near singularity. + terms = 6; + } + else if (absp < 0.0766) + { // Near singularity. + terms = 10; + } + std::streamsize saved_precision = std::cout.precision(3); + std::cout << "abs(p) = " << absp << ", terms = " << terms << std::endl; + std::cout.precision(saved_precision); + } +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_TERMS + + if (absp < 0.01159) + { // Only 6 near-singularity series terms are useful. + return + -1 + + p*(1 + + p*(q[2] + + p*(q[3] + + p*(q[4] + + p*(q[5] + + p*q[6] + ))))); + } + else if (absp < 0.0766) // Use 10 near-singularity series terms. + { // Use 10 near-singularity series terms. + return + -1 + + p*(1 + + p*(q[2] + + p*(q[3] + + p*(q[4] + + p*(q[5] + + p*(q[6] + + p*(q[7] + + p*(q[8] + + p*(q[9] + + p*q[10] + ))))))))); + } + else + { // Use all 20 near-singularity series terms. + return + -1 + + p*(1 + + p*(q[2] + + p*(q[3] + + p*(q[4] + + p*(q[5] + + p*(q[6] + + p*(q[7] + + p*(q[8] + + p*(q[9] + + p*(q[10] + + p*(q[11] + + p*(q[12] + + p*(q[13] + + p*(q[14] + + p*(q[15] + + p*(q[16] + + p*(q[17] + + p*(q[18] + + p*(q[19] + + p*q[20] // Last Fukushima term. + ))))))))))))))))))); + // + // more terms for more precise T: long double ... + //// but makes almost no difference, so don't use more terms? + // p*q[21] + + // p*q[22] + + // p*q[23] + + // p*q[24] + + // p*q[25] + // ))))))))))))))))))); + } + } // template T lambert_w_singularity_series(const T p) + + ///////////////////////////////////////////////////////////////////////////////////////////// + + //! \brief Series expansion used near zero (abs(z) < 0.05). + //! \details + //! Coefficients of the inverted series expansion of the Lambert W function around z = 0. + //! Tosio Fukushima always uses all 17 terms of a Taylor series computed using Wolfram with + //! InverseSeries[Series[z Exp[z],{z,0,17}]] + //! Tosio Fukushima / Journal of Computational and Applied Mathematics 244 (2013) page 86. + + //! Decimal values of specifications for built-in floating-point types below + //! are 21 digits precision == max_digits10 for long double. + //! Care Some coefficients might overflow some fixed_point types. + + //! This version is intended to allow use by user-defined types + //! like Boost.Multiprecision quad and cpp_dec_float types. + //! The three specializations below for built-in float, double + //! (and perhaps long double) will be chosen in preference for these types. + + //! This version uses rationals computed by Wolfram as far as possible, + //! limited by maximum size of uLL integers. + //! For higher term, uses decimal digit strings computed by Wolfram up to the maximum possible using uLL rationals, + //! and then higher coefficients are computed as necessary using function lambert_w0_small_z_series_term + //! until the precision required by the policy is achieved. + //! InverseSeries[Series[z Exp[z],{z,0,34}]] also computed. + + // Series evaluation for LambertW(z) as z -> 0. + // See http://functions.wolfram.com/ElementaryFunctions/ProductLog/06/01/01/0003/ + // http://functions.wolfram.com/ElementaryFunctions/ProductLog/06/01/01/0003/MainEq1.L.gif + + //! \brief lambert_w0_small_z uses a tag_type to select a variant depending on the size of the type. + //! The Lambert W is computed by lambert_w0_small_z for small z. + //! The cutoff for z smallness determined by Tosio Fukushima by trial and error is (abs(z) < 0.05), + //! but the optimum might be a function of the size of the type of z. + + //! \details + //! The tag_type selection is based on the value @c std::numeric_limits::max_digits10. + //! This allows distinguishing between long double types that commonly vary between 64 and 80-bits, + //! and also compilers that have a float type using 64 bits and/or long double using 128-bits. + //! It assumes that max_digits10 is defined correctly or this might fail to make the correct selection. + //! causing very small differences in computing lambert_w that would be very difficult to detect and diagnose. + //! Cannot switch on @c std::numeric_limits<>::max() because comparison values may overflow the compiler limit. + //! Cannot switch on @c std::numeric_limits::max_exponent10() + //! because both 80 and 128 bit floating-point implmentations use 11 bits for the exponent. + //! So must rely on @c std::numeric_limits::max_digits10. + + //! Specialization of float zero series expansion used for small z (abs(z) < 0.05). + //! Specializations of lambert_w0_small_z for built-in types. + //! These specializations should be chosen in preference to T version. + //! For example: lambert_w0_small_z(0.001F) should use the float version. + //! (Parameter Policy is not used by built-in types when all terms are used during an inline computation, + //! but for the tag_type selection to work, they all must include Policy in their signature. + + // Forward declaration of variants of lambert_w0_small_z. + template + T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<0> const&); // for float (32-bit) type. + + template + T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<1> const&); // for double (64-bit) type. + + template + T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<2> const&); // for long double (double extended 80-bit) type. + + template + T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<3> const&); // for float128 + + template + T lambert_w0_small_z(T x, const Policy&, boost::mpl::int_<4> const&); // Generic multiprecision T. + + // Set tag_type depending on max_digits10. + template + T lambert_w0_small_z(T x, const Policy& pol) + { + typedef boost::mpl::int_ + < + std::numeric_limits::max_digits10 <= 9 ? 0 : // for float + std::numeric_limits::max_digits10 <= 17 ? 1 : // for double 64-bit + std::numeric_limits::max_digits10 <= 22 ? 2 : // for 80-bit double extended + std::numeric_limits::max_digits10 < 37 ? 3 : // for float128 128-bit quad + 4 // Generic multiprecision types. + > tag_type; + // std::cout << "tag type = " << tag_type << std::endl; // error C2275: 'tag_type': illegal use of this type as an expression + return lambert_w0_small_z(x, pol, tag_type()); + } // template T lambert_w0_small_z(T x) + + //! Specialization of float (32-bit) series expansion used for small z (abs(z) < 0.05). + // Only 9 Coefficients are computed to 21 decimal digits precision, ample for 32-bit float used by most platforms. + // Taylor series coefficients used are computed by Wolfram to 50 decimal digits using instruction + // N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], + // as proposed by Tosio Fukushima and implemented by Darko Veberic. + + template + T lambert_w0_small_z(T z, const Policy&, boost::mpl::int_<0> const&) + { +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "float lambert_w0_small_z called with z = " << z << " using " << 9 << " terms of precision " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + return + z*(1 - // j1 z^1 term = 1 + z*(1 - // j2 z^2 term = -1 + z*(static_cast(3uLL) / 2uLL - // 3/2 // j3 z^3 term = 1.5. + z*(2.6666666666666666667F - // 8/3 // j4 + z*(5.2083333333333333333F - // -125/24 // j5 + z*(10.8F - // j6 + z*(23.343055555555555556F - // j7 + z*(52.012698412698412698F - // j8 + z*118.62522321428571429F)))))))); // j9 + } // template T lambert_w0_small_z(T x, mpl::int_<0> const&) + + //! Specialization of long double (80-bit double extended) series expansion used for small z (abs(z) < 0.05). + // 17 Coefficients are computed to 21 decimal digits precision suitable for 64-bit double used by most platforms. + // Taylor series coefficients used are computed by Wolfram to 50 decimal digits using instruction + // N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], as proposed by Tosio Fukushima and implemented by Veberic. + + template + T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<1> const&) + { +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "double lambert_w0_small_z called with z = " << z << " using " << 17 << " terms of precision, " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z*(1. - // j1 z^1 + z*(1. - // j2 z^2 + z*(1.5 - // 3/2 // j3 z^3 + z*(2.6666666666666666667 - // 8/3 // j4 + z*(5.2083333333333333333 - // -125/24 // j5 + z*(10.8 - // j6 + z*(23.343055555555555556 - // j7 + z*(52.012698412698412698 - // j8 + z*(118.62522321428571429 - // j9 + z*(275.57319223985890653 - // j10 + z*(649.78717234347442681 - // j11 + z*(1551.1605194805194805 - // j12 + z*(3741.4497029592385495 - // j13 + z*(9104.5002411580189358 - // j14 + z*(22324.308512706601434 - // j15 + z*(55103.621972903835338 - // j16 + z*136808.86090394293563)))))))))))))))); // j17 z^17 + return result; + } // T lambert_w0_small_z(const T z, boost::mpl::int_<1> const&) + + //! Specialization of long double (80-bit double extended) series expansion used for small z (abs(z) < 0.05). + // 21 Coefficients are computed to 21 decimal digits precision suitable for 80-bit long double used by some + // platforms including GCC and Clang when generating for Intel X86 floating-point processors with 80-bit operations enabled (the default). + // (This is NOT used by Microsoft Visual Studio where double and long always both use only 64-bit type.) + template + T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<2> const&) + { +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "long double (80-bit double extended) lambert_w0_small_z called with z = " << z << " using " << 21 << " terms of precision " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z*(1.L - // j1 z^1 + z*(1.L - // j2 z^2 + z*(1.5L - // 3/2 // j3 + z*(2.6666666666666666667L - // 8/3 // j4 + z*(5.2083333333333333333L - // -125/24 // j5 + z*(10.800000000000000000L - // j6 + z*(23.343055555555555556L - // j7 + z*(52.012698412698412698L - // j8 + z*(118.62522321428571429L - // j9 + z*(275.57319223985890653L - // j10 + z*(649.78717234347442681L - // j11 + z*(1551.1605194805194805L - // j12 + z*(3741.4497029592385495L - // j13 + z*(9104.5002411580189358L - // j14 + z*(22324.308512706601434L - // j15 + z*(55103.621972903835338L - // j16 + z*(136808.86090394293563L - // j17 z^17 last term used by Fukushima double. + z*(341422.050665838363317L - // z^18 + z*(855992.9659966075514633L - // z^19 + z*(2.154990206091088289321e6L - // z^20 + z*5.4455529223144624316423e6L // z^21 + )))))))))))))))))))); + + return result; + } // T lambert_w0_small_z(const T z, boost::mpl::int_<1> const&) + + //! Specialization of long double (128-bit quad) series expansion used for small z (abs(z) < 0.05). + // 34 Taylor series coefficients used are computed by Wolfram to 50 decimal digits using instruction + // N[InverseSeries[Series[z Exp[z],{z,0,34}]],50], + // and are suffixed by L as they are assumed of type long double. + // (This is NOT used for 128-bit quad which required a suffix Q + // nor multiprecision type cpp_bin_float_quad that can only be initialised at full precision of the type + // using a decimal digit string like "2.6666666666666666666666666666666666666666666666667".) + + template + T lambert_w0_small_z(const T z, const Policy&, boost::mpl::int_<3> const&) + { +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "long double (128-bit quad) lambert_w0_small_z called with z = " << z << " using " << 34 << " terms of precision " + << std::numeric_limits::max_digits10 << " decimal digits. " << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + T result = + z*(1.L - // j1 + z*(1.L - // j2 + z*(1.5L - // 3/2 // j3 + z*(2.6666666666666666666666666666666666L - // 8/3 // j4 + z*(5.2052083333333333333333333333333333L - // -125/24 // j5 + z*(-10.800000000000000000000000000000000L - // j6 + z*(23.343055555555555555555555555555555L - // j7 + z*(52.0126984126984126984126984126984126L - // j8 + z*(118.625223214285714285714285714285714L - // j9 + z*(275.57319223985890652557319223985890L - // * z ^ 10 - // j10 + z*(649.78717234347442680776014109347442680776014109347L - // j11 + z*(1551.1605194805194805194805194805194805194805194805L - // j12 + z*(3741.4497029592385495163272941050718828496606274384L - // j13 + z*(9104.5002411580189357967135744913522691300469078247L - // j14 + z*(22324.308512706601434280005708577137148565719994291L - // j15 + z*(55103.621972903835337697771560205422639285073147507L - // j16 + z*136808.86090394293563342215789305736395683485630576L)))))))))))))))); // j17 + return result; + } // T lambert_w0_small_z(const T z, boost::mpl::int_<1> const&) + + //! Series functor to compute series term using pow and factorial. + //! \details Functor is called after evaluating polynomial with the coefficients as rationals below. + template + struct lambert_w0_small_z_series_term + { + typedef T result_type; + //! \param _z Lambert W argument z. + //! \param -term -pow<18>(z) / 6402373705728000uLL + //! \param _k number of terms == initially 18 + + // Note *after* evaluating N terms, its internal state has k = N and term = (-1)^N z^N. + + lambert_w0_small_z_series_term(T _z, T _term, int _k) + : k(_k), z(_z), term(_term) { } + + T operator()() + { // Called by sum_series until needs precision set by factor (policy::get_epsilon). + using std::pow; + ++k; + term *= -z / k; + //T t = pow(z, k) * pow(T(k), -1 + k) / factorial(k); // (z^k * k(k-1)^k) / k! + T result = term * pow(T(k), -1 + k); // term * k^(k-1) + // std::cout << " k = " << k << ", term = " << term << ", result = " << result << std::endl; + return result; // + } + private: + int k; + T z; + T term; + }; // template struct lambert_w0_small_z_series_term + + //! Generic variant for T a User-defined types like Boost.Multiprecision. + template + inline T lambert_w0_small_z(T z, const Policy& pol, boost::mpl::int_<4> const&) + { +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "Generic lambert_w0_small_z called with z = " << z << " using as many terms needed for precision." << std::endl; + std::cout << "Argument z is of type " << typeid(T).name() << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + // First several terms of the series are tabulated and evaluated as a polynomial: + // this will save us a bunch of expensive calls to pow. + // Then our series functor is initialized "as if" it had already reached term 18, + // enough evaluation of built-in 64-bit double and float (and 80-bit long double?) types. + + // Coefficients should be stored such that the coefficients for the x^i terms are in poly[i]. + static const T coeff[] = + { + 0, // z^0 Care: zeroth term needed by tools::evaluate_polynomial, but not in the Wolfram equation, so indexes are one different! + 1, // z^1 term. + -1, // z^2 term + static_cast(3uLL) / 2uLL, // z^3 term. + -static_cast(8uLL) / 3uLL, // z^4 + static_cast(125uLL) / 24uLL, // z^5 + -static_cast(54uLL) / 5uLL, // z^6 + static_cast(16807uLL) / 720uLL, // z^7 + -static_cast(16384uLL) / 315uLL, // z^8 + static_cast(531441uLL) / 4480uLL, // z^9 + -static_cast(156250uLL) / 567uLL, // z^10 + static_cast(2357947691uLL) / 3628800uLL, // z^11 + -static_cast(2985984uLL) / 1925uLL, // z^12 + static_cast(1792160394037uLL) / 479001600uLL, // z^13 + -static_cast(7909306972uLL) / 868725uLL, // z^14 + static_cast(320361328125uLL) / 14350336uLL, // z^15 + -static_cast(35184372088832uLL) / 638512875uLL, // z^16 + static_cast(2862423051509815793uLL) / 20922789888000uLL, // z^17 term + -static_cast(5083731656658uLL) / 14889875uLL, + // z^18 term. = 136808.86090394293563342215789305735851647769682393 + + // z^18 is biggest that can be computed as rational using the largest possible uLL integers, + // so higher terms cannot be potentially compiler-computed as uLL rationals. + // Wolfram (5083731656658 z ^ 18) / 14889875 or + // -341422.05066583836331735491399356945575432970390954 z^18 + + // See note below calling the functor to compute another term, + // sufficient for 80-bit long double precision. + // Wolfram -341422.05066583836331735491399356945575432970390954 z^19 term. + // (5480386857784802185939 z^19)/6402373705728000 + // But now this variant is not used to compute long double + // as specializations are provided above. + }; // static const T coeff[] + + /* + Table of 19 computed coefficients: + + #0 0 + #1 1 + #2 -1 + #3 1.5 + #4 -2.6666666666666666666666666666666665382713370408509 + #5 5.2083333333333333333333333333333330765426740817019 + #6 -10.800000000000000000000000000000000616297582203915 + #7 23.343055555555555555555555555555555076212991619177 + #8 -52.012698412698412698412698412698412659282693193402 + #9 118.62522321428571428571428571428571146835390992496 + #10 -275.57319223985890652557319223985891400375196748314 + #11 649.7871723434744268077601410934743969785223845882 + #12 -1551.1605194805194805194805194805194947599566007429 + #13 3741.4497029592385495163272941050719510009019331763 + #14 -9104.5002411580189357967135744913524243896052869184 + #15 22324.308512706601434280005708577137322392070452582 + #16 -55103.621972903835337697771560205423203318720697224 + #17 136808.86090394293563342215789305735851647769682393 + 136808.86090394293563342215789305735851647769682393 == Exactly same as Wolfram computed value. + #18 -341422.05066583836331735491399356947486381600607416 + 341422.05066583836331735491399356945575432970390954 z^19 Wolfram value differs at 36 decimal digit, as expected. + */ + + using boost::math::policies::get_epsilon; // for type T. + using boost::math::tools::sum_series; + using boost::math::tools::evaluate_polynomial; + // http://www.boost.org/doc/libs/release/libs/math/doc/html/math_toolkit/roots/rational.html + + // std::streamsize prec = std::cout.precision(std::numeric_limits ::max_digits10); + + T result = evaluate_polynomial(coeff, z); + // template + // V evaluate_polynomial(const T(&poly)[N], const V& val); + // Size of coeff found from N + //std::cout << "evaluate_polynomial(coeff, z); == " << result << std::endl; + //std::cout << "result = " << result << std::endl; + // It's an artefact of the way I wrote the functor: *after* evaluating N + // terms, its internal state has k = N and term = (-1)^N z^N. So after + // evaluating 18 terms, we initialize the functor to the term we've just + // evaluated, and then when it's called, it increments itself to the next term. + // So 18!is 6402373705728000, which is where that comes from. + + // The 19th coefficient of the polynomial is actually, 19 ^ 18 / 19!= + // 104127350297911241532841 / 121645100408832000 which after removing GCDs + // reduces down to Wolfram rational 5480386857784802185939 / 6402373705728000. + // Wolfram z^19 term +(5480386857784802185939 z^19) /6402373705728000 + // +855992.96599660755146336302506332246623424823099755 z^19 + + //! Evaluate Functor. + lambert_w0_small_z_series_term s(z, -pow<18>(z) / 6402373705728000uLL, 18); + + // Temporary to list the coefficients. + //std::cout << " Table of coefficients" << std::endl; + //std::streamsize saved_precision = std::cout.precision(50); + //for (size_t i = 0; i != 19; i++) + //{ + // std::cout << "#" << i << " " << coeff[i] << std::endl; + //} + //std::cout.precision(saved_precision); + + boost::uintmax_t max_iter = policies::get_max_series_iterations(); // Max iterations from policy. + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + std::cout << "max iter from policy = " << max_iter << std::endl; + // // max iter from policy = 1000000 is default. + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES + + result = sum_series(s, get_epsilon(), max_iter, result); + // result == evaluate_polynomial. + //sum_series(Functor& func, int bits, boost::uintmax_t& max_terms, const U& init_value) + // std::cout << "sum_series(s, get_epsilon(), max_iter, result); = " << result << std::endl; + + //T epsilon = get_epsilon(); + //std::cout << "epilson from policy = " << epsilon << std::endl; + // epilson from policy = 1.93e-34 for T == quad + // 5.35e-51 for t = cpp_bin_float_50 + + // std::cout << " get eps = " << get_epsilon() << std::endl; // quad eps = 1.93e-34, bin_float_50 eps = 5.35e-51 + policies::check_series_iterations("boost::math::lambert_w0_small_z<%1%>(%1%)", max_iter, pol); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS + std::cout << "z = " << z << " needed " << max_iter << " iterations." << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS + //std::cout.precision(prec); // Restore. + return result; + } // template inline T lambert_w0_small_z_series(T z, const Policy& pol) + + ///////////////////////////////////////////////////////////////////////////// + + // Halley refinement, if required, for precision in policy. + // Only perform this if the most precise value possible (usually within one epsilon) is desired. + // The previous Fukushima Schroeder refinement usually gives within several epsilon. + // Halley refinement needs to perform some evaluations of the exp function, and + // may nearly double the execution time. + // However Boost.Math prioritizes accuracy over speed, + // so this extra step is taken by the default policy. + + // Two versions: + // do_while always makes one iteration before testing against a tolerance, + // while_do does a test first, perhaps avoiding an iteration if already within tolerance. + + template + inline + T halley_update(T w0, const T z, const Policy&) + { + // Only if necessary, iterate a few times to refine estimate using Halley's method. + // Inline Halley iteration, rather than calling boost::math::tools::halley_iterate + // since we can simplify the expressions algebraically, + // and don't need most of the error checking of the boilerplate version + // as we know in advance that the function is reasonably well-behaved, + // (and in any case the derivatives require evaluation of Lambert W!). + + //! So also do-while version. + + BOOST_MATH_STD_USING // Aid argument dependent (ADL) lookup of abs. + + using boost::math::constants::exp_minus_one; // 0.36787944 + using boost::math::tools::max_value; + + int iterations = 0; + // int iterations_required = 6; // + int max_iterations = 20; // Only as a check on looping. + + T tolerance = boost::math::policies::get_epsilon(); + T previous_diff = boost::math::tools::max_value(); + T w1 = w0; // Refined estimate. + T expw0 = exp(w0); // Compute z == W * exp(W); from best Lambert W estimate so far. + // Hope that w0 * expw0 == z; + T diff = (w0 * expw0) - z; // Difference from argument z. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + std::streamsize precision = std::cout.precision(std::numeric_limits::max_digits10); // Save. + std::cout.precision(std::numeric_limits::max_digits10); // Show all posssibly significant digits. + std::ios::fmtflags flags(std::cout.flags()); // Save. + std::cout.setf(std::ios_base::showpoint); // Include any trailing zeros. + std::cout << "w = " << w0 << ", z = " << z << ", w * exp(w) = " << w0 * expw0 << ", diff = " << diff << std::endl; + if (diff == 0) + { + std::cout << "Exact, so no Halley refinement from " << w0 << std::endl; + } + else if(abs(diff) <= tolerance) + { + std::cout << "Within tolerance, so no Halley refinement from " << w0 << std::endl; + } + std::cout.precision(precision); // Restore. + std::cout.flags(flags); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + if (diff == 0) // Exact result - common. + { + return w0; + } + // relative does NOT work well, so check for improvement in diff instead. + //else if ((abs(diff) / w0) <= tolerance) + //{ + // return w0; + //} + // else Refine. + do + { + iterations++; +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + std::cout << "Halley iteration #" << iterations << "."<< std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + previous_diff = diff; // Remember so that can check if any new estimate is better. + + // Halley's method from Luu equation 6.39, line 17. + // https://en.wikipedia.org/wiki/Halley%27s_method + // http://www.wolframalpha.com/input/?i=differentiate+productlog(x) + // differentiate productlog(x) d/dx(W(x)) = (W(x))/(x W(x) + x) = e^w (1 + w) + // Wolfram Alpha (d^2 )/(dw^2)(w exp(w) - z) = e^w (w + 2) + // f'(w) = e^w (1 + w) + // f''(w) = e^w (2 + w) , + // f''(w)/f'(w) = (2+w) / (1+w), Luu equation 6.38. + + w1 = w0 // Refine a new estimate using Halley's method using Luu equation 6.39. + - diff / ((expw0 * (w0 + 1) - (w0 + 2) * diff / (w0 + w0 + 2))); + diff = (w1 * exp(w1)) - z; + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + T dis = boost::math::float_distance(w0, w1); + std::cout << "w = " << w0 << ", z = " << z << ", w * exp(w) = " << w0 * expw0 << ", diff = " << diff << std::endl; + int d = static_cast(dis); + if (abs(dis) < 2147483647) + { + std::cout << "float_distance = " << d << std::endl; + } +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + + if (diff == 0) // Exact. + { + return w1; // Exact, or no improvement, so as good as it can be. + } + if (abs(diff) >= abs(previous_diff)) // Latest estimate is not better, or worse, so avoid oscillating result (common when near singularity). + { + return w1; // Or mean (w0 + w1)/2 ?? + } + if (fabs((w0 / w1) - static_cast(1)) < tolerance) + { // Reached estimate of Lambert W within relative tolerance (usually an epsilon or few). + return w1; // Or mean (w0 + w1)/2 ?? + } + w0 = w1; + expw0 = exp(w0); + } + // while ((iterations < iterations_required) || (iterations <= max_iterations)); // Absolute limit during testing - is looping if need this. + while (iterations <= max_iterations); // Absolute limit during testing - is looping if need this. + + return w1; + } // T halley_update(T w0, const T z) + + + //! Halley update version that does one update without a check, + //! and then more updates as necessary. + // TODO Might use a template parameter to avoid duplication? + template + inline + T halley_update_do_while(T w0, const T z) + { + BOOST_MATH_STD_USING // Aid argument dependent lookup of abs. + + using boost::math::constants::exp_minus_one; // 0.36787944 + using boost::math::tools::max_value; + T tolerance = std::numeric_limits::epsilon(); + int iterations = 0; + int min_iterations = 1; // Specify a minimum number of iterations to perform. + int max_iterations = 6; // Should not be needed, but include to avoid risk of looping forever. + // (Might be necessary to increase this?) +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + std::cout << "At least = " << min_iterations << " Halley iterations." << std::endl; + std::cout << "(But not more than " << max_iterations << " Halley iterations.)" << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + + T w1; // Refined estimate. + T previous_diff = boost::math::tools::max_value(); + do + { // Iterate a few times to refine value using Halley's method. + // Inline Halley iteration, rather than calling boost::math::tools::halley_iterate + // since we can simplify the expressions algebraically, + // and don't need most of the error checking of the boilerplate version + // as we know in advance that the function is reasonably well-behaved, + // and in any case the derivatives require evaluation of Lambert W! + + // z == W * exp(W); + T expw0 = exp(w0); // Compute z from best Lambert W estimate so far. + // Hope that w0 * expw0 == z; + T diff = (w0 * expw0) - z; // Difference from argument z. + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + std::cout << "w = " << w0 << ", z = " << z << ", exp(w) = " << expw0 << ", diff = " << diff << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_HALLEY + + iterations++; + // Halley's method from Luu equation 6.39, line 17. + // https://en.wikipedia.org/wiki/Halley%27s_method + // http://www.wolframalpha.com/input/?i=differentiate+productlog(x) + // differentiate productlog(x) d/dx(W(x)) = (W(x))/(x W(x) + x) = e^w (1 + w) + // Wolfram Alpha (d^2 )/(dw^2)(w exp(w) - z) = e^w (w + 2) + // f'(w) = e^w (1 + w) + // f''(w) = e^w (2 + w) , + // f''(w)/f'(w) = (2+w) / (1+w), Luu equation 6.38. + + w1 = w0 // Refine a new estimate using Halley's method using Luu equation 6.39. + - diff / ((expw0 * (w0 + 1) - (w0 + 2) * diff / (w0 + w0 + 2))); + if ( // Reached estimate of Lambert W within relative tolerance (usually an epsilon or few). + (fabs((w0 / w1) - static_cast(1)) < tolerance) + || // Or latest estimate is not better, or worse, so avoid oscillating result (common when near singularity). + (abs(diff) >= abs(previous_diff)) + ) + { + return w1; // No improvement, so as good as it can be. + } + + w0 = w1; + previous_diff = diff; // Remember so that can check if any new estimate is better. + } + while ( + (iterations < min_iterations) || // do minimum iterations. + (iterations <= max_iterations) // Absolute max limit during testing + // (unexpected looping if need this). + ); + return w1; // + } // Halley do while + + //! \brief Schroeder method, fifth-order update formula, + //! \details See T. Fukushima page 80-81, and + //! A. Householder, The Numerical Treatment of a Single Nonlinear Equation, + //! McGraw-Hill, New York, 1970, section 4.4. + //! Fukushima algorithm switches to @c schroeder_update after pre-computed bisections, + //! chosen to ensure that the result will be achieve the +/- 10 epsilon target. + //! \param w Lambert w estimate from bisection. + //! \param y bracketing value from bisection. + //! \returns Refined estimate of Lambert w. + + // Schroeder refinement, called unless NOT required by precision policy. + template + inline + T schroeder_update(const T w, const T y) + { + // Compute derivatives using 5th order Schroeder refinement. + // Since this is the final step, it will always use the highest precision type T. + // Example of Call: + // result = schroeder_update(w, y); + //where + // w is estimate of Lambert W (from bisection or series). + // y is z * e^-w. + + BOOST_MATH_STD_USING // Aid argument dependent lookup of abs. + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + using boost::math::float_distance; + T fd = float_distance(w, y); + std::cout << "Schroder "; + if (abs(fd) < 214748000.) + { + std::cout << " Distance = "<< static_cast(fd); + } + else + { + std::cout << "Difference w - y = " << (w - y) << "."; + } + std::cout << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + // Fukushima equation 18, page 6. + const T f0 = w - y; // f0 = w - y. + const T f1 = 1 + y; // f1 = df/dW + const T f00 = f0 * f0; + const T f11 = f1 * f1; + const T f0y = f0 * y; + const T result = + w - 4 * f0 * (6 * f1 * (f11 + f0y) + f00 * y) / + (f11 * (24 * f11 + 36 * f0y) + + f00 * (6 * y * y + 8 * f1 * y + f0y)); // Fukushima Page 81, equation 21 from equation 20. + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + std::cout << "Schroeder refined " << w << " " << y << ", difference " << w-y << ", change " << w - result << ", to result " << result << std::endl; + std::cout.precision(saved_precision); // Restore. +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + + return result; + } // template T schroeder_update(const T w, const T y) + + ///////////// namespace detail implementations of Lambert W for W0 and W-1 branches. + + //! Compute Lambert W for W0 (or W+) branch. + template + T lambert_w0_imp(const T z, const Policy& pol) + { + // Catch providing an integer value as parameter x to lambert_w, for example, lambert_w(1). + // Need to ensure it is a floating-point type (of the desired type, float 1.F, double 1., or long double 1.L), + // or static_casted integer, for example: static_cast(1) or static_cast(1). + // Want to allow fixed_point types too, so do not just test for floating-point. + // Integral types should been promoted to double by user Lambert w functions. + BOOST_STATIC_ASSERT_MSG(!std::is_integral::value, + "Must be floating-point or fixed type (not integer type), for example: lambert_w0(1.), not lambert_w0(1)!"); + + const char* function = "boost::math::lambert_w0()"; // Used for error messages. + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0 + { + std::size_t saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Lambert_w argument = " << z << std::endl; + std::cout << "Argument Type = " << typeid(T).name() + << ", max_digits10 = " << std::numeric_limits::max_digits10 + << std::setprecision(3) << ", epsilon = " << std::numeric_limits::epsilon() << std::endl; + std::cout.precision(saved_precision); // Restore precision. + } +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0 + + // Check for edge and corner cases first: + + if (boost::math::isnan(z)) + { + return policies::raise_domain_error(function, + "Argument z is NaN!", + z, pol); + } // isnan + if (boost::math::isinf(z)) + { + if (std::numeric_limits::has_infinity) + { + return std::numeric_limits::infinity(); + } + else + { // Arbitrary precision type. + return tools::max_value(); + } + // Or might opt to throw an exception? + //return policies::raise_domain_error(function, + // "Argument z is infinite!", + // z, pol); + } // isinf + + BOOST_MATH_STD_USING // Aid argument dependent lookup of abs. + if (abs(z) <= static_cast(0.05)) + { // z is near zero, so use small z/near-zero series expansion. + // Change to use this series approximation made by Fukushima at 0.05, + // found by trial and error, and may not be best for higher precision. + // Chose float, double, or long double, or multiprecision specializations. + // Higher precision types either need more terms, or change in range of abs(z), + // or use as an approximation? + return detail::lambert_w0_small_z(z, pol); + } + else if (z == -boost::math::constants::exp_minus_one()) + { // At singularity, so return exactly minus one. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SINGULARITY_SERIES + std::cout << "At singularity. " << std::endl; // within epsilon of branch singularity 0.36. +#endif + return static_cast(-1); + } + else if (z < -boost::math::constants::exp_minus_one()) // z < -1/e so out of range of W0 branch (should using W1 branch instead). + { + return policies::raise_domain_error(function, + "Argument z = %1% out of range (-1/e <= z < (std::numeric_limits::max)()) for Lambert W0 branch (use W-1 branch?).", + z, pol); + } + else if (z < static_cast(-0.35)) + { // Near singularity/branch point at z = -0.3678794411714423215955237701614608727 + const T p2 = 2 * (boost::math::constants::e() * z + 1); + if (p2 > 0) + { // Use near-singularity series expansion. + // http://www.boost.org/doc/libs/release/libs/multiprecision/doc/html/boost_multiprecision/intro.html + // Taking care to avoid trouble from expression templates. + T w_series = lambert_w_singularity_series(T(sqrt(p2))); + // Neither Halley nor Schroeder do not improve result for builtin, + // differences about 5e-15, so do NOT use refinement step below unless multiprecision. + // + //int d2 = policies::digits(); // Precision as digits base 2 from policy. + //if (d2 > (std::numeric_limits::digits - 6)) + //{ // If more (fullest) accuracy required, then use refinement. + // T y = z * exp(-w_series); + // T s_result = schroeder_update(w_series, y); + //} + if (std::numeric_limits::digits > 53) + { // Multiprecision, so try a refinement: + w_series = halley_update(w_series, z, pol); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Halley updated to " << w_series << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN + } + return w_series; + } + } // z < -0.35 + // z < -1/e + else + { //! Check if z so big that cannot use lookup table, so approximate and Halley iterate. + // std::cout << " lambert_w_lookup::w0zs[63] " << lambert_w_lookup::w0zs[63] << ' ' << lambert_w_lookup::w0zs[64] << std::endl; + + using namespace boost::math::detail::lambert_w_lookup; + + // lambert_w_lookup::w0zs[63] = 1.4450833904658542e+29, lambert_w_lookup::w0zs[64]= 3.9904954117194348e+29, + if (z >= w0zs[noof_w0zs - 1]) // F[64] = 3.9904954117194348e+29 + { //! z so big that cannot use lookup table, so approximate and Halley iterate. + T guess; + // R.M.Corless, G.H.Gonnet, D.E.G.Hare, D.J.Jeffrey, and D.E.Knuth, “On the Lambert W function, ” Adv.Comput.Math., vol. 5, pp. 329–359, 1996. + // François Chapeau-Blondeau and Abdelilah Monir + // Numerical Evaluation of the Lambert W Function + // IEEE Transactions On Signal Processing, VOL. 50, NO. 9, Sep 2002 + // https://pdfs.semanticscholar.org/7a5a/76a9369586dd0dd34dda156d8f2779d1fd59.pdf + // Estimate Lambert W using ln(z) ... + // This is roughly the power of ten * ln(10) ~= 2.3, n ~= 10^n + // and improve by adding a second term ln(ln(z)) + T lz = log(z); + T llz = log(lz); + guess = lz - llz + (llz / lz); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_HUGE + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "z = " << z << ", guess = " << guess << ", ln(z) = " << lz << ", ln(-ln(z) = " << llz << ", llz/lz = " << (llz / lz) << std::endl; + + // z = 3.9999999999999997e+29, guess = 64.001325187470187, ln(z) = 68.161262057947212, ln(-ln(z) = 4.2218763984580194, llz/lz = 0.061939527980993024 + // After 3 Halley iterations: lambert_w0(3.9999999999999997e+29) = 64.002342375637951 + + int d10 = policies::digits_base10(); // policy template parameter digits10 + int d2 = policies::digits(); // digits base 2 from policy. + std::cout << "digits10 = " << d10 << ", digits2 = " << d2 // For example: digits10 = 1, digits2 = 5 + << std::endl; + std::cout.precision(saved_precision); + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_HUGE + if (policies::digits() < 12) + { // For the worst case near w = 64, the error in the 'guess' is ~0.008, ratio ~ 0.0001 or 1 in 10,000 digits 10 ~= 4, or digits2 ~= 12. + return guess; + } + // else refine for higher precision. + T result = halley_update(guess, z, pol); + return result; + + // Or could throw an exception here. + //// G[k=64] == g[63] == + //return policies::raise_domain_error(function, 3.9999999999999997e+29 + // "Argument z = %1% is too small (< 3.9904954117194348e+29) !", + // z, pol); + } // Too big for lookup table. + + // Argument z is in the 'normal' range and float or double precision, so use Lookup, Bracket, Bisection and Schroeder (and Halley). + if (std::numeric_limits::digits > 53) + { // T is more precise than 64-bit double (or long double, or ?), + // so recurse to compute an approximate double value using only one Schroeder refinement, + // (avoiding any double-precision Halley refinement from policy double_digits2<50> 53 - 3 = 50 + // because are next going to use Halley refinement at full/high precision using this as an approximation). + using boost::math::policies::precision; + using boost::math::policies::digits10; + using boost::math::policies::digits2; + using boost::math::policies::policy; + // Compute a 50-bit precision approximate W0 in a double (no Halley refinement). + T double_approx = static_cast(lambert_w0_imp(static_cast(z), policy >())); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN + std::cout << "Argument Type " << typeid(T).name() << " approximation double = " << double_approx << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_NOT_BUILTIN + // Perform additional few Halley refinement(s) to ensure that + // get a near as possible to correct result (usually +/- one epsilon). + T result = halley_update(double_approx, z, pol); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0__NOT_BUILTIN + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Result " << typeid(T).name() << " precision Halley refinement = " << result << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0__NOT_BUILTIN + return result; + } // digits > 53 - higher precision than double. + else // T is double or less precision. + { + // Use a lookup table + using namespace boost::math::detail::lambert_w_lookup; + // to find the nearest integer part of Lambert W to use as starting point for Bisection, + // Test sequence is n is (0, 1, 2, 4, 8, 16, 32, 64) for W0 branch. + // Since z is probably quite small, start with lowest (n = 0), + // up to largest possible F[k=64] for lookup_t type). + int n; // Indexing W0 lookup table w0zs[n] of z. + for (n = 0; n <= 2; ++n) + { // Try 1st few. + if (w0zs[n] > z) + { // + goto bisect; + } + } + n = 2; + for (int j = 1; j <= 5; ++j) + { + n *= 2; // Try bigger, and bigger steps. + if (w0zs[n] > z) // z <= w0zs[n] + { + goto overshot; // Need to backup! + } + } // for + // get here if + //std::cout << "Too big " << z << std::endl; + //// else z too large :-( + //const char* function = "boost::math::lambert_w0()"; + // This should not now occur (> largest lookup now handled) so should be a logic error? + std::cout << "W = " << n << ", w0zs[n]" << w0zs[n] << ' ' << w0zs[n+1] << ", z = " << z << std::endl; + return policies::raise_domain_error("boost::math::lambert_w0()", + "Argument z = %1% is too large! (Should not occur, please report.)", + z, pol); + overshot: + { + int nh = n / 2; + for (int j = 1; j <= 5; ++j) + { + nh /= 2; + if (nh <= 0) + { + break; + } + if (w0zs[n - nh] > z) + { + n -= nh; + } + } // for + } + + bisect: + --n; // w0zs[n] is nearest below, so n is integer part of W, + // and w0zs[n+1] is next integral part of W. + // These are used as initial values for bisection. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_LOOKUP + std::cout << "Result lookup W0(" << z << ") bisection between w0zs[" << n << "] = " << w0zs[n] << " and w0zs[" << n << "] = " << w0zs[n+1] + << ", bisect mean = " << ( w0zs[n] + w0zs[n + 1]) / 2 << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_LOOKUP + + // Check precision specified by policy in case can return early. + // Use can set precision, for example = 7 from lambert_w0(x, policy >() or lambert_w0(x, policy >() + int d2 = policies::digits(); // digits base 2 from policy (default 24 for float, 53 for double). +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + int d10 = policies::digits_base10(); // policy template parameter digits10 + std::cout << "digits10 = " << d10 << ", digits2 = " << d2 // For example: digits10 = 1, digits2 = 5 + << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + if (d2 <= 7) + { // Only 7 binary digits precision required to hold integer size of w0zs[65], + // so just return the mean of the two nearby integral values. + // This is a *very* approximate estimate, and perhaps not very useful? + return static_cast((w0zs[n] + w0zs[n+1]) / 2); + } + + // Compute the number of bisections that ensure that result is close enough that + // a single 5th order Schroeder update is sufficient to achieve near double (53-bit) accuracy. + int bisections = 8; // Assume minimum number of bisections will be needed (most common case). + if (z <= -0.36) + { // Very near singularity, so need several more bisections. + bisections = noof_sqrts; // 12 + } + else if (z <= -0.3) + { // Near singularity, so need a few more bisections. + bisections = 11; + } + else if (n <= 0) + { // Very small z, so need 3 more bisections. + bisections = 10; + } + else if (n <= 1) + { // Small z, so need just 1 extra bisection. + bisections = 9; + } + // bisections += 2; // Try a few extras to see if this gets closer to 'best possible result'. Probably not. +// But would need more halfs and sqrts to test worst case? +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION + std::cout << "n = " << n << ", so " << bisections << " bisections." << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION + lookup_t dz = static_cast(z); + // Only use lookup_t precision, default double, for bisection. + // Use Halley refinement for higher precisions. + // Perform the bisections fractional bisections for necessary precision. + // Avoid using exponential function for speed. + lookup_t w = static_cast(n); // Fukushima Equation 25, Integral Lambert W. + // lookup_t y = dz * e[n + 1]; // Fukushima Integral +1 Lambert W. + lookup_t y = dz * static_cast(w0es[n + 1]); // Fukushima Equation 26, Integral +1 Lambert W. + for (int j = 0; j < bisections; ++j) + { // Equation 27. + const lookup_t wj = w + static_cast(halves[j]); // Add 1/2, 1/4, 1/8 ... + const lookup_t yj = y * static_cast(sqrtw0s[j]); // Multiply by sqrt(1/e), ... + if (wj < yj) + { + w = wj; + y = yj; + } +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION + std::cout << "#" << std::setw(2) << j << " w = " << w << ", y = " << y << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION + } // for bisections + +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION + std::cout << "Bisection estimate = " << w << " " << y << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_BISECTION + + if (d2 <= 10) + { // Only 10 digits2 required (== digits10 ~= 3 decimal digits precision), + // so just return the nearest bisection. + // (Might make this test depend on size of T?) + return static_cast(w); // Bisection only. + } + else // More than 10 digits2 wanted, so continue with Fukushima's Schroeder refinement. + { + T result = static_cast(schroeder_update(w, y)); + if (d2 <= (std::numeric_limits::digits - 3)) + { // Only enough digits2 required, so just return the Schroeder refined value. + // For example, for float, if (d2 <= 22) then + // digits-3 returns Schroeder result if up to 3 bits might be wrong. +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + std::cout << "Schroeder refinement estimate = " << result << std::endl; +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_SCHROEDER + return result; // Schroeder only. + } + else // Perform additional Halley refinement(s) to ensure that + // get a near as possible to correct result (usually +/- epsilon). + { + result = halley_update(result, z, pol); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_HALLEY + std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Halley refinement estimate = " << result << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_HALLEY + return result; // Halley + } // schroeder or Schroeder and Halley. + } // more than 10 digits2 + } + } // normal range and precision. + throw std::logic_error("No result from lambert_w0_imp! (Please report!)"); // Should never get here. + } // T lambert_w0_imp(const T z, const Policy& pol) + + + //! Lambert W for W-1 branch, -max(z) < z <= -1/e. + // TODO is -max(z) allowed? + template + T lambert_wm1_imp(const T z, const Policy& pol) + { + // Catch providing an integer value as parameter x to lambert_w, for example, lambert_w(1). + // Need to ensure it is a floating-point type (of the desired type, float 1.F, double 1., or long double 1.L), + // or static_casted integer, for example: static_cast(1) or static_cast(1). + // Want to allow fixed_point types too, so do not just test for floating-point. + // Integral types should be promoted to double by user Lambert w functions. + // If integral type provided to user function lambert_w0 or lambert_wm1, + // then should already have been promoted to double. + BOOST_STATIC_ASSERT_MSG(!std::is_integral::value, + "Must be floating-point or fixed type (not integer type), for example: lambert_wm1(1.), not lambert_wm1(1)!"); + + BOOST_MATH_STD_USING // Aid argument dependent lookup (ADL) of abs. + + const char* function = "boost::math::lambert_wm1()"; // Used for error messages. + + // Check for edge and corner cases first: + + if (boost::math::isnan(z)) + { + return policies::raise_domain_error(function, + "Argument z is NaN!", + z, pol); + } // isnan + + if (boost::math::isinf(z)) + { + return policies::raise_domain_error(function, + "Argument z is infinite!", + z, pol); + } // isinf + + if (z == static_cast(0)) + { // z is exactly zero so return -std::numeric_limits::infinity(); + if (std::numeric_limits::has_infinity) + { + return -std::numeric_limits::infinity(); + } + else + { + return -tools::max_value(); + } + } + if (std::numeric_limits::has_denorm) + { // All real types except arbitrary precision. + if (!boost::math::isnormal(z)) + { // Almost zero - might also just return infinity like z == 0 or max_value? + return policies::raise_overflow_error(function, + "Argument z = %1% is denormalized! (must be z > (std::numeric_limits::min)() or z == 0)", + z, pol); + } + } + + if (z > static_cast(0)) + { // + return policies::raise_domain_error(function, + "Argument z = %1% is out of range (z <= 0) for Lambert W-1 branch! (Try Lambert W0 branch?)", + z, pol); + } + if (z > -(std::numeric_limits::min)()) + { // z is denormalized, so cannot be computed. + // -std::numeric_limits::min() is smallest for type T, + // for example, for double: lambert_wm1(-2.2250738585072014e-308) = -714.96865723796634 + return policies::raise_overflow_error(function, + "Argument z = %1% is too small (z < -std::numeric_limits::min so denormalized) for Lambert W-1 branch!", + z, pol); + } + if (z == -boost::math::constants::exp_minus_one()) // == singularity/branch point z = -exp(-1) = -3.6787944. + { // At singularity, so return exactly -1. + return -static_cast(1); + } + // z is too negative for the W-1 (or W0) branch. + if (z < -boost::math::constants::exp_minus_one()) // > singularity/branch point z = -exp(-1) = -3.6787944. + { + return policies::raise_domain_error(function, + "Argument z = %1% is out of range (z < -exp(-1) = -3.6787944... <= 0) for Lambert W-1 (or W0) branch!", + z, pol); + } + if (z < static_cast(-0.35)) + { // Close to singularity/branch point z = -0.3678794411714423215955237701614608727 but on W-1 branch. + const T p2 = 2 * (boost::math::constants::e() * z + 1); + if (p2 == 0) + { // At the singularity at branch point. + return -1; + } + if (p2 > 0) + { + T w_series = lambert_w_singularity_series(T(-sqrt(p2))); + if (std::numeric_limits::digits > 53) + { // Multiprecision, so try a Halley refinement. + w_series = halley_update(w_series, z, pol); +#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_NOT_BUILTIN + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Halley updated to " << w_series << std::endl; + std::cout.precision(saved_precision); +#endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_NOT_BUILTIN + } + return w_series; + } + // Should not get here. + return policies::raise_domain_error(function, + "Argument z = %1% is out of range for Lambert W-1 branch. (Should not get here - please report!)", + z, pol); + + //return policies::raise_domain_error(function, + // "Argument z = %1% is out of range!", + // z, pol); + } // if (z < -0.35) + + using boost::math::detail::lambert_w_lookup::wm1es; + using boost::math::detail::lambert_w_lookup::wm1zs; + using boost::math::detail::lambert_w_lookup::noof_wm1zs; // size == 64 + + // std::cout <<" Wm1zs[63] (== G[64]) = " << " " << wm1zs[63] << std::endl; // Wm1zs[63] (== G[64]) = -1.0264389699511283e-26 + // Check that z argument value is not smaller than lookup_table G[64] + // std::cout << "(z > wm1zs[63]) = " << std::boolalpha << (z > wm1zs[63]) << std::endl; + + if (z >= wm1zs[63]) // wm1zs[63] = -1.0264389699511282259046957018510946438e-26L W = 64.00000000000000000 + { // z >= -1.0264389699511303e-26 (but != 0 and >= std::numeric_limits::min() and so NOT denormalized). + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + // std::cout << "-std::numeric_limits::min() = " << -(std::numeric_limits::min)() << std::endl; + // std::cout << "-std::numeric_limits::min() = " << -(std::numeric_limits::min)() << std::endl; + // -std::numeric_limits::min() = -1.1754943508222875e-38 + // -std::numeric_limits::min() = -2.2250738585072014e-308 + + // N[productlog(-1, -1.1754943508222875 * 10^-38 ), 50] = -91.856775324595479509567756730093823993834155027858 + // N[productlog(-1, -2.2250738585072014e-308 * 10^-308 ), 50] = -1424.8544521230553853558132180518404363617968042942 + // N[productlog(-1, -1.4325445274604020119111357113179868158* 10^-27), 37] = -65.99999999999999999999999999999999955 + + // R.M.Corless, G.H.Gonnet, D.E.G.Hare, D.J.Jeffrey, and D.E.Knuth, + // “On the Lambert W function, ” Adv.Comput.Math., vol. 5, pp. 329–359, 1996. + // François Chapeau-Blondeau and Abdelilah Monir + // Numerical Evaluation of the Lambert W Function + // IEEE Transactions On Signal Processing, VOL. 50, NO. 9, Sep 2002 + // https://pdfs.semanticscholar.org/7a5a/76a9369586dd0dd34dda156d8f2779d1fd59.pdf + // Estimate Lambert W using ln(-z) ... + // This is roughly the power of ten * ln(10) ~= 2.3. n ~= 10^n + // and improve by adding a second term -ln(ln(-z)) + T guess; // bisect lowest possible Gk[=64] (for lookup_t type) + T lz = log(-z); + T llz = log(-lz); + guess = lz - llz + (llz / lz); // Chapeau-Blondeau equation 20, page 2162. + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_TINY + std::cout << "z = " << z << ", guess = " << guess << ", ln(-z) = " << lz << ", ln(-ln(-z) = " << llz << ", llz/lz = " << (llz / lz) << std::endl; + // z = -1.0000000000000001e-30, guess = -73.312782616731482, ln(-z) = -69.077552789821368, ln(-ln(-z) = 4.2352298269101114, llz/lz = -0.061311231447304194 + // z = -9.9999999999999999e-91, guess = -212.56650048504233, ln(-z) = -207.23265836946410, ln(-ln(-z) = 5.3338421155782205, llz/lz = -0.025738424423764311 + // >z = -2.2250738585072014e-308, guess = -714.95942238244606, ln(-z) = -708.39641853226408, ln(-ln(-z) = 6.5630038501819854, llz/lz = -0.0092645920821846622 + int d10 = policies::digits_base10(); // policy template parameter digits10 + int d2 = policies::digits(); // digits base 2 from policy. + std::cout << "digits10 = " << d10 << ", digits2 = " << d2 // For example: digits10 = 1, digits2 = 5 + << std::endl; + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_TINY + if (policies::digits() < 12) + { // For the worst case near w = 64, the error in the 'guess' is ~0.008, ratio ~ 0.0001 or 1 in 10,000 digits 10 ~= 4, or digits2 ~= 12. + return guess; + } + T result = halley_update(guess, z, pol); + std::cout.precision(saved_precision); + return result; + + // Was Fukushima + // G[k=64] == g[63] == -1.02643897e-26 + //return policies::raise_domain_error(function, + // "Argument z = %1% is too small (< -1.02643897e-26) ! (Should not occur, please report.", + // z, pol); + } // Z too small so use approximation and Halley. + // Else Use a lookup table to find the nearest integer part of Lambert W-1 as starting point for Bisection. + + if (std::numeric_limits::digits > 53) + { // T is more precise than 64-bit double (or long double, or ?), + // so compute an approximate value using only one Schroeder refinement, + // (avoiding any double-precision Halley refinement from policy double_digits2<50> 53 - 3 = 50 + // because are next going to use Halley refinement at full/high precision using this as an approximation). + using boost::math::policies::precision; + using boost::math::policies::digits10; + using boost::math::policies::digits2; + using boost::math::policies::policy; + // Compute a 50-bit precision approximate W0 in a double (no Halley refinement). + T double_approx = static_cast(lambert_wm1_imp(static_cast(z), policy >())); + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_NOT_BUILTIN + std::cout << "Argument Type " << typeid(T).name() << " approximation double = " << double_approx << std::endl; + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + // Perform additional Halley refinement(s) to ensure that + // get a near as possible to correct result (usually +/- one epsilon). + T result = halley_update(double_approx, z, pol); + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + std::streamsize saved_precision = std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Result " << typeid(T).name() << " precision Halley refinement = " << result << std::endl; + std::cout.precision(saved_precision); + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + return result; + } // digits > 53 - higher precision than double. + else // T is double or less precision. + { + // Use a lookup table to find the nearest integer part of Lambert W as starting point for Bisection. + using boost::math::detail::lambert_w_lookup::wm1zs; + // Bracketing sequence n = (2, 4, 8, 16, 32, 64) for W-1 branch. (0 is -infinity) + // Since z is probably quite small, start with lowest n (=2). + int n = 2; + if (wm1zs[n - 1] > z) + { + goto bisect; + } + for (int j = 1; j <= 5; ++j) + { + n *= 2; + if (wm1zs[n - 1] > z) + { + goto overshot; + } + } + // else z < g[63] == -1.0264389699511303e-26, so Lambert W-1 integer part > 64. + // This should not now occur (should be caught by test and code above) so should be a logic_error? + return policies::raise_domain_error(function, + "Argument z = %1% is too small (< -1.026439e-26) (logic error - please report!)", + z, pol); + overshot: + { + int nh = n / 2; + for (int j = 1; j <= 5; ++j) + { + nh /= 2; // halve step size. + if (nh <= 0) + { + break; // goto bisect; + } + if (wm1zs[n - nh - 1] > z) + { + n -= nh; + } + } + } + bisect: + --n; // g[n] now holds lambert W of floor integer n and g[n+1] the ceil part. + // These are used as initial values for bisection. + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_LOOKUP + std::cout << "Result lookup W-1(" << z << ") bisection between wm1zs[" << n - 1 << "] = " << wm1zs[n - 1] << " and wm1zs[" << n << "] = " << wm1zs[n] + << ", bisect mean = " << (wm1zs[n - 1] + wm1zs[n]) / 2 << std::endl; + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_LOOKUP + + // W0 version + // // Check precision specified by policy in case can return early. + // // Use can set precision, for example = 7 from lambert_w0(x, policy >() or lambert_w0(x, policy >() + // int d2 = policies::digits(); // digits base 2 from policy (default 24 for float, 53 for double). + //#ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + // int d10 = policies::digits_base10(); // policy template parameter digits10 + // std::cout << "digits10 = " << d10 << ", digits2 = " << d2 // For example: digits10 = 1, digits2 = 5 + // << std::endl; + //#endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + // if (d2 <= 7) + // { // Only 7 binary digits precision required to hold integer size of w0zs[65], + // // so just return the mean of two nearby integral values. + // // This is a *very* approximate estimate, and perhaps not very useful? + // return static_cast((w0zs[n - 1] + w0zs[n]) / 2); + // } + + // Check precision specified by policy. + int d2 = policies::digits(); + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + int d10 = policies::digits_base10(); // policy template parameter digits10 + std::cout << "digits10 = " << d10 << ", digits2 = " << d2 // For example: digits10 = 1, digits2 = 5 + << std::endl; + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + if (d2 < 7) + { // Only 7 binary digits precision required to hold integer size of w0s[65], + // so just return the mean of two nearby integral values. + // This is a *very* approximate estimate, and perhaps not very useful? + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + std::cout << "between wm1zs[" << n << "] = " << wm1zs[n + 1] << " <= " << z << " <= " << wm1zs[n] << std::endl; + // TODO check this. + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_W_PRECISION + return static_cast((wm1zs[n] + wm1zs[n + 1]) / 2); + } // d2 < 7 + + // Compute bisections is the number of bisections computed from n, + // such that a single application of the fifth-order Schroeder update formula + // after the bisections is enough to evaluate Lambert W-1 with (near?) 53-bit accuracy. + // Fukushima established these by trial and error? + int bisections = 11; // Assume maximum number of bisections will be needed (most common case). + if (n >= 8) + { + bisections = 8; + } + else if (n >= 3) + { + bisections = 9; + } + else if (n >= 2) + { + bisections = 10; + } + // Bracketing, Fukushima section 2.3, page 82: + // (Avoiding using exponential function for speed). + // Only use @c lookup_t precision, default double, for bisection (again for speed), + // and use later Halley refinement for higher precisions. + using detail::lambert_w_lookup::halves; + using detail::lambert_w_lookup::sqrtwm1s; + lookup_t w = -static_cast(n); // Equation 25, + lookup_t y = static_cast(z * wm1es[n - 1]); // Equation 26, + // Perform the bisections fractional bisections for necessary precision. + for (int j = 0; j < bisections; ++j) + { // Equation 27. + lookup_t wj = w - halves[j]; // Subtract 1/2, 1/4, 1/8 ... + lookup_t yj = y * sqrtwm1s[j]; // Multiply by sqrt(1/e), ... + if (wj < yj) + { + w = wj; + y = yj; + } + } // for j + if (d2 <= 10) + { // Only 10 digits2 required (== digits10 = 3 decimal digits precision), + // so just return the nearest bisection. + // (Might make this test depend on size of T?) + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1_BISECTION + std::cout << "W-1 Bisection estimates = " << w << " " << y << std::endl; + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1_BISECTION + return static_cast(w); // Bisection only. + } + else // More than 10 digits2 precision wanted, so continue with Fukushima's Schroeder refinement. + { + T result = static_cast(schroeder_update(w, y)); // Schroeder 5th order method refinement. + if (d2 <= (std::numeric_limits::digits - 3)) + { // Only enough digits2 required, so just return the Schroeder refined value. + // For example, for float, if (d2 <= 22) then + // digits-3 returns Schroeder result up to 3 bits might be wrong. + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + std::cout << "Schroeder refinement estimate = " << result << std::endl; + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_WM1 + return result; // Schroeder only. + } + else // Perform additional Halley refinement(s) to ensure that + // get a near as possible to correct result (usually +/- epsilon). + { + result = halley_update(result, z, pol); + #ifdef BOOST_MATH_INSTRUMENT_LAMBERT_W0_HALLEY + std::cout.precision(std::numeric_limits::max_digits10); + std::cout << "Halley refinement estimate = " << result << std::endl; + std::cout.precision(saved_precision); + #endif // BOOST_MATH_INSTRUMENT_LAMBERT_W0_HALLEY + return result; // Halley + } // schroeder or Schroeder and Halley. + } // more than 10 digits2 + } // template T lambert_wm1_imp(const T z) + } +} // namespace detail //////////////////////////////////////////////////////////// + +// Put user functions in a separate namespace. + +namespace FKDVPB { + // User Lambert W0 and Lambert W-1 functions. + + //! W0 branch, -1/e <= z < max(z) + //! W-1 branch -1/e >= z > min(z) + + //! Lambert W0 using User-defined policy. + template + inline + typename tools::promote_args::type + lambert_w0(T z, const Policy& pol) + { + // Promote integer or expression template arguments to double, + // without doing any other internal promotion like float to double. + typedef typename tools::promote_args::type result_type; + return detail::lambert_w0_imp(result_type(z), pol); // + } // lambert_w0(T z, const Policy& pol) + + //! Lambert W0 using default policy. + template + inline + typename tools::promote_args::type + lambert_w0(T z) + { + typedef typename tools::promote_args::type result_type; + return detail::lambert_w0_imp(result_type(z), policies::policy<>()); + } // lambert_w0(T z) + + + //! W-1 branch (-max(z) < z <= -1/e). + + //! Lambert W-1 using User-defined policy. + template + inline + typename tools::promote_args::type + lambert_wm1(T z, const Policy& pol) + { + // Promote integer or expression template arguments to double, + // without doing any other internal promotion like float to double. + typedef typename tools::promote_args::type result_type; + return detail::lambert_wm1_imp(result_type(z), pol); // + } + + //! Lambert W-1 using default policy. + template + inline + typename tools::promote_args::type + lambert_wm1(T z) + { + typedef typename tools::promote_args::type result_type; + return detail::lambert_wm1_imp(result_type(z), policies::policy<>()); + } // lambert_wm1(T z) + + } // namespace FKDVPB { + + } // namespace math +} // namespace boost + +#endif // #ifndef BOOST_MATH_FKDVPB_LAMBERT_W_HPP diff --git a/include/boost/math/special_functions/math_fwd.hpp b/include/boost/math/special_functions/math_fwd.hpp index 4f44f5611..795620b4e 100644 --- a/include/boost/math/special_functions/math_fwd.hpp +++ b/include/boost/math/special_functions/math_fwd.hpp @@ -1085,6 +1085,27 @@ namespace boost const unsigned number_of_bernoullis_b2n, OutputIterator out_it); + // Lambert W: + template + typename boost::math::tools::promote_args::type lambert_w0(T z, const Policy& pol); + template + typename boost::math::tools::promote_args::type lambert_w0(T z); + template + typename boost::math::tools::promote_args::type lambert_wm1(T z, const Policy& pol); + template + typename boost::math::tools::promote_args::type lambert_wm1(T z); + template + typename boost::math::tools::promote_args::type lambert_w0_prime(T z, const Policy& pol); + template + typename boost::math::tools::promote_args::type lambert_w0_prime(T z); + template + typename boost::math::tools::promote_args::type lambert_wm1_prime(T z, const Policy& pol); + template + typename boost::math::tools::promote_args::type lambert_wm1_prime(T z); + + + + } // namespace math } // namespace boost @@ -1643,6 +1664,12 @@ template \ OutputIterator tangent_t2n(int start_index, unsigned number_of_bernoullis_b2n, OutputIterator out_it)\ { return boost::math::tangent_t2n(start_index, number_of_bernoullis_b2n, out_it, Policy()); }\ \ + template inline typename boost::math::tools::promote_args::type lambert_w0(T z) { return boost::math::lambert_w0(z, Policy()); }\ + template inline typename boost::math::tools::promote_args::type lambert_wm1(T z) { return boost::math::lambert_w0(z, Policy()); }\ + template inline typename boost::math::tools::promote_args::type lambert_w0_prime(T z) { return boost::math::lambert_w0(z, Policy()); }\ + template inline typename boost::math::tools::promote_args::type lambert_wm1_prime(T z) { return boost::math::lambert_w0(z, Policy()); }\ + \ + diff --git a/include/boost/math/special_functions/trunc.hpp b/include/boost/math/special_functions/trunc.hpp index 3f80c96fe..8a7b04244 100644 --- a/include/boost/math/special_functions/trunc.hpp +++ b/include/boost/math/special_functions/trunc.hpp @@ -50,7 +50,7 @@ inline typename tools::promote_args::type trunc(const T& v) // implicit convertion to the integer types. For user-defined // number types this will likely not be the case. In that case // these functions should either be specialized for the UDT in -// question, or else overloads should be placed in the same +// question, or else overloads should be placed in the same // namespace as the UDT: these will then be found via argument // dependent lookup. See our concept archetypes for examples. // diff --git a/include/boost/math/tools/test_value.hpp b/include/boost/math/tools/test_value.hpp new file mode 100644 index 000000000..e597f2ad4 --- /dev/null +++ b/include/boost/math/tools/test_value.hpp @@ -0,0 +1,118 @@ +// Copyright Paul A. Bristow 2017. +// Copyright John Maddock 2017. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_value.hpp + +#ifndef TEST_VALUE_HPP +#define TEST_VALUE_HPP + +// BOOST_MATH_TEST_VALUE is used to create a test value of suitable type from a decimal digit string. +// Two parameters, both a floating-point literal double like 1.23 (not long double so no suffix L) +// and a decimal digit string const char* like "1.23" must be provided. +// The decimal value represented must be the same of course, with at least enough precision for long double. +// Note there are two gotchas to this approach: +// * You need all values to be real floating-point values +// * and *MUST* include a decimal point (to avoid confusion with an integer literal). +// * It's slow to compile compared to a simple literal. + +// Speed is not an issue for a few test values, +// but it's not generally usable in large tables +// where you really need everything to be statically initialized. + +// Macro BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE provides a global diagnostic value for create_type. + +#include // For float_64_t, float128_t. Must be first include! +#include +#include +#include + +#ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE +// global int create_type(0); must be defined before including this file. +#endif + +#ifdef BOOST_HAS_FLOAT128 +typedef __float128 largest_float; +#define BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x) x##Q +#define BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS 113 +#else +typedef long double largest_float; +#define BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x) x##L +#define BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS std::numeric_limits::digits +#endif + +template +inline T create_test_value(largest_float val, const char*, const boost::mpl::true_&, const T2&) +{ // Construct from long double or quad parameter val (ignoring string/const char* str). + // (This is case for MPL parameters = true_ and T2 == false_, + // and MPL parameters = true_ and T2 == true_ cpp_bin_float) + // All built-in/fundamental floating-point types, + // and other User-Defined Types that can be constructed without loss of precision + // from long double suffix L (or quad suffix Q), + // + // Choose this method, even if can be constructed from a string, + // because it will be faster, and more likely to be the closest representation. + // (This is case for MPL parameters = mpl::true_ and T2 == mpl::true_). + #ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE + create_type = 1; + #endif + return static_cast(val); +} + +template +inline T create_test_value(largest_float, const char* str, const boost::mpl::false_&, const boost::mpl::true_&) +{ // Construct from decimal digit string const char* @c str (ignoring long double parameter). + // For example, extended precision or other User-Defined types which ARE constructible from a string + // (but not from double, or long double without loss of precision). + // (This is case for MPL parameters = mpl::false_ and T2 == mpl::true_). + #ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE + create_type = 2; + #endif + return T(str); +} + +template +inline T create_test_value(largest_float, const char* str, const boost::mpl::false_&, const boost::mpl::false_&) +{ // Create test value using from lexical cast of decimal digit string const char* str. + // For example, extended precision or other User-Defined types which are NOT constructible from a string + // (NOR constructible from a long double). + // (This is case T1 = mpl::false and T2 == mpl::false). + #ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE + create_type = 3; + #endif + return boost::lexical_cast(str); +} + +// T real type, x a decimal digits representation of a floating-point, for example: 12.34. +// It must include a decimal point (or it would be interpreted as an integer). + +// x is converted to a long double by appending the letter L (to suit long double fundamental type), 12.34L. +// x is also passed as a const char* or string representation "12.34" +// (to suit most other types that cannot be constructed from long double without possible loss). + +// BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x) makes a long double or quad version, with +// suffix a letter L (or Q) to suit long double (or quad) fundamental type, 12.34L or 12.34Q. +// #x makes a decimal digit string version to suit multiprecision and fixed_point constructors, "12.34". +// (Constructing from double or long double (or quad) could lose precision for multiprecision or fixed-point). + +// The matching create_test_value function above is chosen depending on the T1 and T2 mpl bool truths. +// The string version from #x is used if the precision of T is greater than long double. + +// Example: long double test_value = BOOST_MATH_TEST_VALUE(double, 1.23456789); + +#define BOOST_MATH_TEST_VALUE(T, x) create_test_value(\ + BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x),\ + #x,\ + boost::mpl::bool_<\ + std::numeric_limits::is_specialized &&\ + (std::numeric_limits::radix == 2)\ + && (std::numeric_limits::digits <= BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS)\ + && boost::is_convertible::value>(),\ + boost::mpl::bool_<\ + boost::is_constructible::value>()\ +) +#endif // TEST_VALUE_HPP diff --git a/minimax/Jamfile.v2 b/minimax/Jamfile.v2 index adb924eb0..81790f9a6 100644 --- a/minimax/Jamfile.v2 +++ b/minimax/Jamfile.v2 @@ -1,29 +1,29 @@ # Copyright John Maddock 2010 -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at +# Copyright Paul A. Bristow 2018 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt. -# \math_toolkit\libs\math\test\jamfile.v2 -# Runs all math toolkit tests, functions & distributions, -# and build math examples. +# \math_toolkit\libs\math\minimax\jamfile.v2 +# Runs minimax using multiprecision, (rather than gmp and mpfr) -# bring in the rules for testing +# bring in the rules for testing. import modules ; import path ; -project - : requirements +project + : requirements gcc:-Wno-missing-braces darwin:-Wno-missing-braces acc:+W2068,2461,2236,4070,4069 - intel-win:-nologo - intel-win:-nologo + intel-win:-nologo + intel-win:-nologo msvc:all msvc:on msvc:/wd4996 - msvc:/wd4512 - msvc:/wd4610 - msvc:/wd4510 - msvc:/wd4127 + msvc:/wd4512 + msvc:/wd4610 + msvc:/wd4510 + msvc:/wd4127 msvc:/wd4701 # needed for lexical cast - temporary. static borland:static @@ -32,15 +32,15 @@ project BOOST_UBLAS_UNSUPPORTED_COMPILER=0 . ../include_private - $(ntl-path)/include + #$(ntl-path)/include ; +#lib mpfr : gmp : mpfr ; -lib mpfr : gmp : mpfr ; +#lib gmp : : gmp ; -lib gmp : : gmp ; - -exe minimax : f.cpp main.cpp gmp mpfr ; +# exe minimax : f.cpp main.cpp gmp mpfr ; +exe minimax : f.cpp main.cpp ; install bin : minimax ; diff --git a/minimax/f.cpp b/minimax/f.cpp index 2c95378f1..6ea405800 100644 --- a/minimax/f.cpp +++ b/minimax/f.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -358,6 +359,30 @@ mp_type f(const mp_type& x, int variant) mp_type xx = 1 / x; return boost::math::cyl_bessel_k(1, xx) * sqrt(xx) * exp(xx); } + // Lambert W0 + case 40: + return boost::math::lambert_w0(x); + case 41: + { + if (x == 0) + return 1; + return boost::math::lambert_w0(x) / x; + } + case 42: + { + static const mp_type e1 = exp(mp_type(-1)); + return x / -boost::math::lambert_w0(-e1 + x); + } + case 43: + { + mp_type xx = 1 / x; + return 1 / boost::math::lambert_w0(xx); + } + case 44: + { + mp_type ex = exp(x); + return boost::math::lambert_w0(ex) - x; + } } return 0; } diff --git a/minimax/multiprecision.hpp b/minimax/multiprecision.hpp index f8e3d7b90..2f44ac07b 100644 --- a/minimax/multiprecision.hpp +++ b/minimax/multiprecision.hpp @@ -64,6 +64,57 @@ namespace boost { } } +#elif defined(USE_CPP_BIN_FLOAT_100) + +#include + +typedef boost::multiprecision::cpp_bin_float_100 mp_type; + +inline void set_working_precision(int n) +{ +} + +inline void set_output_precision(int n) +{ + std::cout << std::setprecision(n); + std::cerr << std::setprecision(n); +} + +inline mp_type round_to_precision(mp_type m, int bits) +{ + int i; + mp_type f = frexp(m, &i); + f = ldexp(f, bits); + i -= bits; + f = floor(f); + return ldexp(f, i); +} + +inline int get_working_precision() +{ + return std::numeric_limits::digits; +} + +namespace boost { + namespace math { + namespace tools { + + template <> + inline boost::multiprecision::cpp_bin_float_double_extended real_cast(mp_type val) + { + return boost::multiprecision::cpp_bin_float_double_extended(val); + } + template <> + inline boost::multiprecision::cpp_bin_float_quad real_cast(mp_type val) + { + return boost::multiprecision::cpp_bin_float_quad(val); + } + + } + } +} + + #elif defined(USE_MPFR_100) #include diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index d41173d35..3ec90c4a9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -53,6 +53,7 @@ project msvc:/wd4189 # local variable is initialized but not referenced msvc-7.1:../vc71_fix//vc_fix msvc-7.1:off + clang-6.0.0:off # added to see effect. gcc,windows:off borland:static # msvc:/wd4506 has no effect? @@ -421,6 +422,19 @@ test-suite special_fun : [ run test_instantiate1.cpp test_instantiate2.cpp ] [ run test_jacobi.cpp pch_light ../../test/build//boost_unit_test_framework ] [ run test_laguerre.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] + + [ run test_lambert_w.cpp ../../test/build//boost_unit_test_framework ] + [ run test_lambert_w.cpp ../../test/build//boost_unit_test_framework : : : BOOST_MATH_TEST_MULTIPRECISION=1 [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] : test_lambert_w_multiprecision_1 ] + [ run test_lambert_w.cpp ../../test/build//boost_unit_test_framework : : : BOOST_MATH_TEST_MULTIPRECISION=2 [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] : test_lambert_w_multiprecision_2 ] + [ run test_lambert_w.cpp ../../test/build//boost_unit_test_framework : : : BOOST_MATH_TEST_MULTIPRECISION=3 [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] : test_lambert_w_multiprecision_3 ] + [ run test_lambert_w.cpp ../../test/build//boost_unit_test_framework : : : BOOST_MATH_TEST_MULTIPRECISION=4 BOOST_MATH_TEST_FLOAT128 [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] : test_lambert_w_multiprecision_4 ] + [ run test_lambert_w_integrals_float128.cpp ../../test/build//boost_unit_test_framework : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : "-Bstatic -lquadmath -Bdynamic" : no ] [ requires cxx11_auto_declarations cxx11_lambdas cxx11_smart_ptr cxx11_unified_initialization_syntax sfinae_expr ] ] + [ run test_lambert_w_integrals_quad.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_smart_ptr cxx11_unified_initialization_syntax sfinae_expr ] [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] ] + [ run test_lambert_w_integrals_long_double.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_smart_ptr cxx11_unified_initialization_syntax sfinae_expr ] ] + [ run test_lambert_w_integrals_double.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_smart_ptr cxx11_unified_initialization_syntax sfinae_expr ] ] + [ run test_lambert_w_integrals_float.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx11_auto_declarations cxx11_lambdas cxx11_smart_ptr cxx11_unified_initialization_syntax sfinae_expr ] ] + [ run test_lambert_w_derivative.cpp ../../test/build//boost_unit_test_framework : : : BOOST_MATH_TEST_MULTIPRECISION [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : BOOST_MATH_TEST_FLOAT128 "-Bstatic -lquadmath -Bdynamic" ] ] + [ run test_legendre.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : "-Bstatic -lquadmath -Bdynamic" ] ] [ run chebyshev_test.cpp : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : "-Bstatic -lquadmath -Bdynamic" ] ] [ run chebyshev_transform_test.cpp ../config//fftw3f : : : TEST1 [ requires cxx11_auto_declarations cxx11_range_based_for ] [ check-target-builds ../config//has_fftw3 "libfftw3" : : no ] : chebyshev_transform_test_1 ] diff --git a/test/compile_test/instantiate.hpp b/test/compile_test/instantiate.hpp index 1b779908f..032783ec6 100644 --- a/test/compile_test/instantiate.hpp +++ b/test/compile_test/instantiate.hpp @@ -305,6 +305,9 @@ void instantiate(RealType) boost::math::cyl_bessel_j_zero(v1, i, i, oi); boost::math::cyl_neumann_zero(v1, i); boost::math::cyl_neumann_zero(v1, i, i, oi); + boost::math::lambert_w0(v1); + boost::math::lambert_wm1(v1); + boost::math::lambert_w0_prime(v1); #ifdef TEST_COMPLEX boost::math::cyl_hankel_1(v1, v2); boost::math::cyl_hankel_1(i, v2); @@ -506,6 +509,9 @@ void instantiate(RealType) boost::math::cyl_bessel_j_zero(v1 * 1, i, i, oi); boost::math::cyl_neumann_zero(v1 * 1, i); boost::math::cyl_neumann_zero(v1 * 1, i, i, oi); + boost::math::lambert_w0(v1 * 1); + boost::math::lambert_wm1(v1 * 1); + boost::math::lambert_w0_prime(v1 * 1); #ifdef TEST_COMPLEX boost::math::cyl_hankel_1(v1, v2); boost::math::cyl_hankel_1(i, v2); @@ -685,6 +691,9 @@ void instantiate(RealType) boost::math::cyl_bessel_j_zero(v1, i, i, oi, pol); boost::math::cyl_neumann_zero(v1, i, pol); boost::math::cyl_neumann_zero(v1, i, i, oi, pol); + boost::math::lambert_w0(v1, pol); + boost::math::lambert_wm1(v1, pol); + boost::math::lambert_w0_prime(v1, pol); #ifdef TEST_COMPLEX boost::math::cyl_hankel_1(v1, v2, pol); boost::math::cyl_hankel_1(i, v2, pol); @@ -886,6 +895,9 @@ void instantiate(RealType) test::cyl_bessel_j_zero(v1, i, i, oi); test::cyl_neumann_zero(v1, i); test::cyl_neumann_zero(v1, i, i, oi); + test::lambert_w0(v1); + test::lambert_wm1(v1); + test::lambert_w0_prime(v1); #ifdef TEST_COMPLEX test::cyl_hankel_1(v1, v2); test::cyl_hankel_1(i, v2); @@ -1264,6 +1276,9 @@ void instantiate_mixed(RealType) boost::math::sph_neumann_prime(i, i, pol); boost::math::owens_t(fr, dr, pol); boost::math::owens_t(i, s, pol); + boost::math::lambert_w0(i, pol); + boost::math::lambert_wm1(i, pol); + boost::math::lambert_w0_prime(i, pol); #endif #ifdef TEST_GROUP_8 test::tgamma(i); @@ -1410,6 +1425,9 @@ void instantiate_mixed(RealType) test::airy_bi_prime(i); test::owens_t(fr, dr); test::owens_t(i, s); + boost::math::lambert_w0(i); + boost::math::lambert_wm1(i); + boost::math::lambert_w0_prime(i); #endif #endif } diff --git a/test/compile_test/std_real_concept_check.cpp b/test/compile_test/std_real_concept_check.cpp index a83297a79..20638e375 100644 --- a/test/compile_test/std_real_concept_check.cpp +++ b/test/compile_test/std_real_concept_check.cpp @@ -48,6 +48,7 @@ struct numeric_limits static boost::math::concepts::std_real_concept max NULL_MACRO() throw(); static const int digits = 24; static const int digits10 = 6; + static const int max_digits10 = 9; static const bool is_signed = true; static const bool is_integer = false; static const bool is_exact = false; @@ -86,6 +87,7 @@ struct numeric_limits static boost::math::concepts::std_real_concept max NULL_MACRO() throw(); static const int digits = 53; static const int digits10 = 15; + static const int max_digits10 = 17; static const bool is_signed = true; static const bool is_integer = false; static const bool is_exact = false; @@ -124,6 +126,7 @@ struct numeric_limits static boost::math::concepts::std_real_concept max NULL_MACRO() throw(); static const int digits = 64; static const int digits10 = 18; + static const int max_digits10 = 22; static const bool is_signed = true; static const bool is_integer = false; static const bool is_exact = false; @@ -162,6 +165,7 @@ struct numeric_limits static boost::math::concepts::std_real_concept max NULL_MACRO() throw(); static const int digits = 113; static const int digits10 = 33; + static const int max_digits10 = 37; static const bool is_signed = true; static const bool is_integer = false; static const bool is_exact = false; diff --git a/test/lambert_w_high_reference_values.ipp b/test/lambert_w_high_reference_values.ipp new file mode 100644 index 000000000..bcbd824ba --- /dev/null +++ b/test/lambert_w_high_reference_values.ipp @@ -0,0 +1,933 @@ + + +// A collection of big Lambert W test values computed using 100 decimal digits precision. +// C++ floating-point type is RealType. + +// Written by I:\modular-boost\libs\math\test\lambert_w_high_reference_values.cpp Tue Oct 10 14:32:47 2017 + +// Copyright Paul A. Bristow 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Size of arrays of arguments z and Lambert W +static const unsigned int noof_tests = 450; + +// Declare arrays of arguments z and Lambert W(z) + +template +static RealType zs[450]; + +template +static RealType ws[450]; +// The values are defined using the macro BOOST_MATH_TEST_VALUE to ensure +// that both built-in and multiprecision types are correctly initialiased with full precision. +// built-in types like float, double require a floating-point literal like 3.14, +// but multiprecision types require a decimal digit string like "3.14". +// Numerical values are chosen to avoid exactly representable values. + +template +void init_zws() +{ + zs[0] = BOOST_MATH_TEST_VALUE(RealType, 0.5999999999999999777955395074968691915273666381835937500000000000000000000000000000000000000000000000); + ws[0] = BOOST_MATH_TEST_VALUE(RealType, 0.4015636367870725840596232119093618401250821463765781307144089819032551434525179709162089487011544152); + zs[1] = BOOST_MATH_TEST_VALUE(RealType, 1.699999999999999955591079014993738383054733276367187500000000000000000000000000000000000000000000000); + ws[1] = BOOST_MATH_TEST_VALUE(RealType, 0.7796011225311008662356536916883580556792500749037209859530390902424444585607630246126725241921761054); + zs[2] = BOOST_MATH_TEST_VALUE(RealType, 2.199999999999999955591079014993738383054733276367187500000000000000000000000000000000000000000000000); + ws[2] = BOOST_MATH_TEST_VALUE(RealType, 0.8970741340486472832529094012483877796469756302241005331449133504770916140694317158875490176641388180); + zs[3] = BOOST_MATH_TEST_VALUE(RealType, 2.699999999999999955591079014993738383054733276367187500000000000000000000000000000000000000000000000); + ws[3] = BOOST_MATH_TEST_VALUE(RealType, 0.9966287342654774578203091807272871494846015619689842252588224928604557045580020903399140826514419525); + zs[4] = BOOST_MATH_TEST_VALUE(RealType, 3.199999999999999955591079014993738383054733276367187500000000000000000000000000000000000000000000000); + ws[4] = BOOST_MATH_TEST_VALUE(RealType, 1.083216216147652072726562700563454011684986526601600660806443834509711499483389930134543612892458143); + zs[5] = BOOST_MATH_TEST_VALUE(RealType, 3.699999999999999955591079014993738383054733276367187500000000000000000000000000000000000000000000000); + ws[5] = BOOST_MATH_TEST_VALUE(RealType, 1.159953178612022143701086341067113318247085320992117266262618001908576504625710753138135645536760705); + zs[6] = BOOST_MATH_TEST_VALUE(RealType, 7.899999999999999911182158029987476766109466552734375000000000000000000000000000000000000000000000000); + ws[6] = BOOST_MATH_TEST_VALUE(RealType, 1.598067606240196984785534680781403865575242180069554020841247076138095065416648450279328891063284787); + zs[7] = BOOST_MATH_TEST_VALUE(RealType, 8.399999999999999911182158029987476766109466552734375000000000000000000000000000000000000000000000000); + ws[7] = BOOST_MATH_TEST_VALUE(RealType, 1.635986015616761306128656680352102711658504330413761752847915150776296724715125185878789170353595843); + zs[8] = BOOST_MATH_TEST_VALUE(RealType, 8.899999999999999911182158029987476766109466552734375000000000000000000000000000000000000000000000000); + ws[8] = BOOST_MATH_TEST_VALUE(RealType, 1.672019249364215143714449427934723990551745560176619694762404483433765553308432498126823728454150807); + zs[9] = BOOST_MATH_TEST_VALUE(RealType, 9.399999999999999911182158029987476766109466552734375000000000000000000000000000000000000000000000000); + ws[9] = BOOST_MATH_TEST_VALUE(RealType, 1.706351956427362203924265356724807963736463682858697517135613748929048683757986880699421428246987716); + zs[10] = BOOST_MATH_TEST_VALUE(RealType, 9.899999999999999911182158029987476766109466552734375000000000000000000000000000000000000000000000000); + ws[10] = BOOST_MATH_TEST_VALUE(RealType, 1.739142551733351601404562031720166836256093578556407891184787967858395290655747464534386031726054901); + zs[11] = BOOST_MATH_TEST_VALUE(RealType, 20.29999999999999982236431605997495353221893310546875000000000000000000000000000000000000000000000000); + ws[11] = BOOST_MATH_TEST_VALUE(RealType, 2.215253873529547121281024099654049024258898512352506053956048219674735189566735605935868519028886428); + zs[12] = BOOST_MATH_TEST_VALUE(RealType, 20.79999999999999982236431605997495353221893310546875000000000000000000000000000000000000000000000000); + ws[12] = BOOST_MATH_TEST_VALUE(RealType, 2.232037942848570707824689153745947970289311384094147352193787995777039984911477501437729555943891179); + zs[13] = BOOST_MATH_TEST_VALUE(RealType, 21.29999999999999982236431605997495353221893310546875000000000000000000000000000000000000000000000000); + ws[13] = BOOST_MATH_TEST_VALUE(RealType, 2.248461062664032810797682669829614177334474567497410976269795770382256291948680343286055598456930946); + zs[14] = BOOST_MATH_TEST_VALUE(RealType, 21.79999999999999982236431605997495353221893310546875000000000000000000000000000000000000000000000000); + ws[14] = BOOST_MATH_TEST_VALUE(RealType, 2.264538836003641674575766487227130628592151315874095763183468118573707558576164351416415105283538250); + zs[15] = BOOST_MATH_TEST_VALUE(RealType, 22.29999999999999982236431605997495353221893310546875000000000000000000000000000000000000000000000000); + ws[15] = BOOST_MATH_TEST_VALUE(RealType, 2.280285864286149926152034464925849817265625703056626970221292432804223262557041139245862876595960246); + zs[16] = BOOST_MATH_TEST_VALUE(RealType, 45.09999999999999964472863211994990706443786621093750000000000000000000000000000000000000000000000000); + ws[16] = BOOST_MATH_TEST_VALUE(RealType, 2.784730975471531592298228296023808374578957621993717936291403085791375505867336041880555203144734624); + zs[17] = BOOST_MATH_TEST_VALUE(RealType, 45.59999999999999964472863211994990706443786621093750000000000000000000000000000000000000000000000000); + ws[17] = BOOST_MATH_TEST_VALUE(RealType, 2.792846418923343663453351656549736190861671652329623509279809945695836105896108078166742811990549890); + zs[18] = BOOST_MATH_TEST_VALUE(RealType, 46.09999999999999964472863211994990706443786621093750000000000000000000000000000000000000000000000000); + ws[18] = BOOST_MATH_TEST_VALUE(RealType, 2.800879481577357402817907432620737944445117856442267934361375865486347844977822691465905776042660153); + zs[19] = BOOST_MATH_TEST_VALUE(RealType, 46.59999999999999964472863211994990706443786621093750000000000000000000000000000000000000000000000000); + ws[19] = BOOST_MATH_TEST_VALUE(RealType, 2.808831854396542967901795549912651228215931108039763441789870364477609029775521508274395566200250327); + zs[20] = BOOST_MATH_TEST_VALUE(RealType, 47.09999999999999964472863211994990706443786621093750000000000000000000000000000000000000000000000000); + ws[20] = BOOST_MATH_TEST_VALUE(RealType, 2.816705176341098453894950633365766251645928038203952811501285665902169257597881259137118085186909385); + zs[21] = BOOST_MATH_TEST_VALUE(RealType, 94.69999999999999928945726423989981412887573242187500000000000000000000000000000000000000000000000000); + ws[21] = BOOST_MATH_TEST_VALUE(RealType, 3.343650750934026694453868349588423323594970057683998463032955783031328100668795635364027627344237744); + zs[22] = BOOST_MATH_TEST_VALUE(RealType, 95.19999999999999928945726423989981412887573242187500000000000000000000000000000000000000000000000000); + ws[22] = BOOST_MATH_TEST_VALUE(RealType, 3.347704927126314354014952127881909800322435946123852141764728005964807369865451182872647651261526300); + zs[23] = BOOST_MATH_TEST_VALUE(RealType, 95.69999999999999928945726423989981412887573242187500000000000000000000000000000000000000000000000000); + ws[23] = BOOST_MATH_TEST_VALUE(RealType, 3.351738986777429550093016062864573016038852034507491269297237351947364962103400790753443005963042748); + zs[24] = BOOST_MATH_TEST_VALUE(RealType, 96.19999999999999928945726423989981412887573242187500000000000000000000000000000000000000000000000000); + ws[24] = BOOST_MATH_TEST_VALUE(RealType, 3.355753131971562146591617562968067128167583310591520027232903513337163599725129242852363808925517286); + zs[25] = BOOST_MATH_TEST_VALUE(RealType, 96.69999999999999928945726423989981412887573242187500000000000000000000000000000000000000000000000000); + ws[25] = BOOST_MATH_TEST_VALUE(RealType, 3.359747561740841558826391853676861600267695314122615815793967179881207925371616384184099572300595115); + zs[26] = BOOST_MATH_TEST_VALUE(RealType, 193.8999999999999985789145284797996282577514648437500000000000000000000000000000000000000000000000000); + ws[26] = BOOST_MATH_TEST_VALUE(RealType, 3.905067494884853812892253268046191881524005958444131697564696414502793024379297479024372424749155738); + zs[27] = BOOST_MATH_TEST_VALUE(RealType, 194.3999999999999985789145284797996282577514648437500000000000000000000000000000000000000000000000000); + ws[27] = BOOST_MATH_TEST_VALUE(RealType, 3.907117899842841728838668195110205529083454921828289550856799078486258603076095983089478749398791291); + zs[28] = BOOST_MATH_TEST_VALUE(RealType, 194.8999999999999985789145284797996282577514648437500000000000000000000000000000000000000000000000000); + ws[28] = BOOST_MATH_TEST_VALUE(RealType, 3.909163256350964853423766659493861241213806026764267552120249985812650869611378163906691630909022161); + zs[29] = BOOST_MATH_TEST_VALUE(RealType, 195.3999999999999985789145284797996282577514648437500000000000000000000000000000000000000000000000000); + ws[29] = BOOST_MATH_TEST_VALUE(RealType, 3.911203589561939804120818106435026078510094046010650488053868908640181610985462139185885264355418070); + zs[30] = BOOST_MATH_TEST_VALUE(RealType, 195.8999999999999985789145284797996282577514648437500000000000000000000000000000000000000000000000000); + ws[30] = BOOST_MATH_TEST_VALUE(RealType, 3.913238924439855435510247090467540747905833384629853846395096935851013937767959235773727554750691716); + zs[31] = BOOST_MATH_TEST_VALUE(RealType, 392.2999999999999971578290569595992565155029296875000000000000000000000000000000000000000000000000000); + ws[31] = BOOST_MATH_TEST_VALUE(RealType, 4.473790759373999449370836932900380698603499930598355812583693103082314531706325269694063530407295371); + zs[32] = BOOST_MATH_TEST_VALUE(RealType, 392.7999999999999971578290569595992565155029296875000000000000000000000000000000000000000000000000000); + ws[32] = BOOST_MATH_TEST_VALUE(RealType, 4.474831809850706594148527116907834093420614735070734923094248837531977152944616462128228522597237267); + zs[33] = BOOST_MATH_TEST_VALUE(RealType, 393.2999999999999971578290569595992565155029296875000000000000000000000000000000000000000000000000000); + ws[33] = BOOST_MATH_TEST_VALUE(RealType, 4.475871580159411118557716684955449097432917202595361508172914971791116938813457453917365177551691682); + zs[34] = BOOST_MATH_TEST_VALUE(RealType, 393.7999999999999971578290569595992565155029296875000000000000000000000000000000000000000000000000000); + ws[34] = BOOST_MATH_TEST_VALUE(RealType, 4.476910073482067128895548362807270116617425992201334451790294796151311859601136186866422234634474300); + zs[35] = BOOST_MATH_TEST_VALUE(RealType, 394.2999999999999971578290569595992565155029296875000000000000000000000000000000000000000000000000000); + ws[35] = BOOST_MATH_TEST_VALUE(RealType, 4.477947292988721649990662195165267393745885698851422229805500000725367834662498733709056508177586635); + zs[36] = BOOST_MATH_TEST_VALUE(RealType, 789.0999999999999943156581139191985130310058593750000000000000000000000000000000000000000000000000000); + ws[36] = BOOST_MATH_TEST_VALUE(RealType, 5.051256108760896701894497014701900464234767080894666089938550993707446937559086016796794249885331870); + zs[37] = BOOST_MATH_TEST_VALUE(RealType, 789.5999999999999943156581139191985130310058593750000000000000000000000000000000000000000000000000000); + ws[37] = BOOST_MATH_TEST_VALUE(RealType, 5.051784868057603576951613257575983601901987808830448747962646970040648657993520282283444052127887316); + zs[38] = BOOST_MATH_TEST_VALUE(RealType, 790.0999999999999943156581139191985130310058593750000000000000000000000000000000000000000000000000000); + ws[38] = BOOST_MATH_TEST_VALUE(RealType, 5.052313301769511279751645919401950992205691275454166720959640667514665636089541176437079485020106302); + zs[39] = BOOST_MATH_TEST_VALUE(RealType, 790.5999999999999943156581139191985130310058593750000000000000000000000000000000000000000000000000000); + ws[39] = BOOST_MATH_TEST_VALUE(RealType, 5.052841410301356078567942312344300154667895407100576839086958094315459736887113324816418833061471414); + zs[40] = BOOST_MATH_TEST_VALUE(RealType, 791.0999999999999943156581139191985130310058593750000000000000000000000000000000000000000000000000000); + ws[40] = BOOST_MATH_TEST_VALUE(RealType, 5.053369194057116916125095352093384287199494222654442252497641737159855576046108292479434106853949424); + zs[41] = BOOST_MATH_TEST_VALUE(RealType, 1582.699999999999988631316227838397026062011718750000000000000000000000000000000000000000000000000000); + ws[41] = BOOST_MATH_TEST_VALUE(RealType, 5.637454835061548840266762838608075961241621341710147974330149448636747596037634477093772466715414237); + zs[42] = BOOST_MATH_TEST_VALUE(RealType, 1583.199999999999988631316227838397026062011718750000000000000000000000000000000000000000000000000000); + ws[42] = BOOST_MATH_TEST_VALUE(RealType, 5.637723113558296965227371679987485292172631743514584329014143690566703873701821724552724491259434917); + zs[43] = BOOST_MATH_TEST_VALUE(RealType, 1583.699999999999988631316227838397026062011718750000000000000000000000000000000000000000000000000000); + ws[43] = BOOST_MATH_TEST_VALUE(RealType, 5.637991309264171414360659662528863849150184273111159470910102371892495978961903358821307687677635837); + zs[44] = BOOST_MATH_TEST_VALUE(RealType, 1584.199999999999988631316227838397026062011718750000000000000000000000000000000000000000000000000000); + ws[44] = BOOST_MATH_TEST_VALUE(RealType, 5.638259422230692585112176850810679719906543165397130936013277468573934642794738015313302920143170105); + zs[45] = BOOST_MATH_TEST_VALUE(RealType, 1584.699999999999988631316227838397026062011718750000000000000000000000000000000000000000000000000000); + ws[45] = BOOST_MATH_TEST_VALUE(RealType, 5.638527452509332631758526797455952459448323578747282813479742362912515476029842704193513987433394644); + zs[46] = BOOST_MATH_TEST_VALUE(RealType, 3169.899999999999977262632455676794052124023437500000000000000000000000000000000000000000000000000000); + ws[46] = BOOST_MATH_TEST_VALUE(RealType, 6.231791473267630119617879242959215790063493036893226016268939602113573649847295482162112707750032205); + zs[47] = BOOST_MATH_TEST_VALUE(RealType, 3170.399999999999977262632455676794052124023437500000000000000000000000000000000000000000000000000000); + ws[47] = BOOST_MATH_TEST_VALUE(RealType, 6.231927385287213295870608336780291881793156334477048126729390997875392137109426481813143445559537548); + zs[48] = BOOST_MATH_TEST_VALUE(RealType, 3170.899999999999977262632455676794052124023437500000000000000000000000000000000000000000000000000000); + ws[48] = BOOST_MATH_TEST_VALUE(RealType, 6.232063276283731898910298571907046320310117335045161587714712693774590117520030116772150624981197754); + zs[49] = BOOST_MATH_TEST_VALUE(RealType, 3171.399999999999977262632455676794052124023437500000000000000000000000000000000000000000000000000000); + ws[49] = BOOST_MATH_TEST_VALUE(RealType, 6.232199146263736642645449823754129665166787360756180379390602947566154676495717638352300033758537319); + zs[50] = BOOST_MATH_TEST_VALUE(RealType, 3171.899999999999977262632455676794052124023437500000000000000000000000000000000000000000000000000000); + ws[50] = BOOST_MATH_TEST_VALUE(RealType, 6.232334995233775170638824027293638315162522524016381054824777727129130712726670908695639061242466679); + zs[51] = BOOST_MATH_TEST_VALUE(RealType, 6344.299999999999954525264911353588104248046875000000000000000000000000000000000000000000000000000000); + ws[51] = BOOST_MATH_TEST_VALUE(RealType, 6.833478246863882246641726870717734354293613258312600642204509694006101911629409462573891987188902394); + zs[52] = BOOST_MATH_TEST_VALUE(RealType, 6344.799999999999954525264911353588104248046875000000000000000000000000000000000000000000000000000000); + ws[52] = BOOST_MATH_TEST_VALUE(RealType, 6.833546994320183648585668463184962739224514055009742694319567301283431438840148992714833003589320204); + zs[53] = BOOST_MATH_TEST_VALUE(RealType, 6345.299999999999954525264911353588104248046875000000000000000000000000000000000000000000000000000000); + ws[53] = BOOST_MATH_TEST_VALUE(RealType, 6.833615736447355580644330241871199203480309206612453771835338393474744796397760691906867357518286300); + zs[54] = BOOST_MATH_TEST_VALUE(RealType, 6345.799999999999954525264911353588104248046875000000000000000000000000000000000000000000000000000000); + ws[54] = BOOST_MATH_TEST_VALUE(RealType, 6.833684473246229472880513750644790479773339419112019177698673979527062571403548057152859520971070532); + zs[55] = BOOST_MATH_TEST_VALUE(RealType, 6346.299999999999954525264911353588104248046875000000000000000000000000000000000000000000000000000000); + ws[55] = BOOST_MATH_TEST_VALUE(RealType, 6.833753204717636560302304979968870884092145760178319785601850619423465965447586003582575169154068642); + zs[56] = BOOST_MATH_TEST_VALUE(RealType, 12693.09999999999990905052982270717620849609375000000000000000000000000000000000000000000000000000000); + ws[56] = BOOST_MATH_TEST_VALUE(RealType, 7.441712782644182656362567954733218054310136462342348107691598780010100425506115311857309428345451583); + zs[57] = BOOST_MATH_TEST_VALUE(RealType, 12693.59999999999990905052982270717620849609375000000000000000000000000000000000000000000000000000000); + ws[57] = BOOST_MATH_TEST_VALUE(RealType, 7.441747507160214264985418199644937473985616288224663570878672057496608603535012780430207292902251849); + zs[58] = BOOST_MATH_TEST_VALUE(RealType, 12694.09999999999990905052982270717620849609375000000000000000000000000000000000000000000000000000000); + ws[58] = BOOST_MATH_TEST_VALUE(RealType, 7.441782230327669457854270012077867036991216446419568353636383337201042455550854674638301527917381460); + zs[59] = BOOST_MATH_TEST_VALUE(RealType, 12694.59999999999990905052982270717620849609375000000000000000000000000000000000000000000000000000000); + ws[59] = BOOST_MATH_TEST_VALUE(RealType, 7.441816952146653566134582734750438863756337927374408689634478920961001428844890048307273438220285103); + zs[60] = BOOST_MATH_TEST_VALUE(RealType, 12695.09999999999990905052982270717620849609375000000000000000000000000000000000000000000000000000000); + ws[60] = BOOST_MATH_TEST_VALUE(RealType, 7.441851672617271908624631672809964354572668086864496527202255560220860635169893350319392062176172956); + zs[61] = BOOST_MATH_TEST_VALUE(RealType, 25390.69999999999981810105964541435241699218750000000000000000000000000000000000000000000000000000000); + ws[61] = BOOST_MATH_TEST_VALUE(RealType, 8.055751887730669404078767770906158058132806188168812278807914210510560983286915475256358360988722249); + zs[62] = BOOST_MATH_TEST_VALUE(RealType, 25391.19999999999981810105964541435241699218750000000000000000000000000000000000000000000000000000000); + ws[62] = BOOST_MATH_TEST_VALUE(RealType, 8.055769405252722224984585767512155671001638813031160420183226603705765994004819084590112882738598672); + zs[63] = BOOST_MATH_TEST_VALUE(RealType, 25391.69999999999981810105964541435241699218750000000000000000000000000000000000000000000000000000000); + ws[63] = BOOST_MATH_TEST_VALUE(RealType, 8.055786922434032119761416627590837471869427434519755833207927697913643222261584517182449368833198187); + zs[64] = BOOST_MATH_TEST_VALUE(RealType, 25392.19999999999981810105964541435241699218750000000000000000000000000000000000000000000000000000000); + ws[64] = BOOST_MATH_TEST_VALUE(RealType, 8.055804439274612409649066171031328348764224415259811919589560542548689157122598047317682477881428519); + zs[65] = BOOST_MATH_TEST_VALUE(RealType, 25392.69999999999981810105964541435241699218750000000000000000000000000000000000000000000000000000000); + ws[65] = BOOST_MATH_TEST_VALUE(RealType, 8.055821955774476415104661363464701307020928082642356236080373490396966544402792637267897239283500728); + zs[66] = BOOST_MATH_TEST_VALUE(RealType, 50785.89999999999963620211929082870483398437500000000000000000000000000000000000000000000000000000000); + ws[66] = BOOST_MATH_TEST_VALUE(RealType, 8.674936078635983017984123951954366186973716699495736629864279614242284857757227515949355498579742855); + zs[67] = BOOST_MATH_TEST_VALUE(RealType, 50786.39999999999963620211929082870483398437500000000000000000000000000000000000000000000000000000000); + ws[67] = BOOST_MATH_TEST_VALUE(RealType, 8.674944906241453706059825549964349902974004039927040359264744515026031399261972506772892769382818801); + zs[68] = BOOST_MATH_TEST_VALUE(RealType, 50786.89999999999963620211929082870483398437500000000000000000000000000000000000000000000000000000000); + ws[68] = BOOST_MATH_TEST_VALUE(RealType, 8.674953733760944136535950689333928914142360745655931032041490757092023706394578714640748394257288035); + zs[69] = BOOST_MATH_TEST_VALUE(RealType, 50787.39999999999963620211929082870483398437500000000000000000000000000000000000000000000000000000000); + ws[69] = BOOST_MATH_TEST_VALUE(RealType, 8.674962561194455991628227539852952953077017660325256919933499525371091080399080487843972214866128384); + zs[70] = BOOST_MATH_TEST_VALUE(RealType, 50787.89999999999963620211929082870483398437500000000000000000000000000000000000000000000000000000000); + ws[70] = BOOST_MATH_TEST_VALUE(RealType, 8.674971388541990953502931535257895802702731784533955912147060505216027254503478909260086842672435376); + zs[71] = BOOST_MATH_TEST_VALUE(RealType, 101576.2999999999992724042385816574096679687500000000000000000000000000000000000000000000000000000000); + ws[71] = BOOST_MATH_TEST_VALUE(RealType, 9.298691796027940703796635264110043143787629375260023629778966472817740701497549236671005785312535137); + zs[72] = BOOST_MATH_TEST_VALUE(RealType, 101576.7999999999992724042385816574096679687500000000000000000000000000000000000000000000000000000000); + ws[72] = BOOST_MATH_TEST_VALUE(RealType, 9.298696240460783074349599408079465706595862952879886273836096426172349132276424038993850275528909958); + zs[73] = BOOST_MATH_TEST_VALUE(RealType, 101577.2999999999992724042385816574096679687500000000000000000000000000000000000000000000000000000000); + ws[73] = BOOST_MATH_TEST_VALUE(RealType, 9.298700684871954559132336105768526810554925567844384854912235341295989207897852971537611849804454104); + zs[74] = BOOST_MATH_TEST_VALUE(RealType, 101577.7999999999992724042385816574096679687500000000000000000000000000000000000000000000000000000000); + ws[74] = BOOST_MATH_TEST_VALUE(RealType, 9.298705129261455370304348674457005008539407510001142864340543902043967324875967473097043636378431136); + zs[75] = BOOST_MATH_TEST_VALUE(RealType, 101578.2999999999992724042385816574096679687500000000000000000000000000000000000000000000000000000000); + ws[75] = BOOST_MATH_TEST_VALUE(RealType, 9.298709573629285720022020159137488905547397121761906103664298171650800509043504137427221381746219971); + zs[76] = BOOST_MATH_TEST_VALUE(RealType, 203157.0999999999985448084771633148193359375000000000000000000000000000000000000000000000000000000000); + ws[76] = BOOST_MATH_TEST_VALUE(RealType, 9.926524439637924502814640096432844232405010548632138451412344878257773957471037720806722776924379539); + zs[77] = BOOST_MATH_TEST_VALUE(RealType, 203157.5999999999985448084771633148193359375000000000000000000000000000000000000000000000000000000000); + ws[77] = BOOST_MATH_TEST_VALUE(RealType, 9.926526675539305999408212665862555525393765588240345090590079743764108100617622492751979808963302929); + zs[78] = BOOST_MATH_TEST_VALUE(RealType, 203158.0999999999985448084771633148193359375000000000000000000000000000000000000000000000000000000000); + ws[78] = BOOST_MATH_TEST_VALUE(RealType, 9.926528911435230720557328431324006644047736665798752128461117587165401958858923779103702189649205849); + zs[79] = BOOST_MATH_TEST_VALUE(RealType, 203158.5999999999985448084771633148193359375000000000000000000000000000000000000000000000000000000000); + ws[79] = BOOST_MATH_TEST_VALUE(RealType, 9.926531147325698692990351270769682423599587366983008197322280565618502972980495987784743296088232692); + zs[80] = BOOST_MATH_TEST_VALUE(RealType, 203159.0999999999985448084771633148193359375000000000000000000000000000000000000000000000000000000000); + ws[80] = BOOST_MATH_TEST_VALUE(RealType, 9.926533383210709943435448417027560593313394713874207050608325275972416603539435687443617960861779968); + zs[81] = BOOST_MATH_TEST_VALUE(RealType, 406318.6999999999970896169543266296386718750000000000000000000000000000000000000000000000000000000000); + ws[81] = BOOST_MATH_TEST_VALUE(RealType, 10.55800844020678300858930169345380918056498196515847559789815847210932705985905912177836375627483410); + zs[82] = BOOST_MATH_TEST_VALUE(RealType, 406319.1999999999970896169543266296386718750000000000000000000000000000000000000000000000000000000000); + ws[82] = BOOST_MATH_TEST_VALUE(RealType, 10.55800956429896255310197650482898317196991260081559124902272662470413320348092630336068243562420612); + zs[83] = BOOST_MATH_TEST_VALUE(RealType, 406319.6999999999970896169543266296386718750000000000000000000000000000000000000000000000000000000000); + ws[83] = BOOST_MATH_TEST_VALUE(RealType, 10.55801068838976919073174818917086439876248092007955187694896892558084843931726958729264479171526918); + zs[84] = BOOST_MATH_TEST_VALUE(RealType, 406320.1999999999970896169543266296386718750000000000000000000000000000000000000000000000000000000000); + ws[84] = BOOST_MATH_TEST_VALUE(RealType, 10.55801181247920292484283725870748463023358051483533901203380901404123382789184489956279108152225757); + zs[85] = BOOST_MATH_TEST_VALUE(RealType, 406320.6999999999970896169543266296386718750000000000000000000000000000000000000000000000000000000000); + ws[85] = BOOST_MATH_TEST_VALUE(RealType, 10.55801293656726375879945184504060224737500313478807503007007909767698116683614226527139047633989602); + zs[86] = BOOST_MATH_TEST_VALUE(RealType, 812641.8999999999941792339086532592773437500000000000000000000000000000000000000000000000000000000000); + ws[86] = BOOST_MATH_TEST_VALUE(RealType, 11.19277715105303335957380751089185510982783382888615163475399731608447432939046539739780186010205819); + zs[87] = BOOST_MATH_TEST_VALUE(RealType, 812642.3999999999941792339086532592773437500000000000000000000000000000000000000000000000000000000000); + ws[87] = BOOST_MATH_TEST_VALUE(RealType, 11.19277771586759068427927907791232978730718798905802091714667366555094578546313245757715733663116681); + zs[88] = BOOST_MATH_TEST_VALUE(RealType, 812642.8999999999941792339086532592773437500000000000000000000000000000000000000000000000000000000000); + ws[88] = BOOST_MATH_TEST_VALUE(RealType, 11.19277828068180282941234300813602678219028255437023366225732009891673392268727353365317316060216307); + zs[89] = BOOST_MATH_TEST_VALUE(RealType, 812643.3999999999941792339086532592773437500000000000000000000000000000000000000000000000000000000000); + ws[89] = BOOST_MATH_TEST_VALUE(RealType, 11.19277884549566979539611569279587635086022997209586259427933274111486838234969448101548392041897529); + zs[90] = BOOST_MATH_TEST_VALUE(RealType, 812643.8999999999941792339086532592773437500000000000000000000000000000000000000000000000000000000000); + ws[90] = BOOST_MATH_TEST_VALUE(RealType, 11.19277941030919158265371274430325256214137887195738215091099754333691681726015244339566835725917388); + zs[91] = BOOST_MATH_TEST_VALUE(RealType, 1625288.299999999988358467817306518554687500000000000000000000000000000000000000000000000000000000000); + ws[91] = BOOST_MATH_TEST_VALUE(RealType, 11.83051367496350653298055098412234220179378826002120373759410524883137483448021534372309124250280047); + zs[92] = BOOST_MATH_TEST_VALUE(RealType, 1625288.799999999988358467817306518554687500000000000000000000000000000000000000000000000000000000000); + ws[92] = BOOST_MATH_TEST_VALUE(RealType, 11.83051395862415209457082318977971516657969184941726425523617075901029866766828046681793457310563883); + zs[93] = BOOST_MATH_TEST_VALUE(RealType, 1625289.299999999988358467817306518554687500000000000000000000000000000000000000000000000000000000000); + ws[93] = BOOST_MATH_TEST_VALUE(RealType, 11.83051424228471092157568395260197960555928287686416745291625975894259102434581858031780382895984480); + zs[94] = BOOST_MATH_TEST_VALUE(RealType, 1625289.799999999988358467817306518554687500000000000000000000000000000000000000000000000000000000000); + ws[94] = BOOST_MATH_TEST_VALUE(RealType, 11.83051452594518301404831336917713066668156156528094910312372347647232811359765848597839160347350790); + zs[95] = BOOST_MATH_TEST_VALUE(RealType, 1625290.299999999988358467817306518554687500000000000000000000000000000000000000000000000000000000000); + ws[95] = BOOST_MATH_TEST_VALUE(RealType, 11.83051480960556837204189148713503938528677530351797077101569714537002853052165595070586086051268476); + zs[96] = BOOST_MATH_TEST_VALUE(RealType, 3250581.099999999976716935634613037109375000000000000000000000000000000000000000000000000000000000000); + ws[96] = BOOST_MATH_TEST_VALUE(RealType, 12.47094296296818886963926120402441135953658337315101944303084986533714570937576415675358344893956724); + zs[97] = BOOST_MATH_TEST_VALUE(RealType, 3250581.599999999976716935634613037109375000000000000000000000000000000000000000000000000000000000000); + ws[97] = BOOST_MATH_TEST_VALUE(RealType, 12.47094310536827791354684005276581162972597260940308218544073416384666193653508004167640448380153389); + zs[98] = BOOST_MATH_TEST_VALUE(RealType, 3250582.099999999976716935634613037109375000000000000000000000000000000000000000000000000000000000000); + ws[98] = BOOST_MATH_TEST_VALUE(RealType, 12.47094324776834517437426924689730750513918775389042001111365883776867938003401942562632403665303867); + zs[99] = BOOST_MATH_TEST_VALUE(RealType, 3250582.599999999976716935634613037109375000000000000000000000000000000000000000000000000000000000000); + ws[99] = BOOST_MATH_TEST_VALUE(RealType, 12.47094339016839065212822905567651460418204106065566910956134121802725695306834966790193342511971825); + zs[100] = BOOST_MATH_TEST_VALUE(RealType, 3250583.099999999976716935634613037109375000000000000000000000000000000000000000000000000000000000000); + ws[100] = BOOST_MATH_TEST_VALUE(RealType, 12.47094353256841434681539974528531469757660901993281429423438578866652694560046559516127568892277702); + zs[101] = BOOST_MATH_TEST_VALUE(RealType, 6501166.699999999953433871269226074218750000000000000000000000000000000000000000000000000000000000000); + ws[101] = BOOST_MATH_TEST_VALUE(RealType, 13.11382518009820116753812472043031659983277915902361747977758246698818963218870351102587174635468138); + zs[102] = BOOST_MATH_TEST_VALUE(RealType, 6501167.199999999953433871269226074218750000000000000000000000000000000000000000000000000000000000000); + ws[102] = BOOST_MATH_TEST_VALUE(RealType, 13.11382525155825542039438232302300244148043560690215658478061719482590950005701713995387903749679862); + zs[103] = BOOST_MATH_TEST_VALUE(RealType, 6501167.699999999953433871269226074218750000000000000000000000000000000000000000000000000000000000000); + ws[103] = BOOST_MATH_TEST_VALUE(RealType, 13.11382532301830420490055394669714665225378812412907655382308789130810346826183238238494205026247934); + zs[104] = BOOST_MATH_TEST_VALUE(RealType, 6501168.199999999953433871269226074218750000000000000000000000000000000000000000000000000000000000000); + ws[104] = BOOST_MATH_TEST_VALUE(RealType, 13.11382539447834752105747833428094533284492363009218919720913636223924531666313533113069940376003390); + zs[105] = BOOST_MATH_TEST_VALUE(RealType, 6501168.699999999953433871269226074218750000000000000000000000000000000000000000000000000000000000000); + ws[105] = BOOST_MATH_TEST_VALUE(RealType, 13.11382546593838536886599422840946554656689837543146085874754543863703505630372640720269904462657597); + zs[106] = BOOST_MATH_TEST_VALUE(RealType, 13002337.89999999990686774253845214843750000000000000000000000000000000000000000000000000000000000000); + ws[106] = BOOST_MATH_TEST_VALUE(RealType, 13.75895020161120780671931348674820949097896740529525960699598966308825998645623474235280989608207426); + zs[107] = BOOST_MATH_TEST_VALUE(RealType, 13002338.39999999990686774253845214843750000000000000000000000000000000000000000000000000000000000000); + ws[107] = BOOST_MATH_TEST_VALUE(RealType, 13.75895023746031789528316024226987524061812679192459530422102497447151229457148397431817422821296573); + zs[108] = BOOST_MATH_TEST_VALUE(RealType, 13002338.89999999990686774253845214843750000000000000000000000000000000000000000000000000000000000000); + ws[108] = BOOST_MATH_TEST_VALUE(RealType, 13.75895027330942661161180008030275959022186764318551714315248259172940117391346074981407903526666876); + zs[109] = BOOST_MATH_TEST_VALUE(RealType, 13002339.39999999990686774253845214843750000000000000000000000000000000000000000000000000000000000000); + ws[109] = BOOST_MATH_TEST_VALUE(RealType, 13.75895030915853395570533826541548036105186387000942779022045956615233350302797407587096919814230872); + zs[110] = BOOST_MATH_TEST_VALUE(RealType, 13002339.89999999990686774253845214843750000000000000000000000000000000000000000000000000000000000000); + ws[110] = BOOST_MATH_TEST_VALUE(RealType, 13.75895034500763992756388006216453398588380356491793568993934227427809822610433476168943506890083264); + zs[111] = BOOST_MATH_TEST_VALUE(RealType, 26004680.29999999981373548507690429687500000000000000000000000000000000000000000000000000000000000000); + ws[111] = BOOST_MATH_TEST_VALUE(RealType, 14.40613306792050284417781173963237397180782490395810703112834440216893801894568593170935318229410890); + zs[112] = BOOST_MATH_TEST_VALUE(RealType, 26004680.79999999981373548507690429687500000000000000000000000000000000000000000000000000000000000000); + ws[112] = BOOST_MATH_TEST_VALUE(RealType, 14.40613308589978129513196522846261186416164678471600282263899627087138088581441641199768735966026721); + zs[113] = BOOST_MATH_TEST_VALUE(RealType, 26004681.29999999981373548507690429687500000000000000000000000000000000000000000000000000000000000000); + ws[113] = BOOST_MATH_TEST_VALUE(RealType, 14.40613310387905940184947896220802688669394773806882218972392634458869861468933663343960564575146049); + zs[114] = BOOST_MATH_TEST_VALUE(RealType, 26004681.79999999981373548507690429687500000000000000000000000000000000000000000000000000000000000000); + ws[114] = BOOST_MATH_TEST_VALUE(RealType, 14.40613312185833716433036614707023490965167453502978925783310059180078511451175950772072935073580417); + zs[115] = BOOST_MATH_TEST_VALUE(RealType, 26004682.29999999981373548507690429687500000000000000000000000000000000000000000000000000000000000000); + ws[115] = BOOST_MATH_TEST_VALUE(RealType, 14.40613313983761458257463998925009132119130606601627537868443064669880766892972378195587657468067971); + zs[116] = BOOST_MATH_TEST_VALUE(RealType, 52009365.09999999962747097015380859375000000000000000000000000000000000000000000000000000000000000000); + ws[116] = BOOST_MATH_TEST_VALUE(RealType, 15.05521023228726997012482201156518637282193264830919527945406914876568997920242700346221079695380120); + zs[117] = BOOST_MATH_TEST_VALUE(RealType, 52009365.59999999962747097015380859375000000000000000000000000000000000000000000000000000000000000000); + ws[117] = BOOST_MATH_TEST_VALUE(RealType, 15.05521024130213601448008511719004686304317399587148372739532783438679535880037848363163034299000680); + zs[118] = BOOST_MATH_TEST_VALUE(RealType, 52009366.09999999962747097015380859375000000000000000000000000000000000000000000000000000000000000000); + ws[118] = BOOST_MATH_TEST_VALUE(RealType, 15.05521025031700197250576749203130491372164140425152090159289103953022449076855552216703380049236381); + zs[119] = BOOST_MATH_TEST_VALUE(RealType, 52009366.59999999962747097015380859375000000000000000000000000000000000000000000000000000000000000000); + ws[119] = BOOST_MATH_TEST_VALUE(RealType, 15.05521025933186784420187079237695573264884951878394949291027777307830551044730470942903907859985247); + zs[120] = BOOST_MATH_TEST_VALUE(RealType, 52009367.09999999962747097015380859375000000000000000000000000000000000000000000000000000000000000000); + ws[120] = BOOST_MATH_TEST_VALUE(RealType, 15.05521026834673362956839667451494683193779819588967935304969871800597790007309597120010839282494067); + zs[121] = BOOST_MATH_TEST_VALUE(RealType, 104018734.6999999992549419403076171875000000000000000000000000000000000000000000000000000000000000000); + ws[121] = BOOST_MATH_TEST_VALUE(RealType, 15.70603645611954384988565703849374122026373428508755081783343271665282590664826044624943527766443724); + zs[122] = BOOST_MATH_TEST_VALUE(RealType, 104018735.1999999992549419403076171875000000000000000000000000000000000000000000000000000000000000000); + ws[122] = BOOST_MATH_TEST_VALUE(RealType, 15.70603646063864032656975241668975461127107196165116494804371210729046808944447344810561150619338235); + zs[123] = BOOST_MATH_TEST_VALUE(RealType, 104018735.6999999992549419403076171875000000000000000000000000000000000000000000000000000000000000000); + ws[123] = BOOST_MATH_TEST_VALUE(RealType, 15.70603646515773678160916860265187423935628996612114399500020977395299556819648453302891654761783891); + zs[124] = BOOST_MATH_TEST_VALUE(RealType, 104018736.1999999992549419403076171875000000000000000000000000000000000000000000000000000000000000000); + ws[124] = BOOST_MATH_TEST_VALUE(RealType, 15.70603646967683321500390580404963095388245044851589624040628425347772179453119739096056755672581358); + zs[125] = BOOST_MATH_TEST_VALUE(RealType, 104018736.6999999992549419403076171875000000000000000000000000000000000000000000000000000000000000000); + ws[125] = BOOST_MATH_TEST_VALUE(RealType, 15.70603647419592962675396422855255261373445463144900248624737121930916887983366690742461452872492017); + zs[126] = BOOST_MATH_TEST_VALUE(RealType, 208037473.8999999985098838806152343750000000000000000000000000000000000000000000000000000000000000000); + ws[126] = BOOST_MATH_TEST_VALUE(RealType, 16.35848223073077299062251727505489121683259009838940921798588655999804611259258002812224135777088225); + zs[127] = BOOST_MATH_TEST_VALUE(RealType, 208037474.3999999985098838806152343750000000000000000000000000000000000000000000000000000000000000000); + ws[127] = BOOST_MATH_TEST_VALUE(RealType, 16.35848223299572857263748180340545017346327532474491820120184954137107185953921166019993001720173076); + zs[128] = BOOST_MATH_TEST_VALUE(RealType, 208037474.8999999985098838806152343750000000000000000000000000000000000000000000000000000000000000000); + ws[128] = BOOST_MATH_TEST_VALUE(RealType, 16.35848223526068414922688843525885798316030222323873469187325647317943052465221773791004107044449214); + zs[129] = BOOST_MATH_TEST_VALUE(RealType, 208037475.3999999985098838806152343750000000000000000000000000000000000000000000000000000000000000000); + ws[129] = BOOST_MATH_TEST_VALUE(RealType, 16.35848223752563972039073719664683805774368875669934675288398977599557153297961083834670781371769393); + zs[130] = BOOST_MATH_TEST_VALUE(RealType, 208037475.8999999985098838806152343750000000000000000000000000000000000000000000000000000000000000000); + ws[130] = BOOST_MATH_TEST_VALUE(RealType, 16.35848223979059528612902811360111362158185991940560941697071272367978154194499902342370856607708754); + zs[131] = BOOST_MATH_TEST_VALUE(RealType, 416074952.2999999970197677612304687500000000000000000000000000000000000000000000000000000000000000000); + ws[131] = BOOST_MATH_TEST_VALUE(RealType, 17.01243162682130034575873110256608416240774650633244657569787078893008324146198524461655542099298038); + zs[132] = BOOST_MATH_TEST_VALUE(RealType, 416074952.7999999970197677612304687500000000000000000000000000000000000000000000000000000000000000000); + ws[132] = BOOST_MATH_TEST_VALUE(RealType, 17.01243162795629150708082853641104106051019632512982699681254455598867301594442867945136512864263362); + zs[133] = BOOST_MATH_TEST_VALUE(RealType, 416074953.2999999970197677612304687500000000000000000000000000000000000000000000000000000000000000000); + ws[133] = BOOST_MATH_TEST_VALUE(RealType, 17.01243162909128266704320348800926473937585791237790855606445141461836852095077452961626092636714582); + zs[134] = BOOST_MATH_TEST_VALUE(RealType, 416074953.7999999970197677612304687500000000000000000000000000000000000000000000000000000000000000000); + ws[134] = BOOST_MATH_TEST_VALUE(RealType, 17.01243163022627382564585596062316405185836375654028401358545369509940490853717120734831332950839378); + zs[135] = BOOST_MATH_TEST_VALUE(RealType, 416074954.2999999970197677612304687500000000000000000000000000000000000000000000000000000000000000000); + ws[135] = BOOST_MATH_TEST_VALUE(RealType, 17.01243163136126498288878595751514783906405969399381198927824453551799680870112429531524827406849599); + zs[136] = BOOST_MATH_TEST_VALUE(RealType, 832149909.0999999940395355224609375000000000000000000000000000000000000000000000000000000000000000000); + ws[136] = BOOST_MATH_TEST_VALUE(RealType, 17.66778049214031531812887566464334167360339296689778348663603985291368769599603595003791335301847127); + zs[137] = BOOST_MATH_TEST_VALUE(RealType, 832149909.5999999940395355224609375000000000000000000000000000000000000000000000000000000000000000000); + ws[137] = BOOST_MATH_TEST_VALUE(RealType, 17.66778049270898194728430180770507660352501688482825963526508387721596470881802640086432444438183220); + zs[138] = BOOST_MATH_TEST_VALUE(RealType, 832149910.0999999940395355224609375000000000000000000000000000000000000000000000000000000000000000000); + ws[138] = BOOST_MATH_TEST_VALUE(RealType, 17.66778049327764857609902322798165858268194375878492382051153135096018706001421025620618302837247343); + zs[139] = BOOST_MATH_TEST_VALUE(RealType, 832149910.5999999940395355224609375000000000000000000000000000000000000000000000000000000000000000000); + ws[139] = BOOST_MATH_TEST_VALUE(RealType, 17.66778049384631520457303992588186753608991034689224716778630078855718157314806355505907287211879952); + zs[140] = BOOST_MATH_TEST_VALUE(RealType, 832149911.0999999940395355224609375000000000000000000000000000000000000000000000000000000000000000000); + ws[140] = BOOST_MATH_TEST_VALUE(RealType, 17.66778049441498183270635190181448338802862077472029294388350982070482138347224074032285443619695132); + zs[141] = BOOST_MATH_TEST_VALUE(RealType, 1664299822.699999988079071044921875000000000000000000000000000000000000000000000000000000000000000000); + ws[141] = BOOST_MATH_TEST_VALUE(RealType, 18.32443493355098619384682217293063408860906200997313752841900524481309067580120995737616551816092439); + zs[142] = BOOST_MATH_TEST_VALUE(RealType, 1664299823.199999988079071044921875000000000000000000000000000000000000000000000000000000000000000000); + ws[142] = BOOST_MATH_TEST_VALUE(RealType, 18.32443493383586636729058593564510700968056859723407482295764411091295912034128445270765389474245827); + zs[143] = BOOST_MATH_TEST_VALUE(RealType, 1664299823.699999988079071044921875000000000000000000000000000000000000000000000000000000000000000000); + ws[143] = BOOST_MATH_TEST_VALUE(RealType, 18.32443493412074654064899329117538024263559388031556198852503241876369903917896474239140595505063322); + zs[144] = BOOST_MATH_TEST_VALUE(RealType, 1664299824.199999988079071044921875000000000000000000000000000000000000000000000000000000000000000000); + ws[144] = BOOST_MATH_TEST_VALUE(RealType, 18.32443493440562671392204423957266503795757499599462266552348787036107193130165331139452279532396827); + zs[145] = BOOST_MATH_TEST_VALUE(RealType, 1664299824.699999988079071044921875000000000000000000000000000000000000000000000000000000000000000000); + ws[145] = BOOST_MATH_TEST_VALUE(RealType, 18.32443493469050688710973878088817264608384098178071957390837672366993756271957964256600743216105241); + zs[146] = BOOST_MATH_TEST_VALUE(RealType, 3328599649.899999976158142089843750000000000000000000000000000000000000000000000000000000000000000000); + ws[146] = BOOST_MATH_TEST_VALUE(RealType, 18.98231003239481891260092191504387611307556374245363459040509191182476364522292852639675828304549626); + zs[147] = BOOST_MATH_TEST_VALUE(RealType, 3328599650.399999976158142089843750000000000000000000000000000000000000000000000000000000000000000000); + ws[147] = BOOST_MATH_TEST_VALUE(RealType, 18.98231003253751491629227276099735868489913807533078209230206170806009818301507483588395812018180071); + zs[148] = BOOST_MATH_TEST_VALUE(RealType, 3328599650.899999976158142089843750000000000000000000000000000000000000000000000000000000000000000000); + ws[148] = BOOST_MATH_TEST_VALUE(RealType, 18.98231003268021091996224244869947622441697585814089764491702992369913535253679093973930949506347048); + zs[149] = BOOST_MATH_TEST_VALUE(RealType, 3328599651.399999976158142089843750000000000000000000000000000000000000000000000000000000000000000000); + ws[149] = BOOST_MATH_TEST_VALUE(RealType, 18.98231003282290692361083097815664339084466388779045270504033488281798754360146784756669433227860478); + zs[150] = BOOST_MATH_TEST_VALUE(RealType, 3328599651.899999976158142089843750000000000000000000000000000000000000000000000000000000000000000000); + ws[150] = BOOST_MATH_TEST_VALUE(RealType, 18.98231003296560292723803834937527484339490103369072191109295056853780039115858285449484374166727442); + zs[151] = BOOST_MATH_TEST_VALUE(RealType, 6657199304.299999952316284179687500000000000000000000000000000000000000000000000000000000000000000000); + ws[151] = BOOST_MATH_TEST_VALUE(RealType, 19.64132875213179003616975291758951989874401990433758371552185122694920942525627888352145273462707829); + zs[152] = BOOST_MATH_TEST_VALUE(RealType, 6657199304.799999952316284179687500000000000000000000000000000000000000000000000000000000000000000000); + ws[152] = BOOST_MATH_TEST_VALUE(RealType, 19.64132875220325804117690436378168767212426802746287406741611709213764258286377104459764498688864645); + zs[153] = BOOST_MATH_TEST_VALUE(RealType, 6657199305.299999952316284179687500000000000000000000000000000000000000000000000000000000000000000000); + ws[153] = BOOST_MATH_TEST_VALUE(RealType, 19.64132875227472604617870068525483911300930564165365547681598983747537164183343994434536282821758671); + zs[154] = BOOST_MATH_TEST_VALUE(RealType, 6657199305.799999952316284179687500000000000000000000000000000000000000000000000000000000000000000000); + ws[154] = BOOST_MATH_TEST_VALUE(RealType, 19.64132875234619405117514188200977760121223842398566254787219879319150457646967224084449244020129346); + zs[155] = BOOST_MATH_TEST_VALUE(RealType, 6657199306.299999952316284179687500000000000000000000000000000000000000000000000000000000000000000000); + ws[155] = BOOST_MATH_TEST_VALUE(RealType, 19.64132875241766205616622795404730651654599119614988454540132958996734683240549999645255105620924194); + zs[156] = BOOST_MATH_TEST_VALUE(RealType, 13314398613.09999990463256835937500000000000000000000000000000000000000000000000000000000000000000000); + ws[156] = BOOST_MATH_TEST_VALUE(RealType, 20.30142100521655914391255442878301089651369196196103134174539854119020213383927831733183603430177342); + zs[157] = BOOST_MATH_TEST_VALUE(RealType, 13314398613.59999990463256835937500000000000000000000000000000000000000000000000000000000000000000000); + ws[157] = BOOST_MATH_TEST_VALUE(RealType, 20.30142100525234952404035395706360529741527156245298468796715007831095483401537550300887615324478886); + zs[158] = BOOST_MATH_TEST_VALUE(RealType, 13314398614.09999990463256835937500000000000000000000000000000000000000000000000000000000000000000000); + ws[158] = BOOST_MATH_TEST_VALUE(RealType, 20.30142100528813990416681239948722494340433727853716710673987742922492342629617928698493424484032660); + zs[159] = BOOST_MATH_TEST_VALUE(RealType, 13314398614.59999990463256835937500000000000000000000000000000000000000000000000000000000000000000000); + ws[159] = BOOST_MATH_TEST_VALUE(RealType, 20.30142100532393028429192975605397043801464163741054316603439612006945607064743419379856370010506017); + zs[160] = BOOST_MATH_TEST_VALUE(RealType, 13314398615.09999990463256835937500000000000000000000000000000000000000000000000000000000000000000000); + ws[160] = BOOST_MATH_TEST_VALUE(RealType, 20.30142100535972066441570602676394238477992584177232532005791683216204582260476049842371709123221255); + zs[161] = BOOST_MATH_TEST_VALUE(RealType, 26628797230.69999980926513671875000000000000000000000000000000000000000000000000000000000000000000000); + ws[161] = BOOST_MATH_TEST_VALUE(RealType, 20.96252285249100030290397518601237584604981130278135272151318314970663709603528431386852754379112730); + zs[162] = BOOST_MATH_TEST_VALUE(RealType, 26628797231.19999980926513671875000000000000000000000000000000000000000000000000000000000000000000000); + ws[162] = BOOST_MATH_TEST_VALUE(RealType, 20.96252285250892202655550269280724503656839238502424605070290458403293889858635506895301235328962628); + zs[163] = BOOST_MATH_TEST_VALUE(RealType, 26628797231.69999980926513671875000000000000000000000000000000000000000000000000000000000000000000000); + ws[163] = BOOST_MATH_TEST_VALUE(RealType, 20.96252285252684375020669438704809175058008607738171622380516384350651954854873527952764745960194675); + zs[164] = BOOST_MATH_TEST_VALUE(RealType, 26628797232.19999980926513671875000000000000000000000000000000000000000000000000000000000000000000000); + ws[164] = BOOST_MATH_TEST_VALUE(RealType, 20.96252285254476547385755026873492858475358446577888309438244123935827841754860782205245677270931493); + zs[165] = BOOST_MATH_TEST_VALUE(RealType, 26628797232.69999980926513671875000000000000000000000000000000000000000000000000000000000000000000000); + ws[165] = BOOST_MATH_TEST_VALUE(RealType, 20.96252285256268719750807033786776813575757892712773332275219858225617530315647886304839621190128467); + zs[166] = BOOST_MATH_TEST_VALUE(RealType, 53257594465.89999961853027343750000000000000000000000000000000000000000000000000000000000000000000000); + ws[166] = BOOST_MATH_TEST_VALUE(RealType, 21.62457581338519341205251851678920458391270655214844894715960888032707282421057623183584027330480633); + zs[167] = BOOST_MATH_TEST_VALUE(RealType, 53257594466.39999961853027343750000000000000000000000000000000000000000000000000000000000000000000000); + ws[167] = BOOST_MATH_TEST_VALUE(RealType, 21.62457581339416678276021956345172476031736610096020431411495093672867810851785199503170153825356689); + zs[168] = BOOST_MATH_TEST_VALUE(RealType, 53257594466.89999961853027343750000000000000000000000000000000000000000000000000000000000000000000000); + ws[168] = BOOST_MATH_TEST_VALUE(RealType, 21.62457581340314015346783652970955706237153204994825959199820337645288096708677451415833137778699486); + zs[169] = BOOST_MATH_TEST_VALUE(RealType, 53257594467.39999961853027343750000000000000000000000000000000000000000000000000000000000000000000000); + ws[169] = BOOST_MATH_TEST_VALUE(RealType, 21.62457581341211352417536941556270306715209214940104899493615505349319261368609864010882236880736924); + zs[170] = BOOST_MATH_TEST_VALUE(RealType, 53257594467.89999961853027343750000000000000000000000000000000000000000000000000000000000000000000000); + ws[170] = BOOST_MATH_TEST_VALUE(RealType, 21.62457581342108689488281822101116435173593410522139124663366619300728455522058827136186640157150006); + zs[171] = BOOST_MATH_TEST_VALUE(RealType, 106515188936.2999992370605468750000000000000000000000000000000000000000000000000000000000000000000000); + ws[171] = BOOST_MATH_TEST_VALUE(RealType, 22.28752626920637793567947968920002916518604504482941952960611736969307899126632949710731838314939535); + zs[172] = BOOST_MATH_TEST_VALUE(RealType, 106515188936.7999992370605468750000000000000000000000000000000000000000000000000000000000000000000000); + ws[172] = BOOST_MATH_TEST_VALUE(RealType, 22.28752626921087052760828375602269251105477904207815836374209426134525952488796027799984818720270105); + zs[173] = BOOST_MATH_TEST_VALUE(RealType, 106515188937.2999992370605468750000000000000000000000000000000000000000000000000000000000000000000000); + ws[173] = BOOST_MATH_TEST_VALUE(RealType, 22.28752626921536311953706677275954356032288516798309826935596847224545105365234647336923162745148650); + zs[174] = BOOST_MATH_TEST_VALUE(RealType, 106515188937.7999992370605468750000000000000000000000000000000000000000000000000000000000000000000000); + ws[174] = BOOST_MATH_TEST_VALUE(RealType, 22.28752626921985571146582873941058251041835477955178166028729368533877454281235062918122417686844554); + zs[175] = BOOST_MATH_TEST_VALUE(RealType, 106515188938.2999992370605468750000000000000000000000000000000000000000000000000000000000000000000000); + ws[175] = BOOST_MATH_TEST_VALUE(RealType, 22.28752626922434830339456965597580955876917923101340004236523230885349136854795158825395309771408668); + zs[176] = BOOST_MATH_TEST_VALUE(RealType, 213030377877.0999984741210937500000000000000000000000000000000000000000000000000000000000000000000000); + ws[176] = BOOST_MATH_TEST_VALUE(RealType, 22.95132494498044370189057193061229322652798069141485425830911821689634860387911575520267236257752886); + zs[177] = BOOST_MATH_TEST_VALUE(RealType, 213030377877.5999984741210937500000000000000000000000000000000000000000000000000000000000000000000000); + ws[177] = BOOST_MATH_TEST_VALUE(RealType, 22.95132494498269279111869452328160161214112251666554529993600600683165630516488115832469896181046045); + zs[178] = BOOST_MATH_TEST_VALUE(RealType, 213030377878.0999984741210937500000000000000000000000000000000000000000000000000000000000000000000000); + ws[178] = BOOST_MATH_TEST_VALUE(RealType, 22.95132494498494188034681184635345858036581911693953330207290531058251991996389235617064799550256701); + zs[179] = BOOST_MATH_TEST_VALUE(RealType, 213030377878.5999984741210937500000000000000000000000000000000000000000000000000000000000000000000000); + ws[179] = BOOST_MATH_TEST_VALUE(RealType, 22.95132494498719096957492389982786415591514868991446226878757600605585200116652511909077437303220322); + zs[180] = BOOST_MATH_TEST_VALUE(RealType, 213030377879.0999984741210937500000000000000000000000000000000000000000000000000000000000000000000000); + ws[180] = BOOST_MATH_TEST_VALUE(RealType, 22.95132494498944005880303068370481836350218943309407900398512057690019248825245765368449728229683978); + zs[181] = BOOST_MATH_TEST_VALUE(RealType, 426060755758.6999969482421875000000000000000000000000000000000000000000000000000000000000000000000000); + ws[181] = BOOST_MATH_TEST_VALUE(RealType, 23.61592645786617198666287725520753845230512592375445890316693635014712570198963226722376898746719788); + zs[182] = BOOST_MATH_TEST_VALUE(RealType, 426060755759.1999969482421875000000000000000000000000000000000000000000000000000000000000000000000000); + ws[182] = BOOST_MATH_TEST_VALUE(RealType, 23.61592645786729785413748555882136649105347335405111452573107829620408251494572350634646372518383704); + zs[183] = BOOST_MATH_TEST_VALUE(RealType, 426060755759.6999969482421875000000000000000000000000000000000000000000000000000000000000000000000000); + ws[183] = BOOST_MATH_TEST_VALUE(RealType, 23.61592645786842372161209254336341886225886040142987940947830085760720112665756558438930745003988259); + zs[184] = BOOST_MATH_TEST_VALUE(RealType, 426060755760.1999969482421875000000000000000000000000000000000000000000000000000000000000000000000000); + ws[184] = BOOST_MATH_TEST_VALUE(RealType, 23.61592645786954958908669820883369556901450401232685427846511608592597720877347644103130699500020980); + zs[185] = BOOST_MATH_TEST_VALUE(RealType, 426060755760.6999969482421875000000000000000000000000000000000000000000000000000000000000000000000000); + ws[185] = BOOST_MATH_TEST_VALUE(RealType, 23.61592645787067545656130255523219661441362113316725652002816962722163580709685521423287347532232663); + zs[186] = BOOST_MATH_TEST_VALUE(RealType, 852121511521.8999938964843750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[186] = BOOST_MATH_TEST_VALUE(RealType, 24.28128892222268328128985001144764937331876138884560895942339750264590092000743925470556345687342935); + zs[187] = BOOST_MATH_TEST_VALUE(RealType, 852121511522.3999938964843750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[187] = BOOST_MATH_TEST_VALUE(RealType, 24.28128892222324684237927811409049669410007439237129556446372910358198486719174460302964062617263997); + zs[188] = BOOST_MATH_TEST_VALUE(RealType, 852121511522.8999938964843750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[188] = BOOST_MATH_TEST_VALUE(RealType, 24.28128892222381040346870588656954830537234790758650044870333521914480895767426200215549894604462944); + zs[189] = BOOST_MATH_TEST_VALUE(RealType, 852121511523.3999938964843750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[189] = BOOST_MATH_TEST_VALUE(RealType, 24.28128892222437396455813332888480420752271669125958665876145325130479060260809395708493929658437074); + zs[190] = BOOST_MATH_TEST_VALUE(RealType, 852121511523.8999938964843750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[190] = BOOST_MATH_TEST_VALUE(RealType, 24.28128892222493752564756044103626440093831550015823616066865071071416085939189362440560522437633662); + zs[191] = BOOST_MATH_TEST_VALUE(RealType, 1704243023048.299987792968750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[191] = BOOST_MATH_TEST_VALUE(RealType, 24.94737360307934242599198887438952878908310927656184464570761925753018240286868931047379101991192915); + zs[192] = BOOST_MATH_TEST_VALUE(RealType, 1704243023048.799987792968750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[192] = BOOST_MATH_TEST_VALUE(RealType, 24.94737360307962450443999814141844483539218897580562586948087460916859526796854661158129821949554554); + zs[193] = BOOST_MATH_TEST_VALUE(RealType, 1704243023049.299987792968750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[193] = BOOST_MATH_TEST_VALUE(RealType, 24.94737360307990658288800732581258578292473970863909517564631259029126397702972526621236324413898654); + zs[194] = BOOST_MATH_TEST_VALUE(RealType, 1704243023049.799987792968750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[194] = BOOST_MATH_TEST_VALUE(RealType, 24.94737360308018866133601642757195163172921046426642186059476077116782502974396487792107071563852968); + zs[195] = BOOST_MATH_TEST_VALUE(RealType, 1704243023050.299987792968750000000000000000000000000000000000000000000000000000000000000000000000000); + ws[195] = BOOST_MATH_TEST_VALUE(RealType, 24.94737360308047073978402544669654238185405023189173260158888945690205253434326923773189194023738864); + zs[196] = BOOST_MATH_TEST_VALUE(RealType, 3408486046101.099975585937500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[196] = BOOST_MATH_TEST_VALUE(RealType, 25.61414461111548947260493867878972043152010342814420318471604738869697286844100478961752318507519036); + zs[197] = BOOST_MATH_TEST_VALUE(RealType, 3408486046101.599975585937500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[197] = BOOST_MATH_TEST_VALUE(RealType, 25.61414461111563065346677642310736687968026218525976239525273457263820521539063299775063487387395491); + zs[198] = BOOST_MATH_TEST_VALUE(RealType, 3408486046102.099975585937500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[198] = BOOST_MATH_TEST_VALUE(RealType, 25.61414461111577183432861414674405116970646776101208949311878694889082271860331380169436692589290008); + zs[199] = BOOST_MATH_TEST_VALUE(RealType, 3408486046102.599975585937500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[199] = BOOST_MATH_TEST_VALUE(RealType, 25.61414461111591301519045184969977330160478305423319389187668123911964696321646253276250725578473047); + zs[200] = BOOST_MATH_TEST_VALUE(RealType, 3408486046103.099975585937500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[200] = BOOST_MATH_TEST_VALUE(RealType, 25.61414461111605419605228953197453327538127096375508233833690347792194785120108760652931053928666785); + zs[201] = BOOST_MATH_TEST_VALUE(RealType, 6816972092206.699951171875000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[201] = BOOST_MATH_TEST_VALUE(RealType, 26.28156863336777149103602875189240858590017172273907420428241974657782598001971183829692697893931030); + zs[202] = BOOST_MATH_TEST_VALUE(RealType, 6816972092207.199951171875000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[202] = BOOST_MATH_TEST_VALUE(RealType, 26.28156863336784214888845714766036358708919247411311945160997461946071687776487761293647943181961882); + zs[203] = BOOST_MATH_TEST_VALUE(RealType, 6816972092207.699951171875000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[203] = BOOST_MATH_TEST_VALUE(RealType, 26.28156863336791280674088553825278629338313564287800253072882363885000489322789589683564459399860775); + zs[204] = BOOST_MATH_TEST_VALUE(RealType, 6816972092208.199951171875000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[204] = BOOST_MATH_TEST_VALUE(RealType, 26.28156863336798346459331392366967670478275989571118963232699709422043712865525671390927105068828991); + zs[205] = BOOST_MATH_TEST_VALUE(RealType, 6816972092208.699951171875000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[205] = BOOST_MATH_TEST_VALUE(RealType, 26.28156863336805412244574230391103482128882389929014678023920695547828528484685657699485259769208741); + zs[206] = BOOST_MATH_TEST_VALUE(RealType, 13633944184417.89990234375000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[206] = BOOST_MATH_TEST_VALUE(RealType, 26.94961469479482406519801256975073261795345435020419867463744516732345674294492529228061805959732327); + zs[207] = BOOST_MATH_TEST_VALUE(RealType, 13633944184418.39990234375000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[207] = BOOST_MATH_TEST_VALUE(RealType, 26.94961469479485942625415366326146401152634296784764225669862897123975110273521017330868675363150448); + zs[208] = BOOST_MATH_TEST_VALUE(RealType, 13633944184418.89990234375000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[208] = BOOST_MATH_TEST_VALUE(RealType, 26.94961469479489478731029475547705331300159611152098923590641458690234001429707091782045185453438171); + zs[209] = BOOST_MATH_TEST_VALUE(RealType, 13633944184419.39990234375000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[209] = BOOST_MATH_TEST_VALUE(RealType, 26.94961469479493014836643584639750052237930871016343368359473040533580595453533790563068986805724019); + zs[210] = BOOST_MATH_TEST_VALUE(RealType, 13633944184419.89990234375000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[210] = BOOST_MATH_TEST_VALUE(RealType, 26.94961469479496550942257693602280563965957569271416966065840246264353271487273203593602827136860754); + zs[211] = BOOST_MATH_TEST_VALUE(RealType, 27267888368840.29980468750000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[211] = BOOST_MATH_TEST_VALUE(RealType, 27.61825394658132365321120256576467284757880630504909781214698429280000692432972722344046738139460516); + zs[212] = BOOST_MATH_TEST_VALUE(RealType, 27267888368840.79980468750000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[212] = BOOST_MATH_TEST_VALUE(RealType, 27.61825394658134134906748186542924183870772699960645627424489223278978300139646551878816289033156892); + zs[213] = BOOST_MATH_TEST_VALUE(RealType, 27267888368841.29980468750000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[213] = BOOST_MATH_TEST_VALUE(RealType, 27.61825394658135904492376116476972541669907157102168356799570843777496579434166012376207829201011287); + zs[214] = BOOST_MATH_TEST_VALUE(RealType, 27267888368841.79980468750000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[214] = BOOST_MATH_TEST_VALUE(RealType, 27.61825394658137674078004046378612358155285189678948212439453829367032310042341489097274555293104964); + zs[215] = BOOST_MATH_TEST_VALUE(RealType, 27267888368842.29980468750000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[215] = BOOST_MATH_TEST_VALUE(RealType, 27.61825394658139443663631976247843633326907985440455437378340280356184152787821056110486626668344188); + zs[216] = BOOST_MATH_TEST_VALUE(RealType, 54535776737685.09960937500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[216] = BOOST_MATH_TEST_VALUE(RealType, 28.28745947768658520727223095526336163825405277084208702812195528505679434221769385747727596125338626); + zs[217] = BOOST_MATH_TEST_VALUE(RealType, 54535776737685.59960937500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[217] = BOOST_MATH_TEST_VALUE(RealType, 28.28745947768659406252057469058839388454672293394318546730557386127002114050381424035876354103192624); + zs[218] = BOOST_MATH_TEST_VALUE(RealType, 54535776737686.09960937500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[218] = BOOST_MATH_TEST_VALUE(RealType, 28.28745947768660291776891842583233326734639195028174391562886488418853280961627679416432912568037643); + zs[219] = BOOST_MATH_TEST_VALUE(RealType, 54535776737686.59960937500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[219] = BOOST_MATH_TEST_VALUE(RealType, 28.28745947768661177301726216099517978665306130590007248679624798101760603565428399982488746458503134); + zs[220] = BOOST_MATH_TEST_VALUE(RealType, 54535776737687.09960937500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[220] = BOOST_MATH_TEST_VALUE(RealType, 28.28745947768662062826560589607693344246673248684048129447128687164847423418296378412381527867603611); + zs[221] = BOOST_MATH_TEST_VALUE(RealType, 109071553475374.6992187500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[221] = BOOST_MATH_TEST_VALUE(RealType, 28.95720614666117150245004622760057983418314410522200232704276775862146736266761022246497551058785007); + zs[222] = BOOST_MATH_TEST_VALUE(RealType, 109071553475375.1992187500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[222] = BOOST_MATH_TEST_VALUE(RealType, 28.95720614666117593357355743548739761689791124679984859813361369105181180764744313352061153147051630); + zs[223] = BOOST_MATH_TEST_VALUE(RealType, 109071553475375.6992187500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[223] = BOOST_MATH_TEST_VALUE(RealType, 28.95720614666118036469706864335392511390934879372874136387705864492100434790697368864211343956938842); + zs[224] = BOOST_MATH_TEST_VALUE(RealType, 109071553475376.1992187500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[224] = BOOST_MATH_TEST_VALUE(RealType, 28.95720614666118479582057985120016232521745693192563248069260165217440327859683071396461341530343065); + zs[225] = BOOST_MATH_TEST_VALUE(RealType, 109071553475376.6992187500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[225] = BOOST_MATH_TEST_VALUE(RealType, 28.95720614666118922694409105902610925082223584730747380499718597765232850574002390696473010380772407); + zs[226] = BOOST_MATH_TEST_VALUE(RealType, 218143106950753.8984375000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[226] = BOOST_MATH_TEST_VALUE(RealType, 29.62747043118774188445791042660254784305836995418567274505820949514291909017768623167600827442630858); + zs[227] = BOOST_MATH_TEST_VALUE(RealType, 218143106950754.3984375000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[227] = BOOST_MATH_TEST_VALUE(RealType, 29.62747043118774410169407734916144601996690190995667210727468326746331358762853812415838063247614151); + zs[228] = BOOST_MATH_TEST_VALUE(RealType, 218143106950754.8984375000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[228] = BOOST_MATH_TEST_VALUE(RealType, 29.62747043118774631893024427171526754670674911118564686113352807043233143508066106374847716587931185); + zs[229] = BOOST_MATH_TEST_VALUE(RealType, 218143106950755.3984375000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[229] = BOOST_MATH_TEST_VALUE(RealType, 29.62747043118774853616641119426401242327791158113151699337805013921071479703774514043229472570138003); + zs[230] = BOOST_MATH_TEST_VALUE(RealType, 218143106950755.8984375000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[230] = BOOST_MATH_TEST_VALUE(RealType, 29.62747043118775075340257811680768064968038934305320249075139583789870247295334960013555124352240919); + zs[231] = BOOST_MATH_TEST_VALUE(RealType, 436286213901512.2968750000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[231] = BOOST_MATH_TEST_VALUE(RealType, 30.29823029316507210180178060183512748657385641403633979025174401415202721913219462712200443246592017); + zs[232] = BOOST_MATH_TEST_VALUE(RealType, 436286213901512.7968750000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[232] = BOOST_MATH_TEST_VALUE(RealType, 30.29823029316507321122179075661323950744341840810124864190846957886463039301370908060223378776053761); + zs[233] = BOOST_MATH_TEST_VALUE(RealType, 436286213901513.2968750000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[233] = BOOST_MATH_TEST_VALUE(RealType, 30.29823029316507432064180091139008139023924952729670215908087118481612362461688278748768609693129214); + zs[234] = BOOST_MATH_TEST_VALUE(RealType, 436286213901513.7968750000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[234] = BOOST_MATH_TEST_VALUE(RealType, 30.29823029316507543006181106616565313496134977453237197516164536262012622641950793744512740976903471); + zs[235] = BOOST_MATH_TEST_VALUE(RealType, 436286213901514.2968750000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[235] = BOOST_MATH_TEST_VALUE(RealType, 30.29823029316507653948182122093995474160971915271792972354347864284901025700020408100899889264653839); + zs[236] = BOOST_MATH_TEST_VALUE(RealType, 872572427803029.0937500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[236] = BOOST_MATH_TEST_VALUE(RealType, 30.96946505745926454870573870105381204121080031682700579571979417608023815834802026646642551756529303); + zs[237] = BOOST_MATH_TEST_VALUE(RealType, 872572427803029.5937500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[237] = BOOST_MATH_TEST_VALUE(RealType, 30.96946505745926510380014769927740975753101903356685400661074674057071774744000291503524870439239858); + zs[238] = BOOST_MATH_TEST_VALUE(RealType, 872572427803030.0937500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[238] = BOOST_MATH_TEST_VALUE(RealType, 30.96946505745926565889455669750068970579501127050404305753044873046035266500692837850798788777465676); + zs[239] = BOOST_MATH_TEST_VALUE(RealType, 872572427803030.5937500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[239] = BOOST_MATH_TEST_VALUE(RealType, 30.96946505745926621398896569572365188600277702800255782702903047591596266870263378125202082655252570); + zs[240] = BOOST_MATH_TEST_VALUE(RealType, 872572427803031.0937500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[240] = BOOST_MATH_TEST_VALUE(RealType, 30.96946505745926676908337469394629629815431630642638319365662168161753441065238424482444892944732090); + zs[241] = BOOST_MATH_TEST_VALUE(RealType, 1745144855606062.687500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[241] = BOOST_MATH_TEST_VALUE(RealType, 31.64115530270368555045568380920065773046212688909377117164466588005747217968807471779219205518025563); + zs[242] = BOOST_MATH_TEST_VALUE(RealType, 1745144855606063.187500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[242] = BOOST_MATH_TEST_VALUE(RealType, 31.64115530270368582818730773733152774326033355552322481669720170248660524963440150598888428761058290); + zs[243] = BOOST_MATH_TEST_VALUE(RealType, 1745144855606063.687500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[243] = BOOST_MATH_TEST_VALUE(RealType, 31.64115530270368610591893166546231825808704138452046802967409474774871621840817874673003149159950917); + zs[244] = BOOST_MATH_TEST_VALUE(RealType, 1745144855606064.187500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[244] = BOOST_MATH_TEST_VALUE(RealType, 31.64115530270368638365055559359302927494225037613103195710423753246607554009944184530654853462065205); + zs[245] = BOOST_MATH_TEST_VALUE(RealType, 1745144855606064.687500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[245] = BOOST_MATH_TEST_VALUE(RealType, 31.64115530270368666138217952172366079382596053040044774551652253413904860066772832705321064573507936); + zs[246] = BOOST_MATH_TEST_VALUE(RealType, 3490289711212129.875000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[246] = BOOST_MATH_TEST_VALUE(RealType, 32.31328276274725912255296233554695621993095527834620867411002883617418802445848848920754168937963900); + zs[247] = BOOST_MATH_TEST_VALUE(RealType, 3490289711212130.375000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[247] = BOOST_MATH_TEST_VALUE(RealType, 32.31328276274725926150732198502036790376252498771483422694718006966861906633408209207371317438042436); + zs[248] = BOOST_MATH_TEST_VALUE(RealType, 3490289711212130.875000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[248] = BOOST_MATH_TEST_VALUE(RealType, 32.31328276274725940046168163449375969968196897954449339669242357827673003495386979661882340839494652); + zs[249] = BOOST_MATH_TEST_VALUE(RealType, 3490289711212131.375000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[249] = BOOST_MATH_TEST_VALUE(RealType, 32.31328276274725953941604128396713160768928725384088153564235657069057463593847925585888659769685682); + zs[250] = BOOST_MATH_TEST_VALUE(RealType, 3490289711212131.875000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[250] = BOOST_MATH_TEST_VALUE(RealType, 32.31328276274725967837040093344048362778447981060969399609357625315535190876608557675610677996463049); + zs[251] = BOOST_MATH_TEST_VALUE(RealType, 6980579422424264.250000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[251] = BOOST_MATH_TEST_VALUE(RealType, 32.98583023753604679088719826542088155377570542413337629407446260913838808164256711555296996403852860); + zs[252] = BOOST_MATH_TEST_VALUE(RealType, 6980579422424264.750000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[252] = BOOST_MATH_TEST_VALUE(RealType, 32.98583023753604686040692677719047881106035406644182281598485405113601440205771668223737335876041818); + zs[253] = BOOST_MATH_TEST_VALUE(RealType, 6980579422424265.250000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[253] = BOOST_MATH_TEST_VALUE(RealType, 32.98583023753604692992665528896007109314624214905869648472859408505262279189002691227175712262681487); + zs[254] = BOOST_MATH_TEST_VALUE(RealType, 6980579422424265.750000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[254] = BOOST_MATH_TEST_VALUE(RealType, 32.98583023753604699944638380072965840003336967198470969416928902364624408970718516308377862696183264); + zs[255] = BOOST_MATH_TEST_VALUE(RealType, 6980579422424266.250000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[255] = BOOST_MATH_TEST_VALUE(RealType, 32.98583023753604706896611231249924073172173663522057483817054517952187667696583584412183600018203736); + zs[256] = BOOST_MATH_TEST_VALUE(RealType, 13961158844848533.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[256] = BOOST_MATH_TEST_VALUE(RealType, 33.65878151237097095124298518143702077667858405885409410230992311053881919684167704487215990113228857); + zs[257] = BOOST_MATH_TEST_VALUE(RealType, 13961158844848533.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[257] = BOOST_MATH_TEST_VALUE(RealType, 33.65878151237097098602331016061647350048233624732159922625861798362865689925616857240391773797386011); + zs[258] = BOOST_MATH_TEST_VALUE(RealType, 13961158844848534.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[258] = BOOST_MATH_TEST_VALUE(RealType, 33.65878151237097102080363513979592497971278809664223503486445169932919366667254889157695575939580298); + zs[259] = BOOST_MATH_TEST_VALUE(RealType, 13961158844848534.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[259] = BOOST_MATH_TEST_VALUE(RealType, 33.65878151237097105558396011897537521436993960681609063435549611864140613496327710388308960838614952); + zs[260] = BOOST_MATH_TEST_VALUE(RealType, 13961158844848535.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[260] = BOOST_MATH_TEST_VALUE(RealType, 33.65878151237097109036428509815482420445379077784325513095982310255670016959177612483326392564338403); + zs[261] = BOOST_MATH_TEST_VALUE(RealType, 27922317689697070.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[261] = BOOST_MATH_TEST_VALUE(RealType, 34.33212128461954182192055400546316699688336088176596379963232567718408755304304153654131361648983188); + zs[262] = BOOST_MATH_TEST_VALUE(RealType, 27922317689697071.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[262] = BOOST_MATH_TEST_VALUE(RealType, 34.33212128461954183932056272117319130023138040040443817185436738411951668715588692235416442685371045); + zs[263] = BOOST_MATH_TEST_VALUE(RealType, 27922317689697071.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[263] = BOOST_MATH_TEST_VALUE(RealType, 34.33212128461954185672057143688321529225011483876409341147922608312437009263576524224673465664622416); + zs[264] = BOOST_MATH_TEST_VALUE(RealType, 27922317689697072.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[264] = BOOST_MATH_TEST_VALUE(RealType, 34.33212128461954187412058015259323897293956419684494066363206316215367067849034858025290827895879418); + zs[265] = BOOST_MATH_TEST_VALUE(RealType, 27922317689697072.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[265] = BOOST_MATH_TEST_VALUE(RealType, 34.33212128461954189152058886830326234229972847464699107343804000916184280580506932042040723667339789); + zs[266] = BOOST_MATH_TEST_VALUE(RealType, 55844635379394145.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[266] = BOOST_MATH_TEST_VALUE(RealType, 35.00583509707518695935331739482145723160151536139963974678105921205914075721658873691195091665811270); + zs[267] = BOOST_MATH_TEST_VALUE(RealType, 55844635379394146.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[267] = BOOST_MATH_TEST_VALUE(RealType, 35.00583509707518696805806331425691633367772960837479285214203859766210400707178436995199558318407295); + zs[268] = BOOST_MATH_TEST_VALUE(RealType, 55844635379394146.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[268] = BOOST_MATH_TEST_VALUE(RealType, 35.00583509707518697676280923369237535787688894919552843836080956969078417834382970920191457114876432); + zs[269] = BOOST_MATH_TEST_VALUE(RealType, 55844635379394147.00000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[269] = BOOST_MATH_TEST_VALUE(RealType, 35.00583509707518698546755515312783430419899338386184789940110157674808234768343577335618132616959765); + zs[270] = BOOST_MATH_TEST_VALUE(RealType, 55844635379394147.50000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[270] = BOOST_MATH_TEST_VALUE(RealType, 35.00583509707518699417230107256329317264404291237375262922664406743686215997702269925047177260404215); + zs[271] = BOOST_MATH_TEST_VALUE(RealType, 111689270758788295.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[271] = BOOST_MATH_TEST_VALUE(RealType, 35.67990927725728324006370419405415632798743326857723995400791753575138185111578870305633556551072352); + zs[272] = BOOST_MATH_TEST_VALUE(RealType, 111689270758788296.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[272] = BOOST_MATH_TEST_VALUE(RealType, 35.67990927725728324441836204273681992689740132873188907673425746295594512007334247458414132014763631); + zs[273] = BOOST_MATH_TEST_VALUE(RealType, 111689270758788296.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[273] = BOOST_MATH_TEST_VALUE(RealType, 35.67990927725728324877301989141948350632733717201783214881466640664746188099504650620747906266396850); + zs[274] = BOOST_MATH_TEST_VALUE(RealType, 111689270758788297.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[274] = BOOST_MATH_TEST_VALUE(RealType, 35.67990927725728325312767774010214706627724079843506934459363078248730870402916016710439865808348504); + zs[275] = BOOST_MATH_TEST_VALUE(RealType, 111689270758788297.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[275] = BOOST_MATH_TEST_VALUE(RealType, 35.67990927725728325748233558878481060674711220798360083841563700613685981848455744088126373390197417); + zs[276] = BOOST_MATH_TEST_VALUE(RealType, 223378541517576595.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[276] = BOOST_MATH_TEST_VALUE(RealType, 36.35433088203076284552001564306302504927318242547212268948185919007001226970738285875013148048139909); + zs[277] = BOOST_MATH_TEST_VALUE(RealType, 223378541517576596.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[277] = BOOST_MATH_TEST_VALUE(RealType, 36.35433088203076284769844633707581493482388002617376992599930776877134475450868721982023076025880187); + zs[278] = BOOST_MATH_TEST_VALUE(RealType, 223378541517576596.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[278] = BOOST_MATH_TEST_VALUE(RealType, 36.35433088203076284987687703108860481550197556889556172047264008790594215563414633410406449425201228); + zs[279] = BOOST_MATH_TEST_VALUE(RealType, 223378541517576597.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[279] = BOOST_MATH_TEST_VALUE(RealType, 36.35433088203076285205530772510139469130746905363749809470683776684452160660198665349883658913823376); + zs[280] = BOOST_MATH_TEST_VALUE(RealType, 223378541517576597.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[280] = BOOST_MATH_TEST_VALUE(RealType, 36.35433088203076285423373841911418456224036048039957907050688242495780009454648985107688288289511577); + zs[281] = BOOST_MATH_TEST_VALUE(RealType, 446757083035153195.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[281] = BOOST_MATH_TEST_VALUE(RealType, 37.02908764699829400134712267319905290193660562118300513507830516933879565438304668183143470276536696); + zs[282] = BOOST_MATH_TEST_VALUE(RealType, 446757083035153196.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[282] = BOOST_MATH_TEST_VALUE(RealType, 37.02908764699829400243686962505878682539242575730874103256160022984465712010716817361916571773853546); + zs[283] = BOOST_MATH_TEST_VALUE(RealType, 446757083035153196.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[283] = BOOST_MATH_TEST_VALUE(RealType, 37.02908764699829400352661657691852074762947010360082635121697561514628341391528501142658520743630365); + zs[284] = BOOST_MATH_TEST_VALUE(RealType, 446757083035153197.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[284] = BOOST_MATH_TEST_VALUE(RealType, 37.02908764699829400461636352877825466864773866005926109377149011203944059666845076630112677861392210); + zs[285] = BOOST_MATH_TEST_VALUE(RealType, 446757083035153197.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[285] = BOOST_MATH_TEST_VALUE(RealType, 37.02908764699829400570611048063798858844723142668404526295220250731989472007381590773195282652560254); + zs[286] = BOOST_MATH_TEST_VALUE(RealType, 893514166070306395.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[286] = BOOST_MATH_TEST_VALUE(RealType, 37.70416794018226555320136342385675657230684506000440195815556999407621228993843426869578896616407720); + zs[287] = BOOST_MATH_TEST_VALUE(RealType, 893514166070306396.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[287] = BOOST_MATH_TEST_VALUE(RealType, 37.70416794018226555374649355515368083078169249852645520316613216452931331516160499635557395351284563); + zs[288] = BOOST_MATH_TEST_VALUE(RealType, 893514166070306396.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[288] = BOOST_MATH_TEST_VALUE(RealType, 37.70416794018226555429162368645060508895169517366721864271816097307727758099155612982661657855356471); + zs[289] = BOOST_MATH_TEST_VALUE(RealType, 893514166070306397.0000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[289] = BOOST_MATH_TEST_VALUE(RealType, 37.70416794018226555483675381774752934681685308542669227715271188344812250649110477219885655515210238); + zs[290] = BOOST_MATH_TEST_VALUE(RealType, 893514166070306397.5000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[290] = BOOST_MATH_TEST_VALUE(RealType, 37.70416794018226555538188394904445360437716623380487610681084035936986551015065343719767859121005169); + zs[291] = BOOST_MATH_TEST_VALUE(RealType, 1787028332140612795.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[291] = BOOST_MATH_TEST_VALUE(RealType, 38.37956071956958972598482293827813436342029687218073991385854219392107138609564999755336816382782882); + zs[292] = BOOST_MATH_TEST_VALUE(RealType, 1787028332140612796.000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[292] = BOOST_MATH_TEST_VALUE(RealType, 38.37956071956958972625751198813595667063367232609526061670475810039080545891532051379509177129548620); + zs[293] = BOOST_MATH_TEST_VALUE(RealType, 1787028332140612796.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[293] = BOOST_MATH_TEST_VALUE(RealType, 38.37956071956958972653020103799377897777080019019814937016569594146159440469991463659659940941732350); + zs[294] = BOOST_MATH_TEST_VALUE(RealType, 1787028332140612797.000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[294] = BOOST_MATH_TEST_VALUE(RealType, 38.37956071956958972680289008785160128483168046448940617428400853168904877026810016036680752500851451); + zs[295] = BOOST_MATH_TEST_VALUE(RealType, 1787028332140612797.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[295] = BOOST_MATH_TEST_VALUE(RealType, 38.37956071956958972707557913770942359181631314896903102910234868562877910240275114213945213826099662); + zs[296] = BOOST_MATH_TEST_VALUE(RealType, 3574056664281225595.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[296] = BOOST_MATH_TEST_VALUE(RealType, 39.05525549414090357397288405889113661262090376242531189140228166174913957175516846453031303867939318); + zs[297] = BOOST_MATH_TEST_VALUE(RealType, 3574056664281225596.000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[297] = BOOST_MATH_TEST_VALUE(RealType, 39.05525549414090357410928851167839535330306428730324674240848562357412341096307490721136888569495897); + zs[298] = BOOST_MATH_TEST_VALUE(RealType, 3574056664281225596.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[298] = BOOST_MATH_TEST_VALUE(RealType, 39.05525549414090357424569296446565409396615412474444985847532731674289519382422208292858248536499652); + zs[299] = BOOST_MATH_TEST_VALUE(RealType, 3574056664281225597.000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[299] = BOOST_MATH_TEST_VALUE(RealType, 39.05525549414090357438209741725291283461017327474892123960814086338854298164097945903007554014878729); + zs[300] = BOOST_MATH_TEST_VALUE(RealType, 3574056664281225597.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[300] = BOOST_MATH_TEST_VALUE(RealType, 39.05525549414090357451850187004017157523512173731666088581226038564415483571347831884995089329885967); + zs[301] = BOOST_MATH_TEST_VALUE(RealType, 7148113328562451195.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[301] = BOOST_MATH_TEST_VALUE(RealType, 39.73124228804812727783498401939051909927376255844770721021129458386841998457593172268879366938255929); + zs[302] = BOOST_MATH_TEST_VALUE(RealType, 7148113328562451196.000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[302] = BOOST_MATH_TEST_VALUE(RealType, 39.73124228804812727790321522786904454299244826066833347409818515980037701571602976213801388272915974); + zs[303] = BOOST_MATH_TEST_VALUE(RealType, 7148113328562451196.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[303] = BOOST_MATH_TEST_VALUE(RealType, 39.73124228804812727797144643634756998670636416712076247668418626969651461616512047870718042117101998); + zs[304] = BOOST_MATH_TEST_VALUE(RealType, 7148113328562451197.000000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[304] = BOOST_MATH_TEST_VALUE(RealType, 39.73124228804812727803967764482609543041551027780499421796996498319224892410706675338130329932555167); + zs[305] = BOOST_MATH_TEST_VALUE(RealType, 7148113328562451197.500000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[305] = BOOST_MATH_TEST_VALUE(RealType, 39.73124228804812727810790885330462087411988659272102869795618836992299607772559151572452093096440915); + zs[306] = BOOST_MATH_TEST_VALUE(RealType, 14296226657124902395.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[306] = BOOST_MATH_TEST_VALUE(RealType, 40.40751160764139677831479038829047423830224736434558035742829431520714244678484344822558100019565300); + zs[307] = BOOST_MATH_TEST_VALUE(RealType, 14296226657124902396.00000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[307] = BOOST_MATH_TEST_VALUE(RealType, 40.40751160764139677834892001619303911408381392078589832744493031890002310138573922908458466967872121); + zs[308] = BOOST_MATH_TEST_VALUE(RealType, 14296226657124902396.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[308] = BOOST_MATH_TEST_VALUE(RealType, 40.40751160764139677838304964409560398986418751480292907711652892277321787302547236270065870054677767); + zs[309] = BOOST_MATH_TEST_VALUE(RealType, 14296226657124902397.00000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[309] = BOOST_MATH_TEST_VALUE(RealType, 40.40751160764139677841717927199816886564336814639667260644317354730947215019219733421649211897790576); + zs[310] = BOOST_MATH_TEST_VALUE(RealType, 14296226657124902397.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[310] = BOOST_MATH_TEST_VALUE(RealType, 40.40751160764139677845130889990073374142135581556712891542494761299153132137405987788970971812438268); + zs[311] = BOOST_MATH_TEST_VALUE(RealType, 28592453314249804795.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[311] = BOOST_MATH_TEST_VALUE(RealType, 41.08405441107886601512650396438190311342038447614710861375772179966198427428166270594434592188846742); + zs[312] = BOOST_MATH_TEST_VALUE(RealType, 28592453314249804796.00000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[312] = BOOST_MATH_TEST_VALUE(RealType, 41.08405441107886601514357556751058240524595848620754197445685531919168072376579651061145028382528801); + zs[313] = BOOST_MATH_TEST_VALUE(RealType, 28592453314249804796.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[313] = BOOST_MATH_TEST_VALUE(RealType, 41.08405441107886601516064717063926169707123413145503282985377893953090450589428836799851249587190162); + zs[314] = BOOST_MATH_TEST_VALUE(RealType, 28592453314249804797.00000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[314] = BOOST_MATH_TEST_VALUE(RealType, 41.08405441107886601517771877376794098889621141188958117994850309268653710559007455198782311971328872); + zs[315] = BOOST_MATH_TEST_VALUE(RealType, 28592453314249804797.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[315] = BOOST_MATH_TEST_VALUE(RealType, 41.08405441107886601519479037689662028072089032751118702474103821066546000777609078929437453810254475); + zs[316] = BOOST_MATH_TEST_VALUE(RealType, 57184906628499609595.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[316] = BOOST_MATH_TEST_VALUE(RealType, 41.76086208028139568641375217383589970289192285524027455320442791145685452599382947676049441268469929); + zs[317] = BOOST_MATH_TEST_VALUE(RealType, 57184906628499609596.00000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[317] = BOOST_MATH_TEST_VALUE(RealType, 41.76086208028139568642229126383956807166540537938630285140147251930665093550545985675206920712567685); + zs[318] = BOOST_MATH_TEST_VALUE(RealType, 57184906628499609596.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[318] = BOOST_MATH_TEST_VALUE(RealType, 41.76086208028139568643083035384323644043881328226871966400896425186789096638830671067070738275427294); + zs[319] = BOOST_MATH_TEST_VALUE(RealType, 57184906628499609597.00000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[319] = BOOST_MATH_TEST_VALUE(RealType, 41.76086208028139568643936944384690480921214656388752499102690441367931200542104387248813871592477758); + zs[320] = BOOST_MATH_TEST_VALUE(RealType, 57184906628499609597.50000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[320] = BOOST_MATH_TEST_VALUE(RealType, 41.76086208028139568644790853385057317798540522424271883245529430927965143938234514196380710928511911); + zs[321] = BOOST_MATH_TEST_VALUE(RealType, 114369813256999219195.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[321] = BOOST_MATH_TEST_VALUE(RealType, 42.43792639501924322372868984974754605356116106151045232696170539516224623515519078402644518190519926); + zs[322] = BOOST_MATH_TEST_VALUE(RealType, 114369813256999219196.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[322] = BOOST_MATH_TEST_VALUE(RealType, 42.43792639501924322373296098832611092973985577032241411056103015380922288812869321261489035558578068); + zs[323] = BOOST_MATH_TEST_VALUE(RealType, 114369813256999219196.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[323] = BOOST_MATH_TEST_VALUE(RealType, 42.43792639501924322373723212690467580591853181653964600305518073422329365062099792146842039711851587); + zs[324] = BOOST_MATH_TEST_VALUE(RealType, 114369813256999219197.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[324] = BOOST_MATH_TEST_VALUE(RealType, 42.43792639501924322374150326548324068209718920016214800444415729953689875272821439439139550720636184); + zs[325] = BOOST_MATH_TEST_VALUE(RealType, 114369813256999219197.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[325] = BOOST_MATH_TEST_VALUE(RealType, 42.43792639501924322374577440406180555827582792118992011472796001288247842454645211304904107994414835); + zs[326] = BOOST_MATH_TEST_VALUE(RealType, 228739626513998438395.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[326] = BOOST_MATH_TEST_VALUE(RealType, 43.11523950893999669327806140448370484701263856771509543516859403088443747042918192039960542776140113); + zs[327] = BOOST_MATH_TEST_VALUE(RealType, 228739626513998438396.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[327] = BOOST_MATH_TEST_VALUE(RealType, 43.11523950893999669328019774638303987145009950720434908964235203327796824314750703851313533491796197); + zs[328] = BOOST_MATH_TEST_VALUE(RealType, 228739626513998438396.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[328] = BOOST_MATH_TEST_VALUE(RealType, 43.11523950893999669328233408828237489588755577928156076930831411008263669948642260552727690669574065); + zs[329] = BOOST_MATH_TEST_VALUE(RealType, 228739626513998438397.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[329] = BOOST_MATH_TEST_VALUE(RealType, 43.11523950893999669328447043018170992032500738394673047416648028169788047545599558661614805384499252); + zs[330] = BOOST_MATH_TEST_VALUE(RealType, 228739626513998438397.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[330] = BOOST_MATH_TEST_VALUE(RealType, 43.11523950893999669328660677208104494476245432119985820421685056852313720706629294682011828585641323); + zs[331] = BOOST_MATH_TEST_VALUE(RealType, 457479253027996876795.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[331] = BOOST_MATH_TEST_VALUE(RealType, 43.79279392736654193606989766502723445979048572229231465829706451573324407857444527395822448178986007); + zs[332] = BOOST_MATH_TEST_VALUE(RealType, 457479253027996876796.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[332] = BOOST_MATH_TEST_VALUE(RealType, 43.79279392736654193607096621073065351333846093585545869129269708118665210449739974464793810457198837); + zs[333] = BOOST_MATH_TEST_VALUE(RealType, 457479253027996876796.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[333] = BOOST_MATH_TEST_VALUE(RealType, 43.79279392736654193607203475643407256688643498213820013963448351011348495071834636284671248086199837); + zs[334] = BOOST_MATH_TEST_VALUE(RealType, 457479253027996876797.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[334] = BOOST_MATH_TEST_VALUE(RealType, 43.79279392736654193607310330213749162043440786114053900332242380506462727521879656021426226958496105); + zs[335] = BOOST_MATH_TEST_VALUE(RealType, 457479253027996876797.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[335] = BOOST_MATH_TEST_VALUE(RealType, 43.79279392736654193607417184784091067398237957286247528235651796859096373598026176840193967666274999); + zs[336] = BOOST_MATH_TEST_VALUE(RealType, 914958506055993753595.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[336] = BOOST_MATH_TEST_VALUE(RealType, 44.47058248671115033941120173207718823699817102786625650029939710664855583164086060137262356501253929); + zs[337] = BOOST_MATH_TEST_VALUE(RealType, 914958506055993753596.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[337] = BOOST_MATH_TEST_VALUE(RealType, 44.47058248671115033941173618678344688398957490049536600533594487394087848036718134980749301722731352); + zs[338] = BOOST_MATH_TEST_VALUE(RealType, 914958506055993753596.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[338] = BOOST_MATH_TEST_VALUE(RealType, 44.47058248671115033941227064148970553098097848120073848954722626642651975773633390627303626978835838); + zs[339] = BOOST_MATH_TEST_VALUE(RealType, 914958506055993753597.0000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[339] = BOOST_MATH_TEST_VALUE(RealType, 44.47058248671115033941280509619596417797238176998237395293324128442445599007428498582436405251992611); + zs[340] = BOOST_MATH_TEST_VALUE(RealType, 914958506055993753597.5000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[340] = BOOST_MATH_TEST_VALUE(RealType, 44.47058248671115033941333955090222282496378476684027239549398992825366350370700130351606424938744046); + zs[341] = BOOST_MATH_TEST_VALUE(RealType, 1829917012111987507195.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[341] = BOOST_MATH_TEST_VALUE(RealType, 45.14859833536711018965056513946129162668050892446138815518047627051292293694179480812556619549313064); + zs[342] = BOOST_MATH_TEST_VALUE(RealType, 1829917012111987507196.000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[342] = BOOST_MATH_TEST_VALUE(RealType, 45.14859833536711018965083245509992947480958471611883906387208610588018636449042232643595158517689720); + zs[343] = BOOST_MATH_TEST_VALUE(RealType, 1829917012111987507196.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[343] = BOOST_MATH_TEST_VALUE(RealType, 45.14859833536711018965109977073856732293866043477021415438855308555056542965567216193304047624510243); + zs[344] = BOOST_MATH_TEST_VALUE(RealType, 1829917012111987507197.000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[344] = BOOST_MATH_TEST_VALUE(RealType, 45.14859833536711018965136708637720517106773608041551342672987720956394620826737050999033807936751171); + zs[345] = BOOST_MATH_TEST_VALUE(RealType, 1829917012111987507197.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[345] = BOOST_MATH_TEST_VALUE(RealType, 45.14859833536711018965163440201584301919681165305473688089605847796021477615534356598131691567620401); + zs[346] = BOOST_MATH_TEST_VALUE(RealType, 3659834024223975014395.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[346] = BOOST_MATH_TEST_VALUE(RealType, 45.82683491595294384485628384441561575887407603040339098125590070863967384748247436205190625734118932); + zs[347] = BOOST_MATH_TEST_VALUE(RealType, 3659834024223975014396.000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[347] = BOOST_MATH_TEST_VALUE(RealType, 45.82683491595294384485641754511313063272748535532950445484624262655288217284652009573499125927466059); + zs[348] = BOOST_MATH_TEST_VALUE(RealType, 3659834024223975014396.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[348] = BOOST_MATH_TEST_VALUE(RealType, 45.82683491595294384485655124581064550658089466199799712956218289431848176597105298416189805067325255); + zs[349] = BOOST_MATH_TEST_VALUE(RealType, 3659834024223975014397.000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[349] = BOOST_MATH_TEST_VALUE(RealType, 45.82683491595294384485668494650816038043430395040886900540372151194146008896781208120907682446939362); + zs[350] = BOOST_MATH_TEST_VALUE(RealType, 3659834024223975014397.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[350] = BOOST_MATH_TEST_VALUE(RealType, 45.82683491595294384485681864720567525428771322056212008237085847942680460394853644075297572979160046); + zs[351] = BOOST_MATH_TEST_VALUE(RealType, 7319668048447950028795.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[351] = BOOST_MATH_TEST_VALUE(RealType, 46.50528594879636847126393241245457792327323285439769210757132628000124992150426204283421914567175062); + zs[352] = BOOST_MATH_TEST_VALUE(RealType, 7319668048447950028796.000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[352] = BOOST_MATH_TEST_VALUE(RealType, 46.50528594879636847126399928363674654174459322702292762434851831465004062253014895509105956594317070); + zs[353] = BOOST_MATH_TEST_VALUE(RealType, 7319668048447950028796.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[353] = BOOST_MATH_TEST_VALUE(RealType, 46.50528594879636847126406615481891516021595359508227640114777678096555406499545632733378131414467863); + zs[354] = BOOST_MATH_TEST_VALUE(RealType, 7319668048447950028797.000000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[354] = BOOST_MATH_TEST_VALUE(RealType, 46.50528594879636847126413302600108377868731395857573843796910167894841388830471951540349934300433290); + zs[355] = BOOST_MATH_TEST_VALUE(RealType, 7319668048447950028797.500000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[355] = BOOST_MATH_TEST_VALUE(RealType, 46.50528594879636847126419989718325239715867431750331373481249300859924373186247387514132847746950458); + zs[356] = BOOST_MATH_TEST_VALUE(RealType, 14639336096895900057595.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[356] = BOOST_MATH_TEST_VALUE(RealType, 47.18394541655595353726859251633008985542795093110678176543525265373739682696782954566791223497853278); + zs[357] = BOOST_MATH_TEST_VALUE(RealType, 14639336096895900057596.00000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[357] = BOOST_MATH_TEST_VALUE(RealType, 47.18394541655595353726862596204760125343156619941158006313108818081160492017518779133670783309629777); + zs[358] = BOOST_MATH_TEST_VALUE(RealType, 14639336096895900057596.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[358] = BOOST_MATH_TEST_VALUE(RealType, 47.18394541655595353726865940776511265143518146657454680704151512746306706865464848189500564360309989); + zs[359] = BOOST_MATH_TEST_VALUE(RealType, 14639336096895900057597.00000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[359] = BOOST_MATH_TEST_VALUE(RealType, 47.18394541655595353726869285348262404943879673259568199716653349369186125242127832914687715856217447); + zs[360] = BOOST_MATH_TEST_VALUE(RealType, 14639336096895900057597.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[360] = BOOST_MATH_TEST_VALUE(RealType, 47.18394541655595353726872629920013544744241199747498563350614327949806545149014404489639386204785740); + zs[361] = BOOST_MATH_TEST_VALUE(RealType, 29278672193791800115195.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[361] = BOOST_MATH_TEST_VALUE(RealType, 47.86280754988806283869898641897124874506124538863135227082696257498669984107495807372390982964298765); + zs[362] = BOOST_MATH_TEST_VALUE(RealType, 29278672193791800115196.00000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[362] = BOOST_MATH_TEST_VALUE(RealType, 47.86280754988806283869900314675402022865409817077027470411443725892317844329022583627504362168195643); + zs[363] = BOOST_MATH_TEST_VALUE(RealType, 29278672193791800115196.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[363] = BOOST_MATH_TEST_VALUE(RealType, 47.86280754988806283869901987453679171224695095262365180124915574419262646462548365901592461409111822); + zs[364] = BOOST_MATH_TEST_VALUE(RealType, 29278672193791800115197.00000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[364] = BOOST_MATH_TEST_VALUE(RealType, 47.86280754988806283869903660231956319583980373419148356223111803079505365563012583778554165327574562); + zs[365] = BOOST_MATH_TEST_VALUE(RealType, 29278672193791800115197.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[365] = BOOST_MATH_TEST_VALUE(RealType, 47.86280754988806283869905333010233467943265651547376998706032411873046976685354666842288358514164676); + zs[366] = BOOST_MATH_TEST_VALUE(RealType, 58557344387583600230395.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[366] = BOOST_MATH_TEST_VALUE(RealType, 48.54186681407528073135559278566008113536709515501428437696966434378641333376283319594991159197529021); + zs[367] = BOOST_MATH_TEST_VALUE(RealType, 58557344387583600230396.00000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[367] = BOOST_MATH_TEST_VALUE(RealType, 48.54186681407528073135560115194668751765523308763717061100258732052320085375724211692206634017417835); + zs[368] = BOOST_MATH_TEST_VALUE(RealType, 58557344387583600230396.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[368] = BOOST_MATH_TEST_VALUE(RealType, 48.54186681407528073135560951823329389994337102018864925304218198091079593677878710603131163142362874); + zs[369] = BOOST_MATH_TEST_VALUE(RealType, 58557344387583600230397.00000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[369] = BOOST_MATH_TEST_VALUE(RealType, 48.54186681407528073135561788451990028223150895266872030308844832494919980201645683875988736222168974); + zs[370] = BOOST_MATH_TEST_VALUE(RealType, 58557344387583600230397.50000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[370] = BOOST_MATH_TEST_VALUE(RealType, 48.54186681407528073135562625080650666451964688507738376114138635263841366865923999059003342903518356); + zs[371] = BOOST_MATH_TEST_VALUE(RealType, 117114688775167200460795.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[371] = BOOST_MATH_TEST_VALUE(RealType, 49.22111789654023166917393998803300413713566990752077661423207867413014717692257524895032484088002638); + zs[372] = BOOST_MATH_TEST_VALUE(RealType, 117114688775167200460796.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[372] = BOOST_MATH_TEST_VALUE(RealType, 49.22111789654023166917394417234185551157598495411034248495095320790548697334562191650844059041476016); + zs[373] = BOOST_MATH_TEST_VALUE(RealType, 117114688775167200460796.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[373] = BOOST_MATH_TEST_VALUE(RealType, 49.22111789654023166917394835665070688601630000068205128804849669368418438086966818342794853392747975); + zs[374] = BOOST_MATH_TEST_VALUE(RealType, 117114688775167200460797.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[374] = BOOST_MATH_TEST_VALUE(RealType, 49.22111789654023166917395254095955826045661504723590302352470913146623955193835326180329431243220210); + zs[375] = BOOST_MATH_TEST_VALUE(RealType, 117114688775167200460797.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[375] = BOOST_MATH_TEST_VALUE(RealType, 49.22111789654023166917395672526840963489693009377189769137959052125165263899531636372892356694099191); + zs[376] = BOOST_MATH_TEST_VALUE(RealType, 234229377550334400921595.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[376] = BOOST_MATH_TEST_VALUE(RealType, 49.90055569517561383066060297122022964231700635525977853025814596096151371140753421576539605970700861); + zs[377] = BOOST_MATH_TEST_VALUE(RealType, 234229377550334400921596.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[377] = BOOST_MATH_TEST_VALUE(RealType, 49.90055569517561383066060506394202934831808914814125072797602761455168951650504013758145865501313880); + zs[378] = BOOST_MATH_TEST_VALUE(RealType, 234229377550334400921596.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[378] = BOOST_MATH_TEST_VALUE(RealType, 49.90055569517561383066060715666382905431917194101825740114881990816754906595137018341539085351050643); + zs[379] = BOOST_MATH_TEST_VALUE(RealType, 234229377550334400921597.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[379] = BOOST_MATH_TEST_VALUE(RealType, 49.90055569517561383066060924938562876032025473389079854977652284180909237880745343386717050876814609); + zs[380] = BOOST_MATH_TEST_VALUE(RealType, 234229377550334400921597.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[380] = BOOST_MATH_TEST_VALUE(RealType, 49.90055569517561383066061134210742846632133752675887417385913641547631947413421896953677547435497030); + zs[381] = BOOST_MATH_TEST_VALUE(RealType, 468458755100668801843195.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[381] = BOOST_MATH_TEST_VALUE(RealType, 50.58017530742747300164561317487206707303802427632079894747824628285067346330316982070607470750276538); + zs[382] = BOOST_MATH_TEST_VALUE(RealType, 468458755100668801843196.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[382] = BOOST_MATH_TEST_VALUE(RealType, 50.58017530742747300164561422150925313668179339445554345979082699726978571301972512528171705027852141); + zs[383] = BOOST_MATH_TEST_VALUE(RealType, 468458755100668801843196.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[383] = BOOST_MATH_TEST_VALUE(RealType, 50.58017530742747300164561526814643920032556251258917128490508500217173303906663500224210793499988710); + zs[384] = BOOST_MATH_TEST_VALUE(RealType, 468458755100668801843197.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[384] = BOOST_MATH_TEST_VALUE(RealType, 50.58017530742747300164561631478362526396933163072168242282102029755651544382718152403729363436471691); + zs[385] = BOOST_MATH_TEST_VALUE(RealType, 468458755100668801843197.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[385] = BOOST_MATH_TEST_VALUE(RealType, 50.58017530742747300164561736142081132761310074885307687353863288342413292968464676311732042107085768); + zs[386] = BOOST_MATH_TEST_VALUE(RealType, 936917510201337603686395.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[386] = BOOST_MATH_TEST_VALUE(RealType, 51.25997202007432193650352849612123815153250964411406464644865827787863050268704653756510450468474031); + zs[387] = BOOST_MATH_TEST_VALUE(RealType, 936917510201337603686396.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[387] = BOOST_MATH_TEST_VALUE(RealType, 51.25997202007432193650352901957441589305691661161083072884708823213628905949128284896989478240192251); + zs[388] = BOOST_MATH_TEST_VALUE(RealType, 936917510201337603686396.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[388] = BOOST_MATH_TEST_VALUE(RealType, 51.25997202007432193650352954302759363458132357910731756493593984931372662548695557807862606879386793); + zs[389] = BOOST_MATH_TEST_VALUE(RealType, 936917510201337603686397.0000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[389] = BOOST_MATH_TEST_VALUE(RealType, 51.25997202007432193650353006648077137610573054660352515471521312941094320097205602449555540058022069); + zs[390] = BOOST_MATH_TEST_VALUE(RealType, 936917510201337603686397.5000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[390] = BOOST_MATH_TEST_VALUE(RealType, 51.25997202007432193650353058993394911763013751409945349818490807242793878624457548782493981448062444); + zs[391] = BOOST_MATH_TEST_VALUE(RealType, 1873835020402675207372795.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[391] = BOOST_MATH_TEST_VALUE(RealType, 51.93994129964973319883394011661732720227488151829956542922851698538021522622862634588975916535124465); + zs[392] = BOOST_MATH_TEST_VALUE(RealType, 1873835020402675207372796.000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[392] = BOOST_MATH_TEST_VALUE(RealType, 51.93994129964973319883394037840949666263721928778536979472743471765062641780884974428552475054902084); + zs[393] = BOOST_MATH_TEST_VALUE(RealType, 1873835020402675207372796.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[393] = BOOST_MATH_TEST_VALUE(RealType, 51.93994129964973319883394064020166612299955705727110433050351349588454562078192850302725642051437996); + zs[394] = BOOST_MATH_TEST_VALUE(RealType, 1873835020402675207372797.000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[394] = BOOST_MATH_TEST_VALUE(RealType, 51.93994129964973319883394090199383558336189482675676903655675332008197283518512140013107034885992383); + zs[395] = BOOST_MATH_TEST_VALUE(RealType, 1873835020402675207372797.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[395] = BOOST_MATH_TEST_VALUE(RealType, 51.93994129964973319883394116378600504372423259624236391288715419024290806105568721361308270919825427); + zs[396] = BOOST_MATH_TEST_VALUE(RealType, 3747670040805350414745595.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[396] = BOOST_MATH_TEST_VALUE(RealType, 52.62007878346056146247780063098329226609433897361719442410811576805026853884099017649824976199940727); + zs[397] = BOOST_MATH_TEST_VALUE(RealType, 3747670040805350414745596.000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[397] = BOOST_MATH_TEST_VALUE(RealType, 52.62007878346056146247780076191134345038026463351561571010291457420668319272155773248903735412098606); + zs[398] = BOOST_MATH_TEST_VALUE(RealType, 3747670040805350414745596.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[398] = BOOST_MATH_TEST_VALUE(RealType, 52.62007878346056146247780089283939463466619029341401953424656920848830320946683691731827935418315374); + zs[399] = BOOST_MATH_TEST_VALUE(RealType, 3747670040805350414745597.000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[399] = BOOST_MATH_TEST_VALUE(RealType, 52.62007878346056146247780102376744581895211595331240589653907967089512858908148627964024886502244786); + zs[400] = BOOST_MATH_TEST_VALUE(RealType, 3747670040805350414745597.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[400] = BOOST_MATH_TEST_VALUE(RealType, 52.62007878346056146247780115469549700323804161321077479698044596142715933157016436810921898947540595); + zs[401] = BOOST_MATH_TEST_VALUE(RealType, 7495340081610700829491195.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[401] = BOOST_MATH_TEST_VALUE(RealType, 53.30038027115703847068472014163526750381732629683760414977652728398062761064036427478928134573151026); + zs[402] = BOOST_MATH_TEST_VALUE(RealType, 7495340081610700829491196.000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[402] = BOOST_MATH_TEST_VALUE(RealType, 53.30038027115703847068472020711487963758347144588443668839144673170417842073148009181357862290045625); + zs[403] = BOOST_MATH_TEST_VALUE(RealType, 7495340081610700829491196.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[403] = BOOST_MATH_TEST_VALUE(RealType, 53.30038027115703847068472027259449177134961659493126486046636359084837495933357183600363141237458038); + zs[404] = BOOST_MATH_TEST_VALUE(RealType, 7495340081610700829491197.000000000000000000000000000000000000000000000000000000000000000000000000000); + ws[404] = BOOST_MATH_TEST_VALUE(RealType, 53.30038027115703847068472033807410390511576174397808866600127786141321722644722197229302630959626241); + zs[405] = BOOST_MATH_TEST_VALUE(RealType, 7495340081610700829491197.500000000000000000000000000000000000000000000000000000000000000000000000000); + ws[405] = BOOST_MATH_TEST_VALUE(RealType, 53.30038027115703847068472040355371603888190689302490810499618954339870522207301296561534991000788215); + zs[406] = BOOST_MATH_TEST_VALUE(RealType, 14990680163221401658982395.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[406] = BOOST_MATH_TEST_VALUE(RealType, 53.98084171681467787755159106988030207256464803953686021292403364974364396941211684129467560225025844); + zs[407] = BOOST_MATH_TEST_VALUE(RealType, 14990680163221401658982396.00000000000000000000000000000000000000000000000000000000000000000000000000); + ws[407] = BOOST_MATH_TEST_VALUE(RealType, 53.98084171681467787755159110262771031569979287106207337111164630527719599256075269757642116173324604); + zs[408] = BOOST_MATH_TEST_VALUE(RealType, 14990680163221401658982396.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[408] = BOOST_MATH_TEST_VALUE(RealType, 53.98084171681467787755159113537511855883493770258728543740166820971925093788175661756375535881272212); + zs[409] = BOOST_MATH_TEST_VALUE(RealType, 14990680163221401658982397.00000000000000000000000000000000000000000000000000000000000000000000000000); + ws[409] = BOOST_MATH_TEST_VALUE(RealType, 53.98084171681467787755159116812252680197008253411249641179409936306980880537520142720731872132566761); + zs[410] = BOOST_MATH_TEST_VALUE(RealType, 14990680163221401658982397.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[410] = BOOST_MATH_TEST_VALUE(RealType, 53.98084171681467787755159120086993504510522736563770629428893976532886959504115995245775177710906343); + zs[411] = BOOST_MATH_TEST_VALUE(RealType, 29981360326442803317964795.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[411] = BOOST_MATH_TEST_VALUE(RealType, 54.66145922149126997883966829050076057809819624810698856060190684715860519885138243117049542515690575); + zs[412] = BOOST_MATH_TEST_VALUE(RealType, 29981360326442803317964796.00000000000000000000000000000000000000000000000000000000000000000000000000); + ws[412] = BOOST_MATH_TEST_VALUE(RealType, 54.66145922149126997883966830687817369095124791690640312773832665397033630245360990420711923237519040); + zs[413] = BOOST_MATH_TEST_VALUE(RealType, 29981360326442803317964796.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[413] = BOOST_MATH_TEST_VALUE(RealType, 54.66145922149126997883966832325558680380429958570581742183631816048451505614209676496718115749779207); + zs[414] = BOOST_MATH_TEST_VALUE(RealType, 29981360326442803317964797.00000000000000000000000000000000000000000000000000000000000000000000000000); + ws[414] = BOOST_MATH_TEST_VALUE(RealType, 54.66145922149126997883966833963299991665735125450523144289588136670114145991685211886837782686269634); + zs[415] = BOOST_MATH_TEST_VALUE(RealType, 29981360326442803317964797.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[415] = BOOST_MATH_TEST_VALUE(RealType, 54.66145922149126997883966835601041302951040292330464519091701627262021551377788507132840586680788883); + zs[416] = BOOST_MATH_TEST_VALUE(RealType, 59962720652885606635929595.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[416] = BOOST_MATH_TEST_VALUE(RealType, 55.34222902622527492285082008163468252474529527756164189713179328516307078639643598839828067574841328); + zs[417] = BOOST_MATH_TEST_VALUE(RealType, 59962720652885606635929596.00000000000000000000000000000000000000000000000000000000000000000000000000); + ws[417] = BOOST_MATH_TEST_VALUE(RealType, 55.34222902622527492285082008982519917191333658353569747274840211384876036940931564577326778739937441); + zs[418] = BOOST_MATH_TEST_VALUE(RealType, 59962720652885606635929596.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[418] = BOOST_MATH_TEST_VALUE(RealType, 55.34222902622527492285082009801571581908137788950975298008978578466315835299634785170757312736330548); + zs[419] = BOOST_MATH_TEST_VALUE(RealType, 59962720652885606635929597.00000000000000000000000000000000000000000000000000000000000000000000000000); + ws[419] = BOOST_MATH_TEST_VALUE(RealType, 55.34222902622527492285082010620623246624941919548380841915594429760626473715753374464347315926011244); + zs[420] = BOOST_MATH_TEST_VALUE(RealType, 59962720652885606635929597.50000000000000000000000000000000000000000000000000000000000000000000000000); + ws[420] = BOOST_MATH_TEST_VALUE(RealType, 55.34222902622527492285082011439674911341746050145786378994687765267807952189287446302324434670970122); + zs[421] = BOOST_MATH_TEST_VALUE(RealType, 119925441305771213271859195.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[421] = BOOST_MATH_TEST_VALUE(RealType, 56.02314750544467013866005744109529240544406880337828236840721418957676877238000505091488001795651201); + zs[422] = BOOST_MATH_TEST_VALUE(RealType, 119925441305771213271859196.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[422] = BOOST_MATH_TEST_VALUE(RealType, 56.02314750544467013866005744519143435523841317719330604019896557212385538515430219236320724445839119); + zs[423] = BOOST_MATH_TEST_VALUE(RealType, 119925441305771213271859196.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[423] = BOOST_MATH_TEST_VALUE(RealType, 56.02314750544467013866005744928757630503275755100832969491810004678229237746659550758636675578428967); + zs[424] = BOOST_MATH_TEST_VALUE(RealType, 119925441305771213271859197.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[424] = BOOST_MATH_TEST_VALUE(RealType, 56.02314750544467013866005745338371825482710192482335333256461761355207974931688513892197266161560978); + zs[425] = BOOST_MATH_TEST_VALUE(RealType, 119925441305771213271859197.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[425] = BOOST_MATH_TEST_VALUE(RealType, 56.02314750544467013866005745747986020462144629863837695313851827243321750070517122870763907163375388); + zs[426] = BOOST_MATH_TEST_VALUE(RealType, 239850882611542426543718395.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[426] = BOOST_MATH_TEST_VALUE(RealType, 56.70421116075780244670574964727179808543930859767697217112818536350797368921774672172293682473225055); + zs[427] = BOOST_MATH_TEST_VALUE(RealType, 239850882611542426543718396.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[427] = BOOST_MATH_TEST_VALUE(RealType, 56.70421116075780244670574964932030053738025902058853016475070045198068571647010983824313512015423280); + zs[428] = BOOST_MATH_TEST_VALUE(RealType, 239850882611542426543718396.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[428] = BOOST_MATH_TEST_VALUE(RealType, 56.70421116075780244670574965136880298932120944350008815410413130287269798806862098501994253827046627); + zs[429] = BOOST_MATH_TEST_VALUE(RealType, 239850882611542426543718397.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[429] = BOOST_MATH_TEST_VALUE(RealType, 56.70421116075780244670574965341730544126215986641164613918847791618401050401328017984950518641062487); + zs[430] = BOOST_MATH_TEST_VALUE(RealType, 239850882611542426543718397.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[430] = BOOST_MATH_TEST_VALUE(RealType, 56.70421116075780244670574965546580789320311028932320412000374029191462326430408744052796917190438247); + zs[431] = BOOST_MATH_TEST_VALUE(RealType, 479701765223084853087436795.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[431] = BOOST_MATH_TEST_VALUE(RealType, 57.38541661510006336367991531277886245290205731446363101869106286487064764410271386990754234755642855); + zs[432] = BOOST_MATH_TEST_VALUE(RealType, 479701765223084853087436796.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[432] = BOOST_MATH_TEST_VALUE(RealType, 57.38541661510006336367991531380332442757645839844021552846525681769285188952591218983091633569244771); + zs[433] = BOOST_MATH_TEST_VALUE(RealType, 479701765223084853087436796.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[433] = BOOST_MATH_TEST_VALUE(RealType, 57.38541661510006336367991531482778640225085948241680003717195267123172907693535943393631252233528007); + zs[434] = BOOST_MATH_TEST_VALUE(RealType, 479701765223084853087436797.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[434] = BOOST_MATH_TEST_VALUE(RealType, 57.38541661510006336367991531585224837692526056639338454481115042548727920633105560444873053490520850); + zs[435] = BOOST_MATH_TEST_VALUE(RealType, 479701765223084853087436797.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[435] = BOOST_MATH_TEST_VALUE(RealType, 57.38541661510006336367991531687671035159966165036996905138285008045950227771300070359317000082251587); + zs[436] = BOOST_MATH_TEST_VALUE(RealType, 959403530446169706174873595.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[436] = BOOST_MATH_TEST_VALUE(RealType, 58.06676060721227034055554057973484251633179872231742522867479304887734423959008177810507929372980502); + zs[437] = BOOST_MATH_TEST_VALUE(RealType, 959403530446169706174873596.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[437] = BOOST_MATH_TEST_VALUE(RealType, 58.06676060721227034055554058024717646818581642633527555088229229848978856650264562082058837873668151); + zs[438] = BOOST_MATH_TEST_VALUE(RealType, 959403530446169706174873596.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[438] = BOOST_MATH_TEST_VALUE(RealType, 58.06676060721227034055554058075951042003983413035312587282286158171800872330936800027595921615283052); + zs[439] = BOOST_MATH_TEST_VALUE(RealType, 959403530446169706174873597.0000000000000000000000000000000000000000000000000000000000000000000000000); + ws[439] = BOOST_MATH_TEST_VALUE(RealType, 58.06676060721227034055554058127184437189385183437097619449650089856200471001024891674937552098289649); + zs[440] = BOOST_MATH_TEST_VALUE(RealType, 959403530446169706174873597.5000000000000000000000000000000000000000000000000000000000000000000000000); + ws[440] = BOOST_MATH_TEST_VALUE(RealType, 58.06676060721227034055554058178417832374786953838882651590321024902177652660528837051902100823152388); + zs[441] = BOOST_MATH_TEST_VALUE(RealType, 1918807060892339412349747195.500000000000000000000000000000000000000000000000000000000000000000000000); + ws[441] = BOOST_MATH_TEST_VALUE(RealType, 58.74823998642851741200079363299726206609746705489474024632685397908027036826962303868666955918111538); + zs[442] = BOOST_MATH_TEST_VALUE(RealType, 1918807060892339412349747196.000000000000000000000000000000000000000000000000000000000000000000000000); + ws[442] = BOOST_MATH_TEST_VALUE(RealType, 58.74823998642851741200079363325347935999834589083288276642972035197649807101708168936227006895175343); + zs[443] = BOOST_MATH_TEST_VALUE(RealType, 1918807060892339412349747196.500000000000000000000000000000000000000000000000000000000000000000000000); + ws[443] = BOOST_MATH_TEST_VALUE(RealType, 58.74823998642851741200079363350969665389922472677102528646584069120432472031910137509227273894302196); + zs[444] = BOOST_MATH_TEST_VALUE(RealType, 1918807060892339412349747197.000000000000000000000000000000000000000000000000000000000000000000000000); + ws[444] = BOOST_MATH_TEST_VALUE(RealType, 58.74823998642851741200079363376591394780010356270916780643521499676375031617568209591145770868885078); + zs[445] = BOOST_MATH_TEST_VALUE(RealType, 1918807060892339412349747197.500000000000000000000000000000000000000000000000000000000000000000000000); + ws[445] = BOOST_MATH_TEST_VALUE(RealType, 58.74823998642851741200079363402213124170098239864731032633784326865477485858682385185460511772316968); + zs[446] = BOOST_MATH_TEST_VALUE(RealType, 3837614121784678824699494395.500000000000000000000000000000000000000000000000000000000000000000000000); + ws[446] = BOOST_MATH_TEST_VALUE(RealType, 59.42985170775297409538727174934638672334406646723725983740951105026349221807009071850919012663041550); + zs[447] = BOOST_MATH_TEST_VALUE(RealType, 3837614121784678824699494396.000000000000000000000000000000000000000000000000000000000000000000000000); + ws[447] = BOOST_MATH_TEST_VALUE(RealType, 59.42985170775297409538727174947451996655623151231730299282486521387670139214046503255303771865999613); + zs[448] = BOOST_MATH_TEST_VALUE(RealType, 3837614121784678824699494396.500000000000000000000000000000000000000000000000000000000000000000000000); + ws[448] = BOOST_MATH_TEST_VALUE(RealType, 59.42985170775297409538727174960265320976839655739734614822352956044016003363242954851814317386540390); + zs[449] = BOOST_MATH_TEST_VALUE(RealType, 3837614121784678824699494397.000000000000000000000000000000000000000000000000000000000000000000000000); + ws[449] = BOOST_MATH_TEST_VALUE(RealType, 59.42985170775297409538727174973078645298056160247738930360550408995386814254598426640885488605351946); +}; +// End of lambert_w_mp_high_values.ipp diff --git a/test/lambert_w_low_reference_values.ipp b/test/lambert_w_low_reference_values.ipp new file mode 100644 index 000000000..f4466e4c2 --- /dev/null +++ b/test/lambert_w_low_reference_values.ipp @@ -0,0 +1,236 @@ + +// A collection of Lambert W test values computed using 100 decimal digits precision. +// C++ floating-point type is RealType. + +// Written by lambert_w_mp_test_values.cpp Tue Oct 10 14:35:08 2017 + +// Copyright Paul A. Bristow 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Size of arrays of arguments z and Lambert W +static const unsigned int noof_tests = 100; +// Declare arrays of arguments z and Lambert W(z) +// The values are defined using the macro BOOST_MATH_TEST_VALUE to ensure +// that both built-in and multiprecision types are correctly initialiased with full precision. +// built-in types like double require a floating-point literal like 3.14, +// but multiprecision types require a decimal digit string like "3.14". + + +template +static RealType zs[100]; + +template +static RealType ws[100]; + +template +void init_zs() +{ + zs[0] = BOOST_MATH_TEST_VALUE(RealType, -0.35); + zs[1] = BOOST_MATH_TEST_VALUE(RealType, -0.34); + zs[2] = BOOST_MATH_TEST_VALUE(RealType, -0.33); + zs[3] = BOOST_MATH_TEST_VALUE(RealType, -0.32); + zs[4] = BOOST_MATH_TEST_VALUE(RealType, -0.31); + zs[5] = BOOST_MATH_TEST_VALUE(RealType, -0.3); + zs[6] = BOOST_MATH_TEST_VALUE(RealType, -0.29); + zs[7] = BOOST_MATH_TEST_VALUE(RealType, -0.28); + zs[8] = BOOST_MATH_TEST_VALUE(RealType, -0.27); + zs[9] = BOOST_MATH_TEST_VALUE(RealType, -0.26); + zs[10] = BOOST_MATH_TEST_VALUE(RealType, -0.25); + zs[11] = BOOST_MATH_TEST_VALUE(RealType, -0.24); + zs[12] = BOOST_MATH_TEST_VALUE(RealType, -0.23); + zs[13] = BOOST_MATH_TEST_VALUE(RealType, -0.22); + zs[14] = BOOST_MATH_TEST_VALUE(RealType, -0.21); + zs[15] = BOOST_MATH_TEST_VALUE(RealType, -0.2); + zs[16] = BOOST_MATH_TEST_VALUE(RealType, -0.19); + zs[17] = BOOST_MATH_TEST_VALUE(RealType, -0.18); + zs[18] = BOOST_MATH_TEST_VALUE(RealType, -0.17); + zs[19] = BOOST_MATH_TEST_VALUE(RealType, -0.16); + zs[20] = BOOST_MATH_TEST_VALUE(RealType, -0.15); + zs[21] = BOOST_MATH_TEST_VALUE(RealType, -0.14); + zs[22] = BOOST_MATH_TEST_VALUE(RealType, -0.13); + zs[23] = BOOST_MATH_TEST_VALUE(RealType, -0.12); + zs[24] = BOOST_MATH_TEST_VALUE(RealType, -0.11); + zs[25] = BOOST_MATH_TEST_VALUE(RealType, -0.1); + zs[26] = BOOST_MATH_TEST_VALUE(RealType, -0.09); + zs[27] = BOOST_MATH_TEST_VALUE(RealType, -0.08); + zs[28] = BOOST_MATH_TEST_VALUE(RealType, -0.07); + zs[29] = BOOST_MATH_TEST_VALUE(RealType, -0.06); + zs[30] = BOOST_MATH_TEST_VALUE(RealType, -0.05); + zs[31] = BOOST_MATH_TEST_VALUE(RealType, -0.04); + zs[32] = BOOST_MATH_TEST_VALUE(RealType, -0.03); + zs[33] = BOOST_MATH_TEST_VALUE(RealType, -0.02); + zs[34] = BOOST_MATH_TEST_VALUE(RealType, -0.01); + zs[35] = BOOST_MATH_TEST_VALUE(RealType, 0); + zs[36] = BOOST_MATH_TEST_VALUE(RealType, 0.01); + zs[37] = BOOST_MATH_TEST_VALUE(RealType, 0.02); + zs[38] = BOOST_MATH_TEST_VALUE(RealType, 0.03); + zs[39] = BOOST_MATH_TEST_VALUE(RealType, 0.04); + zs[40] = BOOST_MATH_TEST_VALUE(RealType, 0.05); + zs[41] = BOOST_MATH_TEST_VALUE(RealType, 0.06); + zs[42] = BOOST_MATH_TEST_VALUE(RealType, 0.07); + zs[43] = BOOST_MATH_TEST_VALUE(RealType, 0.08); + zs[44] = BOOST_MATH_TEST_VALUE(RealType, 0.09); + zs[45] = BOOST_MATH_TEST_VALUE(RealType, 0.1); + zs[46] = BOOST_MATH_TEST_VALUE(RealType, 0.11); + zs[47] = BOOST_MATH_TEST_VALUE(RealType, 0.12); + zs[48] = BOOST_MATH_TEST_VALUE(RealType, 0.13); + zs[49] = BOOST_MATH_TEST_VALUE(RealType, 0.14); + zs[50] = BOOST_MATH_TEST_VALUE(RealType, 0.15); + zs[51] = BOOST_MATH_TEST_VALUE(RealType, 0.16); + zs[52] = BOOST_MATH_TEST_VALUE(RealType, 0.17); + zs[53] = BOOST_MATH_TEST_VALUE(RealType, 0.18); + zs[54] = BOOST_MATH_TEST_VALUE(RealType, 0.19); + zs[55] = BOOST_MATH_TEST_VALUE(RealType, 0.2); + zs[56] = BOOST_MATH_TEST_VALUE(RealType, 0.21); + zs[57] = BOOST_MATH_TEST_VALUE(RealType, 0.22); + zs[58] = BOOST_MATH_TEST_VALUE(RealType, 0.23); + zs[59] = BOOST_MATH_TEST_VALUE(RealType, 0.24); + zs[60] = BOOST_MATH_TEST_VALUE(RealType, 0.25); + zs[61] = BOOST_MATH_TEST_VALUE(RealType, 0.26); + zs[62] = BOOST_MATH_TEST_VALUE(RealType, 0.27); + zs[63] = BOOST_MATH_TEST_VALUE(RealType, 0.28); + zs[64] = BOOST_MATH_TEST_VALUE(RealType, 0.29); + zs[65] = BOOST_MATH_TEST_VALUE(RealType, 0.3); + zs[66] = BOOST_MATH_TEST_VALUE(RealType, 0.31); + zs[67] = BOOST_MATH_TEST_VALUE(RealType, 0.32); + zs[68] = BOOST_MATH_TEST_VALUE(RealType, 0.33); + zs[69] = BOOST_MATH_TEST_VALUE(RealType, 0.34); + zs[70] = BOOST_MATH_TEST_VALUE(RealType, 0.35); + zs[71] = BOOST_MATH_TEST_VALUE(RealType, 0.36); + zs[72] = BOOST_MATH_TEST_VALUE(RealType, 0.37); + zs[73] = BOOST_MATH_TEST_VALUE(RealType, 0.38); + zs[74] = BOOST_MATH_TEST_VALUE(RealType, 0.39); + zs[75] = BOOST_MATH_TEST_VALUE(RealType, 0.4); + zs[76] = BOOST_MATH_TEST_VALUE(RealType, 0.41); + zs[77] = BOOST_MATH_TEST_VALUE(RealType, 0.42); + zs[78] = BOOST_MATH_TEST_VALUE(RealType, 0.43); + zs[79] = BOOST_MATH_TEST_VALUE(RealType, 0.44); + zs[80] = BOOST_MATH_TEST_VALUE(RealType, 0.45); + zs[81] = BOOST_MATH_TEST_VALUE(RealType, 0.46); + zs[82] = BOOST_MATH_TEST_VALUE(RealType, 0.47); + zs[83] = BOOST_MATH_TEST_VALUE(RealType, 0.48); + zs[84] = BOOST_MATH_TEST_VALUE(RealType, 0.49); + zs[85] = BOOST_MATH_TEST_VALUE(RealType, 0.5); + zs[86] = BOOST_MATH_TEST_VALUE(RealType, 0.51); + zs[87] = BOOST_MATH_TEST_VALUE(RealType, 0.52); + zs[88] = BOOST_MATH_TEST_VALUE(RealType, 0.53); + zs[89] = BOOST_MATH_TEST_VALUE(RealType, 0.54); + zs[90] = BOOST_MATH_TEST_VALUE(RealType, 0.55); + zs[91] = BOOST_MATH_TEST_VALUE(RealType, 0.56); + zs[92] = BOOST_MATH_TEST_VALUE(RealType, 0.57); + zs[93] = BOOST_MATH_TEST_VALUE(RealType, 0.58); + zs[94] = BOOST_MATH_TEST_VALUE(RealType, 0.59); + zs[95] = BOOST_MATH_TEST_VALUE(RealType, 0.6); + zs[96] = BOOST_MATH_TEST_VALUE(RealType, 0.61); + zs[97] = BOOST_MATH_TEST_VALUE(RealType, 0.62); + zs[98] = BOOST_MATH_TEST_VALUE(RealType, 0.63); + zs[99] = BOOST_MATH_TEST_VALUE(RealType, 0.64); +}; + +template +void init_ws() +{ + ws[0] = BOOST_MATH_TEST_VALUE(RealType, -0.7166388164560746087024558456707254694384568520557269136746931098892793788395047380759438025387884858); + ws[1] = BOOST_MATH_TEST_VALUE(RealType, -0.6536945012690894831597997531138338843162464994103594824433758069038214520742337992347211610550053984); + ws[2] = BOOST_MATH_TEST_VALUE(RealType, -0.6032666497551330505290050177510626805198672833479939253720127109209527349195161224268802996274136747); + ws[3] = BOOST_MATH_TEST_VALUE(RealType, -0.5604894830784555743634840504116545657018790664887527536558909385182231717303278501957297352008893216); + ws[4] = BOOST_MATH_TEST_VALUE(RealType, -0.5229899496273203531021836240814670901286300106249589336522580913925382314180343142582930298688973328); + ws[5] = BOOST_MATH_TEST_VALUE(RealType, -0.4894022271802149690362312519962933689234100060163590345114659679736814083816206187318524147462752111); + ws[6] = BOOST_MATH_TEST_VALUE(RealType, -0.4588564134818020881945707413259267147702798410198651247860751590059996177594865393191440343561489497); + ws[7] = BOOST_MATH_TEST_VALUE(RealType, -0.430758874527112757916530656841372138819645982270515538512158436501726397121990576271580205308878207); + ws[8] = BOOST_MATH_TEST_VALUE(RealType, -0.4046836216050718653643250056478665857931866093242449366197023952738694255086107304901218425119878285); + ws[9] = BOOST_MATH_TEST_VALUE(RealType, -0.3803130203301711158303965355878156301369877394562403253806911002545713191362049178479001390543005669); + ws[10] = BOOST_MATH_TEST_VALUE(RealType, -0.3574029561813889030688111040559047533165905550760120436276204485896714025961457962896168513444411851); + ws[11] = BOOST_MATH_TEST_VALUE(RealType, -0.3357611647889027517286807582305262236620547459507665934563718789670705154001183482511605249232725262); + ws[12] = BOOST_MATH_TEST_VALUE(RealType, -0.3152331201461386435765969738874032981030054464637097661705367708887648429131963353376761780892891198); + ws[13] = BOOST_MATH_TEST_VALUE(RealType, -0.2956924930138396731882414803341009766267402363327831485839773479792571341845506001421908939342687003); + ws[14] = BOOST_MATH_TEST_VALUE(RealType, -0.2770344936961942277405408649408688233196542455843784252217943010706466317584602777484953020015260171); + ws[15] = BOOST_MATH_TEST_VALUE(RealType, -0.2591711018190737450566519502154067057135883397008937032218391275127128217957580326598838960236799743); + ws[16] = BOOST_MATH_TEST_VALUE(RealType, -0.2420275690015187377858338945726638068700099098394765251572836145818480355482127963663852628850474365); + ws[17] = BOOST_MATH_TEST_VALUE(RealType, -0.2255398031589208520522477564458058214947250100630471825833168970061295445194143654537283079293631882); + ws[18] = BOOST_MATH_TEST_VALUE(RealType, -0.2096523776773757526854110454922591821172341338912795099236642246474810661153254626268152915502654464); + ws[19] = BOOST_MATH_TEST_VALUE(RealType, -0.1943169925501295306267806518866699513042867279514786976609290465447683131164947042462416733465191705); + ws[20] = BOOST_MATH_TEST_VALUE(RealType, -0.1794912683479847336169956193772297997551233686606176679348345166799006378410748708389238307302596092); + ws[21] = BOOST_MATH_TEST_VALUE(RealType, -0.1651377892669526609846674769813353453533631060975171667664202096550705187268670901310309631101894252); + ws[22] = BOOST_MATH_TEST_VALUE(RealType, -0.1512233352864070831284495817823327538270829682666487244093315171463780635423561100568377622135186516); + ws[23] = BOOST_MATH_TEST_VALUE(RealType, -0.1377182597958957974485081647284122921614575977782346246206306428239556449434198032928042029722138321); + ws[24] = BOOST_MATH_TEST_VALUE(RealType, -0.1245959804557266577566667196210790531357931527107938915216224185712652948438844608688497767379885997); + ws[25] = BOOST_MATH_TEST_VALUE(RealType, -0.1118325591589629648335694568202658422726453622912658633296897727621943319600088273854870109175450158); + ws[26] = BOOST_MATH_TEST_VALUE(RealType, -0.09940635280454481474353567186786621057820625354936552021878311951478746295439312235429098520600693655); + ws[27] = BOOST_MATH_TEST_VALUE(RealType, -0.0872977208615799240409197586602731399264877335001009828544494658243661146551788411188719365040631665); + ws[28] = BOOST_MATH_TEST_VALUE(RealType, -0.07548877886579220591933915955796681153524932228333916508394604171275936228487984352484617277876166196); + ws[29] = BOOST_MATH_TEST_VALUE(RealType, -0.06396318935617251019529498180168867456392641049371161867095248726275828927462089387234209569688498636); + ws[30] = BOOST_MATH_TEST_VALUE(RealType, -0.05270598355154634795995650617915721289427674396592395159861151190218069285705994080638064249535030165); + ws[31] = BOOST_MATH_TEST_VALUE(RealType, -0.0417034084364844738987273381255397678625601003793974576012640697941798897037109744906565096871084505); + ws[32] = BOOST_MATH_TEST_VALUE(RealType, -0.03094279498284817939791038065611524917275896960162863310717850085820789123932160879579380708946724763); + ws[33] = BOOST_MATH_TEST_VALUE(RealType, -0.02041244405580766725973605390749548004158612495647388221129868845283704895545337928376001388096747673); + ws[34] = BOOST_MATH_TEST_VALUE(RealType, -0.01010152719853875327292018767138623973670903993475235877290726369225412969738704722202330440072213641); + ws[35] = BOOST_MATH_TEST_VALUE(RealType, 0); + ws[36] = BOOST_MATH_TEST_VALUE(RealType, 0.009901473843595011885336326816570107953627746494917415482611387085655068978243229360100010886171970918); + ws[37] = BOOST_MATH_TEST_VALUE(RealType, 0.01961158933740562729168248268298370977812129423409093067066057185151403434315590894008191205649296367); + ws[38] = BOOST_MATH_TEST_VALUE(RealType, 0.02913845916787001265458568152535395243296458501852019787733534716701300947181963767753439648797667795); + ws[39] = BOOST_MATH_TEST_VALUE(RealType, 0.03848966594197856933287598180923987047561124099417969473653441752849562865576161248185402614815991005); + ws[40] = BOOST_MATH_TEST_VALUE(RealType, 0.04767230860012937472638890051416087074706296593389050213640962398757901310991378849253401839554247932); + ws[41] = BOOST_MATH_TEST_VALUE(RealType, 0.05669304377414432493107872588796066666126335321712197451058496819855703149168243802045469883952188552); + ws[42] = BOOST_MATH_TEST_VALUE(RealType, 0.06555812274442272075701853672870305774479404911034476570035450491724937359575494857058381868464932454); + ws[43] = BOOST_MATH_TEST_VALUE(RealType, 0.07427342455278083997072135190143718509109109085324108067398934333182382982634706320903236612485848405); + ws[44] = BOOST_MATH_TEST_VALUE(RealType, 0.08284448574644162210327285639805993759142367414790119841318433362329213386156887296335560991622282284); + ws[45] = BOOST_MATH_TEST_VALUE(RealType, 0.0912765271608622642998957214231795686531192240514720326483083946071722462544175516502066459299560671); + ws[46] = BOOST_MATH_TEST_VALUE(RealType, 0.09957447809219979168181062315832233980460887841902355310769435168948823854831388504977465633939682845); + ws[47] = BOOST_MATH_TEST_VALUE(RealType, 0.1077429981622769541092028233904076346257727607117049788632909974125087436037688769621472372382338589); + ws[48] = BOOST_MATH_TEST_VALUE(RealType, 0.1157864971383620420455904585908175984070719832012079215609230219591260309061070611295890797209058613); + ws[49] = BOOST_MATH_TEST_VALUE(RealType, 0.123709152935653268637225082319130933018353939077412845800247462273779701933149651969922416814563643); + ws[50] = BOOST_MATH_TEST_VALUE(RealType, 0.1315149280010344580474416667366241780127651069693569916108339146599686985542850094826506230355645206); + ws[51] = BOOST_MATH_TEST_VALUE(RealType, 0.1392075842516055911219563262364716100580324132271864711438857091601938661529783118284822918918762012); + ws[52] = BOOST_MATH_TEST_VALUE(RealType, 0.1467906967200019429098356045334600022599041243103780089713630524471411698742480890410424556292186252); + ws[53] = BOOST_MATH_TEST_VALUE(RealType, 0.1542676660400332595939671017213220881360827710416946348345860549372631184130438485651715394816551156); + ws[54] = BOOST_MATH_TEST_VALUE(RealType, 0.1616417298902320361099923668622409534689583622433205732144762283241135493479959458637574220609547663); + ws[55] = BOOST_MATH_TEST_VALUE(RealType, 0.1689159734991095651164749037058183987284469135107297553319388783258832067427865597883933228919370041); + ws[56] = BOOST_MATH_TEST_VALUE(RealType, 0.176093339303957105084240207040480028792877878787501699026158171529756555902997254667356047116441788); + ws[57] = BOOST_MATH_TEST_VALUE(RealType, 0.1831766358446274967063002214815526159126266889851598585852904605011653998837614409160807673866997377); + ws[58] = BOOST_MATH_TEST_VALUE(RealType, 0.1901685459646637418459898415362480836632528245228542609994635862866965513625128722227696305198415266); + ws[59] = BOOST_MATH_TEST_VALUE(RealType, 0.1970716343842153356374997203517126469395880032764377549805749691269132085609349859323881542196582345); + ws[60] = BOOST_MATH_TEST_VALUE(RealType, 0.2038883547022401644431818313271398701493524772101596349734062600818193640670940526181645713107956088); + ws[61] = BOOST_MATH_TEST_VALUE(RealType, 0.2106210558793939073502149913441295543358575322967676769367700683562463350885767192666542957075712751); + ws[62] = BOOST_MATH_TEST_VALUE(RealType, 0.2172719882476450310112829333455034301692311490231843772189917487700633677693451679449281229277444); + ws[63] = BOOST_MATH_TEST_VALUE(RealType, 0.2238433090879237484082608989533539628744637740613473368794338628306680030796510487992807280960350863); + ws[64] = BOOST_MATH_TEST_VALUE(RealType, 0.230337087812934212569787581174413102713202565273940246146535096101718965948694383449116272546328387); + ws[65] = BOOST_MATH_TEST_VALUE(RealType, 0.2367553107885593168713669913131022529850076068942822776500825494062035956449035725319106127888967252); + ws[66] = BOOST_MATH_TEST_VALUE(RealType, 0.2430998858240055404991486201117080426760827615791233989777935602839590601547710113790997280209616842); + ws[67] = BOOST_MATH_TEST_VALUE(RealType, 0.2493726463579186824703694885450067240777490681347030220353778714445393581714407422015446216089384764); + ws[68] = BOOST_MATH_TEST_VALUE(RealType, 0.255575355365104731122377747221405406075211883935782315620283025379607560268766005462481341306785098); + ws[69] = BOOST_MATH_TEST_VALUE(RealType, 0.2617097090061743695408970492873886795528577990555646770473973759567281587373896180370074675102853899); + ws[70] = BOOST_MATH_TEST_VALUE(RealType, 0.2677773400403608426926161268075028522173112518463765933864820650709836250445193581155036917009299903); + ws[71] = BOOST_MATH_TEST_VALUE(RealType, 0.2737798210199097285877692071254930326690991310905656381739509847292712940678493744834592887738653624); + ws[72] = BOOST_MATH_TEST_VALUE(RealType, 0.279718667282780030688985330053563938014151152536888220220066424808617200267097125397004630221318648); + ws[73] = BOOST_MATH_TEST_VALUE(RealType, 0.2855953397589067078034135225526038434573251024673070045734211311120835242005964286865861813715576376); + ws[74] = BOOST_MATH_TEST_VALUE(RealType, 0.2914112476039358811079401653518892721830457004248980213382598417991951999360380881350324421536041991); + ws[75] = BOOST_MATH_TEST_VALUE(RealType, 0.2971677506731385467797269622470213419044581015501274519083671027242648030351552074110959032585686367); + ws[76] = BOOST_MATH_TEST_VALUE(RealType, 0.3028661618471218240709788492447379933660682925225029587250796959940885465093714030223793579774677182); + ws[77] = BOOST_MATH_TEST_VALUE(RealType, 0.3085077492199755507560853270193391876750205224915338763730055720866605941218639559241097041425718389); + ws[78] = BOOST_MATH_TEST_VALUE(RealType, 0.3140937381596049465307653625755662577673424859113972924301418900103932566506643308363685842713510345); + ws[79] = BOOST_MATH_TEST_VALUE(RealType, 0.3196253132491970154264844863020982117371468405067770401175599100299218378827848337045090472924417639); + ws[80] = BOOST_MATH_TEST_VALUE(RealType, 0.3251036201180404567925882149866539513803608104758008850889564944127408970111984388784259876569696298); + ws[81] = BOOST_MATH_TEST_VALUE(RealType, 0.3305297671692582446013104792754623176896333299898173620545327652833709938789531370957735854140619173); + ws[82] = BOOST_MATH_TEST_VALUE(RealType, 0.3359048272114117659741549735010360281170238179628782075568249283432891398635686044147614597227718316); + ws[83] = BOOST_MATH_TEST_VALUE(RealType, 0.3412298390003893228052121236143338755316388573531939840892239102731223392510954828680280968924289841); + ws[84] = BOOST_MATH_TEST_VALUE(RealType, 0.3465058086974944293540338951489158955895910665452626949454054191729967933634547552073491283786892473); + ws[85] = BOOST_MATH_TEST_VALUE(RealType, 0.3517337112491958260249093009299510651714642155171118040466438461099606107203387108968323038321915693); + ws[86] = BOOST_MATH_TEST_VALUE(RealType, 0.356914491693587151869424246256045038549439930737927770357941686970717473161840668532896372614750844); + ws[87] = BOOST_MATH_TEST_VALUE(RealType, 0.3620490663982259224347136211795537282192272992052648623722265772196673697387836128745005565051134501); + ws[88] = BOOST_MATH_TEST_VALUE(RealType, 0.3671383242336754065717073384163048245222026534654156361188807018443753849870522240862625069431927886); + ws[89] = BOOST_MATH_TEST_VALUE(RealType, 0.3721831276867561125667772461128588424710274544378039645877124572815833807252348818205119412993474916); + ws[90] = BOOST_MATH_TEST_VALUE(RealType, 0.3771843139172231305654429202753334704233212269643890432815783280355585518582574589199227373679397234); + ws[91] = BOOST_MATH_TEST_VALUE(RealType, 0.3821426957613190709393551341272838157890329391308789323327108090412283903082231991030667000296469447); + ws[92] = BOOST_MATH_TEST_VALUE(RealType, 0.3870590626854075768406867666899001946809584949146574272916930955076684732166306691825829034241158645); + ws[93] = BOOST_MATH_TEST_VALUE(RealType, 0.3919341816926673886247930427095007681691393368820432019135743979557612444151495005378394686508979365); + ws[94] = BOOST_MATH_TEST_VALUE(RealType, 0.3967687981856199130952904028742195107327166899059243534847812692294320278426834780185947685265825165); + ws[95] = BOOST_MATH_TEST_VALUE(RealType, 0.4015636367870725946626664537946836866230027160063511402379921560604975325206585381386527082762303647); + ws[96] = BOOST_MATH_TEST_VALUE(RealType, 0.4063194021218846501705479426913810870863608030934864938423596576756863248859417057476841165663966806); + ws[97] = BOOST_MATH_TEST_VALUE(RealType, 0.4110367795617996075205416204209832162005305795083946929235957810141140813392726029732502982435129093); + ws[98] = BOOST_MATH_TEST_VALUE(RealType, 0.4157164359354393999190647189307900734531788488207389484153095921329397217223528187979110265697876198); + ws[99] = BOOST_MATH_TEST_VALUE(RealType, 0.4203590202054164452305419772022302902943227646223432518640399730485379665439298245520857153666787776); +}; +// End of lambert_w_mp_values.ipp diff --git a/test/test_constant_generate.cpp b/test/test_constant_generate.cpp index 937f26750..53f1c6a73 100644 --- a/test/test_constant_generate.cpp +++ b/test/test_constant_generate.cpp @@ -14,8 +14,9 @@ // To add new constants, add a function that calculates the value of the constant to // boost/math/constants/calculate_constants.hpp +// See http://www.boost.org/doc/libs/release/libs/math/doc/html/math_toolkit/new_const.html -#include +#include // Requires /modular-boost/libs/math/include_private in search path. #include int main() @@ -26,6 +27,7 @@ int main() BOOST_CONSTANTS_GENERATE(twothirds); BOOST_CONSTANTS_GENERATE(two_thirds); BOOST_CONSTANTS_GENERATE(three_quarters); + BOOST_CONSTANTS_GENERATE(sixth); // two and related. BOOST_CONSTANTS_GENERATE(root_two); BOOST_CONSTANTS_GENERATE(root_three); @@ -69,6 +71,7 @@ int main() // Euler's e and related. BOOST_CONSTANTS_GENERATE(e); BOOST_CONSTANTS_GENERATE(exp_minus_half); + BOOST_CONSTANTS_GENERATE(exp_minus_one); BOOST_CONSTANTS_GENERATE(e_pow_pi); BOOST_CONSTANTS_GENERATE(root_e); diff --git a/test/test_constants.cpp b/test/test_constants.cpp index 85e5a893f..3eb2fc1fc 100644 --- a/test/test_constants.cpp +++ b/test/test_constants.cpp @@ -157,6 +157,8 @@ void test_spots(RealType) BOOST_CHECK_CLOSE_FRACTION(0.333333333333333333333333333333333333333L, third(), tolerance); BOOST_CHECK_CLOSE_FRACTION(0.666666666666666666666666666666666666667L, two_thirds(), tolerance); BOOST_CHECK_CLOSE_FRACTION(0.75L, three_quarters(), tolerance); + BOOST_CHECK_CLOSE_FRACTION(0.1666666666666666666666666666666666666667L, sixth(), tolerance); + // Two and related. BOOST_CHECK_CLOSE_FRACTION(sqrt(2.L), root_two(), tolerance); BOOST_CHECK_CLOSE_FRACTION(sqrt(3.L), root_three(), tolerance); @@ -195,8 +197,8 @@ void test_spots(RealType) // Euler BOOST_CHECK_CLOSE_FRACTION(2.71828182845904523536028747135266249775724709369995L, e(), tolerance); - //BOOST_CHECK_CLOSE_FRACTION(exp(-0.5L), exp_minus_half(), tolerance); // See above. + BOOST_CHECK_CLOSE_FRACTION(exp(-1.L), exp_minus_one(), tolerance); BOOST_CHECK_CLOSE_FRACTION(pow(e(), pi()), e_pow_pi(), tolerance); // See also above. BOOST_CHECK_CLOSE_FRACTION(sqrt(e()), root_e(), tolerance); BOOST_CHECK_CLOSE_FRACTION(log10(e()), log10_e(), tolerance); diff --git a/test/test_lambert_w.cpp b/test/test_lambert_w.cpp new file mode 100644 index 000000000..b8a0502e6 --- /dev/null +++ b/test/test_lambert_w.cpp @@ -0,0 +1,1145 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w.cpp +//! \brief Basic sanity tests for Lambert W function using algorithms +// informed by Thomas Luu, Darko Veberic and Tosio Fukushima for W0 +// and rational polynomials by John Maddock. + +// #define BOOST_MATH_TEST_MULTIPRECISION // Add tests for several multiprecision types (not just built-in). +// #define BOOST_MATH_TEST_FLOAT128 // Add test using float128 type (GCC only, needing gnu++17 and quadmath library). + +#ifdef BOOST_MATH_TEST_FLOAT128 +#include // For float_64_t, float128_t. Must be first include! +#endif // #ifdef #ifdef BOOST_MATH_TEST_FLOAT128 +// Needs gnu++17 for BOOST_HAS_FLOAT128 +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include + +#ifdef BOOST_MATH_TEST_MULTIPRECISION +#include // boost::multiprecision::cpp_dec_float_50 +using boost::multiprecision::cpp_dec_float_50; + +#include +using boost::multiprecision::cpp_bin_float_quad; + +#include + +#ifdef BOOST_MATH_TEST_FLOAT128 + +#ifdef BOOST_HAS_FLOAT128 +// Including this header below without float128 triggers: +// fatal error C1189: #error: "Sorry compiler is neither GCC, not Intel, don't know how to configure this header." +#include +using boost::multiprecision::float128; +#endif // ifdef BOOST_HAS_FLOAT128 +#endif // #ifdef #ifdef BOOST_MATH_TEST_FLOAT128 + +#endif // #ifdef BOOST_MATH_TEST_MULTIPRECISION + +//#include // If available. + +#include // for real_concept tests. +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include "table_type.hpp" + +#ifndef SC_ +# define SC_(x) boost::lexical_cast::type>(BOOST_STRINGIZE(x)) +#endif + + +#include +#include +#include +#include +#include + +std::string show_versions(void); + +//! Build a message of information about build, architecture, address model, platform, ... +std::string show_versions(void) +{ + // Some of this information can also be obtained from running with a Custom Post-build step + // adding the option --build_info=yes + // "$(TargetDir)$(TargetName).exe" --build_info=yes + + std::ostringstream message; + + message << "Program: " << __FILE__ << "\n"; +#ifdef __TIMESTAMP__ + message << __TIMESTAMP__; +#endif + message << "\nBuildInfo:\n" " Platform " << BOOST_PLATFORM; + // http://stackoverflow.com/questions/1505582/determining-32-vs-64-bit-in-c +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + message << ", 64-bit."; +#else + message << ", 32-bit."; +#endif + + message << "\n Compiler " BOOST_COMPILER; +#ifdef BOOST_MSC_VER +#ifdef _MSC_FULL_VER + message << "\n MSVC version " << BOOST_STRINGIZE(_MSC_FULL_VER) << "."; +#endif +#ifdef __WIN64 + mess age << "\n WIN64" << std::endl; +#endif // __WIN64 +#ifdef _WIN32 + message << "\n WIN32" << std::endl; +#endif // __WIN32 +#endif +#ifdef __GNUC__ + //PRINT_MACRO(__GNUC__); + //PRINT_MACRO(__GNUC_MINOR__); + //PRINT_MACRO(__GNUC_PATCH__); + std::cout << "GCC " << __VERSION__ << std::endl; + //PRINT_MACRO(LONG_MAX); +#endif // __GNUC__ + +#ifdef __MINGW64__ +std::cout << "MINGW64 " << __MINGW32_MAJOR_VERSION << __MINGW32_MINOR_VERSION << std::endl; +// +// << __MINGW64_MAJOR_VERSION << __MINGW64_MINOR_VERSION << std::endl; not declared in this scope??? +#endif // __MINGW64__ + +#ifdef __MINGW32__ +std::cout << "MINGW64 " << __MINGW32_MAJOR_VERSION << __MINGW32_MINOR_VERSION << std::endl; +#endif // __MINGW32__ + + message << "\n STL " << BOOST_STDLIB; + message << "\n Boost version " << BOOST_VERSION / 100000 << "." << BOOST_VERSION / 100 % 1000 << "." << BOOST_VERSION % 100; + +#ifdef BOOST_MATH_TEST_MULTIPRECISION + message << "\nBOOST_MATH_TEST_MULTIPRECISION defined for multiprecision tests. " << std::endl; +#else + message << "\nBOOST_MATH_TEST_MULTIPRECISION not defined so NO multiprecision tests. " << std::endl; +#endif // BOOST_MATH_TEST_MULTIPRECISION + +#ifdef BOOST_HAS_FLOAT128 + message << "BOOST_HAS_FLOAT128 is defined." << std::endl; +#endif // ifdef BOOST_HAS_FLOAT128 + + message << std::endl; + return message.str(); +} // std::string show_versions() + + +template +void wolfram_test_moderate_values() +{ + // + // Spots of moderate value http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5B-1%2Fe%2Bi,+50%5D,+N%5BLambertW%5B-1%2Fe%2Bi%5D,+50%5D%5D,+%7Bi,+1%2F8,+6,+1%2F8%7D%5D + // + static const boost::array::type, 2>, 96/2> wolfram_test_small_neg = + {{ + {{ SC_(-0.24287944117144232159552377016146086744581113103177), SC_(-0.34187241316000572901412382650748493957063539755395) }},{{ SC_(-0.11787944117144232159552377016146086744581113103177), SC_(-0.13490446826612135454875992607636577833255418182633) }},{{ SC_(0.0071205588285576784044762298385391325541888689682322), SC_(0.0070703912528860797819274709355398032954165697080076) }},{{ SC_(0.13212055882855767840447622983853913255418886896823), SC_(0.11747650174894814471295063763686399700941650918302) }},{{ SC_(0.25712055882855767840447622983853913255418886896823), SC_(0.20869089404810562424547046857454995304964242368484) }},{{ SC_(0.38212055882855767840447622983853913255418886896823), SC_(0.28683366713002653952708635029764106993377156175310) }},{{ SC_(0.50712055882855767840447622983853913255418886896823), SC_(0.35542749308004931507852679571061486656821523044053) }},{{ SC_(0.63212055882855767840447622983853913255418886896823), SC_(0.41670399881776590750659327292575356285757792776250) }},{{ SC_(0.75712055882855767840447622983853913255418886896823), SC_(0.47217430075943420437939326812963066971059146681283) }},{{ SC_(0.88212055882855767840447622983853913255418886896823), SC_(0.52291321715862065064992942239384690347359852107504) }},{{ SC_(1.0071205588285576784044762298385391325541888689682), SC_(0.56971477154593975582335630229323210831843899740884) }},{{ SC_(1.1321205588285576784044762298385391325541888689682), SC_(0.61318350578224462394572352964726524514921241969798) }},{{ SC_(1.2571205588285576784044762298385391325541888689682), SC_(0.65379115237566259933564436658873734121781110980034) }},{{ SC_(1.3821205588285576784044762298385391325541888689682), SC_(0.69191341320406026236753559968630177636780741203666) }},{{ SC_(1.5071205588285576784044762298385391325541888689682), SC_(0.72785472286747598788295903283683432537852776142064) }},{{ SC_(1.6321205588285576784044762298385391325541888689682), SC_(0.76186544538805130363636977458614856100481979440639) }},{{ SC_(1.7571205588285576784044762298385391325541888689682), SC_(0.79415413501531119849043049331889268136479923750037) }},{{ SC_(1.8821205588285576784044762298385391325541888689682), SC_(0.82489647878345700122288701550494847447982817483512) }},{{ SC_(2.0071205588285576784044762298385391325541888689682), SC_(0.85424194939386899439722948096520865643710851410970) }},{{ SC_(2.1321205588285576784044762298385391325541888689682), SC_(0.88231884173371311472940735780441644004275449741412) }},{{ SC_(2.2571205588285576784044762298385391325541888689682), SC_(0.90923814516532488963517314558961057510689871415824) }},{{ SC_(2.3821205588285576784044762298385391325541888689682), SC_(0.93509656212104191797135657485515114635876341802516) }},{{ SC_(2.5071205588285576784044762298385391325541888689682), SC_(0.95997889061117906067636869169049106690165665554172) }},{{ SC_(2.6321205588285576784044762298385391325541888689682), SC_(0.98395992590529701946948066548039809917492328184099) }},{{ SC_(2.7571205588285576784044762298385391325541888689682), SC_(1.0071059939771381126732041109492705496242899774655) }},{{ SC_(2.8821205588285576784044762298385391325541888689682), SC_(1.0294761995723706229651673877352399077168142413723) }},{{ SC_(3.0071205588285576784044762298385391325541888689682), SC_(1.0511234507020167125769191146012321442040919222298) }},{{ SC_(3.1321205588285576784044762298385391325541888689682), SC_(1.0720953062286332723365148290552887215464891915069) }},{{ SC_(3.2571205588285576784044762298385391325541888689682), SC_(1.0924346821831089228990349517861599064007594751702) }},{{ SC_(3.3821205588285576784044762298385391325541888689682), SC_(1.1121804443118533629930276674418322662764569673766) }},{{ SC_(3.5071205588285576784044762298385391325541888689682), SC_(1.1313679082795201044696522785560810652358663683706) }},{{ SC_(3.6321205588285576784044762298385391325541888689682), SC_(1.1500292643692387775614691790201052907317404963905) }},{{ SC_(3.7571205588285576784044762298385391325541888689682), SC_(1.1681939400299161555212785901786587344721733034978) }},{{ SC_(3.8821205588285576784044762298385391325541888689682), SC_(1.1858889109341735194685896928615740804115521714257) }},{{ SC_(4.0071205588285576784044762298385391325541888689682), SC_(1.2031389691267953962289622785796365085402661808452) }},{{ SC_(4.1321205588285576784044762298385391325541888689682), SC_(1.2199669552139996161903252772502362264684476580522) }},{{ SC_(4.2571205588285576784044762298385391325541888689682), SC_(1.2363939602597347325278067608637615539794532870296) }},{{ SC_(4.3821205588285576784044762298385391325541888689682), SC_(1.2524395020361026107226019920575290018966524482736) }},{{ SC_(4.5071205588285576784044762298385391325541888689682), SC_(1.2681216794607666389159742215265331040507889789444) }},{{ SC_(4.6321205588285576784044762298385391325541888689682), SC_(1.2834573083995295018572263393035905604511320189369) }},{{ SC_(4.7571205588285576784044762298385391325541888689682), SC_(1.2984620414827281167361144981111712803667945033184) }},{{ SC_(4.8821205588285576784044762298385391325541888689682), SC_(1.3131504741533499076663954559108617687274731330916) }},{{ SC_(5.0071205588285576784044762298385391325541888689682), SC_(1.3275362388125116267199919229657120782894307415376) }},{{ SC_(5.1321205588285576784044762298385391325541888689682), SC_(1.3416320886383928057123774168081846145768561516693) }},{{ SC_(5.2571205588285576784044762298385391325541888689682), SC_(1.3554499724155634924134183248962114419200302481356) }},{{ SC_(5.3821205588285576784044762298385391325541888689682), SC_(1.3690011015132087699425938733927188719869603184010) }},{{ SC_(5.5071205588285576784044762298385391325541888689682), SC_(1.3822960099853765706075495327819109601506356054327) }},{{ SC_(5.6321205588285576784044762298385391325541888689682), SC_(1.3953446086279755263512146907828727538440007615239) }} + }}; + T tolerance = boost::math::tools::epsilon() * 3; + if (std::numeric_limits::digits10 > 40) + tolerance *= 4; // arbitrary precision types have lower accuracy on exp(z). + for (unsigned i = 0; i < wolfram_test_small_neg.size(); ++i) + { + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(T(wolfram_test_small_neg[i][0])), T(wolfram_test_small_neg[i][1]), tolerance); + } +} + +template +void wolfram_test_small_pos() +{ + // + // Spots near zero and positive http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5BPi+*+10%5Ei,+50%5D,+N%5BLambertW%5BPi+*+10%5Ei%5D,+50%5D%5D,+%7Bi,+-25,+-1%7D%5D + // + static const boost::array::type, 2>, 25> wolfram_test_small_neg = + {{ + {{ SC_(3.1415926535897932384626433832795028841971693993751e-25), SC_(3.1415926535897932384626423963190627752613075159265e-25) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-24), SC_(3.1415926535897932384626335136751017948385505649306e-24) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-23), SC_(3.1415926535897932384625446872354919906109810591160e-23) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-22), SC_(3.1415926535897932384616564228393939483352864153693e-22) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-21), SC_(3.1415926535897932384527737788784135255783814177903e-21) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-20), SC_(3.1415926535897932383639473392686092980134754308784e-20) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-19), SC_(3.1415926535897932374756829431705670227788144495920e-19) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-18), SC_(3.1415926535897932285930389821901443118720934199487e-18) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-17), SC_(3.1415926535897931397665993723859213467937614455864e-17) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-16), SC_(3.1415926535897922515022032743441060948982739088029e-16) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-15), SC_(3.1415926535897833688582422939673934647266189937296e-15) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-14), SC_(3.1415926535896945424186324943442560413318839066091e-14) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-13), SC_(3.1415926535888062780225349125117696393347268403158e-13) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-12), SC_(3.1415926535799236340616005340756885831699803736331e-12) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-11), SC_(3.1415926534910971944564007385929431896486546006413e-11) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-10), SC_(3.1415926526028327988188016713407935109104110982749e-10) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-9), SC_(3.1415926437201888838826995251371676507148394412103e-9) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-8), SC_(3.1415925548937538785102994823474670579278874210259e-8) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-7), SC_(3.1415916666298182234172285804275105377159084331529e-7) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e-6), SC_(3.1415827840319013043684920305205420694740106954961e-6) }},{{ SC_(0.000031415926535897932384626433832795028841971693993751), SC_(0.000031414939621964641052828244109272729597989570861172) }},{{ SC_(0.00031415926535897932384626433832795028841971693993751), SC_(0.00031406061579842362125003023838529350597159230209458) }},{{ SC_(0.0031415926535897932384626433832795028841971693993751), SC_(0.0031317693004296877733926356188004473035977501714541) }},{{ SC_(0.031415926535897932384626433832795028841971693993751), SC_(0.030473027596269883517196555192955092247613270959259) }},{{ SC_(0.31415926535897932384626433832795028841971693993751), SC_(0.24571751376320572448656753973370462139374436325987) }} + }}; + T tolerance = boost::math::tools::epsilon() * 3; + if (std::numeric_limits::digits10 > 40) + tolerance *= 3; // arbitrary precision types have lower accuracy on exp(z). + for (unsigned i = 0; i < wolfram_test_small_neg.size(); ++i) + { + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(T(wolfram_test_small_neg[i][0])), T(wolfram_test_small_neg[i][1]), tolerance); + } +} + +template +void wolfram_test_small_neg() +{ + // + // Spots near zero and negative http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5B-Pi+*+10%5Ei,+50%5D,+N%5BLambertW%5B-Pi+*+10%5Ei%5D,+50%5D%5D,+%7Bi,+-25,+-1%7D%5D + // + static const boost::array::type, 2>, 70/2> wolfram_test_small_neg = + {{ + {{ SC_(-3.1415926535897932384626433832795028841971693993751e-25), SC_(-3.1415926535897932384626443702399429931330312828247e-25) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-24), SC_(-3.1415926535897932384626532528839039735557882339126e-24) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-23), SC_(-3.1415926535897932384627420793235137777833577489360e-23) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-22), SC_(-3.1415926535897932384636303437196118200590533135692e-22) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-21), SC_(-3.1415926535897932384725129876805922428160503997900e-21) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-20), SC_(-3.1415926535897932385613394272903964703901652508759e-20) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-19), SC_(-3.1415926535897932394496038233884387465457126495672e-19) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-18), SC_(-3.1415926535897932483322477843688615495410754197010e-18) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-17), SC_(-3.1415926535897933371586873941730937234835814431099e-17) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-16), SC_(-3.1415926535897942254230834922158298617964738845526e-16) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-15), SC_(-3.1415926535898031080670444726846311337086192655470e-15) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-14), SC_(-3.1415926535898919345066542815166327311524009447840e-14) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-13), SC_(-3.1415926535907801989027527842355365380542172227242e-13) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-12), SC_(-3.1415926535996628428637792513133580846848848572500e-12) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-11), SC_(-3.1415926536884892824781879109701525247983589696795e-11) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-10), SC_(-3.1415926545767536790366733956272068630669876574730e-10) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-9), SC_(-3.1415926634593976860614172823213018318134944055260e-9) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-8), SC_(-3.1415927522858419002979913741894684038594384671969e-8) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-7), SC_(-3.1415936405506984418084674995072645049396296346958e-7) }},{{ SC_(-3.1415926535897932384626433832795028841971693993751e-6), SC_(-3.1416025232407040026008819016148803316716797067967e-6) }},{{ SC_(-0.000031415926535897932384626433832795028841971693993751), SC_(-0.000031416913542850054076094590477471913042739704497976) }},{{ SC_(-0.00031415926535897932384626433832795028841971693993751), SC_(-0.00031425800793839694440655801311183879569843264709852) }},{{ SC_(-0.0031415926535897932384626433832795028841971693993751), SC_(-0.0031515090287677856656576839914749012339811781712486) }},{{ SC_(-0.031415926535897932384626433832795028841971693993751), SC_(-0.032452164493239992272463616095775075564894751832128) }},{{ SC_(-0.31415926535897932384626433832795028841971693993751), SC_(-0.53804834513759287053587977755877044660611017981968) }}, + {{ SC_(-0.090099009900990099009900990099009900990099009900990), SC_(-0.099527797075226962190621767732039397602197803169897)}},{{ SC_(-0.080198019801980198019801980198019801980198019801980), SC_(-0.087534530933383521242151071722737877728489741787814) }},{{ SC_(-0.070297029702970297029702970297029702970297029702970), SC_(-0.075835379000403488962496062196568904002201151736290) }},{{ SC_(-0.060396039603960396039603960396039603960396039603960), SC_(-0.064414449758822413858363348099340678962612835311800) }},{{ SC_(-0.050495049504950495049504950495049504950495049504950), SC_(-0.053257171600878093079366736202964706966166164696873) }},{{ SC_(-0.040594059405940594059405940594059405940594059405941), SC_(-0.042350146588050412657332988380168720859403591863698) }},{{ SC_(-0.030693069306930693069306930693069306930693069306931), SC_(-0.031681024260949098136757222042165581145138786336298) }},{{ SC_(-0.020792079207920792079207920792079207920792079207921), SC_(-0.021238392251213645736199359110665662967213312773617) }},{{ SC_(-0.010891089108910891089108910891089108910891089108911), SC_(-0.011011681049909946810068329378571761407667575030714) }},{{ SC_(-0.00099009900990099009900990099009900990099009900990099), SC_(-0.00099108076440319890968631186785975507712384928918616) }} + }}; + T tolerance = boost::math::tools::epsilon() * 3; + if (std::numeric_limits::digits10 > 40) + tolerance *= 3; // arbitrary precision types have lower accuracy on exp(z). + for (unsigned i = 0; i < wolfram_test_small_neg.size(); ++i) + { + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(T(wolfram_test_small_neg[i][0])), T(wolfram_test_small_neg[i][1]), tolerance); + } +} + +template +void wolfram_test_large(const boost::mpl::true_&) +{ + // + // Spots near the singularity from http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5B-1%2Fe%2B2%5E-i,+50%5D,+N%5BLambertW%5B-1%2Fe+%2B+2%5E-i%5D,+50%5D%5D,+%7Bi,+2,+40%7D%5D + // + static const boost::array::type, 2>, 28/2> wolfram_test_large_data = + { { + {{ SC_(3.1415926535897932384626433832795028841971693993751e350), SC_(800.36444525326526998205084284403447902093784176640) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e400), SC_(915.35945025352715923124904626896745356022974283730) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e450), SC_(1030.3703481552571717312484086444052442055003737018) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e500), SC_(1145.3937726197879355969554296951287620979399652268) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e550), SC_(1260.4273249433458391941776841900870933799293511610) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e600), SC_(1375.4692354682341092954911299903937009237749971748) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e650), SC_(1490.5181612342761763990969379122584268166707632003) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e700), SC_(1605.5730589637597079362569020729894833435943718597) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e750), SC_(1720.6331020467166402802313799793443913873949058922) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e800), SC_(1835.6976244160526737141293452999638879204852786698) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e850), SC_(1950.7660814940759743605616247252782614446819652848) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e900), SC_(2065.8380223354646200773160641407055989098916114637) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e950), SC_(2180.9130693229593212006354812037286740424563145700) }},{{ SC_(3.1415926535897932384626433832795028841971693993751e1000), SC_(2295.9909030845346718801238821248991904602625884450) }} + } }; + T tolerance = boost::math::tools::epsilon() * 3; + if (std::numeric_limits::digits10 > 40) + tolerance *= 3; // arbitrary precision types have lower accuracy on exp(z). + for (unsigned i = 0; i < wolfram_test_large_data.size(); ++i) + { + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(T(wolfram_test_large_data[i][0])), T(wolfram_test_large_data[i][1]), tolerance); + } +} +template +void wolfram_test_large(const boost::mpl::false_&){} + +template +void wolfram_test_large() +{ + wolfram_test_large(boost::mpl::bool_<(std::numeric_limits::max_exponent10 > 1000)>()); +} + + +template +void wolfram_test_near_singularity() +{ + // + // Spots near the singularity from http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5B-1%2Fe%2B2%5E-i,+50%5D,+N%5BLambertW%5B-1%2Fe+%2B+2%5E-i%5D,+50%5D%5D,+%7Bi,+2,+40%7D%5D + // + static const boost::array::type, 2>, 39> wolfram_test_near_singularity_data = + {{ + { { SC_(-0.11787944117144233402427744294982403516769409179688), SC_(-0.13490446826612137099065142885543349308605449591189) } },{ { SC_(-0.24287944117144233402427744294982403516769409179688), SC_(-0.34187241316000575559631565516533717918703951393828) } },{ { SC_(-0.30537944117144233402427744294982403516769409179688), SC_(-0.50704532478540670242736394530166187052909039079642) } },{ { SC_(-0.33662944117144233402427744294982403516769409179688), SC_(-0.63562321628494791544895212508757067989859372121549) } },{ { SC_(-0.35225444117144233402427744294982403516769409179688), SC_(-0.73357201771558852140844624841371893543359405991894) } },{ { SC_(-0.36006694117144233402427744294982403516769409179688), SC_(-0.80685912552602238275976720505076149562188136941981) } },{ { SC_(-0.36397319117144233402427744294982403516769409179688), SC_(-0.86091151614390373770305184939107560322835214525382) } },{ { SC_(-0.36592631617144233402427744294982403516769409179688), SC_(-0.90033567669608907987528169545609510444951296636737) } },{ { SC_(-0.36690287867144233402427744294982403516769409179688), SC_(-0.92884889586304130900291705545970353898661233095513) } },{ { SC_(-0.36739115992144233402427744294982403516769409179688), SC_(-0.94934196763921122756108351994184213101752011076782) } },{ { SC_(-0.36763530054644233402427744294982403516769409179688), SC_(-0.96400324129495105632485735566132352543383271582526) } },{ { SC_(-0.36775737085894233402427744294982403516769409179688), SC_(-0.97445736712728703357755243595334553847237474201138) } },{ { SC_(-0.36781840601519233402427744294982403516769409179688), SC_(-0.98189372378619472154195350108189165241865132390473) } },{ { SC_(-0.36784892359331733402427744294982403516769409179688), SC_(-0.98717434434269671591894280580432721487757138768109) } },{ { SC_(-0.36786418238237983402427744294982403516769409179688), SC_(-0.99091955260257317141206161906086819616043312707614) } },{ { SC_(-0.36787181177691108402427744294982403516769409179688), SC_(-0.99357346775773151586057357459040504547191256911173) } },{ { SC_(-0.36787562647417670902427744294982403516769409179688), SC_(-0.99545290640175819861266174073519228782773422561472) } },{ { SC_(-0.36787753382280952152427744294982403516769409179688), SC_(-0.99678329264937600678258333756796350065436689760936) } },{ { SC_(-0.36787848749712592777427744294982403516769409179688), SC_(-0.99772473035978895659981485126201758865515569761514) } },{ { SC_(-0.36787896433428413089927744294982403516769409179688), SC_(-0.99839078411548014765525278348680286544429555739338) } },{ { SC_(-0.36787920275286323246177744294982403516769409179688), SC_(-0.99886193379608135520603487963907992157933985302350) } },{ { SC_(-0.36787932196215278324302744294982403516769409179688), SC_(-0.99919517626703684624524893082905669989578841060892) } },{ { SC_(-0.36787938156679755863365244294982403516769409179688), SC_(-0.99943085896775657378245957087668418410735469441835) } },{ { SC_(-0.36787941136911994632896494294982403516769409179688), SC_(-0.99959753415605033951327478977234592072050509074480) } },{ { SC_(-0.36787942627028114017662119294982403516769409179688), SC_(-0.99971540249082798050505534900918173321899800190957) } },{ { SC_(-0.36787943372086173710044931794982403516769409179688), SC_(-0.99979875358003464529770521637722571161846456343102) } },{ { SC_(-0.36787943744615203556236338044982403516769409179688), SC_(-0.99985769449598686744630754715710430111838645655608) } },{ { SC_(-0.36787943930879718479332041169982403516769409179688), SC_(-0.99989937341527312969776294577792175610005161268265) } },{ { SC_(-0.36787944024011975940879892732482403516769409179688), SC_(-0.99992884556078314715423832743355922518662235135757) } },{ { SC_(-0.36787944070578104671653818513732403516769409179688), SC_(-0.99994968586433278794146581248117772412549843583586) } },{ { SC_(-0.36787944093861169037040781404357403516769409179688), SC_(-0.99996442235919152892644019456912452486892832990114) } },{ { SC_(-0.36787944105502701219734262849669903516769409179688), SC_(-0.99997484272221444495021480907850566954322542216868) } },{ { SC_(-0.36787944111323467311081003572326153516769409179688), SC_(-0.99998221107553951227244139186618591264285119372063) } },{ { SC_(-0.36787944114233850356754373933654278516769409179688), SC_(-0.99998742131038091608107093454795869661238860012568) } },{ { SC_(-0.36787944115689041879591059114318341016769409179688), SC_(-0.99999110551424805741455916942650424910940130482916) } },{ { SC_(-0.36787944116416637641009401704650372266769409179688), SC_(-0.99999371064603396347995131962984747427523504609782) } },{ { SC_(-0.36787944116780435521718572999816387891769409179688), SC_(-0.99999555275622895023796382943893319302015254415029) } },{ { SC_(-0.36787944116962334462073158647399395704269409179688), SC_(-0.99999685532777825691586263781552103878671869687024) } },{ { SC_(-0.36787944117053283932250451471190899610519409179688), SC_(-0.99999777638786151731498560321162974199505119200634) } } + }}; + T tolerance = boost::math::tools::epsilon() * 3; + if (boost::math::tools::epsilon() <= boost::math::tools::epsilon()) + tolerance *= 5e5; + T endpoint = -boost::math::constants::exp_minus_one(); + for (unsigned i = 0; i < wolfram_test_near_singularity_data.size(); ++i) + { + if (wolfram_test_near_singularity_data[i][0] <= endpoint) + break; + else + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(T(wolfram_test_near_singularity_data[i][0])), T(wolfram_test_near_singularity_data[i][1]), tolerance); + } +} + +template <> +void wolfram_test_near_singularity() +{ + // + // Spot values near the singularity with inputs truncated to float precision, + // from http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5BROUND%5B-1%2Fe%2B2%5E-i,+2%5E-23%5D,+50%5D,+N%5BLambertW%5BROUND%5B-1%2Fe+%2B+2%5E-i,+2%5E-23%5D%5D,+50%5D%5D,+%7Bi,+2,+40%7D%5D + // + static const boost::array, 39> wolfram_test_near_singularity_data = + {{ + {{ -0.11787939071655273437500000000000000000000000000000f, -0.13490440151978599948261696847702203722148729212591f }},{{ -0.24287939071655273437500000000000000000000000000000f, -0.34187230524883404685074938529655332889057132590877f }},{{ -0.30537939071655273437500000000000000000000000000000f, -0.50704515484245965628066570100405225451296978841169f }},{{ -0.33662939071655273437500000000000000000000000000000f, -0.63562295482810970976475066480034941107064440641758f }},{{ -0.35225439071655273437500000000000000000000000000000f, -0.73357162334066102207977288738307124189083069773180f }},{{ -0.36006689071655273437500000000000000000000000000000f, -0.80685854013946199386910756662972252220827924037205f }},{{ -0.36397314071655273437500000000000000000000000000000f, -0.86091065811941702413570870801021404654934249886505f }},{{ -0.36592626571655273437500000000000000000000000000000f, -0.90033443111682454984393817004965279949925483847744f }},{{ -0.36690282821655273437500000000000000000000000000000f, -0.92884710067602836873486989954484681592392882968841f }},{{ -0.36739110946655273437500000000000000000000000000000f, -0.94933939406123900376318336910404763737960907662666f }},{{ -0.36763525009155273437500000000000000000000000000000f, -0.96399956611859464483214118051190513364901860207328f }},{{ -0.36775732040405273437500000000000000000000000000000f, -0.97445213361280651797731195324654593603807971082292f }},{{ -0.36781835556030273437500000000000000000000000000000f, -0.98188628650256330812037232517657284107351472091741f }},{{ -0.36784887313842773437500000000000000000000000000000f, -0.98716379155663346207408852364078406478772014890806f }},{{ -0.36786413192749023437500000000000000000000000000000f, -0.99090459761086986284393759319956676727684106186028f }},{{ -0.36787176132202148437500000000000000000000000000000f, -0.99355229825129408828026714426677096743753950457546f }},{{ -0.36787557601928710937500000000000000000000000000000f, -0.99542297991285328482403963994064328331346049089419f }},{{ -0.36787748336791992187500000000000000000000000000000f, -0.99674107062291256263133271694520294422529881114769f }},{{ -0.36787843704223632812500000000000000000000000000000f, -0.99766536478294767461296564658785293377699068226332f }},{{ -0.36787891387939453125000000000000000000000000000000f, -0.99830783438342654552199009076049244789994050996944f }},{{ -0.36787915229797363281250000000000000000000000000000f, -0.99874733565614076859582844941545958416543067187493f }},{{ -0.36787927150726318359375000000000000000000000000000f, -0.99903989590053869025356285499889881633845057984872f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }},{{ -0.36787939071655273437500000000000000000000000000000f, -0.99947635367299698033494423493356278945921228277354f }} + }}; + float tolerance = boost::math::tools::epsilon() * 16; + float endpoint = -boost::math::constants::exp_minus_one(); + for (unsigned i = 0; i < wolfram_test_near_singularity_data.size(); ++i) + { + if (wolfram_test_near_singularity_data[i][0] <= endpoint) + break; + else + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(wolfram_test_near_singularity_data[i][0]), wolfram_test_near_singularity_data[i][1], tolerance); + } +} + +template <> +void wolfram_test_near_singularity() +{ + // + // Spot values near the singularity with inputs truncated to double precision, + // from http://www.wolframalpha.com/input/?i=TABLE%5B%5BN%5BROUND%5B-1%2Fe%2B2%5E-i,+2%5E-23%5D,+50%5D,+N%5BLambertW%5BROUND%5B-1%2Fe+%2B+2%5E-i,+2%5E-23%5D%5D,+50%5D%5D,+%7Bi,+2,+40%7D%5D + // + static const boost::array, 39> wolfram_test_near_singularity_data = + {{ + {{ -0.11787944117144233402427744294982403516769409179688, -0.13490446826612137099065142885543349308605449591189 }},{{ -0.24287944117144233402427744294982403516769409179688, -0.34187241316000575559631565516533717918703951393828 }},{{ -0.30537944117144233402427744294982403516769409179688, -0.50704532478540670242736394530166187052909039079642 }},{{ -0.33662944117144233402427744294982403516769409179688, -0.63562321628494791544895212508757067989859372121549 }},{{ -0.35225444117144233402427744294982403516769409179688, -0.73357201771558852140844624841371893543359405991894 }},{{ -0.36006694117144233402427744294982403516769409179688, -0.80685912552602238275976720505076149562188136941981 }},{{ -0.36397319117144233402427744294982403516769409179688, -0.86091151614390373770305184939107560322835214525382 }},{{ -0.36592631617144233402427744294982403516769409179688, -0.90033567669608907987528169545609510444951296636737 }},{{ -0.36690287867144233402427744294982403516769409179688, -0.92884889586304130900291705545970353898661233095513 }},{{ -0.36739115992144233402427744294982403516769409179688, -0.94934196763921122756108351994184213101752011076782 }},{{ -0.36763530054644233402427744294982403516769409179688, -0.96400324129495105632485735566132352543383271582526 }},{{ -0.36775737085894233402427744294982403516769409179688, -0.97445736712728703357755243595334553847237474201138 }},{{ -0.36781840601519233402427744294982403516769409179688, -0.98189372378619472154195350108189165241865132390473 }},{{ -0.36784892359331733402427744294982403516769409179688, -0.98717434434269671591894280580432721487757138768109 }},{{ -0.36786418238237983402427744294982403516769409179688, -0.99091955260257317141206161906086819616043312707614 }},{{ -0.36787181177691108402427744294982403516769409179688, -0.99357346775773151586057357459040504547191256911173 }},{{ -0.36787562647417670902427744294982403516769409179688, -0.99545290640175819861266174073519228782773422561472 }},{{ -0.36787753382280952152427744294982403516769409179688, -0.99678329264937600678258333756796350065436689760936 }},{{ -0.36787848749712592777427744294982403516769409179688, -0.99772473035978895659981485126201758865515569761514 }},{{ -0.36787896433428413089927744294982403516769409179688, -0.99839078411548014765525278348680286544429555739338 }},{{ -0.36787920275286323246177744294982403516769409179688, -0.99886193379608135520603487963907992157933985302350 }},{{ -0.36787932196215278324302744294982403516769409179688, -0.99919517626703684624524893082905669989578841060892 }},{{ -0.36787938156679755863365244294982403516769409179688, -0.99943085896775657378245957087668418410735469441835 }},{{ -0.36787941136911994632896494294982403516769409179688, -0.99959753415605033951327478977234592072050509074480 }},{{ -0.36787942627028114017662119294982403516769409179688, -0.99971540249082798050505534900918173321899800190957 }},{{ -0.36787943372086173710044931794982403516769409179688, -0.99979875358003464529770521637722571161846456343102 }},{{ -0.36787943744615203556236338044982403516769409179688, -0.99985769449598686744630754715710430111838645655608 }},{{ -0.36787943930879718479332041169982403516769409179688, -0.99989937341527312969776294577792175610005161268265 }},{{ -0.36787944024011975940879892732482403516769409179688, -0.99992884556078314715423832743355922518662235135757 }},{{ -0.36787944070578104671653818513732403516769409179688, -0.99994968586433278794146581248117772412549843583586 }},{{ -0.36787944093861169037040781404357403516769409179688, -0.99996442235919152892644019456912452486892832990114 }},{{ -0.36787944105502701219734262849669903516769409179688, -0.99997484272221444495021480907850566954322542216868 }},{{ -0.36787944111323467311081003572326153516769409179688, -0.99998221107553951227244139186618591264285119372063 }},{{ -0.36787944114233850356754373933654278516769409179688, -0.99998742131038091608107093454795869661238860012568 }},{{ -0.36787944115689041879591059114318341016769409179688, -0.99999110551424805741455916942650424910940130482916 }},{{ -0.36787944116416637641009401704650372266769409179688, -0.99999371064603396347995131962984747427523504609782 }},{{ -0.36787944116780435521718572999816387891769409179688, -0.99999555275622895023796382943893319302015254415029 }},{{ -0.36787944116962334462073158647399395704269409179688, -0.99999685532777825691586263781552103878671869687024 }},{{ -0.36787944117053283932250451471190899610519409179688, -0.99999777638786151731498560321162974199505119200634 }} + }}; + double tolerance = boost::math::tools::epsilon() * 5; + if (std::numeric_limits::digits >= std::numeric_limits::digits) + tolerance *= 1e5; + else if (std::numeric_limits::digits * 2 >= std::numeric_limits::digits) + tolerance *= 5e4; + double endpoint = -boost::math::constants::exp_minus_one(); + for (unsigned i = 0; i < wolfram_test_near_singularity_data.size(); ++i) + { + if (wolfram_test_near_singularity_data[i][0] <= endpoint) + break; + else + BOOST_CHECK_CLOSE_FRACTION(boost::math::lambert_w0(wolfram_test_near_singularity_data[i][0]), wolfram_test_near_singularity_data[i][1], tolerance); + } +} + +template +void test_spots(RealType) +{ + // (Unused Parameter value, arbitrarily zero, only communicates the floating point type). + // test_spots(0.F); test_spots(0.); test_spots(0.L); + + using boost::math::lambert_w0; + using boost::math::lambert_wm1; + using boost::math::constants::exp_minus_one; + using boost::math::constants::e; + using boost::math::policies::policy; + + /* Example of an exception-free 'ignore_all' policy (possibly ill-advised?). + */ + typedef policy < + boost::math::policies::domain_error, + boost::math::policies::overflow_error, + boost::math::policies::underflow_error, + boost::math::policies::denorm_error, + boost::math::policies::pole_error, + boost::math::policies::evaluation_error + > ignore_all_policy; + +// Test some bad parameters to the function, with default policy and also with ignore_all policy. +#ifndef BOOST_NO_EXCEPTIONS + BOOST_CHECK_THROW(lambert_w0(-1.), std::domain_error); + BOOST_CHECK_THROW(lambert_wm1(-1.), std::domain_error); + if (std::numeric_limits::has_quiet_NaN) + { + BOOST_CHECK_THROW(lambert_w0(std::numeric_limits::quiet_NaN()), std::domain_error); // Would be NaN. + //BOOST_CHECK_EQUAL(lambert_w0(std::numeric_limits::quiet_NaN(), ignore_all_policy()), std::numeric_limits::quiet_NaN()); // Should be NaN. + // Fails as NaN != NaN by definition. + BOOST_CHECK(boost::math::isnan(lambert_w0(std::numeric_limits::quiet_NaN(), ignore_all_policy()))); + //BOOST_MATH_CHECK_EQUAL(boost::math::lambert_w0(std::numeric_limits::infinity(), ignore_all_policy()), std::numeric_limits(std::numeric_limits::infinity()), std::domain_error); // Was if infinity should throw, now infinity. + BOOST_CHECK_THROW(lambert_w0(-static_cast(0.4)), std::domain_error); // Would be complex. + +#else // No exceptions, so set policy to ignore and check result is NaN. + BOOST_MATH_CHECK_EQUAL(boost::math::lambert_w0(std::numeric_limits::quiet_NaN(), ignore_all_policy()), std::numeric_limits(std::numeric_limits::infinity(), ignore_all_policy()), std::numeric_limits(std::numeric_limits::infinity(), ignore_all_policy()), std::numeric_limits::digits > 53) + { // Multiprecision types. + epsilons *= 8; // (Perhaps needed because need slightly longer (55) reference values?). + } + RealType tolerance = boost::math::tools::epsilon() * epsilons; // 2 eps as a fraction. + std::cout << "Tolerance " << epsilons << " * epsilon == " << tolerance << std::endl; +#ifndef BOOST_NO_CXX11_NUMERIC_LIMITS + std::cout << "Precision " << std::numeric_limits::digits10 << " decimal digits, max_digits10 = " << std::numeric_limits ::max_digits10<< std::endl; + // std::cout.precision(std::numeric_limits::digits10); + std::cout.precision(std::numeric_limits ::max_digits10); +#endif + std::cout.setf(std::ios_base::showpoint); // show trailing significant zeros. + std::cout << "-exp(-1) = " << -exp_minus_one() << std::endl; + + wolfram_test_near_singularity(); + wolfram_test_large(); + wolfram_test_small_neg(); + wolfram_test_small_pos(); + wolfram_test_moderate_values(); + + // Test at singularity. + // RealType test_value = BOOST_MATH_TEST_VALUE(RealType, -0.36787944117144232159552377016146086744581113103176783450783680169746149574489980335714727434591964374662732527); + RealType singular_value = -exp_minus_one(); + // -exp(-1) = -0.36787944117144232159552377016146086744581113103176783450783680169746149574489980335714727434591964374662732527 + // lambert_w0[-0.367879441171442321595523770161460867445811131031767834] == -1 + // -0.36787945032119751 + RealType minus_one_value = BOOST_MATH_TEST_VALUE(RealType, -1.); + //std::cout << "singular_value " << singular_value << ", expected Lambert W = " << minus_one_value << std::endl; + + BOOST_CHECK_CLOSE_FRACTION( // Check -exp(-1) = -0.367879450 = -1max + lambert_w0(singular_value), + minus_one_value, + tolerance); // OK + + BOOST_CHECK_CLOSE_FRACTION( // Check -exp(-1) ~= -0.367879450 == -1 + lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.36787944117144232159552377016146086744581113103176783450783680169746149574489980335714727434591964374662732527)), + BOOST_MATH_TEST_VALUE(RealType, -1.), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION( // Check -exp(-1) ~= -0.367879450 == -1 + lambert_w0(-exp_minus_one()), + BOOST_MATH_TEST_VALUE(RealType, -1.), + tolerance); + + // Tests with some spot values computed using + // https://www.wolframalpha.com/input + // For example: N[lambert_w[1], 50] outputs: + // 0.56714329040978387299996866221035554975381578718651 + + // At branch junction singularity. + BOOST_CHECK_CLOSE_FRACTION( // Check -exp(-1) ~= -0.367879450 == -1 + lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.36787944117144232159552377016146086744581113103176783450783680169746149574489980335714727434591964374662732527)), + BOOST_MATH_TEST_VALUE(RealType, -1.), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.1)), + BOOST_MATH_TEST_VALUE(RealType, 0.091276527160862264299895721423179568653119224051472), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(0.2) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.2)), + BOOST_MATH_TEST_VALUE(RealType, 0.16891597349910956511647490370581839872844691351073), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(0.2) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.5)), + BOOST_MATH_TEST_VALUE(RealType, 0.351733711249195826024909300929951065171464215517111804046), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(0.5) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION( + lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.)), + BOOST_MATH_TEST_VALUE(RealType, 0.56714329040978387299996866221035554975381578718651), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(1) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 2.)), + BOOST_MATH_TEST_VALUE(RealType, 0.852605502013725491346472414695317466898453300151403508772), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(2.) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 3.)), + BOOST_MATH_TEST_VALUE(RealType, 1.049908894964039959988697070552897904589466943706341452932), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(3.) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 5.)), + BOOST_MATH_TEST_VALUE(RealType, 1.326724665242200223635099297758079660128793554638047479789), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(0.5) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 6.)), + BOOST_MATH_TEST_VALUE(RealType, 1.432404775898300311234078007212058694786434608804302025655), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(6) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 100.)), + BOOST_MATH_TEST_VALUE(RealType, 3.3856301402900501848882443645297268674916941701578), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(100) + tolerance); + + if (std::numeric_limits::has_infinity) + { + BOOST_CHECK_THROW(lambert_w0(std::numeric_limits::infinity()), std::overflow_error); // If should throw exception for infinity. + //BOOST_CHECK_EQUAL(lambert_w0(std::numeric_limits::infinity()), +std::numeric_limits::infinity()); // message is: + // Error in "test_types": class boost::exception_detail::clone_impl > : + // Error in function boost::math::lambert_w0() : Argument z is infinite! + //BOOST_CHECK_EQUAL(lambert_w0(std::numeric_limits::infinity()), +std::numeric_limits::infinity()); // If infinity allowed. + BOOST_CHECK_THROW(lambert_wm1(std::numeric_limits::infinity()), std::domain_error); // Infinity NOT allowed at all (not an edge case). + } + if (std::numeric_limits::has_quiet_NaN) + { // Argument Z == NaN is always an throwable error for both branches. + // BOOST_CHECK_EQUAL(lambert_w0(std::numeric_limits::quiet_NaN()), +std::numeric_limits::infinity()); // message is: + // Error in function boost::math::lambert_w0(): Argument z is NaN! + BOOST_CHECK_THROW(lambert_w0(std::numeric_limits::quiet_NaN()), std::domain_error); + BOOST_CHECK_THROW(lambert_wm1(std::numeric_limits::quiet_NaN()), std::domain_error); + } + + // denorm - but might be == min or zero? + if (std::numeric_limits::has_denorm == true) + { // Might also return infinity like z == 0? + BOOST_CHECK_THROW(lambert_wm1(std::numeric_limits::denorm_min()), std::overflow_error); + } + + // Tests of Lambert W-1 branch. + BOOST_CHECK_CLOSE_FRACTION( // Check -exp(-1) ~= -0.367879450 == -1 at the singularity branch point. + lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.36787944117144232159552377016146086744581113103176783450783680169746149574489980335714727434591964374662732527)), + BOOST_MATH_TEST_VALUE(RealType, -1.), + tolerance); + + // Near singularity and using series approximation. + // N[productlog(-1, -0.36), 50] = -1.2227701339785059531429380734238623131735264411311 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.36)), + BOOST_MATH_TEST_VALUE(RealType, -1.2227701339785059531429380734238623131735264411311), + 10 * tolerance); // tolerance OK for quad + // -1.2227701339785059531429380734238623131735264411311 + // -1.222770133978505953142938073423862313173526441131033 + + // Just using series approximation (switch at -0.35). + // N[productlog(-0.351), 50] = -0.72398644140937651483634596143951001600417138085814 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.351)), + BOOST_MATH_TEST_VALUE(RealType, -0.72398644140937651483634596143951001600417138085814), + // 2 * tolerance); // Note 2 * tolerance for PB fukushima + // got -0.723986441409376931150560229265736446 without Halley + // exp -0.72398644140937651483634596143951001 + // got -0.72398644140937651483634596143951029 with Halley + 10 * tolerance); // expect -0.72398644140937651 float -0.723987103 needs 10 * tolerance + // 2 * tolerance is fine for double and up. + // Float is OK + + // Same for W-1 branch + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.351)), + BOOST_MATH_TEST_VALUE(RealType, -1.3385736984773431852492145715526995809854973408320), + 10 * tolerance); // 2 tolerance OK for quad + + // Near singularity and NOT using series approximation (switch at -0.35) + // N[productlog(-1, -0.34), 50] + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.34)), + BOOST_MATH_TEST_VALUE(RealType, -1.4512014851325470735077533710339268100722032730024), + 10 * tolerance); // tolerance OK for quad + // + + // Decreasing z until near zero (small z) . + //N[productlog(-1, -0.3), 50] = -1.7813370234216276119741702815127452608215583564545 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.3)), + BOOST_MATH_TEST_VALUE(RealType, -1.7813370234216276119741702815127452608215583564545), + 2 * tolerance); + // -1.78133702342162761197417028151274526082155835645446 + + //N[productlog(-1, -0.2), 50] = -2.5426413577735264242938061566618482901614749075294 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.2)), + BOOST_MATH_TEST_VALUE(RealType, -2.5426413577735264242938061566618482901614749075294), + 2 * tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.1)), + BOOST_MATH_TEST_VALUE(RealType, -3.577152063957297218409391963511994880401796257793), + tolerance); + + //N[productlog(-1, -0.01), 50] = -6.4727751243940046947410578927244880371043455902257 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.01)), + BOOST_MATH_TEST_VALUE(RealType, -6.4727751243940046947410578927244880371043455902257), + tolerance); + + // N[productlog(-1, -0.001), 50] = -9.1180064704027401212583371820468142742704349737639 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.001)), + BOOST_MATH_TEST_VALUE(RealType, -9.1180064704027401212583371820468142742704349737639), + tolerance); + + // N[productlog(-1, -0.000001), 50] = -16.626508901372473387706432163984684996461726803805 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.000001)), + BOOST_MATH_TEST_VALUE(RealType, -16.626508901372473387706432163984684996461726803805), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-12)), + BOOST_MATH_TEST_VALUE(RealType, -31.067172842017230842039496250208586707880448763222), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-25)), + BOOST_MATH_TEST_VALUE(RealType, -61.686695602074505366866968627049381352503620377944), + tolerance); + + // z nearly too small. + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -2e-26)), + BOOST_MATH_TEST_VALUE(RealType, -63.322302839923597803393585145387854867226970485197), + tolerance* 2); + + // z very nearly too small. G(k=64) g[63] = -1.0264389699511303e-26 to using 1.027e-26 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1.027e-26)), + BOOST_MATH_TEST_VALUE(RealType, -63.999444896732265186957073549916026532499356695343), + tolerance); + // So -64 is the most negative value that can be determined using lookup. + // N[productlog(-1, -1.0264389699511303 * 10^-26 ), 50] -63.999999999999997947255011093606206983577811736472 == -64 + // G[k=64] = g[63] = -1.0264389699511303e-26 + + // z too small for G(k=64) g[63] = -1.0264389699511303e-26 to using 1.027e-26 + // N[productlog(-1, -10 ^ -26), 50] = -31.067172842017230842039496250208586707880448763222 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-26)), + BOOST_MATH_TEST_VALUE(RealType, -64.026509628385889681156090340691637712441162092868), + tolerance); // -64.0265121 + + if (std::numeric_limits::has_infinity) + { + BOOST_CHECK_EQUAL(lambert_wm1(0), -std::numeric_limits::infinity()); + } + if (std::numeric_limits::has_quiet_NaN) + { + // BOOST_CHECK_EQUAL(lambert_w0(std::numeric_limits::quiet_NaN()), +std::numeric_limits::infinity()); // message is: + // Error in function boost::math::lambert_w0(): Argument z is NaN! + BOOST_CHECK_THROW(lambert_wm1(std::numeric_limits::quiet_NaN()), std::domain_error); + } + + // W0 Tests for too big and too small to use lookup table. + // Exactly W = 64, not enough to be OK for lookup. + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 3.9904954117194348050619127737142206366920907815909119e+29)), + BOOST_MATH_TEST_VALUE(RealType, 64.0), + tolerance); + + // Just below z for F[64] + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 3.99045411719434e+29)), + BOOST_MATH_TEST_VALUE(RealType, 63.999989810930513468726486827408823607175844852495), tolerance); + // Fails for quad_float -1.22277013397850595265 + // -1.22277013397850595319 + + // Just too big, so using log approx and Halley refinement. + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 4e+29)), + BOOST_MATH_TEST_VALUE(RealType, 64.002342375637950350970694519073803643686041499677), + tolerance); + + // Check at reduced precision. + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 4e+29), policy >()), + BOOST_MATH_TEST_VALUE(RealType, 64.002342375637950350970694519073803643686041499677), + 0.00002); // 0.00001 fails. + + // Tests to ensure that all JM rational polynomials are being checked. + + // 1st polynomal if (z < 0.5) // 0.05 < z < 0.5 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.49)), + BOOST_MATH_TEST_VALUE(RealType, 0.3465058086974944293540338951489158955895910665452626949), + tolerance); + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.051)), + BOOST_MATH_TEST_VALUE(RealType, 0.04858156174600359264950777241723801201748517590507517888), + tolerance); + + // 2st polynomal if 0.5 < z < 2 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.51)), + BOOST_MATH_TEST_VALUE(RealType, 0.3569144916935871518694242462560450385494399307379277704), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.9)), + BOOST_MATH_TEST_VALUE(RealType, 0.8291763302658400337004358009672187071638421282477162293), + tolerance); + + // 3rd polynomials 2 < z < 6 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 2.1)), + BOOST_MATH_TEST_VALUE(RealType, 0.8752187586805470099843211502166029752154384079916131962), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 5.9)), + BOOST_MATH_TEST_VALUE(RealType, 1.422521411785098213935338853943459424120416844150520831), + tolerance); + + // 4th polynomials 6 < z < 18 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 6.1)), + BOOST_MATH_TEST_VALUE(RealType, 1.442152194116056579987235881273412088690824214100254315), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 17.9)), + BOOST_MATH_TEST_VALUE(RealType, 2.129100923757568114366514708174691237123820852409339147), + tolerance); + + // 5th polynomials if (z < 9897.12905874) // 2.8 < log(z) < 9.2 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 18.1)), + BOOST_MATH_TEST_VALUE(RealType, 2.136665501382339778305178680563584563343639180897328666), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 9897.)), + BOOST_MATH_TEST_VALUE(RealType, 7.222751047988674263127929506116648714752441161828893633), + tolerance); + + // 6th polynomials if (z < 7.896296e+13) // 9.2 < log(z) <= 32 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 9999.)), + BOOST_MATH_TEST_VALUE(RealType, 7.231758181708737258902175236106030961433080976032516996), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 7.7e+13)), + BOOST_MATH_TEST_VALUE(RealType, 28.62069643025822480911439831021393125282095606713326376), + tolerance); + + // 7th polynomial // 32 < log(z) < 100 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 8.0e+18)), + BOOST_MATH_TEST_VALUE(RealType, 39.84107480517853176296156400093560722439428484537515586), + tolerance); + + // Largest 32-bit float. (Larger values for other types tested using max()) + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.e38)), + BOOST_MATH_TEST_VALUE(RealType, 83.07844821316409592720410446942538465411465113447713574), + tolerance); + + // Using z small series function if z < 0.05 if (z < -0.051) -0.27 < z < -0.051 + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.28)), + BOOST_MATH_TEST_VALUE(RealType, -0.4307588745271127579165306568413721388196459822705155385), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.25)), + BOOST_MATH_TEST_VALUE(RealType, -0.3574029561813889030688111040559047533165905550760120436), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, +0.25)), + BOOST_MATH_TEST_VALUE(RealType, 0.2038883547022401644431818313271398701493524772101596350), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.051)), // just above 0.05 cutoff. + BOOST_MATH_TEST_VALUE(RealType, -0.05382002772543396036830469500362485089791914689728115249), + tolerance * 4); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.05)), // at cutoff. + BOOST_MATH_TEST_VALUE(RealType, -0.05270598355154634795995650617915721289427674396592395160), + tolerance * 8); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.049)), // Just below cutoff. + BOOST_MATH_TEST_VALUE(RealType, 0.04676143671340832342497289393737051868103596756298863555), + tolerance * 4); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.01)), + BOOST_MATH_TEST_VALUE(RealType, 0.009901473843595011885336326816570107953627746494917415483), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.01)), + BOOST_MATH_TEST_VALUE(RealType, -0.01010152719853875327292018767138623973670903993475235877), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.049)), + BOOST_MATH_TEST_VALUE(RealType, -0.05159448479219405354564920228913331280713177046648170658), + tolerance * 8); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1e-6)), + BOOST_MATH_TEST_VALUE(RealType, 9.999990000014999973333385416558666900096702096424344715e-7), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -1e-6)), + BOOST_MATH_TEST_VALUE(RealType, -1.000001000001500002666671875010800023343107568372593753e-6), + tolerance); + + // Near Smallest float. + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1e-38)), + BOOST_MATH_TEST_VALUE(RealType, 9.99999999999999999999999999999999999990000000000000000e-39), + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -1e-38)), + BOOST_MATH_TEST_VALUE(RealType, -1.000000000000000000000000000000000000010000000000000000e-38), + tolerance); + + // Similar 'too near zero' tests for W-1 branch. + // lambert_wm1(-1.0264389699511283e-26) = -64.000000000000000 + // Exactly z for W=-64 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1.026438969951128225904695701851094643838952857740385870e-26)), + BOOST_MATH_TEST_VALUE(RealType, -64.000000000000000000000000000000000000), + 2 * tolerance); + + // Just more negative than G[64 max] = wm1zs[63] so can't use lookup table. + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1.5e-27)), + BOOST_MATH_TEST_VALUE(RealType, -65.953279000145077719128800110134854577850889171784), + tolerance); // -65.9532776 + + // Just less negative than G[64 max] = wm1zs[63] so can use lookup table. + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1.1e-26)), + BOOST_MATH_TEST_VALUE(RealType, -63.929686062157630858625440758283127600360210072859), + tolerance); + + // N[productlog(-1, -10 ^ -26), 50] = -31.067172842017230842039496250208586707880448763222 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-26)), + BOOST_MATH_TEST_VALUE(RealType, -64.026509628385889681156090340691637712441162092868), + tolerance); + + // 1e-28 is too small + // N[productlog(-1, -10 ^ -28), 50] = -31.067172842017230842039496250208586707880448763222 + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-28)), + BOOST_MATH_TEST_VALUE(RealType, -68.702163291525429160769761667024460023336801014578), + tolerance); + + // Check for overflow when using a double (including when using for approximate value for refinement for higher precision). + + // N[productlog(-1, -10 ^ -30), 50] = -73.373110313822976797067478758120874529181611813766 + //BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e30)), + // BOOST_MATH_TEST_VALUE(RealType, -73.373110313822976797067478758120874529181611813766), + // tolerance); + //unknown location : fatal error : in "test_types" : + //class boost::exception_detail::clone_impl > + // : Error in function boost::math::lambert_wm1() : + // Argument z = -1.00000002e+30 out of range(z < -exp(-1) = -3.6787944) for Lambert W - 1 branch! + + BOOST_CHECK_THROW(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e30)), std::domain_error); + + // Too negative + BOOST_CHECK_THROW(lambert_wm1(RealType(-0.5)), std::domain_error); + + // This fails for fixed_point type used for other tests because out of range? + //BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.0e6)), + //BOOST_MATH_TEST_VALUE(RealType, 11.383358086140052622000156781585004289033774706019), + //// Output from https://www.wolframalpha.com/input/?i=lambert_w0(1e6) + //// tolerance * 1000); // fails for fixed_point type exceeds 0.00015258789063 + // // 15.258789063 + // // 11.383346558 + // tolerance * 100000); + // So need to use some spot tests for specific types, or use a bigger fixed_point type. + + // Check zero. + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.0)), + BOOST_MATH_TEST_VALUE(RealType, 0.0), + tolerance); + // these fail for cpp_dec_float_50 + // 'boost::multiprecision::detail::expression,boost::multiprecision::et_on>,void,void,void>' + // : no appropriate default constructor available + // TODO ??????????? + + } // template void test_spots(RealType) + +BOOST_AUTO_TEST_CASE( test_types ) +{ + BOOST_MATH_CONTROL_FP; + // BOOST_TEST_MESSAGE output only appears if command line has --log_level="message" + // or call set_threshold_level function: + boost::unit_test_framework::unit_test_log.set_threshold_level(boost::unit_test_framework::log_messages); + BOOST_TEST_MESSAGE("\nTest Lambert W function for several types."); + BOOST_TEST_MESSAGE(show_versions()); // Full version of Boost, STL and compiler info. +#ifndef BOOST_MATH_TEST_MULTIPRECISION + // Fundamental built-in types: + test_spots(0.0F); // float + test_spots(0.0); // double +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + if (sizeof(long double) > sizeof(double)) + { // Avoid pointless re-testing if double and long double are identical (for example, MSVC). + test_spots(0.0L); // long double + } + test_spots(boost::math::concepts::real_concept(0)); +#endif + + #else // BOOST_MATH_TEST_MULTIPRECISION + // Multiprecision types: +#if BOOST_MATH_TEST_MULTIPRECISION == 1 + test_spots(static_cast(0)); +#endif +#if BOOST_MATH_TEST_MULTIPRECISION == 2 + test_spots(static_cast(0)); +#endif +#if BOOST_MATH_TEST_MULTIPRECISION == 3 + test_spots(static_cast(0)); +#endif +#endif // ifdef BOOST_MATH_TEST_MULTIPRECISION + + #ifdef BOOST_MATH_TEST_FLOAT128 + std::cout << "\nBOOST_MATH_TEST_FLOAT128 defined for float128 tests." << std::endl; + +#ifdef BOOST_HAS_FLOAT128 + // GCC and Intel only. + // Requires link to libquadmath library, see + // http://www.boost.org/doc/libs/release/libs/multiprecision/doc/html/boost_multiprecision/tut/floats/float128.html + // for example: + // C:\Program Files\mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\lib\gcc\x86_64-w64-mingw32\7.2.0\libquadmath.a + + using boost::multiprecision::float128; + std::cout << "BOOST_HAS_FLOAT128" << std::endl; + + std::cout.precision(std::numeric_limits::max_digits10); + + test_spots(static_cast(0)); +#endif // BOOST_HAS_FLOAT128 +#else + std::cout << "\nBOOST_MATH_TEST_FLOAT128 NOT defined so NO float128 tests." << std::endl; +#endif // #ifdef BOOST_MATH_TEST_FLOAT128 + +} // BOOST_AUTO_TEST_CASE( test_types ) + + +BOOST_AUTO_TEST_CASE( test_range_of_double_values ) +{ + using boost::math::constants::exp_minus_one; + using boost::math::lambert_w0; + + BOOST_TEST_MESSAGE("\nTest Lambert W function type double for range of values."); + + // Want to test almost largest value. + // test_value = (std::numeric_limits::max)() / 4; + // std::cout << std::setprecision(std::numeric_limits::max_digits10) << "Max value = " << test_value << std::endl; + // Can't use a test like this for all types because max_value depends on RealType + // and thus the expected result of lambert_w0 does too. + //BOOST_CHECK_CLOSE_FRACTION(lambert_w0(test_value), + // BOOST_MATH_TEST_VALUE(RealType, ???), + // tolerance); + // So this section just tests a single type, say IEEE 64-bit double, for a range of spot values. + + typedef double RealType; // Some tests assume type is double. + + int epsilons = 1; + RealType tolerance = boost::math::tools::epsilon() * epsilons; // 2 eps as a fraction. + std::cout << "Tolerance " << epsilons << " * epsilon == " << tolerance << std::endl; + +#ifndef BOOST_MATH_TEST_MULTIPRECISION + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.0e-6)), + BOOST_MATH_TEST_VALUE(RealType, 9.9999900000149999733333854165586669000967020964243e-7), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[1e-6],50]) + tolerance); + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.0001)), + BOOST_MATH_TEST_VALUE(RealType, 0.000099990001499733385405869000452213835767629477903460), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[0.001],50]) + tolerance); + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.001)), + BOOST_MATH_TEST_VALUE(RealType, 0.00099900149733853088995782787410778559957065467928884), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[0.001],50]) + tolerance); + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.01)), + BOOST_MATH_TEST_VALUE(RealType, 0.0099014738435950118853363268165701079536277464949174), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[0.01],50]) + tolerance * 25); // <<< Needs a much bigger tolerance??? + // 0.0099014738435951096 this test max_digits10 + // 0.00990147384359511 digits10 + // 0.0099014738435950118 wolfram + // 0.00990147384359501 wolfram digits10 + // 0.0099014738435950119 N[lambert_w[0.01],17] + // 0.00990147384359501 N[lambert_w[0.01],15] which really is more different than expected. + // 0.00990728209160670 approx + // 0.00990147384359511 previous + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.05)), + BOOST_MATH_TEST_VALUE(RealType, 0.047672308600129374726388900514160870747062965933891), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[0.01],50]) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 0.1)), + BOOST_MATH_TEST_VALUE(RealType, 0.091276527160862264299895721423179568653119224051472), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[1],50]) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.)), + BOOST_MATH_TEST_VALUE(RealType, 0.56714329040978387299996866221035554975381578718651), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[1],50]) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 2.)), + BOOST_MATH_TEST_VALUE(RealType, 0.852605502013725491346472414695317466898453300151403508772), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(2.) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 3.)), + BOOST_MATH_TEST_VALUE(RealType, 1.049908894964039959988697070552897904589466943706341452932), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(3.) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 5.)), + BOOST_MATH_TEST_VALUE(RealType, 1.326724665242200223635099297758079660128793554638047479789), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(0.5) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 6.)), + BOOST_MATH_TEST_VALUE(RealType, 1.432404775898300311234078007212058694786434608804302025655), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(6) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 10.)), + BOOST_MATH_TEST_VALUE(RealType, 1.7455280027406993830743012648753899115352881290809), + // Output from https://www.wolframalpha.com/input/ N[lambert_w[10],50]) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 100.)), + BOOST_MATH_TEST_VALUE(RealType, 3.3856301402900501848882443645297268674916941701578), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(100) + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1000.)), + BOOST_MATH_TEST_VALUE(RealType, 5.2496028524015962271260563196973062825214723860596), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(1000) + tolerance); + + // This fails for fixed_point type used for other tests because out of range of the type? + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, 1.0e6)), + BOOST_MATH_TEST_VALUE(RealType, 11.383358086140052622000156781585004289033774706019), + // Output from https://www.wolframalpha.com/input/?i=lambert_w0(1e6) + tolerance); // + + // Tests for double only near the max and the singularity where Lambert_w estimates are less precise. + if (std::numeric_limits::is_specialized) + { // is_specialized means that can use numeric_limits for tests. + // Check near std::numeric_limits<>::max() for type. + //std::cout << std::setprecision(std::numeric_limits::max_digits10) + // << (std::numeric_limits::max)() // == 1.7976931348623157e+308 + // << " " << (std::numeric_limits::max)()/4 // == 4.4942328371557893e+307 + // << std::endl; + + // All these result in faulty error message + // unknown location : fatal error : in "test_range_of_values": class boost::exception_detail::clone_impl >: Error in function boost::math::lambert_w0(): Argument z = %1 too large. + // I:\modular - boost\libs\math\test\test_lambert_w.cpp(456) : last checkpoint + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(1.7976931348623157e+308 ), // max_value for IEEE 64-bit double. + static_cast(703.2270331047701868711791887193075929608934699575820028L), + // N[productlog[0, 1.7976931348623157*10^308 /2],50] == 702.53487067487671916110655783739076368512998658347 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(1.7976931348623157e+308 / 2), // max_value/2 for IEEE 64-bit double. + static_cast(702.53487067487671916110655783739076368512998658347L), + // N[productlog[0, 1.7976931348623157*10^308 /2],50] == 702.53487067487671916110655783739076368512998658347 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(1.7976931348623157e+308 /4), // near max_value/4 for IEEE 64-bit double. + static_cast(701.8427092142920014223182853764045476L), + // N[productlog(0, 1.7976931348623157* 10^308 /4 ), 37] =701.8427092142920014223182853764045476 + // N[productlog(0, 0.25 * 1.7976931348623157*10^307), 37] + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(4.4942328371557893e+307), // max_value/4 for IEEE 64-bit double. + static_cast(701.84270921429200143342782556643059L), + // N[lambert_w[4.4942328371557893e+307], 35] == 701.8427092142920014334278255664305887 + // as a double == 701.83341468208209 + // Lambert computed 702.02379914670587 + 0.000003); // OK Much less precise at the max edge??? + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0((std::numeric_limits::max)()), // max_value for IEEE 64-bit double. + static_cast(703.2270331047701868711791887193075930), + // N[productlog(0, 1.7976931348623157* 10^308), 37] = 703.2270331047701868711791887193075930 + // 703.22700325995515 lambert W + // 703.22703310477016 Wolfram + tolerance * 2e8); // OK but much less accurate near max. + + // Compare precisions very close to the singularity. + // This test value is one epsilon close to the singularity at -exp(-1) * z + // (below which the result has a non-zero imaginary part). + RealType test_value = -exp_minus_one(); + test_value += (std::numeric_limits::epsilon() * 1); + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(test_value), + BOOST_MATH_TEST_VALUE(RealType, -0.99999996349975895), + tolerance * 1000000000); + // -0.99999996788201051 + // -0.99999996349975895 + // Would not expect to get a result closer than sqrt(epsilon)? + } // if (std::numeric_limits::is_specialized) + + // Can only compare float_next for specific type T = double. + // Comparison with Wolfram N[productlog(0,-0.36787944117144228 ), 17] + // Note big loss of precision and big tolerance needed to pass. + BOOST_CHECK_CLOSE_FRACTION( // Check float_next(-exp(-1) ) + lambert_w0(BOOST_MATH_TEST_VALUE(double, -0.36787944117144228)), + BOOST_MATH_TEST_VALUE(RealType, -0.99999998496215738), + 1e8 * tolerance); // diff 6.03558e-09 v 2.2204460492503131e-16 + + BOOST_CHECK_CLOSE_FRACTION( // Check float_next(float_next(-exp(-1) )) + lambert_w0(BOOST_MATH_TEST_VALUE(double, -0.36787944117144222)), + BOOST_MATH_TEST_VALUE(RealType, -0.99999997649828679), + 5e7 * tolerance);// diff 2.30785e-09 v 2.2204460492503131e-16 + + // Compare with previous PB/FK computations at double precision. + BOOST_CHECK_CLOSE_FRACTION( // Check float_next(-exp(-1) ) + lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.36787944117144228)), + BOOST_MATH_TEST_VALUE(RealType, -0.99999997892657588), + tolerance); // diff 6.03558e-09 v 2.2204460492503131e-16 + + BOOST_CHECK_CLOSE_FRACTION( // Check float_next(float_next(-exp(-1) )) + lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.36787944117144222)), + BOOST_MATH_TEST_VALUE(RealType, -0.99999997419043196), + tolerance);// diff 2.30785e-09 v 2.2204460492503131e-16 + + // z increasingly close to singularity. + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.36)), + BOOST_MATH_TEST_VALUE(RealType, -0.8060843159708177782855213616209920019974599683466713016), + 2 * tolerance); // -0.806084335 + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.365)), + BOOST_MATH_TEST_VALUE(RealType, -0.8798200914159538111724840007674053239388642469453350954), + 5 * tolerance); // Note 5 * tolerance + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.3678)), + BOOST_MATH_TEST_VALUE(RealType, -0.9793607149578284774761844434886481686055949229547379368), + 15 * tolerance); // Note 15 * tolerance when this close to singularity. + + // Just using series approximation (Fukushima switch at -0.35, but JM at 0.01 of singularity < -0.3679). + // N[productlog(-0.351), 50] = -0.72398644140937651483634596143951001600417138085814 + // N[productlog(-0.351), 55] = -0.7239864414093765148363459614395100160041713808581379727 + BOOST_CHECK_CLOSE_FRACTION(lambert_w0(BOOST_MATH_TEST_VALUE(RealType, -0.351)), + BOOST_MATH_TEST_VALUE(RealType, -0.72398644140937651483634596143951001600417138085814), + 10 * tolerance); // Note was 2 * tolerance + + // Check value just not using near_singularity series approximation (and using rational polynomial instead). + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.3)), + BOOST_MATH_TEST_VALUE(RealType, -1.7813370234216276119741702815127452608215583564545), + // Output from https://www.wolframalpha.com/input/ + //N[productlog(-1, -0.3), 50] = -1.7813370234216276119741702815127452608215583564545 + tolerance); + + // Using table lookup and schroeder with decreasing z to zero. + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.2)), + BOOST_MATH_TEST_VALUE(RealType, -2.5426413577735264242938061566618482901614749075294), + // N[productlog[-1, -0.2],50] -2.5426413577735264242938061566618482901614749075294 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.1)), + BOOST_MATH_TEST_VALUE(RealType, -3.5771520639572972184093919635119948804017962577931), + //N[productlog(-1, -0.1), 50] = -3.5771520639572972184093919635119948804017962577931 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.001)), + BOOST_MATH_TEST_VALUE(RealType, -9.1180064704027401212583371820468142742704349737639), + // N[productlog(-1, -0.001), 50] = -9.1180064704027401212583371820468142742704349737639 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -0.000001)), + BOOST_MATH_TEST_VALUE(RealType, -16.626508901372473387706432163984684996461726803805), + // N[productlog(-1, -0.000001), 50] = -16.626508901372473387706432163984684996461726803805 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-6)), + BOOST_MATH_TEST_VALUE(RealType, -16.626508901372473387706432163984684996461726803805), + // N[productlog(-1, -10 ^ -6), 50] = -16.626508901372473387706432163984684996461726803805 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1.0e-26)), + BOOST_MATH_TEST_VALUE(RealType, -64.026509628385889681156090340691637712441162092868), + // Output from https://www.wolframalpha.com/input/ + // N[productlog(-1, -1 * 10^-26 ), 50] = -64.026509628385889681156090340691637712441162092868 + tolerance); + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -2e-26)), + BOOST_MATH_TEST_VALUE(RealType, -63.322302839923597803393585145387854867226970485197), + // N[productlog[-1, -2*10^-26],50] = -63.322302839923597803393585145387854867226970485197 + tolerance * 2); + + // Smaller than lookup table, so must use approx and Halley refinements. + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -1e-30)), + BOOST_MATH_TEST_VALUE(RealType, -73.373110313822976797067478758120874529181611813766), + // N[productlog(-1, -10 ^ -30), 50] = -73.373110313822976797067478758120874529181611813766 + tolerance); + + // std::numeric_limits::min +#ifndef BOOST_NO_CXX11_NUMERIC_LIMITS + std::cout.precision(std::numeric_limits::max_digits10); +#endif + std::cout << "(std::numeric_limits::min)() " << (std::numeric_limits::min)() << std::endl; + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, -2.2250738585072014e-308)), + BOOST_MATH_TEST_VALUE(RealType, -714.96865723796647086868547560654825435542227693935), + // N[productlog[-1, -2.2250738585072014e-308],50] = -714.96865723796647086868547560654825435542227693935 + tolerance); + + // For z = 0, W = -infinity + if (std::numeric_limits::has_infinity) + { + BOOST_CHECK_EQUAL(lambert_wm1(BOOST_MATH_TEST_VALUE(RealType, 0.)), + -std::numeric_limits::infinity()); + } + +#elif BOOST_MATH_TEST_MULTIPRECISION == 2 + + // Comparison with Wolfram N[productlog(0,-0.36787944117144228 ), 17] + // Using conversion from double to higher precision cpp_bin_float_quad + using boost::multiprecision::cpp_bin_float_quad; + BOOST_CHECK_CLOSE_FRACTION( // Check float_next(-exp(-1) ) + lambert_w0(BOOST_MATH_TEST_VALUE(cpp_bin_float_quad, -0.36787944117144228)), + BOOST_MATH_TEST_VALUE(cpp_bin_float_quad, -0.99999998496215738), + tolerance); // OK + + BOOST_CHECK_CLOSE_FRACTION( // Check float_next(float_next(-exp(-1) )) + lambert_w0(BOOST_MATH_TEST_VALUE(cpp_bin_float_quad, -0.36787944117144222)), + BOOST_MATH_TEST_VALUE(cpp_bin_float_quad, -0.99999997649828679), + tolerance);// OK +#endif +} // BOOST_AUTO_TEST_CASE(test_range_of_double_values) + diff --git a/test/test_lambert_w_derivative.cpp b/test/test_lambert_w_derivative.cpp new file mode 100644 index 000000000..aa8946efb --- /dev/null +++ b/test/test_lambert_w_derivative.cpp @@ -0,0 +1,124 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w.cpp +//! \brief Basic sanity tests for Lambert W derivative. + +#ifdef BOOST_MATH_TEST_FLOAT128 +#include // For float_64_t, float128_t. Must be first include! +#endif // #ifdef #ifdef BOOST_MATH_TEST_FLOAT128 +// Needs gnu++17 for BOOST_HAS_FLOAT128 +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include + +#ifdef BOOST_MATH_TEST_MULTIPRECISION +#include // boost::multiprecision::cpp_dec_float_50 +using boost::multiprecision::cpp_dec_float_50; + +#include +using boost::multiprecision::cpp_bin_float_quad; + +#ifdef BOOST_MATH_TEST_FLOAT128 + +#ifdef BOOST_HAS_FLOAT128 +// Including this header below without float128 triggers: +// fatal error C1189: #error: "Sorry compiler is neither GCC, not Intel, don't know how to configure this header." +#include +using boost::multiprecision::float128; +#endif // ifdef BOOST_HAS_FLOAT128 +#endif // #ifdef #ifdef BOOST_MATH_TEST_FLOAT128 + +#endif // #ifdef BOOST_MATH_TEST_MULTIPRECISION + +//#include // If available. + +#include // for real_concept tests. +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include +#include +#include +#include +#include + +std::string show_versions(void); + +BOOST_AUTO_TEST_CASE( Derivatives_of_lambert_w ) +{ + std::cout << "Macro BOOST_MATH_LAMBERT_W_DERIVATIVES to test 1st derivatives is defined." << std::endl; + BOOST_TEST_MESSAGE("\nTest Lambert W function 1st differentials."); + + using boost::math::constants::exp_minus_one; + using boost::math::lambert_w0_prime; + using boost::math::lambert_wm1_prime; + + // Derivatives + // https://www.wolframalpha.com/input/?i=derivative+of+productlog(0,+x) + // d/dx(W_0(x)) = W(x)/(x W(x) + x) + // https://www.wolframalpha.com/input/?i=derivative+of+productlog(-1,+x) + // d/dx(W_(-1)(x)) = (W_(-1)(x))/(x W_(-1)(x) + x) + + // 55 decimal digit values added to allow future testing using multiprecision. + + typedef double RealType; + + int epsilons = 1; + RealType tolerance = boost::math::tools::epsilon() * epsilons; // 2 eps as a fraction. + + // derivative of productlog(-1, x) at x = -0.1 == -13.8803 + // (derivative of productlog(-1, x) ) at x = N[-0.1, 55] - but the result disappears! + // (derivative of N[productlog(-1, x), 55] ) at x = N[-0.1, 55] + + // W0 branch + BOOST_CHECK_CLOSE_FRACTION(lambert_w0_prime(BOOST_MATH_TEST_VALUE(RealType, -0.2)), + // BOOST_MATH_TEST_VALUE(RealType, 1.7491967609218355), + BOOST_MATH_TEST_VALUE(RealType, 1.7491967609218358355273514903396335693828167746571404), + tolerance); // 1.7491967609218358355273514903396335693828167746571404 + + BOOST_CHECK_CLOSE_FRACTION(lambert_w0_prime(BOOST_MATH_TEST_VALUE(RealType, 10.)), + BOOST_MATH_TEST_VALUE(RealType, 0.063577133469345105142021311010780887641928338458371618), + tolerance); + +// W-1 branch + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1_prime(BOOST_MATH_TEST_VALUE(RealType, -0.1)), + BOOST_MATH_TEST_VALUE(RealType, -13.880252213229780748699361486619519025203815492277715), + tolerance); + // Lambert W_prime -13.880252213229780748699361486619519025203815492277715, double -13.880252213229781 + + BOOST_CHECK_CLOSE_FRACTION(lambert_wm1_prime(BOOST_MATH_TEST_VALUE(RealType, -0.2)), + BOOST_MATH_TEST_VALUE(RealType, -8.2411940564179044961885598641955579728547896392013239), + tolerance); + // Lambert W_prime -8.2411940564179044961885598641955579728547896392013239, double -8.2411940564179051 + + // Lambert W_prime 0.063577133469345105142021311010780887641928338458371618, double 0.063577133469345098 +}; // BOOST_AUTO_TEST_CASE("Derivatives of lambert_w") + + diff --git a/test/test_lambert_w_integrals_double.cpp b/test/test_lambert_w_integrals_double.cpp new file mode 100644 index 000000000..c286d4b94 --- /dev/null +++ b/test/test_lambert_w_integrals_double.cpp @@ -0,0 +1,266 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w_integrals.cpp +//! \brief quadrature tests that cover the whole range of the Lambert W0 function. + +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include +#include +#include +#include +#include +#include + +std::string show_versions(void); + +// Added code and test for Integral of the Lambert W function: by Nick Thompson. +// https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals + +#include // for integral tests. +#include // for integral tests. +#include // for integral tests. + + using boost::math::policies::policy; + using boost::math::policies::make_policy; + +// using statements needed for changing error handling policy. +using boost::math::policies::evaluation_error; +using boost::math::policies::domain_error; +using boost::math::policies::overflow_error; +using boost::math::policies::ignore_error; +using boost::math::policies::throw_on_error; + +typedef policy< + domain_error, + overflow_error +> no_throw_policy; + +// Assumes that function has a throw policy, for example: +// NOT lambert_w0(1 / (x * x), no_throw_policy()); +// Error in function boost::math::quadrature::exp_sinh::integrate: +// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. +// Please ensure your function evaluates to a finite number of its entire domain. +template +T debug_integration_proc(T x) +{ + T result; // warning C4701: potentially uninitialized local variable 'result' used + // T result = 0 ; // But result may not be assigned below? + try + { + // Assign function call to result in here... + if (x <= sqrt(boost::math::tools::min_value()) ) + { + result = 0; + } + else + { + result = lambert_w0(1 / (x * x)); + } + // result = lambert_w0(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is: + // Error in function boost::math::quadrature::exp_sinh::integrate: + // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. + // Please ensure your function evaluates to a finite number of its entire domain. + + } // try + catch (const std::exception& e) + { + std::cout << "Exception " << e.what() << std::endl; + // set breakpoint here: + std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl; + if (!std::isfinite(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + if (std::isnan(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + } // catch + return result; +} // T debug_integration_proc(T x) + +template +void test_integrals() +{ + // Integral of the Lambert W function: + // https://en.wikipedia.org/wiki/Lambert_W_function + using boost::math::quadrature::tanh_sinh; + using boost::math::quadrature::exp_sinh; + // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html + using std::sqrt; + + std::cout << "Integration of type " << typeid(Real).name() << std::endl; + + Real tol = std::numeric_limits::epsilon(); + { // // Integrate for function lambert_W0(z); + tanh_sinh ts; + Real a = 0; + Real b = boost::math::constants::e(); + auto f = [](Real z)->Real + { + return lambert_w0(z); + }; + Real z = ts.integrate(f, a, b); // OK without any decltype(f) + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e() - 1, tol); + } + { + // Integrate for function lambert_W0(z/(z sqrt(z)). + exp_sinh es; + auto f = [](Real z)->Real + { + return lambert_w0(z)/(z * sqrt(z)); + }; + Real z = es.integrate(f); // OK + BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi(), tol); + } + { + // Integrate for function lambert_W0(1/z^2). + exp_sinh es; + //const Real sqrt_min = sqrt(boost::math::tools::min_value()); // 1.08420217e-19 fo 32-bit float. + // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified + auto f = [](Real z)->Real + { + if (z <= sqrt(boost::math::tools::min_value()) ) + { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter. + return static_cast(0); + } + else + { + return lambert_w0(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen. + } + }; + Real z = es.integrate(f); + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi(), tol); + } +} // template void test_integrals() + + +BOOST_AUTO_TEST_CASE( integrals ) +{ + std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl; + BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals."); + try + { + // using statements needed to change precision policy. + using boost::math::policies::policy; + using boost::math::policies::make_policy; + using boost::math::policies::precision; + using boost::math::policies::digits2; + using boost::math::policies::digits10; + + // using statements needed for changing error handling policy. + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::ignore_error; + using boost::math::policies::throw_on_error; + + typedef policy< + domain_error, + overflow_error + > no_throw_policy; + + /* + // Experiment with better diagnostics. + typedef float Real; + + Real inf = std::numeric_limits::infinity(); + Real max = (std::numeric_limits::max)(); + std::cout.precision(std::numeric_limits::max_digits10); + //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308 + std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf + std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227 + //std::cout << lambert_w0(inf) << std::endl; // inf - will throw. + std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0 + std::cout << "lambert_w0(std::numeric_limits::denorm_min()) = " << lambert_w0(std::numeric_limits::denorm_min()) << std::endl; // 4.94066e-324 + std::cout << "lambert_w0(std::numeric_limits::min()) = " << lambert_w0((std::numeric_limits::min)()) << std::endl; // 2.22507e-308 + + // Approximate the largest lambert_w you can get for type T? + float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max_f " << max_w_f << std::endl; // 84.2879 + Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max " << max_w << std::endl; // 703.227 + + std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; // + std::cout << "test integral 1/z^2" << std::endl; + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1e-10, policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "epsilon = " << std::numeric_limits::epsilon() << std::endl; // + std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19 + std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19 + + + +// Demo debug version. +Real tol = std::numeric_limits::epsilon(); +Real x; +{ + using boost::math::quadrature::exp_sinh; + exp_sinh es; + // Function to be integrated, lambert_w0(1/z^2). + + //auto f = [](Real z)->Real + //{ // Naive - no protection against underflow and subsequent divide by zero. + // return lambert_w0(1 / (z * z)); + //}; + // Diagnostic is: + // Error in function boost::math::lambert_w0: Expected a finite value but got inf + + auto f = [](Real z)->Real + { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things. + return debug_integration_proc(z); + }; + // Exception Error in function boost::math::lambert_w0: Expected a finite value but got inf. + + // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163. + // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23. + x = es.integrate(f); + std::cout << "es.integrate(f) = " << x << std::endl; + BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi(), tol); + // root_two_pi(); + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} + diff --git a/test/test_lambert_w_integrals_float.cpp b/test/test_lambert_w_integrals_float.cpp new file mode 100644 index 000000000..c7a80a1f7 --- /dev/null +++ b/test/test_lambert_w_integrals_float.cpp @@ -0,0 +1,266 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w_integrals.cpp +//! \brief quadrature tests that cover the whole range of the Lambert W0 function. + +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include +#include +#include +#include +#include +#include + +std::string show_versions(void); + +// Added code and test for Integral of the Lambert W function: by Nick Thompson. +// https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals + +#include // for integral tests. +#include // for integral tests. +#include // for integral tests. + + using boost::math::policies::policy; + using boost::math::policies::make_policy; + +// using statements needed for changing error handling policy. +using boost::math::policies::evaluation_error; +using boost::math::policies::domain_error; +using boost::math::policies::overflow_error; +using boost::math::policies::ignore_error; +using boost::math::policies::throw_on_error; + +typedef policy< + domain_error, + overflow_error +> no_throw_policy; + +// Assumes that function has a throw policy, for example: +// NOT lambert_w0(1 / (x * x), no_throw_policy()); +// Error in function boost::math::quadrature::exp_sinh::integrate: +// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. +// Please ensure your function evaluates to a finite number of its entire domain. +template +T debug_integration_proc(T x) +{ + T result; // warning C4701: potentially uninitialized local variable 'result' used + // T result = 0 ; // But result may not be assigned below? + try + { + // Assign function call to result in here... + if (x <= sqrt(boost::math::tools::min_value()) ) + { + result = 0; + } + else + { + result = lambert_w0(1 / (x * x)); + } + // result = lambert_w0(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is: + // Error in function boost::math::quadrature::exp_sinh::integrate: + // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. + // Please ensure your function evaluates to a finite number of its entire domain. + + } // try + catch (const std::exception& e) + { + std::cout << "Exception " << e.what() << std::endl; + // set breakpoint here: + std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl; + if (!std::isfinite(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + if (std::isnan(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + } // catch + return result; +} // T debug_integration_proc(T x) + +template +void test_integrals() +{ + // Integral of the Lambert W function: + // https://en.wikipedia.org/wiki/Lambert_W_function + using boost::math::quadrature::tanh_sinh; + using boost::math::quadrature::exp_sinh; + // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html + using std::sqrt; + + std::cout << "Integration of type " << typeid(Real).name() << std::endl; + + Real tol = std::numeric_limits::epsilon(); + { // // Integrate for function lambert_W0(z); + tanh_sinh ts; + Real a = 0; + Real b = boost::math::constants::e(); + auto f = [](Real z)->Real + { + return lambert_w0(z); + }; + Real z = ts.integrate(f, a, b); // OK without any decltype(f) + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e() - 1, tol); + } + { + // Integrate for function lambert_W0(z/(z sqrt(z)). + exp_sinh es; + auto f = [](Real z)->Real + { + return lambert_w0(z)/(z * sqrt(z)); + }; + Real z = es.integrate(f); // OK + BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi(), tol); + } + { + // Integrate for function lambert_W0(1/z^2). + exp_sinh es; + //const Real sqrt_min = sqrt(boost::math::tools::min_value()); // 1.08420217e-19 fo 32-bit float. + // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified + auto f = [](Real z)->Real + { + if (z <= sqrt(boost::math::tools::min_value()) ) + { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter. + return static_cast(0); + } + else + { + return lambert_w0(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen. + } + }; + Real z = es.integrate(f); + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi(), tol); + } +} // template void test_integrals() + + +BOOST_AUTO_TEST_CASE( integrals ) +{ + std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl; + BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals."); + try + { + // using statements needed to change precision policy. + using boost::math::policies::policy; + using boost::math::policies::make_policy; + using boost::math::policies::precision; + using boost::math::policies::digits2; + using boost::math::policies::digits10; + + // using statements needed for changing error handling policy. + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::ignore_error; + using boost::math::policies::throw_on_error; + + typedef policy< + domain_error, + overflow_error + > no_throw_policy; + + /* + // Experiment with better diagnostics. + typedef float Real; + + Real inf = std::numeric_limits::infinity(); + Real max = (std::numeric_limits::max)(); + std::cout.precision(std::numeric_limits::max_digits10); + //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308 + std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf + std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227 + //std::cout << lambert_w0(inf) << std::endl; // inf - will throw. + std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0 + std::cout << "lambert_w0(std::numeric_limits::denorm_min()) = " << lambert_w0(std::numeric_limits::denorm_min()) << std::endl; // 4.94066e-324 + std::cout << "lambert_w0(std::numeric_limits::min()) = " << lambert_w0((std::numeric_limits::min)()) << std::endl; // 2.22507e-308 + + // Approximate the largest lambert_w you can get for type T? + float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max_f " << max_w_f << std::endl; // 84.2879 + Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max " << max_w << std::endl; // 703.227 + + std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; // + std::cout << "test integral 1/z^2" << std::endl; + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1e-10, policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "epsilon = " << std::numeric_limits::epsilon() << std::endl; // + std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19 + std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19 + + + +// Demo debug version. +Real tol = std::numeric_limits::epsilon(); +Real x; +{ + using boost::math::quadrature::exp_sinh; + exp_sinh es; + // Function to be integrated, lambert_w0(1/z^2). + + //auto f = [](Real z)->Real + //{ // Naive - no protection against underflow and subsequent divide by zero. + // return lambert_w0(1 / (z * z)); + //}; + // Diagnostic is: + // Error in function boost::math::lambert_w0: Expected a finite value but got inf + + auto f = [](Real z)->Real + { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things. + return debug_integration_proc(z); + }; + // Exception Error in function boost::math::lambert_w0: Expected a finite value but got inf. + + // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163. + // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23. + x = es.integrate(f); + std::cout << "es.integrate(f) = " << x << std::endl; + BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi(), tol); + // root_two_pi(); + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} + diff --git a/test/test_lambert_w_integrals_float128.cpp b/test/test_lambert_w_integrals_float128.cpp new file mode 100644 index 000000000..1010b138d --- /dev/null +++ b/test/test_lambert_w_integrals_float128.cpp @@ -0,0 +1,276 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w_integrals.cpp +//! \brief quadrature tests that cover the whole range of the Lambert W0 function. + +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +#ifdef BOOST_HAS_FLOAT128 + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include + +#include + +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include +#include +#include +#include +#include +#include + +std::string show_versions(void); + +// Added code and test for Integral of the Lambert W function: by Nick Thompson. +// https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals + +#include // for integral tests. +#include // for integral tests. +#include // for integral tests. + + using boost::math::policies::policy; + using boost::math::policies::make_policy; + +// using statements needed for changing error handling policy. +using boost::math::policies::evaluation_error; +using boost::math::policies::domain_error; +using boost::math::policies::overflow_error; +using boost::math::policies::ignore_error; +using boost::math::policies::throw_on_error; + +typedef policy< + domain_error, + overflow_error +> no_throw_policy; + +// Assumes that function has a throw policy, for example: +// NOT lambert_w0(1 / (x * x), no_throw_policy()); +// Error in function boost::math::quadrature::exp_sinh::integrate: +// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. +// Please ensure your function evaluates to a finite number of its entire domain. +template +T debug_integration_proc(T x) +{ + T result; // warning C4701: potentially uninitialized local variable 'result' used + // T result = 0 ; // But result may not be assigned below? + try + { + // Assign function call to result in here... + if (x <= sqrt(boost::math::tools::min_value()) ) + { + result = 0; + } + else + { + result = lambert_w0(1 / (x * x)); + } + // result = lambert_w0(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is: + // Error in function boost::math::quadrature::exp_sinh::integrate: + // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. + // Please ensure your function evaluates to a finite number of its entire domain. + + } // try + catch (const std::exception& e) + { + std::cout << "Exception " << e.what() << std::endl; + // set breakpoint here: + std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl; + if (!std::isfinite(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + if (std::isnan(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + } // catch + return result; +} // T debug_integration_proc(T x) + +template +void test_integrals() +{ + // Integral of the Lambert W function: + // https://en.wikipedia.org/wiki/Lambert_W_function + using boost::math::quadrature::tanh_sinh; + using boost::math::quadrature::exp_sinh; + // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html + using std::sqrt; + + std::cout << "Integration of type " << typeid(Real).name() << std::endl; + + Real tol = std::numeric_limits::epsilon(); + { // // Integrate for function lambert_W0(z); + tanh_sinh ts; + Real a = 0; + Real b = boost::math::constants::e(); + auto f = [](Real z)->Real + { + return lambert_w0(z); + }; + Real z = ts.integrate(f, a, b); // OK without any decltype(f) + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e() - 1, tol); + } + { + // Integrate for function lambert_W0(z/(z sqrt(z)). + exp_sinh es; + auto f = [](Real z)->Real + { + return lambert_w0(z)/(z * sqrt(z)); + }; + Real z = es.integrate(f); // OK + BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi(), tol); + } + { + // Integrate for function lambert_W0(1/z^2). + exp_sinh es; + //const Real sqrt_min = sqrt(boost::math::tools::min_value()); // 1.08420217e-19 fo 32-bit float. + // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified + auto f = [](Real z)->Real + { + if (z <= sqrt(boost::math::tools::min_value()) ) + { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter. + return static_cast(0); + } + else + { + return lambert_w0(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen. + } + }; + Real z = es.integrate(f); + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi(), tol); + } +} // template void test_integrals() + + +BOOST_AUTO_TEST_CASE( integrals ) +{ + std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl; + BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals."); + try + { + // using statements needed to change precision policy. + using boost::math::policies::policy; + using boost::math::policies::make_policy; + using boost::math::policies::precision; + using boost::math::policies::digits2; + using boost::math::policies::digits10; + + // using statements needed for changing error handling policy. + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::ignore_error; + using boost::math::policies::throw_on_error; + + typedef policy< + domain_error, + overflow_error + > no_throw_policy; + + /* + // Experiment with better diagnostics. + typedef float Real; + + Real inf = std::numeric_limits::infinity(); + Real max = (std::numeric_limits::max)(); + std::cout.precision(std::numeric_limits::max_digits10); + //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308 + std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf + std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227 + //std::cout << lambert_w0(inf) << std::endl; // inf - will throw. + std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0 + std::cout << "lambert_w0(std::numeric_limits::denorm_min()) = " << lambert_w0(std::numeric_limits::denorm_min()) << std::endl; // 4.94066e-324 + std::cout << "lambert_w0(std::numeric_limits::min()) = " << lambert_w0((std::numeric_limits::min)()) << std::endl; // 2.22507e-308 + + // Approximate the largest lambert_w you can get for type T? + float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max_f " << max_w_f << std::endl; // 84.2879 + Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max " << max_w << std::endl; // 703.227 + + std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; // + std::cout << "test integral 1/z^2" << std::endl; + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1e-10, policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "epsilon = " << std::numeric_limits::epsilon() << std::endl; // + std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19 + std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19 + + + +// Demo debug version. +Real tol = std::numeric_limits::epsilon(); +Real x; +{ + using boost::math::quadrature::exp_sinh; + exp_sinh es; + // Function to be integrated, lambert_w0(1/z^2). + + //auto f = [](Real z)->Real + //{ // Naive - no protection against underflow and subsequent divide by zero. + // return lambert_w0(1 / (z * z)); + //}; + // Diagnostic is: + // Error in function boost::math::lambert_w0: Expected a finite value but got inf + + auto f = [](Real z)->Real + { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things. + return debug_integration_proc(z); + }; + // Exception Error in function boost::math::lambert_w0: Expected a finite value but got inf. + + // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163. + // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23. + x = es.integrate(f); + std::cout << "es.integrate(f) = " << x << std::endl; + BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi(), tol); + // root_two_pi(); + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} + +#else + +int main() { return 0; } + +#endif diff --git a/test/test_lambert_w_integrals_long_double.cpp b/test/test_lambert_w_integrals_long_double.cpp new file mode 100644 index 000000000..180fb330f --- /dev/null +++ b/test/test_lambert_w_integrals_long_double.cpp @@ -0,0 +1,266 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w_integrals.cpp +//! \brief quadrature tests that cover the whole range of the Lambert W0 function. + +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include +#include +#include +#include +#include +#include + +std::string show_versions(void); + +// Added code and test for Integral of the Lambert W function: by Nick Thompson. +// https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals + +#include // for integral tests. +#include // for integral tests. +#include // for integral tests. + + using boost::math::policies::policy; + using boost::math::policies::make_policy; + +// using statements needed for changing error handling policy. +using boost::math::policies::evaluation_error; +using boost::math::policies::domain_error; +using boost::math::policies::overflow_error; +using boost::math::policies::ignore_error; +using boost::math::policies::throw_on_error; + +typedef policy< + domain_error, + overflow_error +> no_throw_policy; + +// Assumes that function has a throw policy, for example: +// NOT lambert_w0(1 / (x * x), no_throw_policy()); +// Error in function boost::math::quadrature::exp_sinh::integrate: +// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. +// Please ensure your function evaluates to a finite number of its entire domain. +template +T debug_integration_proc(T x) +{ + T result; // warning C4701: potentially uninitialized local variable 'result' used + // T result = 0 ; // But result may not be assigned below? + try + { + // Assign function call to result in here... + if (x <= sqrt(boost::math::tools::min_value()) ) + { + result = 0; + } + else + { + result = lambert_w0(1 / (x * x)); + } + // result = lambert_w0(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is: + // Error in function boost::math::quadrature::exp_sinh::integrate: + // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. + // Please ensure your function evaluates to a finite number of its entire domain. + + } // try + catch (const std::exception& e) + { + std::cout << "Exception " << e.what() << std::endl; + // set breakpoint here: + std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl; + if (!std::isfinite(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + if (std::isnan(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + } // catch + return result; +} // T debug_integration_proc(T x) + +template +void test_integrals() +{ + // Integral of the Lambert W function: + // https://en.wikipedia.org/wiki/Lambert_W_function + using boost::math::quadrature::tanh_sinh; + using boost::math::quadrature::exp_sinh; + // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html + using std::sqrt; + + std::cout << "Integration of type " << typeid(Real).name() << std::endl; + + Real tol = std::numeric_limits::epsilon(); + { // // Integrate for function lambert_W0(z); + tanh_sinh ts; + Real a = 0; + Real b = boost::math::constants::e(); + auto f = [](Real z)->Real + { + return lambert_w0(z); + }; + Real z = ts.integrate(f, a, b); // OK without any decltype(f) + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e() - 1, tol); + } + { + // Integrate for function lambert_W0(z/(z sqrt(z)). + exp_sinh es; + auto f = [](Real z)->Real + { + return lambert_w0(z)/(z * sqrt(z)); + }; + Real z = es.integrate(f); // OK + BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi(), tol); + } + { + // Integrate for function lambert_W0(1/z^2). + exp_sinh es; + //const Real sqrt_min = sqrt(boost::math::tools::min_value()); // 1.08420217e-19 fo 32-bit float. + // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified + auto f = [](Real z)->Real + { + if (z <= sqrt(boost::math::tools::min_value()) ) + { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter. + return static_cast(0); + } + else + { + return lambert_w0(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen. + } + }; + Real z = es.integrate(f); + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi(), tol); + } +} // template void test_integrals() + + +BOOST_AUTO_TEST_CASE( integrals ) +{ + std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl; + BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals."); + try + { + // using statements needed to change precision policy. + using boost::math::policies::policy; + using boost::math::policies::make_policy; + using boost::math::policies::precision; + using boost::math::policies::digits2; + using boost::math::policies::digits10; + + // using statements needed for changing error handling policy. + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::ignore_error; + using boost::math::policies::throw_on_error; + + typedef policy< + domain_error, + overflow_error + > no_throw_policy; + + /* + // Experiment with better diagnostics. + typedef float Real; + + Real inf = std::numeric_limits::infinity(); + Real max = (std::numeric_limits::max)(); + std::cout.precision(std::numeric_limits::max_digits10); + //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308 + std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf + std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227 + //std::cout << lambert_w0(inf) << std::endl; // inf - will throw. + std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0 + std::cout << "lambert_w0(std::numeric_limits::denorm_min()) = " << lambert_w0(std::numeric_limits::denorm_min()) << std::endl; // 4.94066e-324 + std::cout << "lambert_w0(std::numeric_limits::min()) = " << lambert_w0((std::numeric_limits::min)()) << std::endl; // 2.22507e-308 + + // Approximate the largest lambert_w you can get for type T? + float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max_f " << max_w_f << std::endl; // 84.2879 + Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max " << max_w << std::endl; // 703.227 + + std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; // + std::cout << "test integral 1/z^2" << std::endl; + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1e-10, policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "epsilon = " << std::numeric_limits::epsilon() << std::endl; // + std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19 + std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19 + + + +// Demo debug version. +Real tol = std::numeric_limits::epsilon(); +Real x; +{ + using boost::math::quadrature::exp_sinh; + exp_sinh es; + // Function to be integrated, lambert_w0(1/z^2). + + //auto f = [](Real z)->Real + //{ // Naive - no protection against underflow and subsequent divide by zero. + // return lambert_w0(1 / (z * z)); + //}; + // Diagnostic is: + // Error in function boost::math::lambert_w0: Expected a finite value but got inf + + auto f = [](Real z)->Real + { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things. + return debug_integration_proc(z); + }; + // Exception Error in function boost::math::lambert_w0: Expected a finite value but got inf. + + // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163. + // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23. + x = es.integrate(f); + std::cout << "es.integrate(f) = " << x << std::endl; + BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi(), tol); + // root_two_pi(); + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} + diff --git a/test/test_lambert_w_integrals_quad.cpp b/test/test_lambert_w_integrals_quad.cpp new file mode 100644 index 000000000..907b43cff --- /dev/null +++ b/test/test_lambert_w_integrals_quad.cpp @@ -0,0 +1,269 @@ +// Copyright Paul A. Bristow 2016, 2017, 2018. +// Copyright John Maddock 2016. + +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// test_lambert_w_integrals.cpp +//! \brief quadrature tests that cover the whole range of the Lambert W0 function. + +#include // for BOOST_MSVC definition etc. +#include // for BOOST_MSVC versions. + +// Boost macros +#define BOOST_TEST_MAIN +#define BOOST_LIB_DIAGNOSTIC "on" // Report library file details. +#include // Boost.Test +// #include // Boost.Test +#include + +#include +#include +#include +#include +using boost::multiprecision::cpp_bin_float_quad; + +#include // isnan, ifinite. +#include // float_next, float_prior +using boost::math::float_next; +using boost::math::float_prior; +#include // ulp + +#include // for create_test_value and macro BOOST_MATH_TEST_VALUE. +#include +using boost::math::policies::digits2; +using boost::math::policies::digits10; +#include // For Lambert W lambert_w function. +using boost::math::lambert_wm1; +using boost::math::lambert_w0; + +#include +#include +#include +#include +#include +#include + +std::string show_versions(void); + +// Added code and test for Integral of the Lambert W function: by Nick Thompson. +// https://en.wikipedia.org/wiki/Lambert_W_function#Definite_integrals + +#include // for integral tests. +#include // for integral tests. +#include // for integral tests. + + using boost::math::policies::policy; + using boost::math::policies::make_policy; + +// using statements needed for changing error handling policy. +using boost::math::policies::evaluation_error; +using boost::math::policies::domain_error; +using boost::math::policies::overflow_error; +using boost::math::policies::ignore_error; +using boost::math::policies::throw_on_error; + +typedef policy< + domain_error, + overflow_error +> no_throw_policy; + +// Assumes that function has a throw policy, for example: +// NOT lambert_w0(1 / (x * x), no_throw_policy()); +// Error in function boost::math::quadrature::exp_sinh::integrate: +// The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. +// Please ensure your function evaluates to a finite number of its entire domain. +template +T debug_integration_proc(T x) +{ + T result; // warning C4701: potentially uninitialized local variable 'result' used + // T result = 0 ; // But result may not be assigned below? + try + { + // Assign function call to result in here... + if (x <= sqrt(boost::math::tools::min_value()) ) + { + result = 0; + } + else + { + result = lambert_w0(1 / (x * x)); + } + // result = lambert_w0(1 / (x * x), no_throw_policy()); // Bad idea, less helpful diagnostic message is: + // Error in function boost::math::quadrature::exp_sinh::integrate: + // The exp_sinh quadrature evaluated your function at a singular point and resulted in inf. + // Please ensure your function evaluates to a finite number of its entire domain. + + } // try + catch (const std::exception& e) + { + std::cout << "Exception " << e.what() << std::endl; + // set breakpoint here: + std::cout << "Unexpected exception thrown in integration code at abscissa (x): " << x << "." << std::endl; + if (!std::isfinite(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + if (std::isnan(result)) + { + // set breakpoint here: + std::cout << "Unexpected non-finite result in integration code at abscissa (x): " << x << "." << std::endl; + } + } // catch + return result; +} // T debug_integration_proc(T x) + +template +void test_integrals() +{ + // Integral of the Lambert W function: + // https://en.wikipedia.org/wiki/Lambert_W_function + using boost::math::quadrature::tanh_sinh; + using boost::math::quadrature::exp_sinh; + // file:///I:/modular-boost/libs/math/doc/html/math_toolkit/quadrature/double_exponential/de_tanh_sinh.html + using std::sqrt; + + std::cout << "Integration of type " << typeid(Real).name() << std::endl; + + Real tol = std::numeric_limits::epsilon(); + { // // Integrate for function lambert_W0(z); + tanh_sinh ts; + Real a = 0; + Real b = boost::math::constants::e(); + auto f = [](Real z)->Real + { + return lambert_w0(z); + }; + Real z = ts.integrate(f, a, b); // OK without any decltype(f) + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::e() - 1, tol); + } + { + // Integrate for function lambert_W0(z/(z sqrt(z)). + exp_sinh es; + auto f = [](Real z)->Real + { + return lambert_w0(z)/(z * sqrt(z)); + }; + Real z = es.integrate(f); // OK + BOOST_CHECK_CLOSE_FRACTION(z, 2 * boost::math::constants::root_two_pi(), tol); + } + { + // Integrate for function lambert_W0(1/z^2). + exp_sinh es; + //const Real sqrt_min = sqrt(boost::math::tools::min_value()); // 1.08420217e-19 fo 32-bit float. + // error C3493: 'sqrt_min' cannot be implicitly captured because no default capture mode has been specified + auto f = [](Real z)->Real + { + if (z <= sqrt(boost::math::tools::min_value()) ) + { // Too small would underflow z * z and divide by zero to overflow 1/z^2 for lambert_w0 z parameter. + return static_cast(0); + } + else + { + return lambert_w0(1 / (z * z)); // warning C4756: overflow in constant arithmetic, even though cannot happen. + } + }; + Real z = es.integrate(f); + BOOST_CHECK_CLOSE_FRACTION(z, boost::math::constants::root_two_pi(), tol); + } +} // template void test_integrals() + + +BOOST_AUTO_TEST_CASE( integrals ) +{ + std::cout << "Macro BOOST_MATH_LAMBERT_W0_INTEGRALS is defined." << std::endl; + BOOST_TEST_MESSAGE("\nTest Lambert W0 integrals."); + try + { + // using statements needed to change precision policy. + using boost::math::policies::policy; + using boost::math::policies::make_policy; + using boost::math::policies::precision; + using boost::math::policies::digits2; + using boost::math::policies::digits10; + + // using statements needed for changing error handling policy. + using boost::math::policies::evaluation_error; + using boost::math::policies::domain_error; + using boost::math::policies::overflow_error; + using boost::math::policies::ignore_error; + using boost::math::policies::throw_on_error; + + typedef policy< + domain_error, + overflow_error + > no_throw_policy; + + /* + // Experiment with better diagnostics. + typedef float Real; + + Real inf = std::numeric_limits::infinity(); + Real max = (std::numeric_limits::max)(); + std::cout.precision(std::numeric_limits::max_digits10); + //std::cout << "lambert_w0(inf) = " << lambert_w0(inf) << std::endl; // lambert_w0(inf) = 1.79769e+308 + std::cout << "lambert_w0(inf, throw_policy()) = " << lambert_w0(inf, no_throw_policy()) << std::endl; // inf + std::cout << "lambert_w0(max) = " << lambert_w0(max) << std::endl; // lambert_w0(max) = 703.227 + //std::cout << lambert_w0(inf) << std::endl; // inf - will throw. + std::cout << "lambert_w0(0) = " << lambert_w0(0.) << std::endl; // 0 + std::cout << "lambert_w0(std::numeric_limits::denorm_min()) = " << lambert_w0(std::numeric_limits::denorm_min()) << std::endl; // 4.94066e-324 + std::cout << "lambert_w0(std::numeric_limits::min()) = " << lambert_w0((std::numeric_limits::min)()) << std::endl; // 2.22507e-308 + + // Approximate the largest lambert_w you can get for type T? + float max_w_f = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max_f " << max_w_f << std::endl; // 84.2879 + Real max_w = boost::math::lambert_w_detail::lambert_w0_approx((std::numeric_limits::max)()); // Corless equation 4.19, page 349, and Chapeau-Blondeau equation 20, page 2162. + std::cout << "w max " << max_w << std::endl; // 703.227 + + std::cout << "lambert_w0(7.2416706213544837e-163) = " << lambert_w0(7.2416706213544837e-163) << std::endl; // + std::cout << "test integral 1/z^2" << std::endl; + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1e-10, policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "ULP = " << boost::math::ulp(1., policy >()) << std::endl; // ULP = 2.2204460492503131e-16 + std::cout << "epsilon = " << std::numeric_limits::epsilon() << std::endl; // + std::cout << "sqrt(max) = " << sqrt(boost::math::tools::max_value() ) << std::endl; // sqrt(max) = 1.8446742974197924e+19 + std::cout << "sqrt(min) = " << sqrt(boost::math::tools::min_value() ) << std::endl; // sqrt(min) = 1.0842021724855044e-19 + + + +// Demo debug version. +Real tol = std::numeric_limits::epsilon(); +Real x; +{ + using boost::math::quadrature::exp_sinh; + exp_sinh es; + // Function to be integrated, lambert_w0(1/z^2). + + //auto f = [](Real z)->Real + //{ // Naive - no protection against underflow and subsequent divide by zero. + // return lambert_w0(1 / (z * z)); + //}; + // Diagnostic is: + // Error in function boost::math::lambert_w0: Expected a finite value but got inf + + auto f = [](Real z)->Real + { // Debug with diagnostics for underflow and subsequent divide by zero and other bad things. + return debug_integration_proc(z); + }; + // Exception Error in function boost::math::lambert_w0: Expected a finite value but got inf. + + // Unexpected exception thrown in integration code at abscissa: 7.2416706213544837e-163. + // Unexpected exception thrown in integration code at abscissa (x): 3.478765835953569e-23. + x = es.integrate(f); + std::cout << "es.integrate(f) = " << x << std::endl; + BOOST_CHECK_CLOSE_FRACTION(x, boost::math::constants::root_two_pi(), tol); + // root_two_pi(); + } + catch (std::exception& ex) + { + std::cout << ex.what() << std::endl; + } +} + diff --git a/tools/lambert_w_high_reference_values.cpp b/tools/lambert_w_high_reference_values.cpp new file mode 100644 index 000000000..3c908cc6e --- /dev/null +++ b/tools/lambert_w_high_reference_values.cpp @@ -0,0 +1,182 @@ +// \modular-boost\libs\math\test\lambert_w_high_reference_values.cpp + +// Copyright Paul A. Bristow 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Write a C++ file \lambert_w_mp_hi_values.ipp +// containing arrays of z arguments and 100 decimal digit precision lambert_w0(z) reference values. +// These can be used in tests of precision of less-precise types like +// built-in float, double, long double and quad and cpp_dec_float_50. + +// These cover the range from 0.5 to (std::numeric_limits<>::max)(); +// The Fukushima algorithm changes from a series function for all z > 0.5. + +// Multiprecision types: +//#include +#include // boost::multiprecision::cpp_dec_float_100 +using boost::multiprecision::cpp_dec_float_100; + +#include // +using boost::math::lambert_w0; +using boost::math::lambert_wm1; + +#include +// using std::cout; using std::std::endl; using std::ios; using std::std::cerr; +#include +using std::setprecision; +using std::showpoint; +#include +using std::ofstream; +#include +#include // for DBL_EPS etc +#include // for numeric limits. +//#include +#include + +const long double eps = std::numeric_limits::epsilon(); + +// Creates if no file exists, & uses default overwrite/ ios::replace. +const char filename[] = "lambert_w_high_reference_values.ipp"; // +std::ofstream fout(filename, std::ios::out); + +typedef cpp_dec_float_100 RealType; // 100 decimal digits for the value fed to macro BOOST_MATH_TEST_VALUE. +// Could also use cpp_dec_float_50 or cpp_bin_float types. + +const int no_of_tests = 450; // 500 overflows float. + +static const float min_z = 0.5F; // for element[0] + +int main() +{ // Make C++ file containing Lambert W test values. + std::cout << filename << " "; + std::cout << std::endl; + std::cout << "Lambert W0 decimal digit precision values for high z argument values." << std::endl; + + if (!fout.is_open()) + { // File failed to open OK. + std::cerr << "Open file " << filename << " failed!" << std::endl; + std::cerr << "errno " << errno << std::endl; + return -1; + } + try + { + int output_precision = std::numeric_limits::digits10; + // cpp_dec_float_100 is ample precision and + // has several extra bits internally so max_digits10 are not needed. + fout.precision(output_precision); + fout << std::showpoint << std::endl; // Do show trailing zeros. + + // Intro for RealType values. + std::cout << "Lambert W test values written to file " << filename << std::endl; + fout << + "\n" + "// A collection of big Lambert W test values computed using " + << output_precision << " decimal digits precision.\n" + "// C++ floating-point type is " << "RealType." "\n" + "\n" + "// Written by " << __FILE__ << " " << __TIMESTAMP__ << "\n" + + "\n" + "// Copyright Paul A. Bristow 2017." "\n" + "// Distributed under the Boost Software License, Version 1.0." "\n" + "// (See accompanying file LICENSE_1_0.txt" "\n" + "// or copy at http://www.boost.org/LICENSE_1_0.txt)" "\n" + << std::endl; + + fout << "// Size of arrays of arguments z and Lambert W" << std::endl; + fout << "static const unsigned int noof_tests = " << no_of_tests << ";" << std::endl; + + // Declare arrays of z and Lambert W. + fout << "\n// Declare arrays of arguments z and Lambert W(z)" << std::endl; + fout << + "\n" + "template ""\n" + "static RealType zs[" << no_of_tests << "];" + << std::endl; + + fout << + "\n" + "template ""\n" + "static RealType ws[" << no_of_tests << "];" + << std::endl; + + fout << "// The values are defined using the macro BOOST_MATH_TEST_VALUE to ensure\n" + "// that both built-in and multiprecision types are correctly initialiased with full precision.\n" + "// built-in types like float, double require a floating-point literal like 3.14,\n" + "// but multiprecision types require a decimal digit string like \"3.14\".\n" + "// Numerical values are chosen to avoid exactly representable values." + << std::endl; + + static const RealType min_z = 0.6; // for element[0] + + const RealType max_z = (std::numeric_limits::max)() / 10; // (std::numeric_limits::max)() to make sure is OK for all floating-point types. + // Less a bit as lambert_w0(max) may be inaccurate. + const RealType step_size = 0.5F; // Increment step size. + const RealType step_factor = 2.f; // Multiple factor, typically 2, 5 or 10. + const int step_modulo = 5; + + RealType z = min_z; + + // Output function to initialize array of arguments z and Lambert W. + fout << + "\n" + << "template \n" + "void init_zws()\n" + "{\n"; + + for (size_t index = 0; (index != no_of_tests); index++) + { + fout + << " zs[" << index << "] = BOOST_MATH_TEST_VALUE(RealType, " + << z // Since start with converting a float may get lots of usefully random digits. + << ");" + << std::endl; + + fout + << " ws[" << index << "] = BOOST_MATH_TEST_VALUE(RealType, " + << lambert_w0(z) + << ");" + << std::endl; + + if ((index % step_modulo) == 0) + { + z *= step_factor; // + } + z += step_size; + if (z >= max_z) + { // Don't go over max for float. + std::cout << "too big z" << std::endl; + break; + } + } // for index + fout << "};" << std::endl; + + fout << "// End of lambert_w_mp_high_values.ipp " << std::endl; + } + catch (std::exception& ex) + { + std::cout << "Exception " << ex.what() << std::endl; + } + + fout.close(); + + std::cout << no_of_tests << " Lambert_w0 values written to files " << __TIMESTAMP__ << std::endl; + return 0; +} // main + + +/* +A few spot checks again Wolfram: + + zs[1] = BOOST_MATH_TEST_VALUE(RealType, 1.6999999999999999555910790149937383830547332763671875); + ws[1] = BOOST_MATH_TEST_VALUE(RealType, 0.7796011225311008662356536916883580556792500749037209859530390902424444585607630246126725241921761054); + Wolfram 0.7796011225311008662356536916883580556792500749037209859530390902424444585607630246126725241921761054 + + zs[99] = BOOST_MATH_TEST_VALUE(RealType, 3250582.599999999976716935634613037109375); + ws[99] = BOOST_MATH_TEST_VALUE(RealType, 12.47094339016839065212822905567651460418204106065566910956134121802725695306834966790193342511971825); + Wolfram 12.47094339016839065212822905567651460418204106065566910956134121802725695306834966790193342511971825 + +*/ + diff --git a/tools/lambert_w_lookup_table_generator.cpp b/tools/lambert_w_lookup_table_generator.cpp new file mode 100644 index 000000000..7918e812f --- /dev/null +++ b/tools/lambert_w_lookup_table_generator.cpp @@ -0,0 +1,469 @@ +// Lambert W lookup table generator lambert_w_lookup_table_generator.cpp + +//! \file +//! Output a table of precomputed array values for Lambert W0 and W-1, +//! and square roots and halves, and powers of e, +// as a .ipp file for use by lambert_w.hpp. + +//! \details Output as long double precision (suffix L) using Boost.Multiprecision +//! to 34 decimal digits precision to cater for platforms that have 128-bit long double. +//! The function bisection can then use any built-in floating-point type, +//! which may have different precision and speed on different platforms. +//! The actual builtin floating-point type of the arrays is chosen by a +//! typedef in \modular-boost\libs\math\include\boost\math\special_functions\lambert_w.hpp +//! by default, for example: typedef double lookup_t; + + +// This includes lookup tables for both branches W0 and W-1. +// Only W-1 is needed by current code that uses JM rational Polynomials, +// but W0 is kept (for now) to allow comparison with the previous FKDVPB version +// that uses lookup for W0 branch as well as W-1. + +#include +#include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. +using boost::math::constants::exp_minus_one; // 0.36787944 +using boost::math::constants::root_e; // 1.64872 +#include +using boost::multiprecision::cpp_bin_float_quad; +using boost::multiprecision::cpp_bin_float_50; + +#include +#include +#include + +/* +typedef double lookup_t; // Type for lookup table (double or float?) + +BOOST_STATIC_CONSTEXPR std::size_t noof_sqrts = 12; +BOOST_STATIC_CONSTEXPR lookup_t a[noof_sqrts] = // 0.6065 0.7788, 0.8824 ... 0.9997, sqrt of previous elements. +{ +0.60653065971263342, 0.77880078307140487, 0.8824969025845954, 0.93941306281347579, 0.96923323447634408, 0.98449643700540841, +0.99221793826024351, 0.99610136947011749, 0.99804878110747547, 0.99902391418197566, 0.99951183793988937, 0.99975588917489722 +}; +BOOST_STATIC_CONSTEXPR lookup_t b[noof_sqrts] = // 0.5 0.25 0.125, 0.0625 ... 0.0002441, halves of previous elements. +{ 0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625, 0.0078125, 0.00390625, 0.001953125, 0.0009765625, 0.00048828125, 0.000244140625 }; + +BOOST_STATIC_CONSTEXPR size_t noof_w0zs = 65; +BOOST_STATIC_CONSTEXPR lookup_t g[noof_w0zs] = // lambert_w[k] for W0 branch. 0 2.7182 14.77 60.2566 ... 1.445e+29 3.990e+29. +{ 0., 2.7182818284590452, 14.7781121978613, 60.256610769563003, 218.39260013257696, 742.06579551288302, 2420.5727609564107, 7676.4321089992102, +23847.663896333826, 72927.755348178456, 220264.65794806717, 658615.558867176, 1953057.4970280471, 5751374.0961159665, 16836459.978306875, 49035260.58708166, +142177768.32812596, 410634196.81078007, 1181879444.4719492, 3391163718.300558, 9703303908.1958056, 27695130424.147509, 78868082614.895014, 224130479263.72476, +635738931116.24334, 1800122483434.6468, 5088969845149.8079, 14365302496248.563, 40495197800161.305, 114008694617177.22, 320594237445733.86, +900514339622670.18, 2526814725845782.2, 7083238132935230.1, 19837699245933466., 55510470830970076., 1.5520433569614703e+17, 4.3360826779369662e+17, +1.2105254067703227e+18, 3.3771426165357561e+18, 9.4154106734807994e+18, 2.6233583234732253e+19, 7.3049547543861044e+19, 2.032970971338619e+20, +5.6547040503180956e+20, 1.5720421975868293e+21, 4.3682149334771265e+21, 1.2132170565093317e+22, 3.3680332378068632e+22, 9.3459982052259885e+22, +2.5923527642935362e+23, 7.1876803203773879e+23, 1.99212416037262e+24, 5.5192924995054165e+24, 1.5286067837683347e+25, 4.2321318958281094e+25, +1.1713293177672778e+26, 3.2408603996214814e+26, 8.9641258264226028e+26, 2.4787141382364034e+27, 6.8520443388941057e+27, 1.8936217407781711e+28, +5.2317811346197018e+28, 1.4450833904658542e+29, 3.9904954117194348e+29 +}; + +BOOST_STATIC_CONSTEXPR std::size_t noof_wm1zs = 66; +BOOST_STATIC_CONSTEXPR lookup_t e[noof_wm1zs] = // lambert_w[k] for W-1 branch. 2.7182 1. 0.3678 0.1353 0.04978 ... 4.359e-28 1.603e-28 +{ +2.7182818284590452, 1., 0.36787944117144232, 0.13533528323661269, 0.049787068367863943, 0.01831563888873418, 0.0067379469990854671, +0.0024787521766663584, 0.00091188196555451621, 0.00033546262790251184, 0.00012340980408667955, 4.5399929762484852e-05, 1.6701700790245659e-05, +6.1442123533282098e-06, 2.2603294069810543e-06, 8.3152871910356788e-07, 3.0590232050182579e-07, 1.1253517471925911e-07, 4.1399377187851667e-08, +1.5229979744712628e-08, 5.6027964375372675e-09, 2.0611536224385578e-09, 7.5825604279119067e-10, 2.7894680928689248e-10, 1.026187963170189e-10, +3.7751345442790977e-11, 1.3887943864964021e-11, 5.1090890280633247e-12, 1.8795288165390833e-12, 6.914400106940203e-13, 2.5436656473769229e-13, +9.3576229688401746e-14, 3.4424771084699765e-14, 1.2664165549094176e-14, 4.6588861451033974e-15, 1.713908431542013e-15, 6.3051167601469894e-16, +2.3195228302435694e-16, 8.5330476257440658e-17, 3.1391327920480296e-17, 1.1548224173015786e-17, 4.248354255291589e-18, 1.5628821893349888e-18, +5.7495222642935598e-19, 2.1151310375910805e-19, 7.7811322411337965e-20, 2.8625185805493936e-20, 1.0530617357553812e-20, 3.8739976286871871e-21, +1.4251640827409351e-21, 5.2428856633634639e-22, 1.9287498479639178e-22, 7.0954741622847041e-23, 2.6102790696677048e-23, 9.602680054508676e-24, +3.532628572200807e-24, 1.2995814250075031e-24, 4.7808928838854691e-25, 1.7587922024243116e-25, 6.4702349256454603e-26, 2.3802664086944006e-26, +8.7565107626965203e-27, 3.2213402859925161e-27, 1.185064864233981e-27, 4.359610000063081e-28, 1.6038108905486378e-28 +}; + +lambert_w0 version of array from Fukushima + +// lambert_w[k] for W-1 branch. 2.7182 1. 0.3678 0.1353 0.04978 ... 4.359e-28 1.603e-28 +e: 2.7182818284590452, 1., 0.36787944117144232, 0.13533528323661269, 0.049787068367863943, 0.01831563888873418, 0.0067379469990854671, +0.0024787521766663584, 0.00091188196555451621, 0.00033546262790251184, 0.00012340980408667955, 4.5399929762484852e-05, 1.6701700790245659e-05, +6.1442123533282098e-06, 2.2603294069810543e-06, 8.3152871910356788e-07, 3.0590232050182579e-07, 1.1253517471925911e-07, 4.1399377187851667e-08, +1.5229979744712628e-08, 5.6027964375372675e-09, 2.0611536224385578e-09, 7.5825604279119067e-10, 2.7894680928689248e-10, 1.026187963170189e-10, +3.7751345442790977e-11, 1.3887943864964021e-11, 5.1090890280633247e-12, 1.8795288165390833e-12, 6.914400106940203e-13, 2.5436656473769229e-13, +9.3576229688401746e-14, 3.4424771084699765e-14, 1.2664165549094176e-14, 4.6588861451033974e-15, 1.713908431542013e-15, 6.3051167601469894e-16, +2.3195228302435694e-16, 8.5330476257440658e-17, 3.1391327920480296e-17, 1.1548224173015786e-17, 4.248354255291589e-18, 1.5628821893349888e-18, +5.7495222642935598e-19, 2.1151310375910805e-19, 7.7811322411337965e-20, 2.8625185805493936e-20, 1.0530617357553812e-20, 3.8739976286871871e-21, +1.4251640827409351e-21, 5.2428856633634639e-22, 1.9287498479639178e-22, 7.0954741622847041e-23, 2.6102790696677048e-23, 9.602680054508676e-24, +3.532628572200807e-24, 1.2995814250075031e-24, 4.7808928838854691e-25, 1.7587922024243116e-25, 6.4702349256454603e-26, 2.3802664086944006e-26, +8.7565107626965203e-27, 3.2213402859925161e-27, 1.185064864233981e-27, 4.359610000063081e-28, 1.6038108905486378e-28 + +// lambert_w[k] for W0 branch. 0 2.7182 14.77 60.2566 ... 1.445e+29 3.990e+29. + +g: 0, 2.7182818284590452, 14.7781121978613, 60.256610769563003, 218.39260013257696, 742.06579551288302, 2420.5727609564107, 7676.4321089992102, +23847.663896333826, 72927.755348178456, 220264.65794806717, 658615.558867176, 1953057.4970280471, 5751374.0961159665, 16836459.978306875, 49035260.58708166, +142177768.32812596, 410634196.81078007, 1181879444.4719492, 3391163718.300558, 9703303908.1958056, 27695130424.147509, 78868082614.895014, 224130479263.72476, +635738931116.24334, 1800122483434.6468, 5088969845149.8079, 14365302496248.563, 40495197800161.305, 114008694617177.22, 320594237445733.86, +900514339622670.18, 2526814725845782.2, 7083238132935230.1, 19837699245933466, 55510470830970076, 1.5520433569614703e+17, 4.3360826779369662e+17, +1.2105254067703227e+18, 3.3771426165357561e+18, 9.4154106734807994e+18, 2.6233583234732253e+19, 7.3049547543861044e+19, 2.032970971338619e+20, +5.6547040503180956e+20, 1.5720421975868293e+21, 4.3682149334771265e+21, 1.2132170565093317e+22, 3.3680332378068632e+22, 9.3459982052259885e+22, +2.5923527642935362e+23, 7.1876803203773879e+23, 1.99212416037262e+24, 5.5192924995054165e+24, 1.5286067837683347e+25, 4.2321318958281094e+25, +1.1713293177672778e+26, 3.2408603996214814e+26, 8.9641258264226028e+26, 2.4787141382364034e+27, 6.8520443388941057e+27, 1.8936217407781711e+28, +5.2317811346197018e+28, 1.4450833904658542e+29, 3.9904954117194348e+29 + + +lambert_wm1 version of arrays from Fukushima +e: 2.7182817459106445 7.3890557289123535 20.085535049438477 54.59814453125 148.41314697265625 403.42874145507813 1096.6329345703125 2980.957275390625 8103.08154296875 22026.458984375 59874.12109375 162754.734375 442413.21875 1202603.75 3269015.75 8886106 24154940 65659932 178482192 485164896 1318814848 3584910336 9744796672 26489102336 72004845568 195729457152 532047822848 1446255919104 3931331100672 10686465835008 29048824659968 78962889850880 214643389759488 583461240832000 1586012102852608 4311227773747200 11719131799748608 31855901283450880 86593318145753088 2.3538502982225101e+17 6.398428560008151e+17 1.7392731886358364e+18 4.7278345784949473e+18 1.2851586685678387e+19 3.493423319351296e+19 9.4961089747571704e+19 2.581309902546461e+20 7.0167278463083348e+20 1.9073443887231177e+21 5.1846992652160593e+21 1.4093473476000776e+22 3.831003235981371e+22 1.0413746376682761e+23 2.8307496154307266e+23 7.6947746628514896e+23 2.0916565667371597e+24 5.6857119515524837e+24 1.5455367020327599e+25 4.2012039964445827e+25 1.1420056438012293e+26 3.1042929865047826e+26 8.4383428037470738e+26 2.2937792813113457e+27 6.2351382164292627e+27 + +g: -0.36787945032119751 -0.27067059278488159 -0.14936122298240662 -0.073262564837932587 -0.033689741045236588 -0.014872515574097633 -0.0063831745646893978 -0.0026837014593183994 -0.0011106884339824319 -0.00045399941154755652 -0.00018371877376921475 -7.3730567237362266e-05 -2.9384291337919421e-05 -1.1641405762929935e-05 -4.5885362851549871e-06 -1.8005634956352878e-06 -7.0378973759943619e-07 -2.7413975089984888e-07 -1.0645318582191976e-07 -4.122309249510181e-08 -1.5923385277005764e-08 -6.1368328196920174e-09 -2.3602335641470518e-09 -9.0603280433754207e-10 -3.471987974901225e-10 -1.3283640853956058e-10 -5.0747316071575455e-11 -1.9360334516105304e-11 -7.3766357605586919e-12 -2.8072891233854591e-12 -1.0671687058344537e-12 -4.0525363013271809e-13 -1.5374336461045079e-13 -5.8272932648966574e-14 -2.206792725173521e-14 -8.3502896573240185e-15 -3.1572303958374423e-15 -1.192871523299666e-15 -4.5038112940094517e-16 -1.699343306816689e-16 -6.4078234365689933e-17 -2.4148019279880996e-17 -9.095073346605316e-18 -3.4237017961279004e-18 -1.2881348671140216e-18 -4.8440896082993503e-19 -1.8207810463107454e-19 -6.8407959442757565e-20 -2.569017156788846e-20 -9.6437611040447661e-21 -3.6186962678628536e-21 -1.357346940624028e-21 -5.0894276378983633e-22 -1.9076220526102576e-22 -7.1477077345829229e-23 -2.6773039821769189e-23 -1.0025130740057213e-23 -3.7527418826161672e-24 -1.4043593713279384e-24 -5.2539147015754201e-25 -1.9650207139502987e-25 -7.3474141096711539e-26 -2.7465588329293218e-26 -1.0264406957471058e-26 + + +a: 1.6487212181091309 1.2840254306793213 1.1331484317779541 1.0644944906234741 1.0317434072494507 1.0157476663589478 1.007843017578125 1.0039138793945313 1.0019550323486328 1.0009770393371582 1.0004884004592896 1.000244140625 + +// These are common to both W0 and W-1 +b: 0.5 0.25 0.125 0.0625 0.03125 0.015625 0.0078125 0.00390625 0.001953125 0.0009765625 0.00048828125 0.000244140625 + +*/ + +// Creates if no file exists, & uses default overwrite/ ios::replace. +//const char filename[] = // "lambert_w_lookup_table.ipp"; // Write to same folder as generator: +//"I:/modular-boost/libs/math/include/boost/math/special_functions/lambert_w_lookup_table.ipp"; +const char filename[] = "lambert_w_lookup_table.ipp"; + +std::ofstream fout(filename, std::ios::out); // File output stream. + +// 128-bit precision type (so that full precision if long double type uses 128-bit). +// typedef cpp_bin_float_quad table_lookup_t; // Output using max_digits10 for 37 decimal digit precision. +// (This is the precision for the tables output as a C++ program, +// not the precision used by the lambert_w.hpp, that defines another typedef lookup_t, default double. + +typedef cpp_bin_float_50 table_lookup_t; // Compute tables to 50 decimla digit precision to avoid slight inaccuracy from repeated multiply. + +// But Output using max_digits10 for 37 decimal digit precision. + +int main() +{ + std::cout << "Lambert W table lookup values." << std::endl; + if (!fout.is_open()) + { // File failed to open OK. + std::cerr << "Open file " << filename << " failed!" << std::endl; + std::cerr << "errno " << errno << std::endl; + return -1; + } + try + { + std::cout << "Lambert W test values writing to file " << filename << std::endl; + int output_precision = std::numeric_limits::max_digits10; // 37 decimal digits. + fout.precision(output_precision); + fout << + "// Copyright Paul A. Bristow 2017." "\n" + "// Distributed under the Boost Software License, Version 1.0." "\n" + "// (See accompanying file LICENSE_1_0.txt" "\n" + "// or copy at http://www.boost.org/LICENSE_1_0.txt)" "\n" + "\n" + "// " << filename << "\n\n" + "// A collection of 128-bit precision integral z argument Lambert W values computed using " + << output_precision << " decimal digits precision.\n" + "// C++ floating-point precision is 128-bit long double.\n" + "// Output as " + << std::numeric_limits::max_digits10 + << " decimal digits, suffixed L.\n" + "\n" + "// C++ floating-point type is provided by lambert_w.hpp typedef." "\n" + "// For example: typedef lookup_t double; (or float or long double)" "\n" + + "\n" + "// Written by " << __FILE__ << " " << __TIMESTAMP__ << "\n" + << std::endl; + + fout << "// Sizes of arrays of z values for Lambert W[0], W[1] ... W[64]" + "\"n""and W[-1], W[-2] ... W[-64]." << std::endl; + + fout << "\nnamespace boost {\nnamespace math {\nnamespace lambert_w_detail {\nnamespace lambert_w_lookup\n{ \n"; + + BOOST_STATIC_CONSTEXPR std::size_t noof_sqrts = 12; + BOOST_STATIC_CONSTEXPR std::size_t noof_halves = 12; + fout << "BOOST_STATIC_CONSTEXPR std::size_t noof_sqrts = " << noof_sqrts << ";" << std::endl; + fout << "BOOST_STATIC_CONSTEXPR std::size_t noof_halves = " << noof_halves << ";" << std::endl; // Common to both branches. + + BOOST_STATIC_CONSTEXPR std::size_t noof_w0zs = 65; // F[k] 0 <= k <= 64. f[0] = F[0], f[64] = F[64] + BOOST_STATIC_CONSTEXPR std::size_t noof_w0es = 66; // noof_w0zs +1 for gratuitous extra power. + BOOST_STATIC_CONSTEXPR std::size_t noof_wm1zs = 64; // G[k] 1 <= k <= 64. (W-1 = 0 would be z = -infinity, so not stored in array) g[0] == G[1], g[63] = G[64] + BOOST_STATIC_CONSTEXPR std::size_t noof_wm1es = 64; // + + fout << "BOOST_STATIC_CONSTEXPR std::size_t noof_w0es = " << noof_w0zs << ";" << std::endl; + fout << "BOOST_STATIC_CONSTEXPR std::size_t noof_w0zs = " << noof_w0zs << ";" << std::endl; + fout << "BOOST_STATIC_CONSTEXPR std::size_t noof_wm1es = " << noof_wm1zs << ";" << std::endl; + fout << "BOOST_STATIC_CONSTEXPR std::size_t noof_wm1zs = " << noof_wm1zs << ";" << std::endl; + + // Defining actual lookup table sqrts of e^k, e^-k = 1/e, etc. + table_lookup_t halves[noof_halves]; // 0.5 0.25 0.125, 0.0625 ... 0.0002441, halves of previous elements. + table_lookup_t sqrtw0s[noof_sqrts]; // 0.6065 0.7788, 0.8824 ... 0.9997, sqrt of previous elements. + table_lookup_t sqrtwm1s[noof_sqrts]; // 1.6487, 1.2840 1.1331 ... 1.00024 , sqrt of previous elements. + table_lookup_t w0es[noof_w0es]; // lambert_w[k] for W0 branch. 2.7182, 1, 0.3678, 0.1353, ... 1.6038e-28 + table_lookup_t w0zs[noof_w0zs]; // lambert_w[k] for W0 branch. 0. , 2.7182, 14.77, 60.2566 ... 1.445e+29, 3.990e+29. + table_lookup_t wm1es[noof_wm1es]; // lambert_w[k] for W-1 branch. 2.7182 7.38905 20.085 ... 6.235e+27 + table_lookup_t wm1zs[noof_wm1zs]; // lambert_w[k] for W-1 branch. -0.3678 ... -1.0264e-26 + + // e values lambert_w[k] for W-1 branch. 2.7182 1. 0.3678 0.1353 0.04978 ... 4.359e-28 1.603e-28 + + using boost::math::constants::e; + using boost::math::constants::exp_minus_one; + + { // z values for integral W F[k] and powers for W0 branch. + table_lookup_t ej = 1; // + w0es[0] = e(); // e = 2.7182 exp(-1) - 1/e exp_minus_one = 0.36787944. + w0es[1] = 1; // e^0 + w0zs[0] = 0; // F[0] = 0 or W0 branch. + for (int j = 1, jj = 2; jj != noof_w0es; ++jj) + { + w0es[jj] = w0es[j] * exp_minus_one(); // previous * 0.36787944. + ej *= e(); // * 2.7182 + w0zs[j] = j * ej; // For W0 branch. + j = jj; // Previous. + } // for + } + // Checks on accuracy of W0 exponents. + + // Checks on e power w0es + + // w0es[64] = 4.3596100000630809736231248158884615452e-28 + // N[e ^ -63, 37] = 4.359610000063080973623124815888459643×10^-28 + // So slight loss at last decimal place. + + // Checks on accuracy of z for integral W0 w0zs + // w0zs[0] = 0, z = -infinity expected? but = zero + // w0zs[1] = 2.7182818284590452353602874713526623144 + // w0[2] z = 14.778112197861300454460854921150012956 + // w0zs[64] = 3.9904954117194348050619127737142022705e+29 + // N[productlog(0, 3.9904954117194348050619127737142022705 10^+29), 37] + // = 63.99999999999999999999999999999999547 + // = 64.0 to 34 decimal digits, so exact. :-) + + { // z values for integral powers G[k] and e^-k for W-1 branch. + // Fukushima indexing of G (k-1) differs by 1 from(k). + // G[0] = -infinity, so his first item in array g[0] is -0.3678 which is G[1] + // and last is g[63] = G[64] = 1.026e-26 + table_lookup_t e1 = 1. / e(); // 1/e = 0.36787944117144233 + table_lookup_t ej = e1; + wm1es[0] = e(); // e = 2.7182 + wm1zs[0] = -e1; // -1/e = - 0.3678 + for (int j = 0, jj = 1; jj != noof_wm1zs; ++jj) + { + ej *= e1; // * 0.3678.. + wm1es[jj] = wm1es[j] * e(); + wm1zs[jj] = -(jj + 1) * ej; + j = jj; // Previous. + } // for + } + + // Checks on W-1 branch accuracy wm1es by comparing with Wolfram. + // exp powers: + // N[e ^ 1, 37] 2.718281828459045235360287471352662498 + // wm1es[0] = 2.7182818284590452353602874713526623144 - close enough. + // N[e ^ 3, 37] 20.08553692318766774092852965458171790 + // computed wm1es[2] 2.0085536923187667740928529654581712847e+01L OK + // e ^ 66 = 4.6071866343312915426773184428060086893349003037096040 × 10^28 + // N[e ^ 66, 34] = 4.607186634331291542677318442806009 10^28 + // computed 4.6071866343312915426773184428059867859e+28L + // N[e ^ 66, 37] = 4.607186634331291542677318442806008689×10^28 + // so suffering some loss of precision by repeated multiplication computation. + // :-( + + // Repeat with cpp_bin_float_50 and correct to 37th decimal digit. + // 4.60718663433129154267731844280600868933490030370929982 + // output std::cout.precision(std::numeric_limits::max_digits10) as 37 decimal digits. + // 4.6071866343312915426773184428060086893e+28L + // N[e ^ 66, 37] = 4.607186634331291542677318442806008689×10^28 + // Agrees exactly for 37th place, so should be read in to nearest representable value. + + + // Checks W-1 branch z values wm1zs + // W-1[0] = -2.7067056647322538378799898994496883858e-01 + // w-1[1] = -1.4936120510359182893802724695018536337e-01 + + // wm1zs[65] -1.4325445274604020119111357113179868158e-27 + + // N[productlog(-1, -1.4325445274604020119111357113179868158* 10^-27), 37] + // = -65.99999999999999999999999999999999955 + // = -66 accurately, so this is OK. + // z = 66 * e^66 = + // =N[-66*e ^ -66, 37] + // -1.432544527460402011911135711317986177×10^-27 + // wm1zs[65] -1.4325445274604020119111357113179868158e-27 + // which agrees well enough to 34 decimal digits. + // last wm1zs[65] = 0 is unused. + + // Halves, common to both W0 and W-1. + halves[0] = static_cast(0.5); // Exactly representable. + for (int j = 0; j != noof_sqrts -1; ++j) + { + halves[j+1] = halves[j] / 2; // Half previous element (/2 will be optimised better?). + } // for j + + // W0 sqrts + sqrtw0s[0] = static_cast(0.606530659712633423603799534991180453441918135487186955682L); + for (int j = 0; j != noof_sqrts -1; ++j) + { + sqrtw0s[j+1] = sqrt(sqrtw0s[j]); // Sqrt of previous element. sqrt(1/e), sqrt(sqrt(1/e)) ... + } // for j + + // W-1 sqrts + sqrtwm1s[0] = root_e(); + for (int j = 0; j != noof_sqrts -1; ++j) + { + sqrtwm1s[j+1] = sqrt(sqrtwm1s[j]); // Sqrt of previous element. sqrt(1/e), sqrt(sqrt(1/e)) ... + } // for j + + // Output values as C++ arrays, + // using BOOST_STATIC_CONSTEXPR as static and constexpr as possible for platform. + fout << std::noshowpoint; // Do show NOT trailing zeros for halves and sqrts values. + + fout << + "\n" "BOOST_STATIC_CONSTEXPR lookup_t halves[noof_halves] = " // + "\n" "{ // Common to Lambert W0 and W-1 (and exactly representable)." << "\n "; + for (int i = 0; i != noof_halves; i++) + { + fout << halves[i] << 'L'; + if (i != noof_halves - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + else + { + fout << std::endl; + } + } + fout << "}; // halves, 0.5, 0.25, ... 0.000244140625, common to W0 and W-1." << std::endl; + + fout << + "\n" "BOOST_STATIC_CONSTEXPR lookup_t sqrtw0s[noof_sqrts] = " // + "\n" "{ // For Lambert W0 only." << "\n "; + for (int i = 0; i != noof_sqrts; i++) + { + fout << sqrtw0s[i] << 'L'; + if (i != noof_sqrts - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + else + { + fout << std::endl; + } + } + fout << "}; // sqrtw0s" << std::endl; + + fout << + "\n" "BOOST_STATIC_CONSTEXPR lookup_t sqrtwm1s[noof_sqrts] = " // + "\n" "{ // For Lambert W-1 only." << "\n "; + for (int i = 0; i != noof_sqrts; i++) + { + fout << sqrtwm1s[i] << 'L'; + if (i != noof_sqrts - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + else + { + fout << std::endl; + } + } + fout << "}; // sqrtwm1s" << std::endl; + + fout << std::scientific // May be needed to avoid very large dddddddddddddddd.ddddddddddddddd output? + << std::showpoint; // Do show trailing zeros for sqrts and halves. + + // Two W0 arrays + + fout << // W0 e values. + // Fukushima code generates an extra unused power, but it is not output by using noof_w0zs instead of noof_w0es. + "\n" "BOOST_STATIC_CONSTEXPR lookup_t w0es[noof_w0zs] = " // + "\n" "{ // Fukushima e powers array e[0] = 2.718, 1., e[2] = e^-1 = 0.135, e[3] = e^-2 = 0.133 ... e[64] = 4.3596100000630809736231248158884615452e-28." << "\n "; + for (int i = 0; i != noof_w0zs; i++) + { + fout << w0es[i] << 'L'; + if (i != noof_w0es - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + if (i % 4 == 0) + { + fout << "\n "; + } + } + fout << "\n}; // w0es" << std::endl; + + fout << // W0 z values for W[1], . + "\n" "BOOST_STATIC_CONSTEXPR lookup_t w0zs[noof_w0zs] = " // + "\n" "{ // z values for W[0], W[1], W[2] ... W[64] (Fukushima array Fk). " << "\n "; + for (int i = 0; i != noof_w0zs; i++) + { + fout << w0zs[i] << 'L'; + if (i != noof_w0zs - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + if (i % 4 == 0) + { + fout << "\n "; + } + } + fout << "\n}; // w0zs" << std::endl; + + // Two arrays for w-1 + + fout << // W-1 e values. + "\n" "BOOST_STATIC_CONSTEXPR lookup_t wm1es[noof_wm1es] = " // + "\n" "{ // Fukushima e array e[0] = e^1 = 2.718, e[1] = e^2 = 7.39 ... e[64] = 4.60718e+28." << "\n "; + for (int i = 0; i != noof_wm1es; i++) + { + fout << wm1es[i] << 'L'; + if (i != noof_wm1es - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + if (i % 4 == 0) + { + fout << "\n "; + } + } + fout << "\n}; // wm1es" << std::endl; + + fout << // Wm1 z values for integral K. + "\n" "BOOST_STATIC_CONSTEXPR lookup_t wm1zs[noof_wm1zs] = " // + "\n" "{ // Fukushima G array of z values for integral K, (Fukushima Gk) g[0] (k = -1) = 1 ... g[64] = -1.0264389699511303e-26." << "\n "; + for (int i = 0; i != noof_wm1zs; i++) + { + fout << wm1zs[i] << 'L'; + if (i != noof_wm1zs - 1) + { // Omit trailing comma on last element. + fout << ", "; + } + if (i % 4 == 0) + { // 4 values per line. + fout << "\n "; + } + } + fout << "\n}; // wm1zs" << std::endl; + + fout << "} // namespace lambert_w_lookup\n} // namespace lambert_w_detail\n} // namespace math\n} // namespace boost" << std::endl; + } + catch (std::exception& ex) + { + std::cout << "Exception " << ex.what() << std::endl; + } + fout.close(); + return 0; + +} // int main() + +/* + +Original arrays as output by Veberic/Fukushima code: + +w0 branch + +W-1 branch + +e: 2.7182818284590451 7.3890560989306495 20.085536923187664 54.598150033144229 148.41315910257657 403.42879349273500 1096.6331584284583 2980.9579870417274 8103.0839275753815 22026.465794806707 59874.141715197788 162754.79141900383 442413.39200892020 1202604.2841647759 3269017.3724721079 8886110.5205078647 24154952.753575277 65659969.137330450 178482300.96318710 485165195.40978980 1318815734.4832134 3584912846.1315880 9744803446.2488918 26489122129.843441 72004899337.385788 195729609428.83853 532048240601.79797 1446257064291.4734 3931334297144.0371 10686474581524.447 29048849665247.383 78962960182680.578 214643579785915.75 583461742527454.00 1586013452313428.3 4311231547115188.5 11719142372802592. 31855931757113704. 86593400423993600. 2.3538526683701958e+17 6.3984349353005389e+17 1.7392749415204982e+18 4.7278394682293381e+18 1.2851600114359284e+19 3.4934271057485025e+19 9.4961194206024286e+19 2.5813128861900616e+20 7.0167359120976157e+20 1.9073465724950953e+21 5.1847055285870605e+21 1.4093490824269355e+22 3.8310080007165677e+22 1.0413759433029062e+23 2.8307533032746866e+23 7.6947852651419974e+23 2.0916594960129907e+24 5.6857199993359170e+24 1.5455389355900996e+25 4.2012104037905024e+25 1.1420073898156810e+26 3.1042979357019109e+26 8.4383566687414291e+26 2.2937831594696028e+27 6.2351490808115970e+27 + +g: -0.36787944117144233 -0.27067056647322540 -0.14936120510359185 -0.073262555554936742 -0.033689734995427351 -0.014872513059998156 -0.0063831737588816162 -0.0026837010232200957 -0.0011106882367801162 -0.00045399929762484866 -0.00018371870869270232 -7.3730548239938541e-05 -2.9384282290753722e-05 -1.1641402067449956e-05 -4.5885348075273889e-06 -1.8005627955081467e-06 -7.0378941219347870e-07 -2.7413963540482742e-07 -1.0645313231320814e-07 -4.1223072448771179e-08 -1.5923376898615014e-08 -6.1368298043116385e-09 -2.3602323152914367e-09 -9.0603229062698418e-10 -3.4719859662410078e-10 -1.3283631472964657e-10 -5.0747278046555293e-11 -1.9360320299432585e-11 -7.3766303773930841e-12 -2.8072868906520550e-12 -1.0671679036256938e-12 -4.0525329757101402e-13 -1.5374324278841227e-13 -5.8272886672428505e-14 -2.2067908660514491e-14 -8.3502821888768594e-15 -3.1572276215253082e-15 -1.1928704609782527e-15 -4.5038074274761624e-16 -1.6993417021166378e-16 -6.4078169762734621e-17 -2.4147993510032983e-17 -9.0950634616416589e-18 -3.4236981860988753e-18 -1.2881333612472291e-18 -4.8440839844747606e-19 -1.8207788854829806e-19 -6.8407875971564987e-20 -2.5690139750481013e-20 -9.6437492398196038e-21 -3.6186918227652047e-21 -1.3573451162272088e-21 -5.0894204288896066e-22 -1.9076194289884390e-22 -7.1476978375412793e-23 -2.6773000149758669e-23 -1.0025115553818592e-23 -3.7527362568743735e-24 -1.4043571811296988e-24 -5.2539064576179218e-25 -1.9650175744554385e-25 -7.3474021582506962e-26 -2.7465543000397468e-26 -1.0264389699511303e-26 + +a: 1.6487212707001282 1.2840254166877414 1.1331484530668263 1.0644944589178595 1.0317434074991028 1.0157477085866857 1.0078430972064480 1.0039138893383477 1.0019550335910028 1.0009770394924165 1.0004884004786945 1.0002441704297478 + +b: 0.50000000000000000 0.25000000000000000 0.12500000000000000 0.062500000000000000 0.031250000000000000 0.015625000000000000 0.0078125000000000000 0.0039062500000000000 0.0019531250000000000 0.00097656250000000000 0.00048828125000000000 0.00024414062500000000 + + +*/ + + diff --git a/tools/lambert_w_low_reference_values.cpp b/tools/lambert_w_low_reference_values.cpp new file mode 100644 index 000000000..0c0a0f72d --- /dev/null +++ b/tools/lambert_w_low_reference_values.cpp @@ -0,0 +1,237 @@ +// lambert_w_test_values.cpp + +// Copyright Paul A. Bristow 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Write a C++ file J:\Cpp\Misc\lambert_w_1000_test_values\lambert_w_mp_values.ipp +// containing arrays of z arguments and 100 decimal digit precision lambert_w0(z) reference values. +// These can be used in tests of precision of less-precise types like +// built-in float, double, long double and quad and cpp_dec_float_50. + +// Multiprecision types: +//#include +#include // boost::multiprecision::cpp_dec_float_100 +using boost::multiprecision::cpp_dec_float_100; + +#include // +using boost::math::lambert_w0; + +#include +// using std::cout; using std::std::endl; using std::ios; using std::std::cerr; +#include +using std::setprecision; +using std::showpoint; +#include +using std::ofstream; +#include +#include // for DBL_EPS etc +#include // for numeric limits. +//#include +#include +#include +using std::transform; + +const char* prefix = "static "; // "static const" or "static constexpr" or just "const "" or "" even? +// But problems with VS2017 and GCC not accepting same format mean only static at present. + +const long double eps = std::numeric_limits::epsilon(); + +/* + +// Sample test values from Wolfram. +template +static RealType zs[noof_tests]; + +template +void init_zs() +{ + zs[0] = BOOST_MATH_TEST_VALUE(RealType, -0.35); + zs[1] = BOOST_MATH_TEST_VALUE(RealType, -0.3); + zs[2] = BOOST_MATH_TEST_VALUE(RealType, -0.01); + zs[3] = BOOST_MATH_TEST_VALUE(RealType, +0.01); + zs[4] = BOOST_MATH_TEST_VALUE(RealType, 0.1); + zs[5] = BOOST_MATH_TEST_VALUE(RealType, 0.5); + zs[6] = BOOST_MATH_TEST_VALUE(RealType, 1.); + zs[7] = BOOST_MATH_TEST_VALUE(RealType, 2.); + zs[8] = BOOST_MATH_TEST_VALUE(RealType, 5.); + zs[9] = BOOST_MATH_TEST_VALUE(RealType, 10.); + zs[10] = BOOST_MATH_TEST_VALUE(RealType, 100.); + zs[11] = BOOST_MATH_TEST_VALUE(RealType, 1e+6); +}; + +// 'Known good' Lambert w values using N[productlog(-0.3), 50] +// evaluated to full precision of RealType (up to 50 decimal digits). +template +static RealType ws[noof_tests]; + +template +void init_ws() +{ + ws[0] = BOOST_MATH_TEST_VALUE(RealType, -0.7166388164560738505881698000038650406110575701385055261614344530078353170171071547711151137001759321); + ws[1] = BOOST_MATH_TEST_VALUE(RealType, -0.4894022271802149690362312519962933689234100060163590345114659679736814083816206187318524147462752111); + ws[2] = BOOST_MATH_TEST_VALUE(RealType, -0.01010152719853875327292018767138623973670903993475235877290726369225412969738704722202330440072213641); + ws[3] = BOOST_MATH_TEST_VALUE(RealType, 0.009901473843595011885336326816570107953627746494917415482611387085655068978243229360100010886171970918); + ws[4] = BOOST_MATH_TEST_VALUE(RealType, 0.09127652716086226429989572142317956865311922405147203264830839460717224625441755165020664592995606710); + ws[5] = BOOST_MATH_TEST_VALUE(RealType, 0.3517337112491958260249093009299510651714642155171118040466438461099606107203387108968323038321915693); + ws[6] = BOOST_MATH_TEST_VALUE(RealType, 0.5671432904097838729999686622103555497538157871865125081351310792230457930866845666932194469617522946); // Output from https://www.wolframalpha.com/input/?i=lambert_w0(1) + ws[7] = BOOST_MATH_TEST_VALUE(RealType, 0.8526055020137254913464724146953174668984533001514035087721073946525150656742630448965773783502494847); + ws[8] = BOOST_MATH_TEST_VALUE(RealType, 1.326724665242200223635099297758079660128793554638047479789290393025342679920536226774469916608426789); // https://www.wolframalpha.com/input/?i=N%5Bproductlog(5),+100%5D + ws[9] = BOOST_MATH_TEST_VALUE(RealType, 1.745528002740699383074301264875389911535288129080941331322206048555557259941551704989523510778883075); + ws[10] = BOOST_MATH_TEST_VALUE(RealType, 3.385630140290050184888244364529726867491694170157806680386174654885206544913039277686735236213650781); + ws[11] = BOOST_MATH_TEST_VALUE(RealType, 11.38335808614005262200015678158500428903377470601886512143238610626898610768018867797709315493717650); + ////W(1e35) = 76.256377207295812974093508663841808129811690961764 too big for float. +}; + +*/ + +// Global so accessible from output_value. +// Creates if no file exists, & uses default overwrite/ ios::replace. +const char filename[] = "lambert_w_low_reference values.ipp"; // +std::ofstream fout(filename, std::ios::out); ; // + +// 100 decimal digits for the value fed to macro BOOST_MATH_TEST_VALUE +typedef cpp_dec_float_100 RealType; + +void output_value(size_t index, RealType value) +{ + fout + << " zs[" << index << "] = BOOST_MATH_TEST_VALUE(RealType, " + << value + << ");" + << std::endl; + return; +} + +void output_lambert_w0(size_t index, RealType value) +{ + fout + << " ws[" << index << "] = BOOST_MATH_TEST_VALUE(RealType, " + << lambert_w0(value) + << ");" + << std::endl; + return; +} + +int main() +{ // Make C++ file containing Lambert W test values. + std::cout << filename << " "; +#ifdef __TIMESTAMP__ + std::cout << __TIMESTAMP__; +#endif + std::cout << std::endl; + std::cout << "Lambert W0 decimal digit precision values." << std::endl; + + // Note __FILE__ & __TIMESTAMP__ are ANSI standard C & thus Std C++? + + if (!fout.is_open()) + { // File failed to open OK. + std::cerr << "Open file " << filename << " failed!" << std::endl; + std::cerr << "errno " << errno << std::endl; + return -1; + } + + int output_precision = std::numeric_limits::digits10; + // cpp_dec_float_100 is ample precision and + // has several extra bits internally so max_digits10 are not needed. + fout.precision(output_precision); + + int no_of_tests = 100; + + // Intro for RealType values. + std::cout << "Lambert W test values written to file " << filename << std::endl; + fout << + "\n" + "// A collection of Lambert W test values computed using " + << output_precision << " decimal digits precision.\n" + "// C++ floating-point type is " << "RealType." "\n" + "\n" + "// Written by " << __FILE__ << " " << __TIMESTAMP__ << "\n" + + "\n" + "// Copyright Paul A. Bristow 2017." "\n" + "// Distributed under the Boost Software License, Version 1.0." "\n" + "// (See accompanying file LICENSE_1_0.txt" "\n" + "// or copy at http://www.boost.org/LICENSE_1_0.txt)" "\n" + << std::endl; + + fout << "// Size of arrays of arguments z and Lambert W" << std::endl; + fout << "static const unsigned int noof_tests = " << no_of_tests << ";" << std::endl; + + // Declare arrays of z and Lambert W. + + fout << "// Declare arrays of arguments z and Lambert W(z)" << std::endl; + fout << "// The values are defined using the macro BOOST_MATH_TEST_VALUE to ensure\n" + "// that both built-in and multiprecision types are correctly initialiased with full precision.\n" + "// built-in types like double require a floating-point literal like 3.14,\n" + "// but multiprecision types require a decimal digit string like \"3.14\".\n" + << std::endl; + fout << + "\n" + "template ""\n" + "static RealType zs[" << no_of_tests << "];" + << std::endl; + + fout << + "\n" + "template ""\n" + "static RealType ws[" << no_of_tests << "];" + << std::endl; + + RealType max_z("10"); + RealType min_z("-0.35"); + RealType step_size("0.01"); + size_t step_count = no_of_tests; + + // Output to initialize array of arguments z for Lambert W. + fout << + "\n" + << "template \n" + "void init_zs()\n" + "{\n"; + + RealType z = min_z; + for (size_t i = 0; (i < no_of_tests); i++) + { + output_value(i, z); + z += step_size; + } + fout << "};" << std::endl; + + // Output array of Lambert W values. + fout << + "\n" + << "template \n" + "void init_ws()\n" + "{\n"; + + z = min_z; + for (size_t i = 0; (i < step_count); i++) + { + output_lambert_w0(i, z); + z += step_size; + } + fout << "};" << std::endl; + + fout << "// End of lambert_w_mp_values.ipp " << std::endl; + fout.close(); + + std::cout << "Lambert_w0 values written to files " << __TIMESTAMP__ << std::endl; + return 0; +} // main + + +/* + +start and finish checks again Wolfram Alpha: +ws[0] = BOOST_MATH_TEST_VALUE(RealType, -0.7166388164560738505881698000038650406110575701385055261614344530078353170171071547711151137001759321); +Wolfram N[productlog(-0.35), 100] -0.7166388164560738505881698000038650406110575701385055261614344530078353170171071547711151137001759321 + + +ws[19] = BOOST_MATH_TEST_VALUE(RealType, 0.7397278549447991214587608743391115983469848985053641692586810406118264600667862543570373167046626221); +Wolfram N[productlog(1.55), 100] 0.7397278549447991214587608743391115983469848985053641692586810406118264600667862543570373167046626221 + + +*/ +