The OpenD Programming Language

1 /++
2 Base numeric algorithms.
3 
4 Reworked part of `std.numeric`.
5 
6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
7 Authors: Ilia Ki (API, findLocalMin, findRoot extension), Don Clugston (findRoot), Lars Tandle Kyllingstad (diff)
8 +/
9 module mir.numeric;
10 
11 import mir.internal.utility: isFloatingPoint;
12 import mir.math.common;
13 import mir.math.ieee;
14 import mir.exception: toMutable;
15 
16 version(D_Exceptions)
17 {
18     private static immutable findRoot_badBounds = new Exception("findRoot: f(ax) and f(bx) must have opposite signs to bracket the root.");
19     private static immutable findRoot_nanX = new Exception("findRoot/findLocalMin: ax or bx is NaN.");
20     private static immutable findRoot_nanY = new Exception("findRoot/findLocalMin: f(x) returned NaN.");
21 }
22 
23 /++
24 +/
25 enum mir_find_root_status
26 {
27     /// Success
28     success,
29     ///
30     badBounds,
31     /// 
32     nanX,
33     ///
34     nanY,
35 }
36 
37 /// ditto
38 alias FindRootStatus = mir_find_root_status;
39 
40 /++
41 +/
42 struct mir_find_root_result(T)
43 {
44     /// Left bound
45     T ax = 0;
46     /// Rifht bound
47     T bx = 0;
48     /// `f(ax)` or `f(ax).fabs.fmin(T.max / 2).copysign(f(ax))`.
49     T ay = 0;
50     /// `f(bx)` or `f(bx).fabs.fmin(T.max / 2).copysign(f(bx))`.
51     T by = 0;
52     /// Amount of target function calls.
53     uint iterations;
54 
55 @safe pure @nogc scope const @property:
56 
57     /++
58     Returns: self
59     Required_versions:`D_Exceptions`
60     Throws: `Exception` if $(LREF FindRootResult.status) isn't $(LREF mir_find_root_status.success).
61     +/
62     version(D_Exceptions)
63     ref validate() inout return
64     {
65         with(FindRootStatus) final switch(status)
66         {
67             case success: return this;
68             case badBounds: throw findRoot_badBounds.toMutable;
69             case nanX: throw findRoot_nanX.toMutable;
70             case nanY: throw findRoot_nanY.toMutable;
71         }
72     }
73 
74 extern(C++) nothrow:
75 
76     /++
77     Returns: $(LREF mir_find_root_status)
78     +/
79     FindRootStatus status()
80     {
81         with(FindRootStatus) return
82             ax != ax || bx != bx ? nanX :
83             ay != ay || by != by ? nanY :
84             ay.signbit == by.signbit && ay != 0 && by != 0 ? badBounds :
85             success;
86     }
87 
88     /++
89     A bound that corresponds to the minimal absolute function value.
90 
91     Returns: `!(ay.fabs > by.fabs) ? ax : bx`
92     +/
93     T x()
94     {
95         return !(ay.fabs > by.fabs) ? ax : bx;
96     }
97 
98     /++
99     The minimal of absolute function values.
100 
101     Returns: `!(ay.fabs > by.fabs) ? ay : by`
102     +/
103     T y()
104     {
105         return !(ay.fabs > by.fabs) ? ay : by;
106     }
107 }
108 
109 /// ditto
110 alias FindRootResult = mir_find_root_result;
111 
112 /++
113 Find root of a real function f(x) by bracketing, allowing the
114 termination condition to be specified.
115 
116 Given a function `f` and a range `[a .. b]` such that `f(a)`
117 and `f(b)` have opposite signs or at least one of them equals ±0,
118 returns the value of `x` in
119 the range which is closest to a root of `f(x)`.  If `f(x)`
120 has more than one root in the range, one will be chosen
121 arbitrarily.  If `f(x)` returns NaN, NaN will be returned;
122 otherwise, this algorithm is guaranteed to succeed.
123 
124 Uses an algorithm based on TOMS748, which uses inverse cubic
125 interpolation whenever possible, otherwise reverting to parabolic
126 or secant interpolation. Compared to TOMS748, this implementation
127 improves worst-case performance by a factor of more than 100, and
128 typical performance by a factor of 2. For 80-bit reals, most
129 problems require 8 to 15 calls to `f(x)` to achieve full machine
130 precision. The worst-case performance (pathological cases) is
131 approximately twice the number of bits.
132 
133 References: "On Enclosing Simple Roots of Nonlinear Equations",
134 G. Alefeld, F.A. Potra, Yixun Shi, Mathematics of Computation 61,
135 pp733-744 (1993).  Fortran code available from $(HTTP
136 www.netlib.org,www.netlib.org) as algorithm TOMS478.
137 
138 Params:
139 f = Function to be analyzed. `f(ax)` and `f(bx)` should have opposite signs, or `lowerBound` and/or `upperBound`
140     should be defined to perform initial interval extension.
141 tolerance = Defines an early termination condition. Receives the
142             current upper and lower bounds on the root. The
143             delegate must return `true` when these bounds are
144             acceptable. If this function always returns `false` or
145             it is null, full machine precision will be achieved.
146 ax = Left inner bound of initial range of `f` known to contain the root.
147 bx = Right inner bound of initial range of `f` known to contain the root. Can be equal to `ax`.
148 fax = Value of `f(ax)` (optional).
149 fbx = Value of `f(bx)` (optional).
150 lowerBound = Lower outer bound for interval extension (optional)
151 upperBound = Upper outer bound for interval extension (optional)
152 maxIterations = Appr. maximum allowed number of function calls (inluciding function calls for grid).
153 steps = Number of nodes in the left side and right side regular grids (optional). If it equals to `0` (default),
154         then the algorithm uses `ieeeMean` for searching. The algorithm performs searching if
155         `sgn(fax)` equals to `sgn(fbx)` and at least one outer bound allows to extend the inner initial range.
156 
157 Returns: $(LREF FindRootResult)
158 +/
159 @fmamath
160 FindRootResult!T findRoot(alias f, alias tolerance = null, T)(
161     const T ax,
162     const T bx,
163     const T fax = T.nan,
164     const T fbx = T.nan,
165     const T lowerBound = T.nan,
166     const T upperBound = T.nan,
167     uint maxIterations = T.sizeof * 16,
168     uint steps = 0,
169     )
170     if (
171         isFloatingPoint!T && __traits(compiles, T(f(T.init))) &&
172         (
173             is(typeof(tolerance) == typeof(null)) || 
174             __traits(compiles, {auto _ = bool(tolerance(T.init, T.init));}
175         )
176     ))
177 {
178     if (false) // break attributes
179         T y = f(T(1));
180     scope funInst = delegate(T x) {
181         return T(f(x));
182     };
183     scope fun = funInst.trustedAllAttr;
184 
185     static if (is(typeof(tolerance) == typeof(null)))
186     {
187         alias tol = tolerance;
188     }
189     else
190     {
191         if (false) // break attributes
192             bool b = tolerance(T(1), T(1));
193         scope tolInst = delegate(T a, T b) { return bool(tolerance(a, b)); };
194         scope tol = tolInst.trustedAllAttr;
195     }
196     return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, fun, tol);
197 }
198 
199 ///
200 // @nogc
201 version(mir_test) @safe unittest
202 {
203     import mir.math.common: log, exp, fabs;
204 
205     auto logRoot = findRoot!log(0, double.infinity).validate.x;
206     assert(logRoot == 1);
207 
208     auto shift = 1;
209     auto expm1Root = findRoot!(x => exp(x) - shift)
210         (-double.infinity, double.infinity).validate.x;
211     assert(expm1Root == 0);
212 
213     auto approxLogRoot = findRoot!(log, (a, b) => fabs(a - b) < 1e-5)(0, double.infinity).validate.x;
214     assert(fabs(approxLogRoot - 1) < 1e-5);
215 }
216 
217 /// With adaptive bounds
218 version(mir_test) @safe unittest
219 {
220     import mir.math.common: log, exp, fabs;
221 
222     auto logRoot = findRoot!log(
223             10, 10, // assume we have one initial point
224             double.nan, double.nan, // fa, fb aren't provided by user
225             -double.infinity, double.infinity, // all space is available for the bounds extension.
226         ).validate.x;
227     assert(logRoot == 1);
228 
229     auto shift = 1;
230     alias expm1Fun = (double x) => exp(x) - shift;
231     auto expm1RootRet = findRoot!expm1Fun
232         (
233             11, 10, // reversed order for interval is always OK
234             expm1Fun(11), expm1Fun(10), // the order must be the same as bounds
235             0, double.infinity, // space for the bounds extension.
236         ).validate;
237     assert(expm1Fun(expm1RootRet.x) == 0);
238 
239     auto approxLogRoot = findRoot!(log, (a, b) => fabs(a - b) < 1e-5)(
240             -1e10, +1e10,
241             double.nan, double.nan,
242             0, double.infinity,
243         ).validate.x;
244     assert(fabs(approxLogRoot - 1) < 1e-5);
245 }
246 
247 /// ditto
248 version(mir_test)
249 unittest
250 {
251     import core.stdc.tgmath: atan;
252     import mir.math;
253     import std.meta: AliasSeq;
254 
255     const double[2][3] boundaries = [
256         [0.4, 0.6],
257         [1.4, 1.6],
258         [0.1, 2.1]];
259     
260     enum root = 1.0;
261 
262     foreach(fun; AliasSeq!(
263         (double x) => x ^^ 2 - root,
264         (double x) => root - x ^^ 2,
265         (double x) => atan(x - root),
266     ))
267     {
268         foreach(ref bounds; boundaries)
269         {
270             auto result = findRoot!fun(
271                 bounds[0], bounds[1],
272                 double.nan, double.nan, // f(a) and f(b) not provided
273                 -double.max, double.max, // user provided outer bounds
274             );
275             assert(result.validate.x == root);
276         }
277     }
278 
279     foreach(ref bounds; boundaries)
280     {
281         auto result = findRoot!(x => sin(x - root))(
282             bounds[0], bounds[1],
283             double.nan, double.nan, // f(a) and f(b) not provided
284             -10, 10, // user provided outer bounds
285             100, // max iterations,
286             10, // steps for grid
287         );
288         assert(result.validate.x == root);
289     }
290     // single initial point, grid, positive direction
291     auto result = findRoot!((double x ) => sin(x - root))(
292         0.1, 0.1, // initial point, a = b
293         double.nan, double.nan, // f(a) = f(b) not provided
294         -100.0, 100.0, // user provided outer bounds
295         150, // max iterations,
296         100, // number of steps for grid
297     );
298     assert(result.validate.x == root);
299 
300     // single initial point, grid, negative direction
301     result = findRoot!((double x ) => sin(x - root))(
302         0.1, 0.1, // initial point a = b
303         double.nan, double.nan, // f(a) = f(b) not provided
304         100.0, -100.0, // user provided outer bounds, reversed order
305         150, // max iterations,
306         100, // number of steps for grid
307     );
308     assert(result.validate.x == double(root - PI)); // Left side root!
309 }
310 
311 /++
312 With adaptive bounds and single initial point.
313 Reverse outer bound order controls first step direction
314 in case of `f(a) == f(b)`.
315 +/
316 version(mir_test)
317 unittest
318 {
319 	enum root = 1.0;
320 
321     // roots are +/- `root`
322     alias fun = (double x) => x * x - root;
323 
324 	double lowerBound = -10.0;
325 	double upperBound = 10.0;
326 
327     assert(
328         findRoot!fun(
329             0, 0, // initial interval
330             double.nan, double.nan,
331             lowerBound, upperBound,
332             // positive direction has higher priority
333         ).validate.x == root
334     );
335 
336     assert(
337         findRoot!fun(
338             0, 0, // initial interval
339             double.nan, double.nan,
340             upperBound, lowerBound,
341             // reversed order
342         ).validate.x == -root // other root
343     );
344 }
345 
346 /// $(LREF findRoot) implementations.
347 export @fmamath FindRootResult!float findRootImpl(
348     float ax,
349     float bx,
350     float fax,
351     float fbx,
352     float lowerBound,
353     float upperBound,
354     uint maxIterations,
355     uint steps,
356     scope float delegate(float) @safe pure nothrow @nogc f,
357     scope bool delegate(float, float) @safe pure nothrow @nogc tolerance, //can be null
358 ) @safe pure nothrow @nogc
359 {
360     pragma(inline, false);
361     return findRootImplGen!float(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, f, tolerance);
362 }
363 
364 /// ditto
365 export @fmamath FindRootResult!double findRootImpl(
366     double ax,
367     double bx,
368     double fax,
369     double fbx,
370     double lowerBound,
371     double upperBound,
372     uint maxIterations,
373     uint steps,
374     scope double delegate(double) @safe pure nothrow @nogc f,
375     scope bool delegate(double, double) @safe pure nothrow @nogc tolerance, //can be null
376 ) @safe pure nothrow @nogc
377 {
378     pragma(inline, false);
379     return findRootImplGen!double(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, f, tolerance);
380 }
381 
382 /// ditto
383 export @fmamath FindRootResult!real findRootImpl(
384     real ax,
385     real bx,
386     real fax,
387     real fbx,
388     real lowerBound,
389     real upperBound,
390     uint maxIterations,
391     uint steps,
392     scope real delegate(real) @safe pure nothrow @nogc f,
393     scope bool delegate(real, real) @safe pure nothrow @nogc tolerance, //can be null
394 ) @safe pure nothrow @nogc
395 {
396     pragma(inline, false);
397     return findRootImplGen!real(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, f, tolerance);
398 }
399 
400 private @fmamath FindRootResult!T findRootImplGen(T)(
401     T a,
402     T b,
403     T fa,
404     T fb,
405     T lb,
406     T ub,
407     uint maxIterations,
408     uint steps,
409     scope const T delegate(T) @safe pure nothrow @nogc f,
410     scope const bool delegate(T, T) @safe pure nothrow @nogc tolerance, //can be null
411 ) @safe pure nothrow @nogc
412     if (isFloatingPoint!T)
413 {
414     version(LDC) pragma(inline, true);
415     // Author: Don Clugston. This code is (heavily) modified from TOMS748
416     // (www.netlib.org).  The changes to improve the worst-cast performance are
417     // entirely original.
418     
419     // Author: Ilia Ki (Bounds extension logic,
420     // API improvements, infinity and huge numbers handing, compiled code size reduction)
421 
422     T d;  // [a .. b] is our current bracket. d is the third best guess.
423     T fd; // Value of f at d.
424     bool done = false; // Has a root been found?
425     uint iterations;
426 
427     static void swap(ref T a, ref T b)
428     {
429         T t = a; a = b; b = t;
430     }
431 
432     bool exit()
433     {
434         pragma(inline, false);
435         return done
436             || iterations >= maxIterations
437             || b == nextUp(a)
438             || tolerance !is null && tolerance(a, b);
439     }
440 
441     // Test the function at point c; update brackets accordingly
442     void bracket(T c)
443     {
444         pragma(inline, false);
445         T fc = f(c);
446         iterations++;
447         if (fc == 0 || fc != fc) // Exact solution, or NaN
448         {
449             a = c;
450             fa = fc;
451             d = c;
452             fd = fc;
453             done = true;
454             return;
455         }
456 
457         fc = fc.fabs.fmin(T.max / 2).copysign(fc);
458 
459         // Determine new enclosing interval
460         if (signbit(fa) != signbit(fc))
461         {
462             d = b;
463             fd = fb;
464             b = c;
465             fb = fc;
466         }
467         else
468         {
469             d = a;
470             fd = fa;
471             a = c;
472             fa = fc;
473         }
474     }
475 
476    /* Perform a secant interpolation. If the result would lie on a or b, or if
477      a and b differ so wildly in magnitude that the result would be meaningless,
478      perform a bisection instead.
479     */
480     static T secantInterpolate(T a, T b, T fa, T fb)
481     {
482         pragma(inline, false);
483         if (a - b == a && b != 0
484          || b - a == b && a != 0)
485         {
486             // Catastrophic cancellation
487             return ieeeMean(a, b);
488         }
489         // avoid overflow
490         T m = fa - fb;
491         T wa = fa / m;
492         T wb = fb / m;
493         T c = b * wa - a * wb;
494         if (c == a || c == b || c != c || c.fabs == T.infinity)
495             c = a.half + b.half;
496         return c;
497     }
498 
499     /* Uses 'numsteps' newton steps to approximate the zero in [a .. b] of the
500        quadratic polynomial interpolating f(x) at a, b, and d.
501        Returns:
502          The approximate zero in [a .. b] of the quadratic polynomial.
503     */
504     T newtonQuadratic(int numsteps)
505     {
506         // Find the coefficients of the quadratic polynomial.
507         const T a0 = fa;
508         const T a1 = (fb - fa) / (b - a);
509         const T a2 = ((fd - fb) / (d - b) - a1) / (d - a);
510 
511         // Determine the starting point of newton steps.
512         T c = a2.signbit != fa.signbit ? a : b;
513 
514         // start the safeguarded newton steps.
515         foreach (int i; 0 .. numsteps)
516         {
517             const T pc = a0 + (a1 + a2 * (c - b))*(c - a);
518             const T pdc = a1 + a2*((2 * c) - (a + b));
519             if (pdc == 0)
520                 return a - a0 / a1;
521             else
522                 c = c - pc / pdc;
523         }
524         return c;
525     }
526 
527     // Starting with the second iteration, higher-order interpolation can
528     // be used.
529     int itnum = 1;   // Iteration number
530     int baditer = 1; // Num bisections to take if an iteration is bad.
531     T c, e;  // e is our fourth best guess
532     T fe;
533     bool left;
534 
535     // Allow a and b to be provided in reverse order
536     if (a > b)
537     {
538         swap(a, b);
539         swap(fa, fb);
540     }
541 
542     if (a != a || b != b)
543     {
544         done = true;
545         goto whileloop;
546     }
547 
548     if (lb != lb)
549     {
550         lb = a;
551     }
552 
553     if (ub != ub)
554     {
555         ub = b;
556     }
557 
558     if (lb > ub)
559     {
560         swap(lb, ub);
561         left = true;
562     }
563 
564     if (lb == -T.infinity)
565     {
566         lb = -T.max;
567     }
568 
569     if (ub == +T.infinity)
570     {
571         ub = +T.max;
572     }
573 
574     if (!(lb <= a))
575     {
576         a = lb;
577         fa = T.nan;
578     }
579 
580     if (!(b <= ub))
581     {
582         a = lb;
583         fa = T.nan;
584     }
585 
586     if (fa != fa)
587     {
588         fa = f(a);
589         iterations++;
590     }
591 
592     // On the first iteration we take a secant step:
593     if (fa == 0 || fa != fa)
594     {
595         done = true;
596         b = a;
597         fb = fa;
598         goto whileloop;
599     }
600 
601     if (fb != fb)
602     {
603         if (a == b)
604         {
605             fb = fa;
606         }
607         else
608         {
609             fb = f(b);
610             iterations++;
611         }
612     }
613 
614     if (fb == 0 || fb != fb)
615     {
616         done = true;
617         a = b;
618         fa = fb;
619         goto whileloop;
620     }
621 
622     if (fa.fabs < fb.fabs)
623     {
624         left = true;
625     }
626     else
627     if (fa.fabs > fb.fabs)
628     {
629         left = false;
630     }
631 
632     // extend inner boundaries
633     if (fa.signbit == fb.signbit)
634     {
635         T lx = a;
636         T ux = b;
637         T ly = fa;
638         T uy = fb;
639         const sb = fa.signbit;
640 
641         import mir.ndslice.topology: linspace;
642 
643         typeof(linspace!T([2], [lx, lb])) lgrid, ugrid;
644         if (steps)
645         {
646             lgrid = linspace!T([steps + 1], [lx, lb]);
647             lgrid.popFront;
648             ugrid = linspace!T([steps + 1], [ux, ub]);
649             ugrid.popFront;
650         }
651 
652         for(T mx;;)
653         {
654             bool lw = lb < lx;
655             bool uw = ux < ub;
656 
657             if (!lw && !uw || iterations >= maxIterations)
658             {
659                 done = true;
660                 goto whileloop;
661             }
662 
663             if (lw && (!uw || left))
664             {
665                 if (lgrid.empty)
666                 {
667                     mx = ieeeMean(lb, lx);
668                     if (mx == lx)
669                         mx = lb;
670                 }
671                 else
672                 {
673                     mx = lgrid.front;
674                     lgrid.popFront;
675                     if (mx == lx)
676                         continue;
677                 }
678                 T my = f(mx);
679                 iterations++;
680                 if (my == 0)
681                 {
682                     a = b = mx;
683                     fa = fb = my;
684                     done = true;
685                     goto whileloop;
686                 }
687                 if (mx != mx)
688                 {
689                     lb = mx;
690                     continue;
691                 }
692                 if (my.signbit == sb)
693                 {
694                     lx = mx;
695                     ly = my;
696                     if (lgrid.empty)
697                     {
698                         left = ly.fabs < uy.fabs;
699                     }
700                     continue;
701                 }
702                 a = mx;
703                 fa = my;
704                 b = lx;
705                 fb = ly;
706                 break;
707             }
708             else
709             {
710                 if (ugrid.empty)
711                 {
712                     mx = ieeeMean(ub, ux);
713                     if (mx == ux)
714                         mx = ub;
715                 }
716                 else
717                 {
718                     mx = ugrid.front;
719                     ugrid.popFront;
720                     if (mx == ux)
721                         continue;
722                 }
723                 T my = f(mx);
724                 iterations++;
725                 if (my == 0)
726                 {
727                     a = b = mx;
728                     fa = fb = my;
729                     done = true;
730                     goto whileloop;
731                 }
732                 if (mx != mx)
733                 {
734                     ub = mx;
735                     continue;
736                 }
737                 if (my.signbit == sb)
738                 {
739                     ux = mx;
740                     uy = my;
741                     if (ugrid.empty)
742                     {
743                         left = !(ly.fabs > uy.fabs);
744                     }
745                     continue;
746                 }
747                 b = mx;
748                 fb = my;
749                 a = ux;
750                 fa = uy;
751                 break;
752             }
753         }
754     }
755 
756     fa = fa.fabs.fmin(T.max / 2).copysign(fa);
757     fb = fb.fabs.fmin(T.max / 2).copysign(fb);
758     bracket(secantInterpolate(a, b, fa, fb));
759 
760 whileloop:
761     while (!exit)
762     {
763         T a0 = a, b0 = b; // record the brackets
764 
765         // Do two higher-order (cubic or parabolic) interpolation steps.
766         foreach (int QQ; 0 .. 2)
767         {
768             // Cubic inverse interpolation requires that
769             // all four function values fa, fb, fd, and fe are distinct;
770             // otherwise use quadratic interpolation.
771             bool distinct = (fa != fb) && (fa != fd) && (fa != fe)
772                          && (fb != fd) && (fb != fe) && (fd != fe);
773             // The first time, cubic interpolation is impossible.
774             if (itnum<2) distinct = false;
775             bool ok = distinct;
776             if (distinct)
777             {
778                 // Cubic inverse interpolation of f(x) at a, b, d, and e
779                 const q11 = (d - e) * fd / (fe - fd);
780                 const q21 = (b - d) * fb / (fd - fb);
781                 const q31 = (a - b) * fa / (fb - fa);
782                 const d21 = (b - d) * fd / (fd - fb);
783                 const d31 = (a - b) * fb / (fb - fa);
784 
785                 const q22 = (d21 - q11) * fb / (fe - fb);
786                 const q32 = (d31 - q21) * fa / (fd - fa);
787                 const d32 = (d31 - q21) * fd / (fd - fa);
788                 const q33 = (d32 - q22) * fa / (fe - fa);
789                 c = a + (q31 + q32 + q33);
790                 if (c != c || (c <= a) || (c >= b))
791                 {
792                     // DAC: If the interpolation predicts a or b, it's
793                     // probable that it's the actual root. Only allow this if
794                     // we're already close to the root.
795                     if (c == a && (a - b != a || a - b != -b))
796                     {
797                         auto down = !(a - b != a);
798                         if (down)
799                             c = -c;
800                         c = c.nextUp;
801                         if (down)
802                             c = -c;
803                     }
804                     else
805                     {
806                         ok = false;
807                     }
808 
809                 }
810             }
811             if (!ok)
812             {
813                 // DAC: Alefeld doesn't explain why the number of newton steps
814                 // should vary.
815                 c = newtonQuadratic(2 + distinct);
816                 if (c != c || (c <= a) || (c >= b))
817                 {
818                     // Failure, try a secant step:
819                     c = secantInterpolate(a, b, fa, fb);
820                 }
821             }
822             ++itnum;
823             e = d;
824             fe = fd;
825             bracket(c);
826             if (exit)
827                 break whileloop;
828             if (itnum == 2)
829                 continue whileloop;
830         }
831 
832         // Now we take a double-length secant step:
833         T u;
834         T fu;
835         if (fabs(fa) < fabs(fb))
836         {
837             u = a;
838             fu = fa;
839         }
840         else
841         {
842             u = b;
843             fu = fb;
844         }
845         c = u - 2 * (fu / (fb - fa)) * (b - a);
846 
847         // DAC: If the secant predicts a value equal to an endpoint, it's
848         // probably false.
849         if (c == a || c == b || c != c || fabs(c - u) > (b - a) * 0.5f)
850         {
851             if ((a - b) == a || (b - a) == b)
852             {
853                 c = ieeeMean(a, b);
854             }
855             else
856             {
857                 c = a.half + b.half;
858             }
859         }
860         e = d;
861         fe = fd;
862         bracket(c);
863         if (exit)
864             break;
865 
866         // IMPROVE THE WORST-CASE PERFORMANCE
867         // We must ensure that the bounds reduce by a factor of 2
868         // in binary space! every iteration. If we haven't achieved this
869         // yet, or if we don't yet know what the exponent is,
870         // perform a binary chop.
871 
872         if ((a == 0
873           || b == 0
874           || fabs(a) >= 0.5f * fabs(b)
875           && fabs(b) >= 0.5f * fabs(a))
876           && b - a < 0.25f * (b0 - a0))
877         {
878             baditer = 1;
879             continue;
880         }
881 
882         // DAC: If this happens on consecutive iterations, we probably have a
883         // pathological function. Perform a number of bisections equal to the
884         // total number of consecutive bad iterations.
885 
886         if (b - a < 0.25f * (b0 - a0))
887             baditer = 1;
888         foreach (int QQ; 0 .. baditer)
889         {
890             e = d;
891             fe = fd;
892             bracket(ieeeMean(a, b));
893         }
894         ++baditer;
895     }
896     return typeof(return)(a, b, fa, fb, iterations);
897 }
898 
899 version(mir_test) @safe unittest
900 {
901     import mir.math.constant;
902 
903     int numProblems = 0;
904     int numCalls;
905 
906     void testFindRoot(real delegate(real) @nogc @safe nothrow pure f , real x1, real x2, int line = __LINE__) //@nogc @safe nothrow pure
907     {
908         //numCalls=0;
909         //++numProblems;
910         assert(x1 == x1 && x2 == x2);
911         auto result = findRoot!f(x1, x2).validate;
912 
913         auto flo = f(result.ax);
914         auto fhi = f(result.bx);
915         if (flo != 0)
916         {
917             assert(flo.signbit != fhi.signbit);
918         }
919     }
920 
921     // Test functions
922     real cubicfn(real x) @nogc @safe nothrow pure
923     {
924         //++numCalls;
925         if (x>float.max)
926             x = float.max;
927         if (x<-float.max)
928             x = -float.max;
929         // This has a single real root at -59.286543284815
930         return 0.386*x*x*x + 23*x*x + 15.7*x + 525.2;
931     }
932     // Test a function with more than one root.
933     real multisine(real x) { ++numCalls; return sin(x); }
934     testFindRoot( &multisine, 6, 90);
935     testFindRoot(&cubicfn, -100, 100);
936     testFindRoot( &cubicfn, -double.max, real.max);
937 
938 
939 /* Tests from the paper:
940  * "On Enclosing Simple Roots of Nonlinear Equations", G. Alefeld, F.A. Potra,
941  *   Yixun Shi, Mathematics of Computation 61, pp733-744 (1993).
942  */
943     // Parameters common to many alefeld tests.
944     int n;
945     real ale_a, ale_b;
946 
947     int powercalls = 0;
948 
949     real power(real x)
950     {
951         ++powercalls;
952         ++numCalls;
953         return pow(x, n) + double.min_normal;
954     }
955     int [] power_nvals = [3, 5, 7, 9, 19, 25];
956     // Alefeld paper states that pow(x, n) is a very poor case, where bisection
957     // outperforms his method, and gives total numcalls =
958     // 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit),
959     // 0.5f624 for brent (6.8/bit)
960     // ... but that is for double, not real80.
961     // This poor performance seems mainly due to catastrophic cancellation,
962     // which is avoided here by the use of ieeeMean().
963     // I get: 231 (0.48/bit).
964     // IE this is 10X faster in Alefeld's worst case
965     numProblems=0;
966     foreach (k; power_nvals)
967     {
968         n = k;
969         //testFindRoot(&power, -1, 10);
970     }
971 
972     int powerProblems = numProblems;
973 
974     // Tests from Alefeld paper
975 
976     int [9] alefeldSums;
977     real alefeld0(real x)
978     {
979         ++alefeldSums[0];
980         ++numCalls;
981         real q =  sin(x) - x/2;
982         for (int i=1; i<20; ++i)
983             q+=(2*i-5.0)*(2*i-5.0) / ((x-i*i)*(x-i*i)*(x-i*i));
984         return q;
985     }
986     real alefeld1(real x)
987     {
988         ++numCalls;
989         ++alefeldSums[1];
990         return ale_a*x + exp(ale_b * x);
991     }
992     real alefeld2(real x)
993     {
994         ++numCalls;
995         ++alefeldSums[2];
996         return pow(x, n) - ale_a;
997     }
998     real alefeld3(real x)
999     {
1000         ++numCalls;
1001         ++alefeldSums[3];
1002         return (1.0 +pow(1.0L-n, 2))*x - pow(1.0L-n*x, 2);
1003     }
1004     real alefeld4(real x)
1005     {
1006         ++numCalls;
1007         ++alefeldSums[4];
1008         return x*x - pow(1-x, n);
1009     }
1010     real alefeld5(real x)
1011     {
1012         ++numCalls;
1013         ++alefeldSums[5];
1014         return (1+pow(1.0L-n, 4))*x - pow(1.0L-n*x, 4);
1015     }
1016     real alefeld6(real x)
1017     {
1018         ++numCalls;
1019         ++alefeldSums[6];
1020         return exp(-n*x)*(x-1.01L) + pow(x, n);
1021     }
1022     real alefeld7(real x)
1023     {
1024         ++numCalls;
1025         ++alefeldSums[7];
1026         return (n*x-1) / ((n-1)*x);
1027     }
1028 
1029     numProblems=0;
1030     testFindRoot(&alefeld0, PI_2, PI);
1031     for (n=1; n <= 10; ++n)
1032     {
1033         testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L);
1034     }
1035     ale_a = -40; ale_b = -1;
1036     testFindRoot(&alefeld1, -9, 31);
1037     ale_a = -100; ale_b = -2;
1038     testFindRoot(&alefeld1, -9, 31);
1039     ale_a = -200; ale_b = -3;
1040     testFindRoot(&alefeld1, -9, 31);
1041     int [] nvals_3 = [1, 2, 5, 10, 15, 20];
1042     int [] nvals_5 = [1, 2, 4, 5, 8, 15, 20];
1043     int [] nvals_6 = [1, 5, 10, 15, 20];
1044     int [] nvals_7 = [2, 5, 15, 20];
1045 
1046     for (int i=4; i<12; i+=2)
1047     {
1048        n = i;
1049        ale_a = 0.2;
1050        testFindRoot(&alefeld2, 0, 5);
1051        ale_a=1;
1052        testFindRoot(&alefeld2, 0.95, 4.05);
1053        testFindRoot(&alefeld2, 0, 1.5);
1054     }
1055     foreach (i; nvals_3)
1056     {
1057         n=i;
1058         testFindRoot(&alefeld3, 0, 1);
1059     }
1060     foreach (i; nvals_3)
1061     {
1062         n=i;
1063         testFindRoot(&alefeld4, 0, 1);
1064     }
1065     foreach (i; nvals_5)
1066     {
1067         n=i;
1068         testFindRoot(&alefeld5, 0, 1);
1069     }
1070     foreach (i; nvals_6)
1071     {
1072         n=i;
1073         testFindRoot(&alefeld6, 0, 1);
1074     }
1075     foreach (i; nvals_7)
1076     {
1077         n=i;
1078         testFindRoot(&alefeld7, 0.01L, 1);
1079     }
1080     real worstcase(real x)
1081     {
1082         ++numCalls;
1083         return x<0.3*real.max? -0.999e-3: 1.0;
1084     }
1085     testFindRoot(&worstcase, -real.max, real.max);
1086 
1087     // just check that the double + float cases compile
1088     findRoot!(x => 0)(-double.max, double.max);
1089     findRoot!(x => -0.0)(-float.max, float.max);
1090 /*
1091    int grandtotal=0;
1092    foreach (calls; alefeldSums)
1093    {
1094        grandtotal+=calls;
1095    }
1096    grandtotal-=2*numProblems;
1097    printf("\nALEFELD TOTAL = %d avg = %f (alefeld avg=19.3 for double)\n",
1098    grandtotal, (1.0*grandtotal)/numProblems);
1099    powercalls -= 2*powerProblems;
1100    printf("POWER TOTAL = %d avg = %f ", powercalls,
1101         (1.0*powercalls)/powerProblems);
1102 */
1103     //Issue 14231
1104     auto xp = findRoot!(x => x)(0f, 1f);
1105     auto xn = findRoot!(x => x)(-1f, -0f);
1106 }
1107 
1108 /++
1109 +/
1110 struct FindLocalMinResult(T)
1111 {
1112     ///
1113     T x = 0;
1114     ///
1115     T y = 0;
1116     ///
1117     T error = 0;
1118 
1119 @safe pure @nogc scope const @property:
1120 
1121     /++
1122     Returns: self
1123     Required_versions:`D_Exceptions`
1124     Throws: `Exception` if $(LREF FindRootResult.status) isn't $(LREF mir_find_root_status.success).
1125     +/
1126     version(D_Exceptions)
1127     ref validate() return
1128     {
1129         with(FindRootStatus) final switch(status)
1130         {
1131             case success: return this;
1132             case badBounds: throw findRoot_badBounds.toMutable;
1133             case nanX: throw findRoot_nanX.toMutable;
1134             case nanY: throw findRoot_nanY.toMutable;
1135         }
1136     }
1137 
1138 extern(C++) nothrow:
1139 
1140     /++
1141     Returns: $(LREF mir_find_root_status)
1142     +/
1143     FindRootStatus status()
1144     {
1145         with(FindRootStatus) return
1146             x != x ? nanX :
1147             y != y ? nanY :
1148             success;
1149     }
1150 }
1151 
1152 /++
1153 Find a real minimum of a real function `f(x)` via bracketing.
1154 Given a function `f` and a range `(ax .. bx)`,
1155 returns the value of `x` in the range which is closest to a minimum of `f(x)`.
1156 `f` is never evaluted at the endpoints of `ax` and `bx`.
1157 If `f(x)` has more than one minimum in the range, one will be chosen arbitrarily.
1158 If `f(x)` returns NaN or -Infinity, `(x, f(x), NaN)` will be returned;
1159 otherwise, this algorithm is guaranteed to succeed.
1160 Params:
1161     f = Function to be analyzed
1162     ax = Left bound of initial range of f known to contain the minimum.
1163     bx = Right bound of initial range of f known to contain the minimum.
1164     relTolerance = Relative tolerance.
1165     absTolerance = Absolute tolerance.
1166     N = number of addition inner points of uniform grid.
1167         The algorithm computes function value for the N points.
1168         Then reassing ax to the point that preceeds the grid's argmin,
1169         reassing bx to the point that follows the the grid's argmin.
1170         The search interval reduces (N + 1) / 2 times.
1171 Preconditions:
1172     `ax` and `bx` shall be finite reals. $(BR)
1173     `relTolerance` shall be normal positive real. $(BR)
1174     `absTolerance` shall be normal positive real no less then `T.epsilon*2`.
1175 Returns:
1176     A $(LREF FindLocalMinResult) consisting of `x`, `y = f(x)` and `error = 3 * (absTolerance * fabs(x) + relTolerance)`.
1177     The method used is a combination of golden section search and
1178 successive parabolic interpolation. Convergence is never much slower
1179 than that for a Fibonacci search.
1180 References:
1181     "Algorithms for Minimization without Derivatives", Richard Brent, Prentice-Hall, Inc. (1973)
1182 See_Also: $(LREF findRoot), $(REF isNormal, std,math)
1183 +/
1184 FindLocalMinResult!T findLocalMin(alias f, T)(
1185     const T ax,
1186     const T bx,
1187     const T relTolerance = sqrt(T.epsilon),
1188     const T absTolerance = sqrt(T.epsilon),
1189     size_t N = 0,
1190 )
1191     if (isFloatingPoint!T && __traits(compiles, T(f(T.init))))
1192 {
1193     if (false) // break attributes
1194     {
1195         T y = f(T(123));
1196     }
1197     scope funInst = delegate(T x) {
1198         return T(f(x));
1199     };
1200     scope fun = funInst.trustedAllAttr;
1201 
1202     return findLocalMinImpl(ax, bx, relTolerance, absTolerance, fun, N);
1203 }
1204 
1205 @fmamath
1206 private FindLocalMinResult!float findLocalMinImpl(
1207     const float ax,
1208     const float bx,
1209     const float relTolerance,
1210     const float absTolerance,
1211     scope const float delegate(float) @safe pure nothrow @nogc f,
1212     size_t N,
1213     ) @safe pure nothrow @nogc
1214 {
1215     pragma(inline, false);
1216     return findLocalMinImplGen!float(ax, bx, relTolerance, absTolerance, f, N);
1217 }
1218 
1219 @fmamath
1220 private FindLocalMinResult!double findLocalMinImpl(
1221     const double ax,
1222     const double bx,
1223     const double relTolerance,
1224     const double absTolerance,
1225     scope const double delegate(double) @safe pure nothrow @nogc f,
1226     size_t N,
1227     ) @safe pure nothrow @nogc
1228 {
1229     pragma(inline, false);
1230     return findLocalMinImplGen!double(ax, bx, relTolerance, absTolerance, f, N);
1231 }
1232 
1233 @fmamath
1234 private FindLocalMinResult!real findLocalMinImpl(
1235     const real ax,
1236     const real bx,
1237     const real relTolerance,
1238     const real absTolerance,
1239     scope const real delegate(real) @safe pure nothrow @nogc f,
1240     size_t N,
1241     ) @safe pure nothrow @nogc
1242 {
1243     pragma(inline, false);
1244     return findLocalMinImplGen!real(ax, bx, relTolerance, absTolerance, f, N);
1245 }
1246 
1247 @fmamath
1248 private FindLocalMinResult!T findLocalMinImplGen(T)(
1249     T ax,
1250     T bx,
1251     const T relTolerance,
1252     const T absTolerance,
1253     scope const T delegate(T) @safe pure nothrow @nogc f,
1254     size_t N,
1255     )
1256     if (isFloatingPoint!T && __traits(compiles, {T _ = f(T.init);}))
1257 in
1258 {
1259     assert(relTolerance.fabs >= T.min_normal && relTolerance.fabs < T.infinity, "relTolerance is not normal floating point number");
1260     assert(absTolerance.fabs >= T.min_normal && absTolerance.fabs < T.infinity, "absTolerance is not normal floating point number");
1261     assert(relTolerance >= 0, "absTolerance is not positive");
1262     assert(absTolerance >= T.epsilon*2, "absTolerance is not greater then `2*T.epsilon`");
1263 }
1264 do
1265 {
1266     if (N > 1)
1267     {
1268         import mir.ndslice.topology: linspace;
1269 
1270         auto xPoints = linspace!T([N + 2], [ax, bx]);
1271         size_t idx;
1272         double value = double.infinity;
1273         foreach (i; 0 .. N)
1274         {
1275             auto y = f(xPoints[i + 1]);
1276             if (y < value)
1277             {
1278                 value = y;
1279                 idx = i;
1280             }
1281         }
1282         ax = xPoints[idx + 0];
1283         bx = xPoints[idx + 2];
1284     }
1285 
1286     version(LDC) pragma(inline, true);
1287     // c is the squared inverse of the golden ratio
1288     // (3 - sqrt(5))/2
1289     // Value obtained from Wolfram Alpha.
1290     enum T c   = 0x0.61c8864680b583ea0c633f9fa31237p+0L;
1291     enum T cm1 = 0x0.9e3779b97f4a7c15f39cc0605cedc8p+0L;
1292     T tolerance;
1293     T a = ax > bx ? bx : ax;
1294     T b = ax > bx ? ax : bx;
1295     if (a < -T.max)
1296         a = -T.max;
1297     if (b > +T.max)
1298         b = +T.max;
1299     // sequence of declarations suitable for SIMD instructions
1300     T  v = a * cm1 + b * c;
1301     assert(v.fabs < T.infinity);
1302     T fv = v == v ? f(v) : v;
1303     if (fv != fv || fv == -T.infinity)
1304     {
1305         return typeof(return)(v, fv, T.init);
1306     }
1307     T  w = v;
1308     T fw = fv;
1309     T  x = v;
1310     T fx = fv;
1311     size_t i;
1312     for (T d = 0, e = 0;;)
1313     {
1314         i++;
1315         T m = (a + b) * 0.5f;
1316         // This fix is not part of the original algorithm
1317         if (!(m.fabs < T.infinity)) // fix infinity loop. Issue can be reproduced in R.
1318         {
1319             m = a.half + b.half;
1320         }
1321         tolerance = absTolerance * fabs(x) + relTolerance;
1322         const t2 = tolerance * 2;
1323         // check stopping criterion
1324         if (!(fabs(x - m) > t2 - (b - a) * 0.5f))
1325         {
1326             break;
1327         }
1328         T p = 0;
1329         T q = 0;
1330         T r = 0;
1331         // fit parabola
1332         if (fabs(e) > tolerance)
1333         {
1334             const  xw =  x -  w;
1335             const fxw = fx - fw;
1336             const  xv =  x -  v;
1337             const fxv = fx - fv;
1338             const xwfxv = xw * fxv;
1339             const xvfxw = xv * fxw;
1340             p = xv * xvfxw - xw * xwfxv;
1341             q = (xvfxw - xwfxv) * 2;
1342             if (q > 0)
1343                 p = -p;
1344             else
1345                 q = -q;
1346             r = e;
1347             e = d;
1348         }
1349         T u;
1350         // a parabolic-interpolation step
1351         if (fabs(p) < fabs(q * r * 0.5f) && p > q * (a - x) && p < q * (b - x))
1352         {
1353             d = p / q;
1354             u = x + d;
1355             // f must not be evaluated too close to a or b
1356             if (u - a < t2 || b - u < t2)
1357                 d = x < m ? tolerance: -tolerance;
1358         }
1359         // a golden-section step
1360         else
1361         {
1362             e = (x < m ? b : a) - x;
1363             d = c * e;
1364         }
1365         // f must not be evaluated too close to x
1366         u = x + (fabs(d) >= tolerance ? d: d > 0 ? tolerance: -tolerance);
1367         const fu = f(u);
1368         if (fu != fu || fu == -T.infinity)
1369         {
1370             return typeof(return)(u, fu, T.init);
1371         }
1372         //  update  a, b, v, w, and x
1373         if (fu <= fx)
1374         {
1375             (u < x ? b : a) = x;
1376             v = w; fv = fw;
1377             w = x; fw = fx;
1378             x = u; fx = fu;
1379         }
1380         else
1381         {
1382             (u < x ? a : b) = u;
1383             if (fu <= fw || w == x)
1384             {
1385                 v = w; fv = fw;
1386                 w = u; fw = fu;
1387             }
1388             else if (fu <= fv || v == x || v == w)
1389             { // do not remove this braces
1390                 v = u; fv = fu;
1391             }
1392         }
1393     }
1394     return typeof(return)(x, fx, tolerance * 3);
1395 }
1396 
1397 ///
1398 //@nogc
1399 version(mir_test) @safe unittest
1400 {
1401     import mir.math.common: approxEqual;
1402 
1403     double shift = 4;
1404 
1405     auto ret = findLocalMin!(x => (x-shift)^^2)(-1e7, 1e7).validate;
1406     assert(ret.x.approxEqual(shift));
1407     assert(ret.y.approxEqual(0.0));
1408 }
1409 
1410 ///
1411 version(mir_test) @safe unittest
1412 {
1413     import mir.math.common: approxEqual, log, fabs;
1414     alias AliasSeq(T...) = T;
1415     static foreach (T; AliasSeq!(double, float, real))
1416     {
1417         {
1418             auto ret = findLocalMin!(x => (x-4)^^2)(T.min_normal, T(1e7)).validate;
1419             assert(ret.x.approxEqual(T(4)));
1420             assert(ret.y.approxEqual(T(0)));
1421         }
1422         {
1423             auto ret = findLocalMin!(x => fabs(x-1))(-T.max/4, T.max/4, T.min_normal, 2*T.epsilon).validate;
1424             assert(approxEqual(ret.x, T(1)));
1425             assert(approxEqual(ret.y, T(0)));
1426             assert(ret.error <= 10 * T.epsilon);
1427         }
1428         {
1429             auto ret = findLocalMin!(x => T.init)(0, 1, T.min_normal, 2*T.epsilon);
1430             assert(ret.status ==  FindRootStatus.nanY);
1431         }
1432         {
1433             auto ret = findLocalMin!log( 0, 1, T.min_normal, 2*T.epsilon).validate;
1434             assert(ret.error < 3.00001 * ((2*T.epsilon)*fabs(ret.x)+ T.min_normal));
1435             assert(ret.x >= 0 && ret.x <= ret.error);
1436         }
1437         {
1438             auto ret = findLocalMin!log(0, T.max, T.min_normal, 2*T.epsilon).validate;
1439             assert(ret.y < -18);
1440             assert(ret.error < 5e-08);
1441             assert(ret.x >= 0 && ret.x <= ret.error);
1442         }
1443         {
1444             auto ret = findLocalMin!(x => -fabs(x))(-1, 1, T.min_normal, 2*T.epsilon).validate;
1445             assert(ret.x.fabs.approxEqual(T(1)));
1446             assert(ret.y.fabs.approxEqual(T(1)));
1447             assert(ret.error.approxEqual(T(0)));
1448         }
1449     }
1450 }
1451 
1452 /// Tries to find a global minimum using uniform grid to reduce the search interval
1453 version(mir_test)
1454 unittest
1455 {
1456     import mir.math.common: cos;
1457     import mir.test;
1458 
1459     alias f = x => cos(x)  + x / 10;
1460 
1461     findLocalMin!f(-4.0, 6.0, double.epsilon, 2 * double.epsilon)
1462         .validate.x.shouldApprox == 3.041425233955413;
1463     // Use 10 inner points on uniform grid to reduce the interval
1464     // reduces the interval of search 5.5 = (10 + 1) / 2  times
1465     findLocalMin!f(-4.0, 6.0, double.epsilon, 2 * double.epsilon, 10)
1466         .validate.x.shouldApprox == -3.2417600767368255;
1467 }
1468 
1469 /++
1470 A set of one or two smile roots.
1471 
1472 Because we assume that volatility smile is convex the equantion above can have no more then two roots.
1473 
1474 The `left` and `right` members are equal if the smile has only one root.
1475 +/
1476 struct SmileRoots(T)
1477     if (__traits(isFloating, T))
1478 {
1479     import mir.small_array: SmallArray;
1480 
1481     ///
1482     T left;
1483     ///
1484     T right;
1485 
1486 @safe pure nothrow @nogc:
1487 
1488     ///
1489     this(T value)
1490     {
1491         left = right = value;
1492     }
1493 
1494     ///
1495     this(T left, T right)
1496     {
1497         this.left = left;
1498         this.right = right;
1499     }
1500 
1501     ///
1502     this(SmallArray!(T, 2) array)
1503     {
1504         if (array.length == 2)
1505             this(array[0], array[1]);
1506         else
1507         if (array.length == 1)
1508             this(array[0]);
1509         else
1510             this(T.nan, T.nan);
1511     }
1512 
1513     ///
1514     inout(T)[] opIndex() @trusted inout
1515     {
1516         return (&left)[0 .. 2];
1517     }
1518 
1519     ///
1520     size_t count() const
1521     {
1522         return 1 + (left != right);
1523     }
1524 }
1525 
1526 /++
1527 +/
1528 struct FindSmileRootsResult(T)
1529     if (__traits(isFloating, T))
1530 {
1531     import mir.algebraic: Nullable;
1532 
1533     /++
1534     Left result if any
1535     +/
1536     Nullable!(FindRootResult!T) leftResult;
1537     /++
1538     Right result if any
1539     +/
1540     Nullable!(FindRootResult!T) rightResult;
1541     /++
1542     +/
1543     Nullable!(FindLocalMinResult!T) localMinResult;
1544 
1545 @safe pure @nogc scope const @property:
1546 
1547     /++
1548     Returns: $(LREF mir_find_root_status)
1549     +/
1550     FindRootStatus status()
1551     {
1552         if (leftResult && leftResult.get.status)
1553             return leftResult.get.status;
1554         if (rightResult && rightResult.get.status)
1555             return rightResult.get.status;
1556         if (!leftResult && !rightResult)
1557             return FindRootStatus.badBounds;
1558         return FindRootStatus.success;
1559     }
1560 
1561     ///
1562     version(D_Exceptions)
1563     ref validate() inout return
1564     {
1565         with(FindRootStatus) final switch(status)
1566         {
1567             case success: return this;
1568             case badBounds: throw findRoot_badBounds.toMutable;
1569             case nanX: throw findRoot_nanX.toMutable;
1570             case nanY: throw findRoot_nanY.toMutable;
1571         }
1572     }
1573 
1574     /++
1575     Returns: `SmallArray!(T, 2)` of roots if any.
1576     +/
1577     auto roots()()
1578     {
1579         import mir.small_array;
1580         SmallArray!(T, 2) ret;
1581         if (leftResult)
1582             ret.append(leftResult.get.x);
1583         if (rightResult)
1584             ret.append(rightResult.get.x);
1585         return ret;
1586     }
1587 
1588     /++
1589     Returns: $(LREF SmileRoots).
1590     +/
1591     SmileRoots!double smileRoots()()
1592     {
1593         return typeof(return)(roots);
1594     }
1595 }
1596 
1597 /++
1598 Find one or two roots of a real function f(x) using combination of $(LREF FindRoot) and $(LREF FindLocalMin).
1599 
1600 Params:
1601     f = Function to be analyzed. If `f(ax)` and `f(bx)` have opposite signs one root is returned,
1602         otherwise the implementation tries to find a local minimum and returns two roots.
1603         At least one of `f(ax)` and `f(bx)` should be greater or equal to zero.
1604     tolerance = Defines an early termination condition. Receives the
1605         current upper and lower bounds on the root. The delegate must return `true` when these bounds are
1606         acceptable. If this function always returns `false` or it is null, full machine precision will be achieved.
1607     ax = Left inner bound of initial range of `f` known to contain the roots.
1608     bx = Right inner bound of initial range of `f` known to contain the roots. Can be equal to `ax`.
1609     fax = Value of `f(ax)` (optional).
1610     fbx = Value of `f(bx)` (optional).
1611     relTolerance = Relative tolerance used by $(LREF findLocalMin).
1612     absTolerance = Absolute tolerance used by $(LREF findLocalMin).
1613     lowerBound = 
1614     upperBound =
1615     maxIterations = Appr. maximum allowed number of function calls for each $(LREF findRoot) call.
1616     steps = 
1617 
1618 Returns: $(LREF FindSmileRootsResult)
1619 +/
1620 FindSmileRootsResult!T findSmileRoots(alias f, alias tolerance = null, T)(
1621     const T ax,
1622     const T bx,
1623     const T fax = T.nan,
1624     const T fbx = T.nan,
1625     const T relTolerance = sqrt(T.epsilon),
1626     const T absTolerance = sqrt(T.epsilon),
1627     const T lowerBound = T.nan,
1628     const T upperBound = T.nan,
1629     uint maxIterations = T.sizeof * 16,
1630     uint steps = 0,
1631     )
1632 if (__traits(isFloating, T))
1633 {
1634     FindSmileRootsResult!T ret;
1635     auto res = findRoot!(f, tolerance)(ax, bx, fax, fbx, T.nan, T.nan, maxIterations);
1636     if (res.status == FindRootStatus.success)
1637     {
1638         (res.bx.signbit ? ret.leftResult : ret.rightResult) = res;
1639     }
1640     else
1641     if (res.status == FindRootStatus.badBounds)
1642     {
1643         ret.localMinResult = findLocalMin!f(ax, bx, relTolerance, absTolerance);
1644         ret.leftResult = findRoot!(f, tolerance)(ax, ret.localMinResult.x, T.nan, ret.localMinResult.y, T.nan, T.nan, maxIterations);
1645         ret.rightResult = findRoot!(f, tolerance)(ret.localMinResult.x, bx, ret.localMinResult.y, T.nan, T.nan, T.nan, maxIterations);
1646     }
1647     return ret;
1648 }
1649 
1650 /// Smile
1651 version(mir_test)
1652 unittest
1653 {
1654     import mir.math.common: approxEqual;
1655     auto result = findSmileRoots!(x => x ^^ 2 - 1)(-10.0, 10.0);
1656     assert(result.roots.length == 2);
1657     assert(result.roots[0].approxEqual(-1));
1658     assert(result.roots[1].approxEqual(+1));
1659     assert(result.smileRoots.left.approxEqual(-1));
1660     assert(result.smileRoots.right.approxEqual(+1));
1661     assert(result.leftResult);
1662     assert(result.rightResult);
1663     assert(result.localMinResult);
1664     assert(result.localMinResult.get.x.approxEqual(0));
1665     assert(result.localMinResult.get.y.approxEqual(-1));
1666 }
1667 
1668 /// Skew
1669 version(mir_test)
1670 unittest
1671 {
1672     import mir.math.common: approxEqual;
1673     auto result = findSmileRoots!(x => x ^^ 2 - 1)(0.5, 10.0);
1674     assert(result.roots.length == 1);
1675     assert(result.roots[0].approxEqual(+1));
1676     assert(result.smileRoots.left.approxEqual(+1));
1677     assert(result.smileRoots.right.approxEqual(+1));
1678     assert(!result.leftResult);
1679     assert(result.rightResult);
1680 }
1681 
1682 version(mir_test)
1683 unittest
1684 {
1685     auto result = findSmileRoots!(x => x ^^ 2 - 1)(-10.0, -0.5);
1686     assert(result.roots.length == 1);
1687     assert(result.roots[0].approxEqual(-1));
1688     assert(result.smileRoots.left.approxEqual(-1));
1689     assert(result.smileRoots.right.approxEqual(-1));
1690     assert(result.leftResult);
1691     assert(!result.rightResult);
1692 }
1693 
1694 // force disabled FMA math
1695 private static auto half(T)(const T x)
1696 {
1697     pragma(inline, false);
1698     return x * 0.5f;
1699 }
1700 
1701 private auto trustedAllAttr(T)(T t) @trusted
1702 {
1703     import std.traits;
1704     enum attrs = (functionAttributes!T & ~FunctionAttribute.system) 
1705         | FunctionAttribute.pure_
1706         | FunctionAttribute.safe
1707         | FunctionAttribute.nogc
1708         | FunctionAttribute.nothrow_;
1709     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
1710 }
1711 
1712 /++
1713 Calculate the derivative of a function.
1714 This function uses Ridders' method of extrapolating the results
1715 of finite difference formulas for consecutively smaller step sizes,
1716 with an improved stopping criterion described in the Numerical Recipes
1717 books by Press et al.
1718 
1719 This method gives a much higher degree of accuracy in the answer
1720 compared to a single finite difference calculation, but requires
1721 more function evaluations; typically 6 to 12. The maximum number
1722 of function evaluations is $(D 24).
1723 
1724 Params:
1725     f = The function of which to take the derivative.
1726     x = The point at which to take the derivative.
1727     h = A "characteristic scale" over which the function changes.
1728     factor = Stepsize is decreased by factor at each iteration.
1729     safe = Return when error is SAFE worse than the best so far.
1730 
1731 References:
1732 $(UL
1733     $(LI
1734         C. J. F. Ridders,
1735         $(I Accurate computation of F'(x) and F'(x)F''(x)).
1736         Advances in Engineering Software, vol. 4 (1982), issue 2, p. 75.)
1737     $(LI
1738         W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery,
1739         $(I Numerical Recipes in C++) (2nd ed.).
1740         Cambridge University Press, 2003.)
1741 )
1742 +/
1743 @fmamath
1744 DiffResult!T diff(alias f, T)(const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
1745 {
1746     if (false) // break attributes
1747         T y = f(T(1));
1748     scope funInst = delegate(T x) {
1749         return T(f(x));
1750     };
1751     scope fun = funInst.trustedAllAttr;
1752     return diffImpl(fun, x, h, factor, safe);
1753 }
1754 
1755 ///ditto
1756 DiffResult!T diffImpl(T)
1757     (scope const T delegate(T) @safe pure nothrow @nogc f, const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
1758     @trusted pure nothrow @nogc
1759 in {
1760     assert(h < T.max);
1761     assert(h > T.min_normal);
1762 }
1763 do {
1764     // Set up Romberg tableau.
1765     import mir.ndslice.slice: sliced;
1766     pragma(inline, false);
1767 
1768     enum tableauSize = 16;
1769     T[tableauSize ^^ 2] workspace = void;
1770     auto tab = workspace[].sliced(tableauSize, tableauSize);
1771 
1772     // From the NR book: Stop when the difference between consecutive
1773     // approximations is bigger than SAFE*error, where error is an
1774     // estimate of the absolute error in the current (best) approximation.
1775 
1776     // First approximation: A_0
1777     T result = void;
1778     T error = T.max;
1779     T hh = h;
1780 
1781     tab[0,0] = (f(x + h) - f(x - h))  / (2 * h);
1782     foreach (n; 1 .. tableauSize)
1783     {
1784         // Decrease h.
1785         hh /= factor;
1786 
1787         // Compute A_n
1788         tab[0, n] = (f(x + hh) - f(x - hh)) / (2 * hh);
1789 
1790         T facm = 1;
1791         foreach (m; 1 .. n + 1)
1792         {
1793             facm *= factor ^^ 2;
1794 
1795             // Compute B_(n-1), C_(n-2), ...
1796             T upLeft  = tab[m - 1, n - 1];
1797             T up      = tab[m - 1, n];
1798             T current = (facm * up - upLeft) / (facm - 1);
1799             tab[m, n] = current;
1800 
1801             // Calculate and check error.
1802             T currentError = fmax(fabs(current - upLeft), fabs(current - up));
1803             if (currentError <= error)
1804             {
1805                 result = current;
1806                 error = currentError;
1807             }
1808         }
1809 
1810         if (fabs(tab[n, n] - tab[n - 1, n - 1]) >= safe * error)
1811             break;
1812     }
1813 
1814     return typeof(return)(result, error);
1815 }
1816 
1817 ///
1818 version(mir_test)
1819 unittest
1820 {
1821     import mir.math.common;
1822 
1823     auto f(double x) { return exp(x) / (sin(x) - x ^^ 2); }
1824     auto d(double x) { return -(exp(x) * ((x - 2) * x - sin(x) + cos(x)))/(x^^2 - sin(x))^^2; }
1825     auto r = diff!f(1.0, 0.01);
1826     assert (approxEqual(r.value, d(1)));
1827 }
1828 
1829 /++
1830 +/
1831 struct DiffResult(T)
1832     if (__traits(isFloating, T))
1833 {
1834     ///
1835     T value = 0;
1836     ///
1837     T error = 0;
1838 }
1839 
1840 /++
1841 Integrates function on the interval `[a, b]` using adaptive Gauss-Lobatto algorithm.
1842 
1843 References:
1844     W. Gander and W. Gautschi. Adaptive Quadrature — Revisited
1845 
1846 Params:
1847     f = function to integrate. `f` should be valid on interval `[a, b]` including the bounds.
1848     a = finite left interval bound
1849     b = finite right interval bound
1850     tolerance = (optional) relative tolerance should be greater or equal to `T.epsilon`
1851 
1852 Returns:
1853     Integral value
1854 +/
1855 T integrate(alias f, T)(const T a, const T b, const T tolerance = T.epsilon)
1856     if (__traits(isFloating, T))
1857 {
1858     if (false) // break attributes
1859         T y = f(T(1));
1860     scope funInst = delegate(T x) {
1861         return T(f(x));
1862     };
1863     scope fun = funInst.trustedAllAttr;
1864     return integrateImpl(fun, a, b, tolerance);
1865 }
1866 
1867 /// ditto
1868 @fmamath
1869 T integrateImpl(T)(
1870     scope const T delegate(T) @safe pure nothrow @nogc f,
1871     const T a,
1872     const T b,
1873     const T tolerance = T.epsilon,
1874 ) @safe pure nothrow @nogc
1875     if (__traits(isFloating, T))
1876 in {
1877     assert(-T.infinity < a);
1878     assert(b < +T.infinity);
1879     assert(a < b);
1880     assert(tolerance >= T.epsilon);
1881 }
1882 do {
1883     pragma(inline, false);
1884     enum T alpha = 0.816496580927726032732428024901963797322;
1885     enum T beta = 0.447213595499957939281834733746255247088;
1886     enum T x1 = 0.94288241569947971905635175843185720232;
1887     enum T x2 = 0.64185334234978130978123554132903188394;
1888     enum T x3 = 0.23638319966214988028222377349205292599;
1889     enum T A = 0.015827191973480183087169986733305510591;
1890     enum T B = 0.094273840218850045531282505077108171960;
1891     enum T C = 0.19507198733658539625363597980210298680;
1892     enum T D = 0.18882157396018245442000533937297167125;
1893     enum T E = 0.19977340522685852679206802206648840246;
1894     enum T F = 0.22492646533333952701601768799639508076;
1895     enum T G = 0.24261107190140773379964095790325635233;
1896     enum T A1 = 77 / 1470.0L;
1897     enum T B1 = 432 / 1470.0L;
1898     enum T C1 = 625 / 1470.0L;
1899     enum T D1 = 672 / 1470.0L;
1900     enum T A2 = 1 / 6.0L;
1901     enum T B2 = 5 / 6.0L;
1902 
1903     T m = (a + b) * 0.5f;
1904     // This fix is not part of the original algorithm
1905     if (!(m.fabs < T.infinity))
1906     {
1907         m = a.half + b.half;
1908     }
1909     T h = (b - a) * 0.5f;
1910     // This fix is not part of the original algorithm
1911     if (!(h.fabs < T.infinity))
1912     {
1913         h = b.half - a.half;
1914     }
1915     T[13] x = [
1916         a,
1917         m - x1 * h,
1918         m - alpha * h,
1919         m - x2 * h,
1920         m - beta * h,
1921         m - x3 * h,
1922         m,
1923         m + x3 * h,
1924         m + beta * h,
1925         m + x2 * h,
1926         m + alpha * h,
1927         m + x1 * h,
1928         b,
1929     ];
1930     T[13] y = [
1931         f(x[ 0]),
1932         f(x[ 1]),
1933         f(x[ 2]),
1934         f(x[ 3]),
1935         f(x[ 4]),
1936         f(x[ 5]),
1937         f(x[ 6]),
1938         f(x[ 7]),
1939         f(x[ 8]),
1940         f(x[ 9]),
1941         f(x[10]),
1942         f(x[11]),
1943         f(x[12]),
1944     ];
1945     T i2 = h * (
1946         + A2 * (y[0] + y[12])
1947         + B2 * (y[4] + y[ 8])
1948     );
1949     T i1 = h * (
1950         + A1 * (y[0] + y[12])
1951         + B1 * (y[2] + y[10])
1952         + C1 * (y[4] + y[ 8])
1953         + D1 * (y[6]        )
1954     );
1955     T si = h * (
1956         + A * (y[0] + y[12])
1957         + B * (y[1] + y[11])
1958         + C * (y[2] + y[10])
1959         + D * (y[3] + y[ 9])
1960         + E * (y[4] + y[ 8])
1961         + F * (y[5] + y[ 7])
1962         + G * (y[6]        )
1963     );
1964     T erri1 = fabs(i1 - si);
1965     T erri2 = fabs(i2 - si);
1966     T R = erri1 / erri2;
1967     T tol = tolerance;
1968     if (tol < T.epsilon)
1969         tol = T.epsilon;
1970     if (0 < R && R < 1)
1971         tol /= R;
1972     si *= tol / T.epsilon;
1973     if (si == 0)
1974         si = b - a;
1975     
1976     if (!(si.fabs < T.infinity))
1977         return T.nan;
1978 
1979     T step(const T a, const T b, const T fa, const T fb)
1980     {
1981         T m = (a + b) * 0.5f;
1982         // This fix is not part of the original algorithm
1983         if (!(m.fabs < T.infinity))
1984         {
1985             m = a.half + b.half;
1986         }
1987         T h = (b - a) * 0.5f;
1988         if (!(h.fabs < T.infinity))
1989         {
1990             h = b.half - a.half;
1991         }
1992         T[5] x = [
1993             m - alpha * h,
1994             m - beta * h,
1995             m,
1996             m + beta * h,
1997             m + alpha * h,
1998         ];
1999         T[5] y = [
2000             f(x[0]),
2001             f(x[1]),
2002             f(x[2]),
2003             f(x[3]),
2004             f(x[4]),
2005         ];
2006         T i2 = h * (
2007             + A2 * (fa + fb)
2008             + B2 * (y[1] + y[3])
2009         );
2010         T i1 = h * (
2011             + A1 * (fa + fb)
2012             + B1 * (y[0] + y[4])
2013             + C1 * (y[1] + y[3])
2014             + D1 * y[2]
2015         );
2016         auto sic = si + (i1 - i2);
2017         if (!(i1.fabs < T.infinity) || sic == si || !(a < x[0]) || !(x[4] < b))
2018         {
2019             return i1;
2020         }
2021         return
2022             + (step(   a, x[0],   fa, y[0])
2023             +  step(x[0], x[1], y[0], y[1]))
2024             + (step(x[1], x[2], y[1], y[2])
2025             +  step(x[2], x[3], y[2], y[3]))
2026             + (step(x[3], x[4], y[3], y[4])
2027             +  step(x[4],    b, y[4],   fb));
2028     }
2029 
2030     foreach (i; 0 .. 12)
2031         x[i] = step(x[i], x[i + 1], y[i], y[i + 1]);
2032 
2033     return
2034         + ((x[ 0]
2035         +   x[ 1])
2036         +  (x[ 2]
2037         +   x[ 3]))
2038         + ((x[ 4]
2039         +   x[ 5])
2040         +  (x[ 6]
2041         +   x[ 7]))
2042         + ((x[ 8]
2043         +   x[ 9])
2044         +  (x[10]
2045         +   x[11]));
2046 }
2047 
2048 ///
2049 version(mir_test)
2050 unittest
2051 {
2052     import mir.math.common;
2053     import mir.math.constant;
2054 
2055     alias cosh = x => 0.5 * (exp(x) + exp(-x));
2056     enum Pi = double(PI);
2057 
2058     assert(integrate!exp(0.0, 1.0).approxEqual(double(E - 1)));
2059     assert(integrate!(x => x >= 3)(0.0, 10.0).approxEqual(7.0));
2060     assert(integrate!sqrt(0.0, 1.0).approxEqual(2.0 / 3));
2061     assert(integrate!(x => 23.0 / 25 * cosh(x) - cos(x))(-1.0, 1.0).approxEqual(0.479428226688801667));
2062     assert(integrate!(x => 1 / (x ^^ 4 + x ^^ 2 + 0.9))(-1.0, 1.0).approxEqual(1.5822329637294));
2063     assert(integrate!(x => sqrt(x ^^ 3))(0.0, 1.0).approxEqual(0.4));
2064     assert(integrate!(x => x ? 1 / sqrt(x) : 0)(0.0, 1.0).approxEqual(2));
2065     assert(integrate!(x => 1 / (1 + x ^^ 4))(0.0, 1.0).approxEqual(0.866972987339911));
2066     assert(integrate!(x => 2 / (2 + sin(10 * Pi * x)))(0.0, 1.0).approxEqual(1.1547005383793));
2067     assert(integrate!(x => 1 / (1 + x))(0.0, 1.0).approxEqual(0.6931471805599));
2068     assert(integrate!(x => 1 / (1 + exp(x)))(0.0, 1.0).approxEqual(0.3798854930417));
2069     assert(integrate!(x => exp(x) - 1 ? x / (exp(x) - 1) : 0)(0.0, 1.0).approxEqual(0.777504634112248));
2070     assert(integrate!(x => sin(100 * Pi * x) / (Pi * x))(0.1, 1.0).approxEqual(0.0090986375391668));
2071     assert(integrate!(x => sqrt(50.0) * exp(-50 * Pi * x ^^ 2))(0.0, 10.0).approxEqual(0.5));
2072     assert(integrate!(x => 25 * exp(-25 * x))(0.0, 10.0).approxEqual(1.0));
2073     assert(integrate!(x => 50 / Pi * (2500 * x ^^ 2 + 1))(0.0, 10.0).approxEqual(1.3263071079268e+7));
2074     assert(integrate!(x => 50 * (sin(50 * Pi * x) / (50 * Pi * x)) ^^ 2)(0.01, 1.0).approxEqual(0.11213930374164));
2075     assert(integrate!(x => cos(cos(x) + 3 * sin(x) + 2 * cos(2 * x) + 3 * sin(2 * x) + 3 * cos(3 * x)))(0.0, Pi).approxEqual(0.83867634269443));
2076     assert(integrate!(x => x > 1e-15 ? log(x) : 0)(0.0, 1.0).approxEqual(-1));
2077     assert(integrate!(x => 1 / (x ^^ 2 + 1.005))(-1.0, 1.0).approxEqual(1.5643964440690));
2078     assert(integrate!(x => 1 / cosh(20 * (x - 0.2)) + 1 / cosh(400 * (x - 0.04)) + 1 / cosh(8000 * (x - 0.008)))(0.0, 1.0).approxEqual(0.16349495585710));
2079     assert(integrate!(x => 4 * Pi ^^ 2 * x * sin(20 * Pi * x) * cos(2 * Pi * x))(0.0, 1.0).approxEqual(-0.6346651825434));
2080     assert(integrate!(x => 1 / (1 + (230 * x - 30) ^^ 2))(0.0, 1.0).approxEqual(0.013492485649468));
2081 }