[gnumeric] LAMBERTW: New function.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] LAMBERTW: New function.
- Date: Fri, 16 Nov 2018 18:49:43 +0000 (UTC)
commit 2652e10ea76f1d92904abf9671c743d3547935bc
Author: Morten Welinder <terra gnome org>
Date: Fri Nov 16 13:47:51 2018 -0500
LAMBERTW: New function.
This computes W_0 or W_-1.
This probably isn't perfect near the branch point.
NEWS | 1 +
plugins/fn-math/functions.c | 29 ++++++++++++++
plugins/fn-math/plugin.xml.in | 1 +
src/mathfunc.c | 88 +++++++++++++++++++++++++++++++++++++++++++
src/mathfunc.h | 1 +
5 files changed, 120 insertions(+)
---
diff --git a/NEWS b/NEWS
index 807776e83..4b4f4289b 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@ Morten:
* Fix conditional style crash.
* Fix applix locale problem. [#362]
* Fix applix encoding and escape problems. [#363]
+ * New LAMBERTW function.
--------------------------------------------------------------------------
Gnumeric 1.12.43
diff --git a/plugins/fn-math/functions.c b/plugins/fn-math/functions.c
index b89626742..3b4d6f0cd 100644
--- a/plugins/fn-math/functions.c
+++ b/plugins/fn-math/functions.c
@@ -1253,6 +1253,32 @@ gnumeric_int (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
/***************************************************************************/
+static GnmFuncHelp const help_lambertw[] = {
+ { GNM_FUNC_HELP_NAME, F_("LAMBERTW:the Lambert W function")},
+ { GNM_FUNC_HELP_ARG, F_("x:number")},
+ { GNM_FUNC_HELP_ARG, F_("k:branch")},
+ { GNM_FUNC_HELP_NOTE, F_("@k defaults to 0, the principal branch.") },
+ { GNM_FUNC_HELP_NOTE, F_("@k must be either 0 or -1.") },
+ { GNM_FUNC_HELP_EXAMPLES, "=LAMBERTW(3)" },
+ { GNM_FUNC_HELP_EXAMPLES, "=LAMBERTW(3,-1)" },
+ { GNM_FUNC_HELP_SEEALSO, "EXP"},
+ { GNM_FUNC_HELP_END}
+};
+
+static GnmValue *
+gnumeric_lambertw (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
+{
+ gnm_float x = value_get_as_float (argv[0]);
+ gnm_float k = argv[1] ? value_get_as_float (argv[1]) : 0;
+
+ if (k != 0 && k != -1)
+ return value_new_error_NUM (ei->pos);
+
+ return value_new_float (gnm_lambert_w (x, (int)k));
+}
+
+/***************************************************************************/
+
static GnmFuncHelp const help_log[] = {
{ GNM_FUNC_HELP_NAME, F_("LOG:logarithm of @{x} with base @{base}")},
{ GNM_FUNC_HELP_ARG, F_("x:positive number")},
@@ -3574,6 +3600,9 @@ GnmFuncDescriptor const math_functions[] = {
gnumeric_int, NULL,
GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST,
GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
+ { "lambertw", "f|f", help_lambertw,
+ gnumeric_lambertw, NULL,
+ GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
{ "lcm", NULL, help_lcm,
NULL, gnumeric_lcm,
GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
diff --git a/plugins/fn-math/plugin.xml.in b/plugins/fn-math/plugin.xml.in
index 3778e63e1..7dfcb9e20 100644
--- a/plugins/fn-math/plugin.xml.in
+++ b/plugins/fn-math/plugin.xml.in
@@ -59,6 +59,7 @@
<function name="hypot"/>
<function name="igamma"/>
<function name="int"/>
+ <function name="lambertw"/>
<function name="lcm"/>
<function name="linsolve"/>
<function name="ln"/>
diff --git a/src/mathfunc.c b/src/mathfunc.c
index 0c4e6735b..f8546def1 100644
--- a/src/mathfunc.c
+++ b/src/mathfunc.c
@@ -5019,6 +5019,94 @@ gnm_agm (gnm_float a, gnm_float b)
return a / scale;
}
+/**
+ * gnm_lambert_w:
+ * @x: a number
+ * @k: branch, either 0 or -1
+ *
+ * Returns: The arithmetic-geometric mean of @a and @b.
+ */
+gnm_float
+gnm_lambert_w (gnm_float x, int k)
+{
+ gnm_float w;
+ static const gnm_float one_over_e = 1 / M_Egnum;
+ static const gnm_float sqrt_one_over_e = gnm_sqrt (1 / M_Egnum);
+ static const gboolean debug = FALSE;
+ gnm_float wmin, wmax;
+ int i, imax = 20;
+
+ if (gnm_isnan (x) || x < -one_over_e)
+ return gnm_nan;
+ else if (x == -one_over_e)
+ return -1;
+
+ if (k == 0) {
+ if (x == gnm_pinf)
+ return gnm_pinf;
+ if (x < 0)
+ w = 1.5 * (gnm_sqrt (x + one_over_e) - sqrt_one_over_e);
+ else if (x < 10)
+ w = gnm_sqrt (x) / 1.7;
+ else {
+ gnm_float l1 = gnm_log (x);
+ gnm_float l2 = gnm_log (l1);
+ w = l1 - l2;
+ }
+ wmin = -1;
+ wmax = gnm_pinf;
+ } else if (k == -1) {
+ if (x >= 0)
+ return (x == 0) ? gnm_ninf : gnm_nan;
+ if (x < -0.1)
+ w = -1 - 3 * gnm_sqrt (x + one_over_e);
+ else {
+ gnm_float l1 = gnm_log (-x);
+ gnm_float l2 = gnm_log (-l1);
+ w = l1 - l2;
+ }
+
+ wmin = gnm_ninf;
+ wmax = -1;
+ } else
+ return gnm_nan;
+
+ if (debug) g_printerr ("x = %.20g w=%.20g\n", x, w);
+ for (i = 0; i < imax; i++) {
+ gnm_float ew = gnm_exp (w);
+ gnm_float wew = w * ew;
+ gnm_float d1 = ew * (w + 1);
+ gnm_float d2 = ew * (w + 2);
+ gnm_float dw;
+ gnm_float wold = w;
+
+ dw = (-2 * ((wew - x) * d1) / (2 * d1 * d1 - (wew - x) * d2));
+ w += dw;
+
+ if (w <= wmin || w >= wmax) {
+ // We overshot
+ gnm_float l = (w < wmin ? wmin : wmax);
+ g_printerr (" (%2d w = %.20g)\n", i, w);
+ dw = (l - wold) * 15 / 16;
+ w = wold + dw;
+ }
+
+ if (debug) {
+ g_printerr (" %2d w = %.20g\n", i, w);
+ if (i == imax - 1) {
+ g_printerr (" wew = %.20g\n", wew);
+ g_printerr (" d1 = %.20g\n", d1);
+ g_printerr (" d2 = %.20g\n", d2);
+ }
+ }
+
+ if (gnm_abs (dw) <= 2 * GNM_EPSILON * gnm_abs (w))
+ break;
+ }
+
+ return w;
+}
+
/**
* pow1p:
diff --git a/src/mathfunc.h b/src/mathfunc.h
index 32a68ab8a..997c8ba7d 100644
--- a/src/mathfunc.h
+++ b/src/mathfunc.h
@@ -38,6 +38,7 @@ gnm_float gnm_owent (gnm_float h, gnm_float a);
gnm_float gnm_logcf (gnm_float x, gnm_float i, gnm_float d);
gnm_float expmx2h (gnm_float x);
gnm_float gnm_agm(gnm_float a, gnm_float b);
+gnm_float gnm_lambert_w(gnm_float x, int k);
/* "d": density. */
/* "p": distribution function. */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]