[gnumeric] Mathfunc: start extracting non-R code for d/p/q functions.



commit 07bf7d7bf960f568a526003bb740d796b8c5f0a1
Author: Morten Welinder <terra gnome org>
Date:   Tue Dec 31 17:46:51 2013 -0500

    Mathfunc: start extracting non-R code for d/p/q functions.

 ChangeLog            |    5 +
 plugins/fn-r/extra.c |   21 +---
 src/Makefile.am      |    2 +
 src/mathfunc.c       |  278 +-----------------------------------------------
 src/mathfunc.h       |   15 ---
 src/sf-dpq.c         |  291 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/sf-dpq.h         |   22 ++++
 7 files changed, 326 insertions(+), 308 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 5309379..6e8bb8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-12-31  Morten Welinder  <terra gnome org>
+
+       * src/sf-dpq.c (pfuncinverter, discpfuncinverter): Extract from
+       mathfunc.c
+
 2013-12-30  Morten Welinder  <terra gnome org>
 
        * src/mathfunc.c (dnorm): Improve accuracy for x>5 (normalized).
diff --git a/plugins/fn-r/extra.c b/plugins/fn-r/extra.c
index 62eff8b..03c64cc 100644
--- a/plugins/fn-r/extra.c
+++ b/plugins/fn-r/extra.c
@@ -1,24 +1,11 @@
 #include <gnumeric-config.h>
 #include "gnumeric.h"
 #include <mathfunc.h>
+#include <sf-dpq.h>
 #include <sf-trig.h>
 #include <sf-gamma.h>
 #include "extra.h"
 
-#define ML_ERR_return_NAN { return gnm_nan; }
-
-/* ------------------------------------------------------------------------- */
-/* --- BEGIN MAGIC R SOURCE MARKER --- */
-
-#define R_Q_P01_check(p)                       \
-    if ((log_p && p > 0) ||                    \
-       (!log_p && (p < 0 || p > 1)) )          \
-       ML_ERR_return_NAN
-
-
-/* ------------------------------------------------------------------------ */
-/* --- END MAGIC R SOURCE MARKER --- */
-
 gnm_float
 qcauchy (gnm_float p, gnm_float location, gnm_float scale,
         gboolean lower_tail, gboolean log_p)
@@ -26,8 +13,10 @@ qcauchy (gnm_float p, gnm_float location, gnm_float scale,
        if (gnm_isnan(p) || gnm_isnan(location) || gnm_isnan(scale))
                return p + location + scale;
 
-       R_Q_P01_check(p);
-       if (scale < 0 || !gnm_finite(scale)) ML_ERR_return_NAN;
+       if (log_p ? (p > 0) : (p < 0 || p > 1))
+               return gnm_nan;
+
+       if (scale < 0 || !gnm_finite(scale)) return gnm_nan;
 
        if (log_p) {
                if (p > -1)
diff --git a/src/Makefile.am b/src/Makefile.am
index 215be42..027db57 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -154,6 +154,7 @@ libspreadsheet_la_SOURCES =                 \
        selection.c                             \
        session.c                               \
        sf-bessel.c                             \
+       sf-dpq.c                                \
        sf-gamma.c                              \
        sf-trig.c                               \
        sheet.c                                 \
@@ -283,6 +284,7 @@ libspreadsheet_include_HEADERS =    \
        selection.h                             \
        session.h                               \
        sf-bessel.h                             \
+       sf-dpq.h                                \
        sf-gamma.h                              \
        sf-trig.h                               \
        sheet.h                                 \
diff --git a/src/mathfunc.c b/src/mathfunc.c
index ade4892..4235a10 100644
--- a/src/mathfunc.c
+++ b/src/mathfunc.c
@@ -36,7 +36,7 @@
 #include <gnumeric-config.h>
 #include "gnumeric.h"
 #include "mathfunc.h"
-#include "sf-trig.h"
+#include "sf-dpq.h"
 #include "sf-gamma.h"
 #include <glib/gi18n-lib.h>
 
@@ -4728,282 +4728,6 @@ pf (gnm_float x, gnm_float n1, gnm_float n2, gboolean lower_tail, gboolean log_p
 
 /* ------------------------------------------------------------------------ */
 
-#undef DEBUG_pfuncinverter
-
-/**
- * pfuncinverter:
- * @p:
- * @shape:
- * @lower_tail:
- * @log_p:
- * @xlow:
- * @xhigh:
- * @x0:
- * @pfunc: (scope call):
- * @dpfunc_dx: (scope call):
- *
- * Returns:
- **/
-gnm_float
-pfuncinverter (gnm_float p, const gnm_float shape[],
-              gboolean lower_tail, gboolean log_p,
-              gnm_float xlow, gnm_float xhigh, gnm_float x0,
-              GnmPFunc pfunc, GnmDPFunc dpfunc_dx)
-{
-       gboolean have_xlow = gnm_finite (xlow);
-       gboolean have_xhigh = gnm_finite (xhigh);
-       gnm_float exlow, exhigh;
-       gnm_float x = 0, e = 0, px;
-       int i;
-
-       g_return_val_if_fail (pfunc != NULL, gnm_nan);
-
-       R_Q_P01_check (p);
-       if (p == R_DT_0) return xlow;
-       if (p == R_DT_1) return xhigh;
-
-       exlow = R_DT_0 - p;
-       exhigh = R_DT_1 - p;
-       if (!lower_tail) {
-               exlow = -exlow;
-               exhigh = -exhigh;
-       }
-
-#ifdef DEBUG_pfuncinverter
-       g_printerr ("p=%.15g\n", p);
-#endif
-
-       for (i = 0; i < 100; i++) {
-               if (i == 0) {
-                       if (x0 > xlow && x0 < xhigh)
-                               /* Use supplied guess.  */
-                               x = x0;
-                       else if (have_xlow && x0 <= xlow)
-                               x = xlow + have_xhigh ? (xhigh - xlow) / 100 : 1;
-                       else if (have_xhigh && x0 >= xhigh)
-                               x = xhigh - have_xlow ? (xhigh - xlow) / 100 : 1;
-                       else
-                               x = 0;  /* Whatever */
-               } else if (i == 1) {
-                       /*
-                        * Under the assumption that the initial guess was
-                        * good, pick a nearby point that is hopefully on
-                        * the other side.  If we already have both sides,
-                        * just bisect.
-                        */
-                       if (have_xlow && have_xhigh)
-                               x = (xlow + xhigh) / 2;
-                       else if (have_xlow)
-                               x = xlow * 1.1;
-                       else
-                               x = xhigh / 1.1;
-               } else if (have_xlow && have_xhigh) {
-                       switch (i % 8) {
-                       case 0:
-                               x = xhigh - (xhigh - xlow) *
-                                       (exhigh / (exhigh - exlow));
-                               break;
-                       case 4:
-                               /* Half-way in log-space.  */
-                               if (xlow >= 0 && xhigh >= 0)
-                                       x = gnm_sqrt (MAX (GNM_MIN, xlow)) * gnm_sqrt (xhigh);
-                               else if (xlow <= 0 && xhigh <= 0)
-                                       x = -gnm_sqrt (-xlow) * gnm_sqrt (MAX (GNM_MIN, -xhigh));
-                               else
-                                       x = 0;
-                               break;
-                       case 2:
-                               x = (xhigh + 1000 * xlow) / 1001;
-                               break;
-                       case 6:
-                               x = (1000 * xhigh + xlow) / 1001;
-                               break;
-                       default:
-                               x = (xhigh + xlow) / 2;
-                       }
-               } else if (have_xlow) {
-                       /* Agressively seek right in search of xhigh.  */
-                       x = (xlow < 1) ? 1 : (2 * i) * xlow;
-               } else {
-                       /* Agressively seek left in search of xlow.  */
-                       x = (xhigh > -1) ? -1 : (2 * i) * xhigh;
-               }
-
-       newton_retry:
-               if ((have_xlow && x <= xlow) || (have_xhigh && x >= xhigh))
-                       continue;
-
-               px = pfunc (x, shape, lower_tail, log_p);
-               e = px - p;
-               if (!lower_tail) e = -e;
-
-#ifdef DEBUG_pfuncinverter
-               g_printerr ("%3d:  x=%.15g  e=%.15g  l=%.15g  h=%.15g\n",
-                           i, x, e, xlow, xhigh);
-#endif
-
-               if (e == 0)
-                       goto done;
-               else if (e > 0) {
-                       xhigh = x;
-                       exhigh = e;
-                       have_xhigh = TRUE;
-               } else if (e < 0) {
-                       xlow = x;
-                       exlow = e;
-                       have_xlow = TRUE;
-               } else {
-                       /* We got a NaN.  */
-               }
-
-               if (have_xlow && have_xhigh) {
-                       gnm_float prec = (xhigh - xlow) /
-                               (gnm_abs (xlow) + gnm_abs (xhigh));
-                       if (prec < GNM_EPSILON * 4) {
-                               x = (xhigh + xlow) / 2;
-                               e = pfunc (x, shape, lower_tail, log_p) - p;
-                               if (!lower_tail) e = -e;
-                               goto done;
-                       }
-
-                       if (dpfunc_dx && i % 3 < 2 && (i == 0 || prec < 0.05)) {
-                               gnm_float d = dpfunc_dx (x, shape, log_p);
-                               if (log_p) d = gnm_exp (d - px);
-#ifdef DEBUG_pfuncinverter
-                               g_printerr ("Newton: d=%-.14g\n", d);
-#endif
-                               if (d) {
-                                       /*
-                                        * Deliberately overshoot a bit to help
-                                        * with getting good points on both
-                                        * sides of the root.
-                                        */
-                                       x = x - e / d * 1.000001;
-                                       if (x > xlow && x < xhigh) {
-#ifdef DEBUG_pfuncinverter
-                                               g_printerr ("Newton ok\n");
-#endif
-                                               i++;
-                                               goto newton_retry;
-                                       }
-                               } else {
-#ifdef DEBUG_pfuncinverter
-                                               g_printerr ("Newton d=0\n");
-#endif
-                               }
-                       }
-               }
-       }
-
-       ML_ERROR(ME_PRECISION);
- done:
-       /* Make sure to keep a lucky near-hit.  */
-
-       if (have_xhigh && gnm_abs (e) > exhigh)
-               e = exhigh, x = xhigh;
-       if (have_xlow && gnm_abs (e) > -exlow)
-               e = exlow, x = xlow;
-
-#ifdef DEBUG_pfuncinverter
-       g_printerr ("--> %.15g\n\n", x);
-#endif
-       return x;
-}
-
-/**
- * discpfuncinverter:
- * @p:
- * @shape:
- * @lower_tail:
- * @log_p:
- * @xlow:
- * @xhigh:
- * @x0:
- * @pfunc: (scope call):
- *
- * Discrete pfuncs only.  (Specifically: only integer x are allowed).
- * Returns:
- */
-gnm_float
-discpfuncinverter (gnm_float p, const gnm_float shape[],
-                  gboolean lower_tail, gboolean log_p,
-                  gnm_float xlow, gnm_float xhigh, gnm_float x0,
-                  GnmPFunc pfunc)
-{
-       gboolean have_xlow = gnm_finite (xlow);
-       gboolean have_xhigh = gnm_finite (xhigh);
-       gnm_float step;
-       int i;
-
-       R_Q_P01_check (p);
-       if (p == R_DT_0) return xlow;
-       if (p == R_DT_1) return xhigh;
-
-       if (gnm_finite (x0) && x0 >= xlow && x0 <= xhigh)
-               ; /* Nothing -- guess is good.  */
-       else if (have_xlow && have_xhigh)
-               x0 = (xlow + xhigh) / 2;
-       else if (have_xhigh)
-               x0 = xhigh;
-       else if (have_xlow)
-               x0 = xlow;
-       else
-               x0 = 0;
-       x0 = gnm_floor (x0 + 0.5);
-       step = 1 + gnm_floor (gnm_abs (x0) * GNM_EPSILON);
-
-#if 0
-       g_printerr ("step=%.20g\n", step);
-#endif
-
-       for (i = 1; 1; i++) {
-               gnm_float ex0 = pfunc (x0, shape, lower_tail, log_p) - p;
-#if 0
-               g_printerr ("x=%.20g  e=%.20g\n", x0, ex0);
-#endif
-               if (!lower_tail) ex0 = -ex0;
-               if (ex0 <= 0)
-                       xlow = x0, have_xlow = TRUE;
-               if (ex0 >= 0)
-                       xhigh = x0, have_xhigh = TRUE, step = -gnm_abs (step);
-
-               if (i > 1 && have_xlow && have_xhigh) {
-                       gnm_float xmid = gnm_floor ((xlow + xhigh) / 2);
-                       if (xmid - xlow < 0.5 ||
-                           xmid - xlow < gnm_abs (xlow) * GNM_EPSILON)
-                               return xhigh;
-                       x0 = xmid;
-               } else {
-                       gnm_float x1 = x0 + step;
-
-                       if (x1 == x0) {
-                               /* Probably infinite.  */
-                               return gnm_nan;
-                       } else if (x1 >= xlow && x1 <= xhigh) {
-                               x0 = x1;
-                               step *= 2 * i;
-                       } else {
-                               /* We went off the edge by walking too fast.  */
-                               gnm_float newstep = 1 + gnm_floor (gnm_abs (x0) * GNM_EPSILON);
-                               step = (step > 0) ? newstep : -newstep;
-                               x1 = x0 + step;
-                               if (x1 >= xlow && x1 <= xhigh)
-                                       continue;
-                               /*
-                                * We don't seem to find a finite x on the
-                                * other side of the root.
-                                */
-                               return (step > 0) ? xhigh : xlow;
-                       }
-               }
-       }
-
-       g_assert_not_reached ();
-}
-
-
-/* ------------------------------------------------------------------------ */
-
 static gnm_float
 ppois1 (gnm_float x, const gnm_float *plambda,
        gboolean lower_tail, gboolean log_p)
diff --git a/src/mathfunc.h b/src/mathfunc.h
index f43825e..fb52594 100644
--- a/src/mathfunc.h
+++ b/src/mathfunc.h
@@ -128,21 +128,6 @@ gnm_float qtukey(gnm_float p, gnm_float nmeans, gnm_float df, gnm_float nranges,
 
 /* ------------------------------------------------------------------------- */
 
-typedef gnm_float (*GnmPFunc) (gnm_float x, const gnm_float shape[],
-                              gboolean lower_tail, gboolean log_p);
-typedef gnm_float (*GnmDPFunc) (gnm_float x, const gnm_float shape[],
-                               gboolean log_p);
-
-gnm_float pfuncinverter (gnm_float p, const gnm_float shape[],
-                        gboolean lower_tail, gboolean log_p,
-                        gnm_float xlow, gnm_float xhigh, gnm_float x0,
-                        GnmPFunc pfunc, GnmDPFunc dpfunc_dx);
-gnm_float discpfuncinverter (gnm_float p, const gnm_float shape[],
-                            gboolean lower_tail, gboolean log_p,
-                            gnm_float xlow, gnm_float xhigh, gnm_float x0,
-                            GnmPFunc pfunc);
-
-/* ------------------------------------------------------------------------- */
 /* Matrix functions. */
 
 typedef struct {
diff --git a/src/sf-dpq.c b/src/sf-dpq.c
new file mode 100644
index 0000000..b0709d3
--- /dev/null
+++ b/src/sf-dpq.c
@@ -0,0 +1,291 @@
+#include <gnumeric-config.h>
+#include <sf-dpq.h>
+
+#define R_D__0 (log_p ? gnm_ninf : 0.0)
+#define R_D__1 (log_p ? 0.0 : 1.0)
+#define R_DT_0 (lower_tail ? R_D__0 : R_D__1)
+#define R_DT_1 (lower_tail ? R_D__1 : R_D__0)
+
+/* ------------------------------------------------------------------------- */
+
+#undef DEBUG_pfuncinverter
+
+/**
+ * pfuncinverter:
+ * @p:
+ * @shape:
+ * @lower_tail:
+ * @log_p:
+ * @xlow:
+ * @xhigh:
+ * @x0:
+ * @pfunc: (scope call):
+ * @dpfunc_dx: (scope call):
+ *
+ * Returns:
+ **/
+gnm_float
+pfuncinverter (gnm_float p, const gnm_float shape[],
+              gboolean lower_tail, gboolean log_p,
+              gnm_float xlow, gnm_float xhigh, gnm_float x0,
+              GnmPFunc pfunc, GnmDPFunc dpfunc_dx)
+{
+       gboolean have_xlow = gnm_finite (xlow);
+       gboolean have_xhigh = gnm_finite (xhigh);
+       gnm_float exlow, exhigh;
+       gnm_float x = 0, e = 0, px;
+       int i;
+
+       g_return_val_if_fail (pfunc != NULL, gnm_nan);
+
+       if (log_p ? (p > 0) : (p < 0 || p > 1))
+               return gnm_nan;
+
+       if (p == R_DT_0) return xlow;
+       if (p == R_DT_1) return xhigh;
+
+       exlow = R_DT_0 - p;
+       exhigh = R_DT_1 - p;
+       if (!lower_tail) {
+               exlow = -exlow;
+               exhigh = -exhigh;
+       }
+
+#ifdef DEBUG_pfuncinverter
+       g_printerr ("p=%.15g\n", p);
+#endif
+
+       for (i = 0; i < 100; i++) {
+               if (i == 0) {
+                       if (x0 > xlow && x0 < xhigh)
+                               /* Use supplied guess.  */
+                               x = x0;
+                       else if (have_xlow && x0 <= xlow)
+                               x = xlow + have_xhigh ? (xhigh - xlow) / 100 : 1;
+                       else if (have_xhigh && x0 >= xhigh)
+                               x = xhigh - have_xlow ? (xhigh - xlow) / 100 : 1;
+                       else
+                               x = 0;  /* Whatever */
+               } else if (i == 1) {
+                       /*
+                        * Under the assumption that the initial guess was
+                        * good, pick a nearby point that is hopefully on
+                        * the other side.  If we already have both sides,
+                        * just bisect.
+                        */
+                       if (have_xlow && have_xhigh)
+                               x = (xlow + xhigh) / 2;
+                       else if (have_xlow)
+                               x = xlow * 1.1;
+                       else
+                               x = xhigh / 1.1;
+               } else if (have_xlow && have_xhigh) {
+                       switch (i % 8) {
+                       case 0:
+                               x = xhigh - (xhigh - xlow) *
+                                       (exhigh / (exhigh - exlow));
+                               break;
+                       case 4:
+                               /* Half-way in log-space.  */
+                               if (xlow >= 0 && xhigh >= 0)
+                                       x = gnm_sqrt (MAX (GNM_MIN, xlow)) * gnm_sqrt (xhigh);
+                               else if (xlow <= 0 && xhigh <= 0)
+                                       x = -gnm_sqrt (-xlow) * gnm_sqrt (MAX (GNM_MIN, -xhigh));
+                               else
+                                       x = 0;
+                               break;
+                       case 2:
+                               x = (xhigh + 1000 * xlow) / 1001;
+                               break;
+                       case 6:
+                               x = (1000 * xhigh + xlow) / 1001;
+                               break;
+                       default:
+                               x = (xhigh + xlow) / 2;
+                       }
+               } else if (have_xlow) {
+                       /* Agressively seek right in search of xhigh.  */
+                       x = (xlow < 1) ? 1 : (2 * i) * xlow;
+               } else {
+                       /* Agressively seek left in search of xlow.  */
+                       x = (xhigh > -1) ? -1 : (2 * i) * xhigh;
+               }
+
+       newton_retry:
+               if ((have_xlow && x <= xlow) || (have_xhigh && x >= xhigh))
+                       continue;
+
+               px = pfunc (x, shape, lower_tail, log_p);
+               e = px - p;
+               if (!lower_tail) e = -e;
+
+#ifdef DEBUG_pfuncinverter
+               g_printerr ("%3d:  x=%.15g  e=%.15g  l=%.15g  h=%.15g\n",
+                           i, x, e, xlow, xhigh);
+#endif
+
+               if (e == 0)
+                       goto done;
+               else if (e > 0) {
+                       xhigh = x;
+                       exhigh = e;
+                       have_xhigh = TRUE;
+               } else if (e < 0) {
+                       xlow = x;
+                       exlow = e;
+                       have_xlow = TRUE;
+               } else {
+                       /* We got a NaN.  */
+               }
+
+               if (have_xlow && have_xhigh) {
+                       gnm_float prec = (xhigh - xlow) /
+                               (gnm_abs (xlow) + gnm_abs (xhigh));
+                       if (prec < GNM_EPSILON * 4) {
+                               x = (xhigh + xlow) / 2;
+                               e = pfunc (x, shape, lower_tail, log_p) - p;
+                               if (!lower_tail) e = -e;
+                               goto done;
+                       }
+
+                       if (dpfunc_dx && i % 3 < 2 && (i == 0 || prec < 0.05)) {
+                               gnm_float d = dpfunc_dx (x, shape, log_p);
+                               if (log_p) d = gnm_exp (d - px);
+#ifdef DEBUG_pfuncinverter
+                               g_printerr ("Newton: d=%-.14g\n", d);
+#endif
+                               if (d) {
+                                       /*
+                                        * Deliberately overshoot a bit to help
+                                        * with getting good points on both
+                                        * sides of the root.
+                                        */
+                                       x = x - e / d * 1.000001;
+                                       if (x > xlow && x < xhigh) {
+#ifdef DEBUG_pfuncinverter
+                                               g_printerr ("Newton ok\n");
+#endif
+                                               i++;
+                                               goto newton_retry;
+                                       }
+                               } else {
+#ifdef DEBUG_pfuncinverter
+                                               g_printerr ("Newton d=0\n");
+#endif
+                               }
+                       }
+               }
+       }
+
+#ifdef DEBUG_pfuncinverter
+       g_printerr ("Failed to converge\n");
+#endif
+
+ done:
+       /* Make sure to keep a lucky near-hit.  */
+
+       if (have_xhigh && gnm_abs (e) > exhigh)
+               e = exhigh, x = xhigh;
+       if (have_xlow && gnm_abs (e) > -exlow)
+               e = exlow, x = xlow;
+
+#ifdef DEBUG_pfuncinverter
+       g_printerr ("--> %.15g\n\n", x);
+#endif
+       return x;
+}
+
+/**
+ * discpfuncinverter:
+ * @p:
+ * @shape:
+ * @lower_tail:
+ * @log_p:
+ * @xlow:
+ * @xhigh:
+ * @x0:
+ * @pfunc: (scope call):
+ *
+ * Discrete pfuncs only.  (Specifically: only integer x are allowed).
+ * Returns:
+ */
+gnm_float
+discpfuncinverter (gnm_float p, const gnm_float shape[],
+                  gboolean lower_tail, gboolean log_p,
+                  gnm_float xlow, gnm_float xhigh, gnm_float x0,
+                  GnmPFunc pfunc)
+{
+       gboolean have_xlow = gnm_finite (xlow);
+       gboolean have_xhigh = gnm_finite (xhigh);
+       gnm_float step;
+       int i;
+
+       if (log_p ? (p > 0) : (p < 0 || p > 1))
+               return gnm_nan;
+
+       if (p == R_DT_0) return xlow;
+       if (p == R_DT_1) return xhigh;
+
+       if (gnm_finite (x0) && x0 >= xlow && x0 <= xhigh)
+               ; /* Nothing -- guess is good.  */
+       else if (have_xlow && have_xhigh)
+               x0 = (xlow + xhigh) / 2;
+       else if (have_xhigh)
+               x0 = xhigh;
+       else if (have_xlow)
+               x0 = xlow;
+       else
+               x0 = 0;
+       x0 = gnm_floor (x0 + 0.5);
+       step = 1 + gnm_floor (gnm_abs (x0) * GNM_EPSILON);
+
+#if 0
+       g_printerr ("step=%.20g\n", step);
+#endif
+
+       for (i = 1; 1; i++) {
+               gnm_float ex0 = pfunc (x0, shape, lower_tail, log_p) - p;
+#if 0
+               g_printerr ("x=%.20g  e=%.20g\n", x0, ex0);
+#endif
+               if (!lower_tail) ex0 = -ex0;
+               if (ex0 <= 0)
+                       xlow = x0, have_xlow = TRUE;
+               if (ex0 >= 0)
+                       xhigh = x0, have_xhigh = TRUE, step = -gnm_abs (step);
+
+               if (i > 1 && have_xlow && have_xhigh) {
+                       gnm_float xmid = gnm_floor ((xlow + xhigh) / 2);
+                       if (xmid - xlow < 0.5 ||
+                           xmid - xlow < gnm_abs (xlow) * GNM_EPSILON)
+                               return xhigh;
+                       x0 = xmid;
+               } else {
+                       gnm_float x1 = x0 + step;
+
+                       if (x1 == x0) {
+                               /* Probably infinite.  */
+                               return gnm_nan;
+                       } else if (x1 >= xlow && x1 <= xhigh) {
+                               x0 = x1;
+                               step *= 2 * i;
+                       } else {
+                               /* We went off the edge by walking too fast.  */
+                               gnm_float newstep = 1 + gnm_floor (gnm_abs (x0) * GNM_EPSILON);
+                               step = (step > 0) ? newstep : -newstep;
+                               x1 = x0 + step;
+                               if (x1 >= xlow && x1 <= xhigh)
+                                       continue;
+                               /*
+                                * We don't seem to find a finite x on the
+                                * other side of the root.
+                                */
+                               return (step > 0) ? xhigh : xlow;
+                       }
+               }
+       }
+
+       g_assert_not_reached ();
+}
+
+/* ------------------------------------------------------------------------ */
diff --git a/src/sf-dpq.h b/src/sf-dpq.h
new file mode 100644
index 0000000..ce23223
--- /dev/null
+++ b/src/sf-dpq.h
@@ -0,0 +1,22 @@
+#ifndef GNM_SF_DPQ_H_
+#define GNM_SF_DPQ_H_
+
+#include <numbers.h>
+
+typedef gnm_float (*GnmPFunc) (gnm_float x, const gnm_float shape[],
+                              gboolean lower_tail, gboolean log_p);
+typedef gnm_float (*GnmDPFunc) (gnm_float x, const gnm_float shape[],
+                               gboolean log_p);
+
+gnm_float pfuncinverter (gnm_float p, const gnm_float shape[],
+                        gboolean lower_tail, gboolean log_p,
+                        gnm_float xlow, gnm_float xhigh, gnm_float x0,
+                        GnmPFunc pfunc, GnmDPFunc dpfunc_dx);
+gnm_float discpfuncinverter (gnm_float p, const gnm_float shape[],
+                            gboolean lower_tail, gboolean log_p,
+                            gnm_float xlow, gnm_float xhigh, gnm_float x0,
+                            GnmPFunc pfunc);
+
+/* ------------------------------------------------------------------------- */
+
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]