[gnumeric] special functions: extra gamma-related functions to sf-gamma.c



commit cc6bd2f2f5041d6774b01f0440264060c5e553ee
Author: Morten Welinder <terra gnome org>
Date:   Mon Nov 18 10:30:08 2013 -0500

    special functions: extra gamma-related functions to sf-gamma.c

 plugins/fn-derivatives/options.c |   13 +-
 plugins/fn-math/functions.c      |    1 +
 plugins/fn-r/extra.c             |    1 +
 plugins/fn-stat/functions.c      |    1 +
 src/Makefile.am                  |    2 +
 src/gnm-random.c                 |    1 +
 src/mathfunc.c                   | 1027 +-------------------------------------
 src/mathfunc.h                   |   15 +-
 src/rangefunc.c                  |    1 +
 src/sf-gamma.c                   | 1020 +++++++++++++++++++++++++++++++++++++
 src/sf-gamma.h                   |   21 +
 11 files changed, 1066 insertions(+), 1037 deletions(-)
---
diff --git a/plugins/fn-derivatives/options.c b/plugins/fn-derivatives/options.c
index 724151d..530c30b 100644
--- a/plugins/fn-derivatives/options.c
+++ b/plugins/fn-derivatives/options.c
@@ -27,16 +27,15 @@
  */
 #include <gnumeric-config.h>
 #include <gnumeric.h>
-
-#include "func.h"
-#include "mathfunc.h"
-#include "value.h"
-#include "gnm-i18n.h"
-
-#include "numbers.h"
+#include <gnm-i18n.h>
 #include <goffice/goffice.h>
 #include <gnm-plugin.h>
 
+#include <func.h>
+#include <mathfunc.h>
+#include <sf-gamma.h>
+#include <value.h>
+
 #include <math.h>
 #include <string.h>
 
diff --git a/plugins/fn-math/functions.c b/plugins/fn-math/functions.c
index 86b6d99..34fb8c3 100644
--- a/plugins/fn-math/functions.c
+++ b/plugins/fn-math/functions.c
@@ -29,6 +29,7 @@
 #include <workbook.h>
 #include <mathfunc.h>
 #include <sf-trig.h>
+#include <sf-gamma.h>
 #include <rangefunc.h>
 #include <collect.h>
 #include <value.h>
diff --git a/plugins/fn-r/extra.c b/plugins/fn-r/extra.c
index 343af6b..62eff8b 100644
--- a/plugins/fn-r/extra.c
+++ b/plugins/fn-r/extra.c
@@ -2,6 +2,7 @@
 #include "gnumeric.h"
 #include <mathfunc.h>
 #include <sf-trig.h>
+#include <sf-gamma.h>
 #include "extra.h"
 
 #define ML_ERR_return_NAN { return gnm_nan; }
diff --git a/plugins/fn-stat/functions.c b/plugins/fn-stat/functions.c
index cecc049..5c5a80e 100644
--- a/plugins/fn-stat/functions.c
+++ b/plugins/fn-stat/functions.c
@@ -25,6 +25,7 @@
 #include <gnumeric.h>
 #include <func.h>
 #include <mathfunc.h>
+#include <sf-gamma.h>
 #include <rangefunc.h>
 #include <regression.h>
 #include <sheet.h>
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c53a43..34e8680 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -153,6 +153,7 @@ libspreadsheet_la_SOURCES =                 \
        search.c                                \
        selection.c                             \
        session.c                               \
+       sf-gamma.c                              \
        sf-trig.c                               \
        sheet.c                                 \
        sheet-view.c                            \
@@ -280,6 +281,7 @@ libspreadsheet_include_HEADERS =    \
        search.h                                \
        selection.h                             \
        session.h                               \
+       sf-gamma.h                              \
        sf-trig.h                               \
        sheet.h                                 \
        sheet-view.h                            \
diff --git a/src/gnm-random.c b/src/gnm-random.c
index b651a1d..8752039 100644
--- a/src/gnm-random.c
+++ b/src/gnm-random.c
@@ -6,6 +6,7 @@
 #include "gnumeric.h"
 #include "gnm-random.h"
 #include "mathfunc.h"
+#include "sf-gamma.h"
 #include <glib/gstdio.h>
 #ifdef G_OS_WIN32
 #include <windows.h>
diff --git a/src/mathfunc.c b/src/mathfunc.c
index c59ef46..c5ef536 100644
--- a/src/mathfunc.c
+++ b/src/mathfunc.c
@@ -37,6 +37,7 @@
 #include "gnumeric.h"
 #include "mathfunc.h"
 #include "sf-trig.h"
+#include "sf-gamma.h"
 #include <glib/gi18n-lib.h>
 
 #include <math.h>
@@ -53,14 +54,12 @@
 #define IEEE_754
 #endif
 
-#define M_LN_SQRT_2PI   GNM_const(0.918938533204672741780329736406)  /* log(sqrt(2*pi)) */
 #define M_SQRT_32       GNM_const(5.656854249492380195206754896838)  /* sqrt(32) */
 #define M_1_SQRT_2PI    GNM_const(0.398942280401432677939946059934)  /* 1/sqrt(2pi) */
 #define M_SQRT_2dPI     GNM_const(0.797884560802865355879892119869)  /* sqrt(2/pi) */
 #define M_2PIgnum       (2 * M_PIgnum)
 
-#define ML_UNDERFLOW (GNM_EPSILON * GNM_EPSILON)
-#define ML_ERROR(cause) /* Nothing */
+#define ML_ERROR(cause) do { } while(0)
 #define MATHLIB_ERROR(_a,_b) return gnm_nan;
 #define MATHLIB_WARNING g_warning
 #define MATHLIB_WARNING2 g_warning
@@ -79,10 +78,6 @@ static gnm_float bessel_y_ex(gnm_float x, gnm_float alpha, gnm_float *by);
 static gnm_float bessel_k(gnm_float x, gnm_float alpha, gnm_float expo);
 static gnm_float bessel_y(gnm_float x, gnm_float alpha);
 
-static void pochhammer_small_n (gnm_float x, gnm_float n, GnmQuad *r);
-
-static void gamma_error_factor (GnmQuad *res, const GnmQuad *x);
-
 /* MW ---------------------------------------------------------------------- */
 
 void
@@ -824,120 +819,6 @@ gnm_float ppois(gnm_float x, gnm_float lambda, gboolean lower_tail, gboolean log
 }
 
 /* ------------------------------------------------------------------------ */
-/* Imported src/nmath/stirlerr.c from R.  */
-/*
- *  AUTHOR
- *    Catherine Loader, catherine research bell-labs com 
- *    October 23, 2000.
- *
- *  Merge in to R:
- *     Copyright (C) 2000, The R Core Development Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
- *  USA.
- *
- *
- *  DESCRIPTION
- *
- *    Computes the log of the error term in Stirling's formula.
- *      For n > 15, uses the series 1/12n - 1/360n^3 + ...
- *      For n <=15, integers or half-integers, uses stored values.
- *      For other n < 15, uses lgamma directly (don't use this to
- *        write lgamma!)
- *
- * Merge in to R:
- * Copyright (C) 2000, The R Core Development Team
- * R has lgammafn, and lgamma is not part of ISO C
- */
-
-
-/* stirlerr(n) = gnm_log(n!) - gnm_log( gnm_sqrt(2*pi*n)*(n/e)^n )
- *             = gnm_log Gamma(n+1) - 1/2 * [gnm_log(2*pi) + gnm_log(n)] - n*[gnm_log(n) - 1]
- *             = gnm_log Gamma(n+1) - (n + 1/2) * gnm_log(n) + n - gnm_log(2*pi)/2
- *
- * see also lgammacor() in ./lgammacor.c  which computes almost the same!
- */
-
-gnm_float stirlerr(gnm_float n)
-{
-
-#define S0 GNM_const(0.083333333333333333333)       /* 1/12 */
-#define S1 GNM_const(0.00277777777777777777778)     /* 1/360 */
-#define S2 GNM_const(0.00079365079365079365079365)  /* 1/1260 */
-#define S3 GNM_const(0.000595238095238095238095238) /* 1/1680 */
-#define S4 GNM_const(0.0008417508417508417508417508)/* 1/1188 */
-
-/*
-  error for 0, 0.5, 1.0, 1.5, ..., 14.5, 15.0.
-*/
-    static const gnm_float sferr_halves[31] = {
-       0.0, /* n=0 - wrong, place holder only */
-       GNM_const(0.1534264097200273452913848),  /* 0.5 */
-       GNM_const(0.0810614667953272582196702),  /* 1.0 */
-       GNM_const(0.0548141210519176538961390),  /* 1.5 */
-       GNM_const(0.0413406959554092940938221),  /* 2.0 */
-       GNM_const(0.03316287351993628748511048), /* 2.5 */
-       GNM_const(0.02767792568499833914878929), /* 3.0 */
-       GNM_const(0.02374616365629749597132920), /* 3.5 */
-       GNM_const(0.02079067210376509311152277), /* 4.0 */
-       GNM_const(0.01848845053267318523077934), /* 4.5 */
-       GNM_const(0.01664469118982119216319487), /* 5.0 */
-       GNM_const(0.01513497322191737887351255), /* 5.5 */
-       GNM_const(0.01387612882307074799874573), /* 6.0 */
-       GNM_const(0.01281046524292022692424986), /* 6.5 */
-       GNM_const(0.01189670994589177009505572), /* 7.0 */
-       GNM_const(0.01110455975820691732662991), /* 7.5 */
-       GNM_const(0.010411265261972096497478567), /* 8.0 */
-       GNM_const(0.009799416126158803298389475), /* 8.5 */
-       GNM_const(0.009255462182712732917728637), /* 9.0 */
-       GNM_const(0.008768700134139385462952823), /* 9.5 */
-       GNM_const(0.008330563433362871256469318), /* 10.0 */
-       GNM_const(0.007934114564314020547248100), /* 10.5 */
-       GNM_const(0.007573675487951840794972024), /* 11.0 */
-       GNM_const(0.007244554301320383179543912), /* 11.5 */
-       GNM_const(0.006942840107209529865664152), /* 12.0 */
-       GNM_const(0.006665247032707682442354394), /* 12.5 */
-       GNM_const(0.006408994188004207068439631), /* 13.0 */
-       GNM_const(0.006171712263039457647532867), /* 13.5 */
-       GNM_const(0.005951370112758847735624416), /* 14.0 */
-       GNM_const(0.005746216513010115682023589), /* 14.5 */
-       GNM_const(0.005554733551962801371038690)  /* 15.0 */
-    };
-    gnm_float nn;
-
-    if (n <= 15.0) {
-       nn = n + n;
-       if (nn == (int)nn) return(sferr_halves[(int)nn]);
-       return(lgamma1p (n ) - (n + 0.5)*gnm_log(n) + n - M_LN_SQRT_2PI);
-    }
-
-    nn = n*n;
-    if (n>500) return((S0-S1/nn)/n);
-    if (n> 80) return((S0-(S1-S2/nn)/nn)/n);
-    if (n> 35) return((S0-(S1-(S2-S3/nn)/nn)/nn)/n);
-    /* 15 < n <= 35 : */
-    return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n);
-}
-/* Cleaning up done by tools/import-R:  */
-#undef S0
-#undef S1
-#undef S2
-#undef S3
-#undef S4
-
-/* ------------------------------------------------------------------------ */
 /* Imported src/nmath/bd0.c from R.  */
 /*
  *  AUTHOR
@@ -1204,8 +1085,8 @@ static const gnm_float M_cutoff = M_LN2gnum * GNM_MAX_EXP / GNM_EPSILON;/*=GNM_c
  *
  * auxilary in log1pmx() and lgamma1p()
  */
-static gnm_float
-logcf (gnm_float x, gnm_float i, gnm_float d)
+gnm_float
+gnm_logcf (gnm_float x, gnm_float i, gnm_float d)
 {
     gnm_float c1 = 2 * d;
     gnm_float c2 = i + d;
@@ -1267,79 +1148,11 @@ gnm_float log1pmx (gnm_float x)
            return term * ((((two / 9 * y + two / 7) * y + two / 5) * y +
                            two / 3) * y - x);
        else
-           return term * (2 * y * logcf (y, 3, 2) - x);
+           return term * (2 * y * gnm_logcf (y, 3, 2) - x);
     }
 }
 
 
-/* Compute  gnm_log(gamma(a+1))  accurately also for small a (0 < a < 0.5). */
-gnm_float lgamma1p (gnm_float a)
-{
-    const gnm_float eulers_const =      GNM_const(0.5772156649015328606065120900824024);
-
-    /* coeffs[i] holds (zeta(i+2)-1)/(i+2) , i = 1:N, N = 40 : */
-    const int N = 40;
-    static const gnm_float coeffs[40] = {
-       GNM_const(0.3224670334241132182362075833230126e-0),
-       GNM_const(0.6735230105319809513324605383715000e-1),
-       GNM_const(0.2058080842778454787900092413529198e-1),
-       GNM_const(0.7385551028673985266273097291406834e-2),
-       GNM_const(0.2890510330741523285752988298486755e-2),
-       GNM_const(0.1192753911703260977113935692828109e-2),
-       GNM_const(0.5096695247430424223356548135815582e-3),
-       GNM_const(0.2231547584535793797614188036013401e-3),
-       GNM_const(0.9945751278180853371459589003190170e-4),
-       GNM_const(0.4492623673813314170020750240635786e-4),
-       GNM_const(0.2050721277567069155316650397830591e-4),
-       GNM_const(0.9439488275268395903987425104415055e-5),
-       GNM_const(0.4374866789907487804181793223952411e-5),
-       GNM_const(0.2039215753801366236781900709670839e-5),
-       GNM_const(0.9551412130407419832857179772951265e-6),
-       GNM_const(0.4492469198764566043294290331193655e-6),
-       GNM_const(0.2120718480555466586923135901077628e-6),
-       GNM_const(0.1004322482396809960872083050053344e-6),
-       GNM_const(0.4769810169363980565760193417246730e-7),
-       GNM_const(0.2271109460894316491031998116062124e-7),
-       GNM_const(0.1083865921489695409107491757968159e-7),
-       GNM_const(0.5183475041970046655121248647057669e-8),
-       GNM_const(0.2483674543802478317185008663991718e-8),
-       GNM_const(0.1192140140586091207442548202774640e-8),
-       GNM_const(0.5731367241678862013330194857961011e-9),
-       GNM_const(0.2759522885124233145178149692816341e-9),
-       GNM_const(0.1330476437424448948149715720858008e-9),
-       GNM_const(0.6422964563838100022082448087644648e-10),
-       GNM_const(0.3104424774732227276239215783404066e-10),
-       GNM_const(0.1502138408075414217093301048780668e-10),
-       GNM_const(0.7275974480239079662504549924814047e-11),
-       GNM_const(0.3527742476575915083615072228655483e-11),
-       GNM_const(0.1711991790559617908601084114443031e-11),
-       GNM_const(0.8315385841420284819798357793954418e-12),
-       GNM_const(0.4042200525289440065536008957032895e-12),
-       GNM_const(0.1966475631096616490411045679010286e-12),
-       GNM_const(0.9573630387838555763782200936508615e-13),
-       GNM_const(0.4664076026428374224576492565974577e-13),
-       GNM_const(0.2273736960065972320633279596737272e-13),
-       GNM_const(0.1109139947083452201658320007192334e-13)
-    };
-
-    const gnm_float c = GNM_const(0.2273736845824652515226821577978691e-12);/* zeta(N+2)-1 */
-    gnm_float lgam;
-    int i;
-
-    if (gnm_abs (a) >= 0.5)
-       return gnm_lgamma (a + 1);
-
-    /* Abramowitz & Stegun 6.1.33,
-     * also  http://functions.wolfram.com/06.11.06.0008.01 */
-    lgam = c * logcf (-a / 2, N + 2, 1);
-    for (i = N - 1; i >= 0; i--)
-       lgam = coeffs[i] - a * lgam;
-
-    return (a * lgam - eulers_const) * a - log1pmx (a);
-} /* lgamma1p */
-
-
-
 /*
  * Compute the log of a sum from logs of terms, i.e.,
  *
@@ -1770,10 +1583,10 @@ gnm_float pgamma(gnm_float x, gnm_float alph, gnm_float scale, gboolean lower_ta
  * type naming, this is what I have been using for Gnumeric 1.4.1.
 
  * This could be included into R as-is, but you might want to benefit from
- * making logcf, log1pmx, lgamma1p, and possibly logspace_add/logspace_sub
+ * making gnm_logcf, log1pmx, lgamma1p, and possibly logspace_add/logspace_sub
  * available to other parts of R.
 
- * MM: I've not (yet?) taken  logcf(), but the other four
+ * MM: I've not (yet?) taken  gnm_logcf(), but the other four
  */
 
 
@@ -1933,266 +1746,6 @@ gnm_float pgamma(gnm_float x, gnm_float alph, gnm_float scale, gboolean lower_ta
 #undef max_it
 
 /* ------------------------------------------------------------------------ */
-/* Imported src/nmath/chebyshev.c from R.  */
-/*
- *  Mathlib : A C Library of Special Functions
- *  Copyright (C) 1998 Ross Ihaka
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- *  02110-1301 USA.
- *
- *  SYNOPSIS
- *
- *    int chebyshev_init(double *dos, int nos, double eta)
- *    double chebyshev_eval(double x, double *a, int n)
- *
- *  DESCRIPTION
- *
- *    "chebyshev_init" determines the number of terms for the
- *    double precision orthogonal series "dos" needed to insure
- *    the error is no larger than "eta".  Ordinarily eta will be
- *    chosen to be one-tenth machine precision.
- *
- *    "chebyshev_eval" evaluates the n-term Chebyshev series
- *    "a" at "x".
- *
- *  NOTES
- *
- *    These routines are translations into C of Fortran routines
- *    by W. Fullerton of Los Alamos Scientific Laboratory.
- *
- *    Based on the Fortran routine dcsevl by W. Fullerton.
- *    Adapted from R. Broucke, Algorithm 446, CACM., 16, 254 (1973).
- */
-
-
-/* NaNs propagated correctly */
-
-
-/* Definition of function chebyshev_init removed.  */
-
-
-static gnm_float chebyshev_eval(gnm_float x, const gnm_float *a, const int n)
-{
-    gnm_float b0, b1, b2, twox;
-    int i;
-
-    if (n < 1 || n > 1000) ML_ERR_return_NAN;
-
-    if (x < -1.1 || x > 1.1) ML_ERR_return_NAN;
-
-    twox = x * 2;
-    b2 = b1 = 0;
-    b0 = 0;
-    for (i = 1; i <= n; i++) {
-       b2 = b1;
-       b1 = b0;
-       b0 = twox * b1 - b2 + a[n - i];
-    }
-    return (b0 - b2) * 0.5;
-}
-
-/* ------------------------------------------------------------------------ */
-/* Imported src/nmath/lgammacor.c from R.  */
-/*
- *  Mathlib : A C Library of Special Functions
- *  Copyright (C) 1998 Ross Ihaka
- *  Copyright (C) 2000-2001 The R Development Core Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
- *  USA.
- *
- *  SYNOPSIS
- *
- *    #include <Rmath.h>
- *    double lgammacor(double x);
- *
- *  DESCRIPTION
- *
- *    Compute the log gamma correction factor for x >= 10 so that
- *
- *    log(gamma(x)) = .5*log(2*pi) + (x-.5)*log(x) -x + lgammacor(x)
- *
- *    [ lgammacor(x) is called Del(x)  in other contexts (e.g. dcdflib)]
- *
- *  NOTES
- *
- *    This routine is a translation into C of a Fortran subroutine
- *    written by W. Fullerton of Los Alamos Scientific Laboratory.
- *
- *  SEE ALSO
- *
- *    Loader(1999)'s stirlerr() {in ./stirlerr.c} is *very* similar in spirit,
- *    is faster and cleaner, but is only defined "fast" for half integers.
- */
-
-
-static gnm_float lgammacor(gnm_float x)
-{
-    static const gnm_float algmcs[15] = {
-       GNM_const(+.1666389480451863247205729650822e+0),
-       GNM_const(-.1384948176067563840732986059135e-4),
-       GNM_const(+.9810825646924729426157171547487e-8),
-       GNM_const(-.1809129475572494194263306266719e-10),
-       GNM_const(+.6221098041892605227126015543416e-13),
-       GNM_const(-.3399615005417721944303330599666e-15),
-       GNM_const(+.2683181998482698748957538846666e-17),
-       GNM_const(-.2868042435334643284144622399999e-19),
-       GNM_const(+.3962837061046434803679306666666e-21),
-       GNM_const(-.6831888753985766870111999999999e-23),
-       GNM_const(+.1429227355942498147573333333333e-24),
-       GNM_const(-.3547598158101070547199999999999e-26),
-       GNM_const(+.1025680058010470912000000000000e-27),
-       GNM_const(-.3401102254316748799999999999999e-29),
-       GNM_const(+.1276642195630062933333333333333e-30)
-    };
-
-    gnm_float tmp;
-
-#ifdef NOMORE_FOR_THREADS
-    static int nalgm = 0;
-    static gnm_float xbig = 0, xmax = 0;
-
-    /* Initialize machine dependent constants, the first time gamma() is called.
-       FIXME for threads ! */
-    if (nalgm == 0) {
-       /* For IEEE gnm_float precision : nalgm = 5 */
-       nalgm = chebyshev_init(algmcs, 15, GNM_EPSILON/2);/*was d1mach(3)*/
-       xbig = 1 / gnm_sqrt(GNM_EPSILON/2); /* ~ 94906265.6 for IEEE gnm_float */
-       xmax = gnm_exp(fmin2(gnm_log(GNM_MAX / 12), -gnm_log(12 * GNM_MIN)));
-       /*   = GNM_MAX / 48 ~= 3.745e306 for IEEE gnm_float */
-    }
-#else
-/* For IEEE gnm_float precision GNM_EPSILON = 2^-52 = GNM_const(2.220446049250313e-16) :
- *   xbig = 2 ^ 26.5
- *   xmax = GNM_MAX / 48 =  2^1020 / 3 */
-# define nalgm 5
-# define xbig  GNM_const(94906265.62425156)
-# define xmax  GNM_const(3.745194030963158e306)
-#endif
-
-    if (x < 10)
-       ML_ERR_return_NAN
-    else if (x >= xmax) {
-       ML_ERROR(ME_UNDERFLOW);
-       return ML_UNDERFLOW;
-    }
-    else if (x < xbig) {
-       tmp = 10 / x;
-       return chebyshev_eval(tmp * tmp * 2 - 1, algmcs, nalgm) / x;
-    }
-    else return 1 / (x * 12);
-}
-/* Cleaning up done by tools/import-R:  */
-#undef nalgm
-#undef xbig
-#undef xmax
-
-/* ------------------------------------------------------------------------ */
-/* Imported src/nmath/lbeta.c from R.  */
-/*
- *  Mathlib : A C Library of Special Functions
- *  Copyright (C) 1998 Ross Ihaka
- *  Copyright (C) 2000 The R Development Core Team
- *  Copyright (C) 2003 The R Foundation
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
- *  USA.
- *
- *  SYNOPSIS
- *
- *    #include <Rmath.h>
- *    double lbeta(double a, double b);
- *
- *  DESCRIPTION
- *
- *    This function returns the value of the log beta function.
- *
- *  NOTES
- *
- *    This routine is a translation into C of a Fortran subroutine
- *    by W. Fullerton of Los Alamos Scientific Laboratory.
- */
-
-
-static gnm_float lbeta(gnm_float a, gnm_float b)
-{
-    gnm_float corr, p, q;
-
-    p = q = a;
-    if(b < p) p = b;/* := min(a,b) */
-    if(b > q) q = b;/* := max(a,b) */
-
-#ifdef IEEE_754
-    if(gnm_isnan(a) || gnm_isnan(b))
-       return a + b;
-#endif
-
-    /* both arguments must be >= 0 */
-
-    if (p < 0)
-       ML_ERR_return_NAN
-    else if (p == 0) {
-       return gnm_pinf;
-    }
-    else if (!gnm_finite(q)) {
-       return gnm_ninf;
-    }
-
-    if (p >= 10) {
-       /* p and q are big. */
-       corr = lgammacor(p) + lgammacor(q) - lgammacor(p + q);
-       return gnm_log(q) * -0.5 + M_LN_SQRT_2PI + corr
-               + (p - 0.5) * gnm_log(p / (p + q)) + q * gnm_log1p(-p / (p + q));
-    }
-    else if (q >= 10) {
-       /* p is small, but q is big. */
-       corr = lgammacor(q) - lgammacor(p + q);
-       return gnm_lgamma(p) + corr + p - p * gnm_log(p + q)
-               + (q - 0.5) * gnm_log1p(-p / (p + q));
-    }
-    else
-       /* p and q are small: p <= q < 10. */
-       return gnm_lgamma (p) + gnm_lgamma (q) - gnm_lgamma (p + q);
-}
-
-/* ------------------------------------------------------------------------ */
 /* Imported src/nmath/dt.c from R.  */
 /*
  *  AUTHOR
@@ -6704,7 +6257,7 @@ logfbitdif (gnm_float x)
 {
        gnm_float y = 1 / (2 * x + 3);
        gnm_float y2 = y * y;
-       return y2 * logcf (y2, 3, 2);
+       return y2 * gnm_logcf (y2, 3, 2);
 }
 
 /*
@@ -6725,7 +6278,7 @@ static const gnm_float lfbc8 = GNM_const (3.5068606896459316479e-01);
 static const gnm_float lfbc9 = GNM_const (1.6769998201671114808);
 
 /* This is also stirlerr(x+1).  */
-gnm_float
+static gnm_float
 logfbit (gnm_float x)
 {
        /*
@@ -7814,7 +7367,7 @@ qbeta (gnm_float p, gnm_float pin, gnm_float qin, gboolean lower_tail, gboolean
                 * The density function is U-shaped.
                 */
                gnm_float phalf = pbeta (0.5, pin, qin, lower_tail, log_p);
-               gnm_float lb = lbeta (pin, qin);
+               gnm_float lb = gnm_lbeta (pin, qin);
 
                if (!lower_tail == (p > phalf)) {
                        /*
@@ -7933,325 +7486,6 @@ pbinom2 (gnm_float x0, gnm_float x1, gnm_float n, gnm_float p)
 
 /* ------------------------------------------------------------------------- */
 
-gnm_float
-combin (gnm_float n, gnm_float k)
-{
-       GnmQuad m1, m2, m3;
-       int e1, e2, e3;
-       gboolean ok;
-
-       if (k < 0 || k > n || n != gnm_floor (n) || k != gnm_floor (k))
-               return gnm_nan;
-
-       k = MIN (k, n - k);
-       if (k == 0)
-               return 1;
-       if (k == 1)
-               return n;
-
-       ok = (n < G_MAXINT &&
-             !qfactf (n, &m1, &e1) &&
-             !qfactf (k, &m2, &e2) &&
-             !qfactf (n - k, &m3, &e3));
-
-       if (ok) {
-               void *state = gnm_quad_start ();
-               gnm_float c;
-               gnm_quad_mul (&m2, &m2, &m3);
-               gnm_quad_div (&m1, &m1, &m2);
-               c = gnm_ldexp (gnm_quad_value (&m1), e1 - e2 - e3);
-               gnm_quad_end (state);
-               return c;
-       }
-
-       if (k < 30) {
-               void *state = gnm_quad_start ();
-               GnmQuad p, a, b;
-               gnm_float c;
-               int i;
-
-               gnm_quad_init (&p, 1);
-               for (i = 0; i < k; i++) {
-                       gnm_quad_init (&a, n - i);
-                       gnm_quad_mul (&p, &p, &a);
-
-                       gnm_quad_init (&b, i + 1);
-                       gnm_quad_div (&p, &p, &b);
-               }
-
-               c = gnm_quad_value (&p);
-               gnm_quad_end (state);
-               return c;
-       }
-
-       {
-               gnm_float lp = pochhammer (n - k + 1, k, TRUE) -
-                       gnm_lgamma (k + 1);
-               return gnm_floor (0.5 + gnm_exp (lp));
-       }
-}
-
-gnm_float
-permut (gnm_float n, gnm_float k)
-{
-       if (k < 0 || k > n || n != gnm_floor (n) || k != gnm_floor (k))
-               return gnm_nan;
-
-       return pochhammer (n - k + 1, k, FALSE);
-}
-
-static void
-rescale_mant_exp (GnmQuad *mant, int *exp2)
-{
-       GnmQuad s;
-       int e;
-
-       (void)gnm_frexp (gnm_quad_value (mant), &e);
-       *exp2 += e;
-       gnm_quad_init (&s, gnm_ldexp (1.0, -e));
-       gnm_quad_mul (mant, mant, &s);
-}
-
-/* Tabulate up to, but not including, this number.  */
-#define QFACTI_LIMIT 10000
-
-static gboolean
-qfacti (int n, GnmQuad *mant, int *exp2)
-{
-       static GnmQuad mants[QFACTI_LIMIT];
-       static int exp2s[QFACTI_LIMIT];
-       static int init = 0;
-
-       if (n < 0 || n >= QFACTI_LIMIT) {
-               *mant = gnm_quad_zero;
-               *exp2 = 0;
-               return TRUE;
-       }
-
-       if (n >= init) {
-               void *state = gnm_quad_start ();
-
-               if (init == 0) {
-                       gnm_quad_init (&mants[0], 0.5);
-                       exp2s[0] = 1;
-                       init++;
-               }
-
-               while (n >= init) {
-                       GnmQuad m;
-
-                       gnm_quad_init (&m, init);
-                       gnm_quad_mul (&mants[init], &m, &mants[init - 1]);
-                       exp2s[init] = exp2s[init - 1];
-                       rescale_mant_exp (&mants[init], &exp2s[init]);
-
-                       init++;
-               }
-
-               gnm_quad_end (state);
-       }
-
-       *mant = mants[n];
-       *exp2 = exp2s[n];
-       return FALSE;
-}
-
-/* 0: ok, 1: overflow, 2: nan */
-int
-qfactf (gnm_float x, GnmQuad *mant, int *exp2)
-{
-       void *state;
-       gboolean res = 0;
-
-       if (gnm_isnan (x))
-               return 2;
-
-       if (x >= G_MAXINT / 2)
-               return 1;
-
-       if (x == gnm_floor (x)) {
-               /* Integer or infinite.  */
-               if (x < 0)
-                       return 2;
-
-               if (!qfacti ((int)x, mant, exp2))
-                       return 0;
-       }
-
-       state = gnm_quad_start ();
-
-       if (x < -1) {
-               if (qfactf (-x - 1, mant, exp2))
-                       res = 1;
-               else {
-                       GnmQuad b;
-
-                       gnm_quad_init (&b, -gnm_sinpi (x)); /* ? */
-                       gnm_quad_mul (&b, &b, mant);
-                       gnm_quad_div (mant, &gnm_quad_pi, &b);
-                       *exp2 = -*exp2;
-               }
-       } else if (x >= QFACTI_LIMIT - 0.5) {
-               /*
-                * Let y = x + 1 = m * 2^e; c = sqrt(2Pi).
-                *
-                * G(y) = c * y^(y-1/2) * exp(-y) * E(y)
-                *      = c * (y/e)^y / sqrt(y) * E(y)
-                */
-               GnmQuad y, f1, f2, f3, f4;
-               gnm_float ef2;
-               gboolean debug = FALSE;
-
-               if (debug) g_printerr ("x=%.20g\n", x);
-
-               gnm_quad_init (&y, x + 1);
-               *exp2 = 0;
-
-               /* sqrt(2Pi) */
-               gnm_quad_add (&f1, &gnm_quad_pi, &gnm_quad_pi);
-               gnm_quad_sqrt (&f1, &f1);
-               if (debug) g_printerr ("f1=%.20g\n", gnm_quad_value (&f1));
-
-               /* (y/e)^y */
-               gnm_quad_div (&f2, &y, &gnm_quad_e);
-               gnm_quad_pow (&f2, &ef2, &f2, &y);
-               if (ef2 > G_MAXINT || ef2 < G_MININT)
-                       res = 1;
-               else
-                       *exp2 += (int)ef2;
-               if (debug) g_printerr ("f2=%.20g\n", gnm_quad_value (&f2));
-
-               /* sqrt(y) */
-               gnm_quad_sqrt (&f3, &y);
-               if (debug) g_printerr ("f3=%.20g\n", gnm_quad_value (&f3));
-
-               /* E(x) */
-               gamma_error_factor (&f4, &y);
-               if (debug) g_printerr ("f4=%.20g\n", gnm_quad_value (&f4));
-
-               gnm_quad_mul (mant, &f1, &f2);
-               gnm_quad_div (mant, mant, &f3);
-               gnm_quad_mul (mant, mant, &f4);
-
-               if (debug) g_printerr ("G(x+1)=%.20g * 2^%d %s\n", gnm_quad_value (mant), *exp2, res ? 
"overflow" : "");
-       } else {
-               GnmQuad s, mFw;
-               gnm_float w, f;
-               int eFw;
-
-               /*
-                * w integer, |f|<=0.5, x=w+f.
-                *
-                * Do this before we do the stepping below which would kill
-                * up to 4 bits of accuracy of f.
-                */
-               w = gnm_floor (x + 0.5);
-               f = x - w;
-
-               gnm_quad_init (&s, 1);
-               while (x < 20) {
-                       GnmQuad a;
-                       x++;
-                       w++;
-                       gnm_quad_init (&a, x);
-                       gnm_quad_mul (&s, &s, &a);
-               }
-
-               if (qfacti ((int)w, &mFw, &eFw)) {
-                       res = 1;
-               } else {
-                       GnmQuad r;
-
-                       pochhammer_small_n (w + 1, f, &r);
-                       gnm_quad_mul (mant, &mFw, &r);
-                       gnm_quad_div (mant, mant, &s);
-                       *exp2 = eFw;
-               }
-       }
-
-       if (res == 0)
-               rescale_mant_exp (mant, exp2);
-
-       gnm_quad_end (state);
-       return res;
-}
-
-/**
- * gnm_fact:
- * @x: number
- *
- * Returns: the factorial of @x, which must not be a negative integer.
- */
-gnm_float
-gnm_fact (gnm_float x)
-{
-       GnmQuad r;
-       int e;
-
-       switch (qfactf (x, &r, &e)) {
-       case 0: return ldexp (gnm_quad_value (&r), e);
-       case 1: return gnm_pinf;
-       default: return gnm_nan;
-       }
-}
-
-/**
- * beta:
- * @a: a number
- * @b: a number
- *
- * Returns: the Beta function evaluated at @a and @b.
- */
-gnm_float
-beta (gnm_float a, gnm_float b)
-{
-       int sign;
-       gnm_float absres = gnm_exp (lbeta3 (a, b, &sign));
-
-       return sign == -1 ? -absres : absres;
-}
-
-/**
- * lbeta3:
- * @a: a number
- * @b: a number
- * @sign: (out): the sign
- *
- * Returns: the logarithm of the absolute value of the Beta function
- * evaluated at @a and @b.  The sign will be stored in @sign as -1 or
- * +1.  This function is useful because the result of the beta
- * function can be too large for doubles.
- */
-gnm_float
-lbeta3 (gnm_float a, gnm_float b, int *sign)
-{
-       int sign_a, sign_b, sign_ab;
-       gnm_float ab = a + b;
-       gnm_float res_a, res_b, res_ab;
-
-       *sign = 1;
-       if (a > 0 && b > 0)
-               return lbeta (a, b);
-
-#ifdef IEEE_754
-       if (gnm_isnan(ab))
-               return ab;
-#endif
-
-       if ((a <= 0 && a == gnm_floor (a)) ||
-           (b <= 0 && b == gnm_floor (b)) ||
-           (ab <= 0 && ab == gnm_floor (ab)))
-               return gnm_nan;
-
-       res_a = gnm_lgamma_r (a, &sign_a);
-       res_b = gnm_lgamma_r (b, &sign_b);
-       res_ab = gnm_lgamma_r (ab, &sign_ab);
-
-       *sign = sign_a * sign_b * sign_ab;
-       return res_a + res_b - res_ab;
-}
-
-
 /**
  * pow1p:
  * @x: a number
@@ -8939,247 +8173,6 @@ gnm_owent (gnm_float h, gnm_float a)
 
 /* ------------------------------------------------------------------------- */
 
-static void
-gamma_error_factor (GnmQuad *res, const GnmQuad *x)
-{
-       gnm_float num[] = {
-               1.,
-               1.,
-               -139.,
-               -571.,
-               163879.,
-               5246819.,
-               -534703531.,
-               -4483131259.
-       };
-       gnm_float den[] = {
-               12.,
-               288.,
-               51840.,
-               2488320.,
-               209018880.,
-               75246796800.,
-               902961561600.,
-               86684309913600.
-       };
-       GnmQuad zn;
-       int i;
-
-       gnm_quad_init (&zn, 1);
-       *res = zn;
-
-       for (i = 0; i < (int)G_N_ELEMENTS (num); i++) {
-               GnmQuad t, c;
-               gnm_quad_mul (&zn, &zn, x);
-               gnm_quad_init (&c, den[i]);
-               gnm_quad_mul (&t, &zn, &c);
-               gnm_quad_init (&c, num[i]);
-               gnm_quad_div (&t, &c, &t);
-               gnm_quad_add (res, res, &t);
-       }
-}
-
-static void
-pochhammer_small_n (gnm_float x, gnm_float n, GnmQuad *res)
-{
-       GnmQuad qx, qn, qr, qs, qone, f1, f2, f3, f4, f5;
-       gnm_float r;
-       gboolean debug = FALSE;
-
-       g_return_if_fail (x >= 20);
-       g_return_if_fail (gnm_abs (n) <= 1);
-
-       /*
-        * G(x)   = c * x^(x-1/2) * exp(-x) * E(x)
-        * G(x+n) = c * (x+n)^(x+n-1/2) * exp(-(x+n)) * E(x+n)
-        *        = c * (x+n)^(x-1/2) * (x+n)^n * exp(-x) * exp(-n) * E(x+n)
-        *
-        * G(x+n)/G(x)
-        * = (1+n/x)^(x-1/2) * (x+n)^n * exp(-n) * E(x+n)/E(x)
-        * = (1+n/x)^x / sqrt(1+n/x) * (x+n)^n * exp(-n) * E(x+n)/E(x)
-        * = exp(x*log(1+n/x) - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
-        * = exp(x*log1p(n/x) - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
-        * = exp(x*(log1pmx(n/x)+n/x) - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
-        * = exp(x*log1pmx(n/x) + n - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
-        * = exp(x*log1pmx(n/x)) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
-        */
-
-       gnm_quad_init (&qx, x);
-       gnm_quad_init (&qn, n);
-
-       gnm_quad_div (&qr, &qn, &qx);
-       r = gnm_quad_value (&qr);
-
-       gnm_quad_add (&qs, &qx, &qn);
-
-       gnm_quad_init (&qone, 1);
-
-       /* exp(x*log1pmx(n/x)) */
-       gnm_quad_mul12 (&f1, log1pmx (r), x);  /* sub-opt */
-       gnm_quad_exp (&f1, NULL, &f1);
-       if (debug) g_printerr ("f1=%.20g\n", gnm_quad_value (&f1));
-
-       /* sqrt(1+n/x) */
-       gnm_quad_add (&f2, &qone, &qr);
-       gnm_quad_sqrt (&f2, &f2);
-       if (debug) g_printerr ("f2=%.20g\n", gnm_quad_value (&f2));
-
-       /* (x+n)^n */
-       gnm_quad_pow (&f3, NULL, &qs, &qn);
-       if (debug) g_printerr ("f3=%.20g\n", gnm_quad_value (&f3));
-
-       /* E(x+n) */
-       gamma_error_factor (&f4, &qs);
-       if (debug) g_printerr ("f4=%.20g\n", gnm_quad_value (&f4));
-
-       /* E(x) */
-       gamma_error_factor (&f5, &qx);
-       if (debug) g_printerr ("f5=%.20g\n", gnm_quad_value (&f5));
-
-       gnm_quad_div (res, &f1, &f2);
-       gnm_quad_mul (res, res, &f3);
-       gnm_quad_mul (res, res, &f4);
-       gnm_quad_div (res, res, &f5);
-}
-
-static gnm_float
-pochhammer_naive (gnm_float x, int n)
-{
-       void *state = gnm_quad_start ();
-       GnmQuad qp, qx, qone;
-       gnm_float r;
-
-       gnm_quad_init (&qone, 1);
-       qp = qone;
-       gnm_quad_init (&qx, x);
-       while (n-- > 0) {
-               gnm_quad_mul (&qp, &qp, &qx);
-               gnm_quad_add (&qx, &qx, &qone);
-       }
-       r = gnm_quad_value (&qp);
-       gnm_quad_end (state);
-       return r;
-}
-
-
-
-/*
- * Pochhammer's symbol: (x)_n = Gamma(x+n)/Gamma(x).
- *
- * While n is often an integer, that is not a requirement.
- */
-
-gnm_float
-pochhammer (gnm_float x, gnm_float n, gboolean give_log)
-{
-       gnm_float rn, rx, lr;
-       GnmQuad m1, m2;
-       int e1, e2;
-
-       if (gnm_isnan (x) || gnm_isnan (n))
-               return gnm_nan;
-
-       /* This isn't a fundamental restriction, but one we impose.  */
-       if (x <= 0 || x + n <= 0)
-               return gnm_nan;
-
-       if (n == 0)
-               return give_log ? 0 : 1;
-
-       rx = gnm_floor (x + 0.5);
-       rn = gnm_floor (n + 0.5);
-
-       /*
-        * Use naive multiplication when n is a small integer.
-        * We don't want to use this if x is also an integer
-        * (but we might do so below if x is insanely large).
-        */
-       if (n == rn && x != rx && n >= 0 && n < 40) {
-               gnm_float r = pochhammer_naive (x, (int)n);
-               return give_log ? gnm_log (r) : r;
-       }
-
-       if (!qfactf (x + n - 1, &m1, &e1) &&
-           !qfactf (x - 1, &m2, &e2)) {
-               void *state = gnm_quad_start ();
-               int de = e1 - e2;
-               GnmQuad qr;
-               gnm_float r;
-
-               gnm_quad_div (&qr, &m1, &m2);
-               r = gnm_quad_value (&qr);
-               gnm_quad_end (state);
-
-               return give_log
-                       ? gnm_log (r) + M_LN2gnum * de
-                       : gnm_ldexp (r, de);
-       }
-
-       /*
-        * We have left the common cases.  One of x+n and x is
-        * insanely big, possibly both.
-        */
-
-       if (gnm_abs (x) < 1) {
-               /* n is big. */
-               if (give_log)
-                       goto via_log;
-               else
-                       return gnm_pinf;
-       }
-
-       if (n < 0) {
-               gnm_float r = pochhammer (x + n, -n, give_log);
-               return give_log ? -r : 1 / r;
-       }
-
-       if (n == rn && n >= 0 && n < 100) {
-               gnm_float r = pochhammer_naive (x, (int)n);
-               return give_log ? gnm_log (r) : r;
-       }
-
-       if (gnm_abs (n) < 1) {
-               /* x is big.  */
-               void *state = gnm_quad_start ();
-               GnmQuad qr;
-               gnm_float r;
-               pochhammer_small_n (x, n, &qr);
-               r = gnm_quad_value (&qr);
-               gnm_quad_end (state);
-               return give_log ? gnm_log (r) : r;
-       }
-
-       /* Panic mode.  */
-       g_printerr ("x=%.20g  n=%.20g\n", x, n);
-via_log:
-       lr = ((x - 0.5) * gnm_log1p (n / x) +
-             n * gnm_log (x + n) -
-             n +
-             (lgammacor (x + n) - lgammacor (x)));
-       return give_log ? lr : gnm_exp (lr);
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * gnm_gamma:
- * @x: a number
- *
- * Returns: the Gamma function evaluated at @x for positive or
- * non-integer @x.
- */
-gnm_float
-gnm_gamma (gnm_float x)
-{
-       if (gnm_abs (x) < 0.5) {
-               gnm_float a = gnm_exp (gnm_lgamma (x));
-               return (x > 0) ? a : -a;
-       } else
-               return gnm_fact (x - 1);
-}
-
-/* ------------------------------------------------------------------------- */
-
 gnm_float
 gnm_bessel_i (gnm_float x, gnm_float alpha)
 {
diff --git a/src/mathfunc.h b/src/mathfunc.h
index 7781b6f..d5f3ab6 100644
--- a/src/mathfunc.h
+++ b/src/mathfunc.h
@@ -23,25 +23,19 @@ G_BEGIN_DECLS
 #define M_LN10gnum  
GNM_const(2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598)
 #define M_SQRT2gnum 
GNM_const(1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327642)
 #define        M_Egnum         GNM_const(2.718281828459045235360287471352662497757247)
+#define M_LN_SQRT_2PI   GNM_const(0.918938533204672741780329736406)  /* log(sqrt(2*pi)) */
 
 /* ------------------------------------------------------------------------- */
 
 gnm_float log1pmx (gnm_float x);
 gnm_float swap_log_tail (gnm_float lp);
-gnm_float lgamma1p (gnm_float a);
 gnm_float pow1p (gnm_float x, gnm_float y);
 gnm_float pow1pm1 (gnm_float x, gnm_float y);
 gnm_float gnm_trunc (gnm_float x);
-gnm_float logfbit (gnm_float x);
 gnm_float logspace_add (gnm_float logx, gnm_float logy);
 gnm_float logspace_sub (gnm_float logx, gnm_float logy);
-gnm_float stirlerr(gnm_float n);
 gnm_float gnm_owent (gnm_float h, gnm_float a);
-gnm_float pochhammer (gnm_float x, gnm_float n, gboolean give_log);
-gnm_float gnm_gamma (gnm_float x);
-
-gnm_float beta (gnm_float a, gnm_float b);
-gnm_float lbeta3 (gnm_float a, gnm_float b, int *sign);
+gnm_float gnm_logcf (gnm_float x, gnm_float i, gnm_float d);
 
 gnm_float gnm_bessel_i (gnm_float x, gnm_float alpha);
 gnm_float gnm_bessel_j (gnm_float x, gnm_float alpha);
@@ -172,11 +166,6 @@ void gnm_matrix_multiply (GnmMatrix *C, const GnmMatrix *A, const GnmMatrix *B);
 gboolean gnm_matrix_eigen (GnmMatrix const *m, GnmMatrix *EIG, gnm_float *eigenvalues);
 /* ------------------------------------------------------------------------- */
 
-gnm_float combin (gnm_float n, gnm_float k);
-gnm_float permut (gnm_float n, gnm_float k);
-int       qfactf (gnm_float x, GnmQuad *mant, int *exp2);
-gnm_float gnm_fact (gnm_float x);
-
 gint gnm_float_equal (gnm_float const *a, const gnm_float *b);
 guint gnm_float_hash (gnm_float const *d);
 
diff --git a/src/rangefunc.c b/src/rangefunc.c
index 2eab119..44c7bb3 100644
--- a/src/rangefunc.c
+++ b/src/rangefunc.c
@@ -12,6 +12,7 @@
 #include "rangefunc.h"
 
 #include "mathfunc.h"
+#include "sf-gamma.h"
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/sf-gamma.c b/src/sf-gamma.c
new file mode 100644
index 0000000..7cd0bd7
--- /dev/null
+++ b/src/sf-gamma.c
@@ -0,0 +1,1020 @@
+#include <gnumeric-config.h>
+#include <sf-gamma.h>
+#include <sf-trig.h>
+#include <mathfunc.h>
+
+#define ML_ERR_return_NAN { return gnm_nan; }
+#define ML_UNDERFLOW (GNM_EPSILON * GNM_EPSILON)
+#define ML_ERROR(cause) do { } while(0)
+
+/* Compute  gnm_log(gamma(a+1))  accurately also for small a (0 < a < 0.5). */
+gnm_float lgamma1p (gnm_float a)
+{
+    const gnm_float eulers_const =      GNM_const(0.5772156649015328606065120900824024);
+
+    /* coeffs[i] holds (zeta(i+2)-1)/(i+2) , i = 1:N, N = 40 : */
+    const int N = 40;
+    static const gnm_float coeffs[40] = {
+       GNM_const(0.3224670334241132182362075833230126e-0),
+       GNM_const(0.6735230105319809513324605383715000e-1),
+       GNM_const(0.2058080842778454787900092413529198e-1),
+       GNM_const(0.7385551028673985266273097291406834e-2),
+       GNM_const(0.2890510330741523285752988298486755e-2),
+       GNM_const(0.1192753911703260977113935692828109e-2),
+       GNM_const(0.5096695247430424223356548135815582e-3),
+       GNM_const(0.2231547584535793797614188036013401e-3),
+       GNM_const(0.9945751278180853371459589003190170e-4),
+       GNM_const(0.4492623673813314170020750240635786e-4),
+       GNM_const(0.2050721277567069155316650397830591e-4),
+       GNM_const(0.9439488275268395903987425104415055e-5),
+       GNM_const(0.4374866789907487804181793223952411e-5),
+       GNM_const(0.2039215753801366236781900709670839e-5),
+       GNM_const(0.9551412130407419832857179772951265e-6),
+       GNM_const(0.4492469198764566043294290331193655e-6),
+       GNM_const(0.2120718480555466586923135901077628e-6),
+       GNM_const(0.1004322482396809960872083050053344e-6),
+       GNM_const(0.4769810169363980565760193417246730e-7),
+       GNM_const(0.2271109460894316491031998116062124e-7),
+       GNM_const(0.1083865921489695409107491757968159e-7),
+       GNM_const(0.5183475041970046655121248647057669e-8),
+       GNM_const(0.2483674543802478317185008663991718e-8),
+       GNM_const(0.1192140140586091207442548202774640e-8),
+       GNM_const(0.5731367241678862013330194857961011e-9),
+       GNM_const(0.2759522885124233145178149692816341e-9),
+       GNM_const(0.1330476437424448948149715720858008e-9),
+       GNM_const(0.6422964563838100022082448087644648e-10),
+       GNM_const(0.3104424774732227276239215783404066e-10),
+       GNM_const(0.1502138408075414217093301048780668e-10),
+       GNM_const(0.7275974480239079662504549924814047e-11),
+       GNM_const(0.3527742476575915083615072228655483e-11),
+       GNM_const(0.1711991790559617908601084114443031e-11),
+       GNM_const(0.8315385841420284819798357793954418e-12),
+       GNM_const(0.4042200525289440065536008957032895e-12),
+       GNM_const(0.1966475631096616490411045679010286e-12),
+       GNM_const(0.9573630387838555763782200936508615e-13),
+       GNM_const(0.4664076026428374224576492565974577e-13),
+       GNM_const(0.2273736960065972320633279596737272e-13),
+       GNM_const(0.1109139947083452201658320007192334e-13)
+    };
+
+    const gnm_float c = GNM_const(0.2273736845824652515226821577978691e-12);/* zeta(N+2)-1 */
+    gnm_float lgam;
+    int i;
+
+    if (gnm_abs (a) >= 0.5)
+       return gnm_lgamma (a + 1);
+
+    /* Abramowitz & Stegun 6.1.33,
+     * also  http://functions.wolfram.com/06.11.06.0008.01 */
+    lgam = c * gnm_logcf (-a / 2, N + 2, 1);
+    for (i = N - 1; i >= 0; i--)
+       lgam = coeffs[i] - a * lgam;
+
+    return (a * lgam - eulers_const) * a - log1pmx (a);
+} /* lgamma1p */
+
+/* ------------------------------------------------------------------------ */
+
+/* Imported src/nmath/stirlerr.c from R.  */
+/*
+ *  AUTHOR
+ *    Catherine Loader, catherine research bell-labs com 
+ *    October 23, 2000.
+ *
+ *  Merge in to R:
+ *     Copyright (C) 2000, The R Core Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ *  USA.
+ *
+ *
+ *  DESCRIPTION
+ *
+ *    Computes the log of the error term in Stirling's formula.
+ *      For n > 15, uses the series 1/12n - 1/360n^3 + ...
+ *      For n <=15, integers or half-integers, uses stored values.
+ *      For other n < 15, uses lgamma directly (don't use this to
+ *        write lgamma!)
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ * R has lgammafn, and lgamma is not part of ISO C
+ */
+
+
+/* stirlerr(n) = gnm_log(n!) - gnm_log( gnm_sqrt(2*pi*n)*(n/e)^n )
+ *             = gnm_log Gamma(n+1) - 1/2 * [gnm_log(2*pi) + gnm_log(n)] - n*[gnm_log(n) - 1]
+ *             = gnm_log Gamma(n+1) - (n + 1/2) * gnm_log(n) + n - gnm_log(2*pi)/2
+ *
+ * see also lgammacor() in ./lgammacor.c  which computes almost the same!
+ */
+
+gnm_float stirlerr(gnm_float n)
+{
+
+#define S0 GNM_const(0.083333333333333333333)       /* 1/12 */
+#define S1 GNM_const(0.00277777777777777777778)     /* 1/360 */
+#define S2 GNM_const(0.00079365079365079365079365)  /* 1/1260 */
+#define S3 GNM_const(0.000595238095238095238095238) /* 1/1680 */
+#define S4 GNM_const(0.0008417508417508417508417508)/* 1/1188 */
+
+/*
+  error for 0, 0.5, 1.0, 1.5, ..., 14.5, 15.0.
+*/
+    static const gnm_float sferr_halves[31] = {
+       0.0, /* n=0 - wrong, place holder only */
+       GNM_const(0.1534264097200273452913848),  /* 0.5 */
+       GNM_const(0.0810614667953272582196702),  /* 1.0 */
+       GNM_const(0.0548141210519176538961390),  /* 1.5 */
+       GNM_const(0.0413406959554092940938221),  /* 2.0 */
+       GNM_const(0.03316287351993628748511048), /* 2.5 */
+       GNM_const(0.02767792568499833914878929), /* 3.0 */
+       GNM_const(0.02374616365629749597132920), /* 3.5 */
+       GNM_const(0.02079067210376509311152277), /* 4.0 */
+       GNM_const(0.01848845053267318523077934), /* 4.5 */
+       GNM_const(0.01664469118982119216319487), /* 5.0 */
+       GNM_const(0.01513497322191737887351255), /* 5.5 */
+       GNM_const(0.01387612882307074799874573), /* 6.0 */
+       GNM_const(0.01281046524292022692424986), /* 6.5 */
+       GNM_const(0.01189670994589177009505572), /* 7.0 */
+       GNM_const(0.01110455975820691732662991), /* 7.5 */
+       GNM_const(0.010411265261972096497478567), /* 8.0 */
+       GNM_const(0.009799416126158803298389475), /* 8.5 */
+       GNM_const(0.009255462182712732917728637), /* 9.0 */
+       GNM_const(0.008768700134139385462952823), /* 9.5 */
+       GNM_const(0.008330563433362871256469318), /* 10.0 */
+       GNM_const(0.007934114564314020547248100), /* 10.5 */
+       GNM_const(0.007573675487951840794972024), /* 11.0 */
+       GNM_const(0.007244554301320383179543912), /* 11.5 */
+       GNM_const(0.006942840107209529865664152), /* 12.0 */
+       GNM_const(0.006665247032707682442354394), /* 12.5 */
+       GNM_const(0.006408994188004207068439631), /* 13.0 */
+       GNM_const(0.006171712263039457647532867), /* 13.5 */
+       GNM_const(0.005951370112758847735624416), /* 14.0 */
+       GNM_const(0.005746216513010115682023589), /* 14.5 */
+       GNM_const(0.005554733551962801371038690)  /* 15.0 */
+    };
+    gnm_float nn;
+
+    if (n <= 15.0) {
+       nn = n + n;
+       if (nn == (int)nn) return(sferr_halves[(int)nn]);
+       return(lgamma1p (n ) - (n + 0.5)*gnm_log(n) + n - M_LN_SQRT_2PI);
+    }
+
+    nn = n*n;
+    if (n>500) return((S0-S1/nn)/n);
+    if (n> 80) return((S0-(S1-S2/nn)/nn)/n);
+    if (n> 35) return((S0-(S1-(S2-S3/nn)/nn)/nn)/n);
+    /* 15 < n <= 35 : */
+    return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n);
+}
+/* Cleaning up done by tools/import-R:  */
+#undef S0
+#undef S1
+#undef S2
+#undef S3
+#undef S4
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/chebyshev.c from R.  */
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ *  02110-1301 USA.
+ *
+ *  SYNOPSIS
+ *
+ *    int chebyshev_init(double *dos, int nos, double eta)
+ *    double chebyshev_eval(double x, double *a, int n)
+ *
+ *  DESCRIPTION
+ *
+ *    "chebyshev_init" determines the number of terms for the
+ *    double precision orthogonal series "dos" needed to insure
+ *    the error is no larger than "eta".  Ordinarily eta will be
+ *    chosen to be one-tenth machine precision.
+ *
+ *    "chebyshev_eval" evaluates the n-term Chebyshev series
+ *    "a" at "x".
+ *
+ *  NOTES
+ *
+ *    These routines are translations into C of Fortran routines
+ *    by W. Fullerton of Los Alamos Scientific Laboratory.
+ *
+ *    Based on the Fortran routine dcsevl by W. Fullerton.
+ *    Adapted from R. Broucke, Algorithm 446, CACM., 16, 254 (1973).
+ */
+
+
+/* NaNs propagated correctly */
+
+
+/* Definition of function chebyshev_init removed.  */
+
+
+static gnm_float chebyshev_eval(gnm_float x, const gnm_float *a, const int n)
+{
+    gnm_float b0, b1, b2, twox;
+    int i;
+
+    if (n < 1 || n > 1000) ML_ERR_return_NAN;
+
+    if (x < -1.1 || x > 1.1) ML_ERR_return_NAN;
+
+    twox = x * 2;
+    b2 = b1 = 0;
+    b0 = 0;
+    for (i = 1; i <= n; i++) {
+       b2 = b1;
+       b1 = b0;
+       b0 = twox * b1 - b2 + a[n - i];
+    }
+    return (b0 - b2) * 0.5;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/lgammacor.c from R.  */
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000-2001 The R Development Core Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ *  USA.
+ *
+ *  SYNOPSIS
+ *
+ *    #include <Rmath.h>
+ *    double lgammacor(double x);
+ *
+ *  DESCRIPTION
+ *
+ *    Compute the log gamma correction factor for x >= 10 so that
+ *
+ *    log(gamma(x)) = .5*log(2*pi) + (x-.5)*log(x) -x + lgammacor(x)
+ *
+ *    [ lgammacor(x) is called Del(x)  in other contexts (e.g. dcdflib)]
+ *
+ *  NOTES
+ *
+ *    This routine is a translation into C of a Fortran subroutine
+ *    written by W. Fullerton of Los Alamos Scientific Laboratory.
+ *
+ *  SEE ALSO
+ *
+ *    Loader(1999)'s stirlerr() {in ./stirlerr.c} is *very* similar in spirit,
+ *    is faster and cleaner, but is only defined "fast" for half integers.
+ */
+
+
+static gnm_float lgammacor(gnm_float x)
+{
+    static const gnm_float algmcs[15] = {
+       GNM_const(+.1666389480451863247205729650822e+0),
+       GNM_const(-.1384948176067563840732986059135e-4),
+       GNM_const(+.9810825646924729426157171547487e-8),
+       GNM_const(-.1809129475572494194263306266719e-10),
+       GNM_const(+.6221098041892605227126015543416e-13),
+       GNM_const(-.3399615005417721944303330599666e-15),
+       GNM_const(+.2683181998482698748957538846666e-17),
+       GNM_const(-.2868042435334643284144622399999e-19),
+       GNM_const(+.3962837061046434803679306666666e-21),
+       GNM_const(-.6831888753985766870111999999999e-23),
+       GNM_const(+.1429227355942498147573333333333e-24),
+       GNM_const(-.3547598158101070547199999999999e-26),
+       GNM_const(+.1025680058010470912000000000000e-27),
+       GNM_const(-.3401102254316748799999999999999e-29),
+       GNM_const(+.1276642195630062933333333333333e-30)
+    };
+
+    gnm_float tmp;
+
+#ifdef NOMORE_FOR_THREADS
+    static int nalgm = 0;
+    static gnm_float xbig = 0, xmax = 0;
+
+    /* Initialize machine dependent constants, the first time gamma() is called.
+       FIXME for threads ! */
+    if (nalgm == 0) {
+       /* For IEEE gnm_float precision : nalgm = 5 */
+       nalgm = chebyshev_init(algmcs, 15, GNM_EPSILON/2);/*was d1mach(3)*/
+       xbig = 1 / gnm_sqrt(GNM_EPSILON/2); /* ~ 94906265.6 for IEEE gnm_float */
+       xmax = gnm_exp(fmin2(gnm_log(GNM_MAX / 12), -gnm_log(12 * GNM_MIN)));
+       /*   = GNM_MAX / 48 ~= 3.745e306 for IEEE gnm_float */
+    }
+#else
+/* For IEEE gnm_float precision GNM_EPSILON = 2^-52 = GNM_const(2.220446049250313e-16) :
+ *   xbig = 2 ^ 26.5
+ *   xmax = GNM_MAX / 48 =  2^1020 / 3 */
+# define nalgm 5
+# define xbig  GNM_const(94906265.62425156)
+# define xmax  GNM_const(3.745194030963158e306)
+#endif
+
+    if (x < 10)
+       ML_ERR_return_NAN
+    else if (x >= xmax) {
+       ML_ERROR(ME_UNDERFLOW);
+       return ML_UNDERFLOW;
+    }
+    else if (x < xbig) {
+       tmp = 10 / x;
+       return chebyshev_eval(tmp * tmp * 2 - 1, algmcs, nalgm) / x;
+    }
+    else return 1 / (x * 12);
+}
+/* Cleaning up done by tools/import-R:  */
+#undef nalgm
+#undef xbig
+#undef xmax
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/lbeta.c from R.  */
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000 The R Development Core Team
+ *  Copyright (C) 2003 The R Foundation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ *  USA.
+ *
+ *  SYNOPSIS
+ *
+ *    #include <Rmath.h>
+ *    double lbeta(double a, double b);
+ *
+ *  DESCRIPTION
+ *
+ *    This function returns the value of the log beta function.
+ *
+ *  NOTES
+ *
+ *    This routine is a translation into C of a Fortran subroutine
+ *    by W. Fullerton of Los Alamos Scientific Laboratory.
+ */
+
+
+gnm_float gnm_lbeta(gnm_float a, gnm_float b)
+{
+    gnm_float corr, p, q;
+
+    p = q = a;
+    if(b < p) p = b;/* := min(a,b) */
+    if(b > q) q = b;/* := max(a,b) */
+
+#ifdef IEEE_754
+    if(gnm_isnan(a) || gnm_isnan(b))
+       return a + b;
+#endif
+
+    /* both arguments must be >= 0 */
+
+    if (p < 0)
+       ML_ERR_return_NAN
+    else if (p == 0) {
+       return gnm_pinf;
+    }
+    else if (!gnm_finite(q)) {
+       return gnm_ninf;
+    }
+
+    if (p >= 10) {
+       /* p and q are big. */
+       corr = lgammacor(p) + lgammacor(q) - lgammacor(p + q);
+       return gnm_log(q) * -0.5 + M_LN_SQRT_2PI + corr
+               + (p - 0.5) * gnm_log(p / (p + q)) + q * gnm_log1p(-p / (p + q));
+    }
+    else if (q >= 10) {
+       /* p is small, but q is big. */
+       corr = lgammacor(q) - lgammacor(p + q);
+       return gnm_lgamma(p) + corr + p - p * gnm_log(p + q)
+               + (q - 0.5) * gnm_log1p(-p / (p + q));
+    }
+    else
+       /* p and q are small: p <= q < 10. */
+       return gnm_lgamma (p) + gnm_lgamma (q) - gnm_lgamma (p + q);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/**
+ * gnm_gamma:
+ * @x: a number
+ *
+ * Returns: the Gamma function evaluated at @x for positive or
+ * non-integer @x.
+ */
+gnm_float
+gnm_gamma (gnm_float x)
+{
+       if (gnm_abs (x) < 0.5) {
+               gnm_float a = gnm_exp (gnm_lgamma (x));
+               return (x > 0) ? a : -a;
+       } else
+               return gnm_fact (x - 1);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * gnm_fact:
+ * @x: number
+ *
+ * Returns: the factorial of @x, which must not be a negative integer.
+ */
+gnm_float
+gnm_fact (gnm_float x)
+{
+       GnmQuad r;
+       int e;
+
+       switch (qfactf (x, &r, &e)) {
+       case 0: return ldexp (gnm_quad_value (&r), e);
+       case 1: return gnm_pinf;
+       default: return gnm_nan;
+       }
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * beta:
+ * @a: a number
+ * @b: a number
+ *
+ * Returns: the Beta function evaluated at @a and @b.
+ */
+gnm_float
+beta (gnm_float a, gnm_float b)
+{
+       int sign;
+       gnm_float absres = gnm_exp (lbeta3 (a, b, &sign));
+
+       return sign == -1 ? -absres : absres;
+}
+
+/**
+ * lbeta3:
+ * @a: a number
+ * @b: a number
+ * @sign: (out): the sign
+ *
+ * Returns: the logarithm of the absolute value of the Beta function
+ * evaluated at @a and @b.  The sign will be stored in @sign as -1 or
+ * +1.  This function is useful because the result of the beta
+ * function can be too large for doubles.
+ */
+gnm_float
+lbeta3 (gnm_float a, gnm_float b, int *sign)
+{
+       int sign_a, sign_b, sign_ab;
+       gnm_float ab = a + b;
+       gnm_float res_a, res_b, res_ab;
+
+       *sign = 1;
+       if (a > 0 && b > 0)
+               return gnm_lbeta (a, b);
+
+#ifdef IEEE_754
+       if (gnm_isnan(ab))
+               return ab;
+#endif
+
+       if ((a <= 0 && a == gnm_floor (a)) ||
+           (b <= 0 && b == gnm_floor (b)) ||
+           (ab <= 0 && ab == gnm_floor (ab)))
+               return gnm_nan;
+
+       res_a = gnm_lgamma_r (a, &sign_a);
+       res_b = gnm_lgamma_r (b, &sign_b);
+       res_ab = gnm_lgamma_r (ab, &sign_ab);
+
+       *sign = sign_a * sign_b * sign_ab;
+       return res_a + res_b - res_ab;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+gamma_error_factor (GnmQuad *res, const GnmQuad *x)
+{
+       gnm_float num[] = {
+               1.,
+               1.,
+               -139.,
+               -571.,
+               163879.,
+               5246819.,
+               -534703531.,
+               -4483131259.
+       };
+       gnm_float den[] = {
+               12.,
+               288.,
+               51840.,
+               2488320.,
+               209018880.,
+               75246796800.,
+               902961561600.,
+               86684309913600.
+       };
+       GnmQuad zn;
+       int i;
+
+       gnm_quad_init (&zn, 1);
+       *res = zn;
+
+       for (i = 0; i < (int)G_N_ELEMENTS (num); i++) {
+               GnmQuad t, c;
+               gnm_quad_mul (&zn, &zn, x);
+               gnm_quad_init (&c, den[i]);
+               gnm_quad_mul (&t, &zn, &c);
+               gnm_quad_init (&c, num[i]);
+               gnm_quad_div (&t, &c, &t);
+               gnm_quad_add (res, res, &t);
+       }
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+pochhammer_small_n (gnm_float x, gnm_float n, GnmQuad *res)
+{
+       GnmQuad qx, qn, qr, qs, qone, f1, f2, f3, f4, f5;
+       gnm_float r;
+       gboolean debug = FALSE;
+
+       g_return_if_fail (x >= 20);
+       g_return_if_fail (gnm_abs (n) <= 1);
+
+       /*
+        * G(x)   = c * x^(x-1/2) * exp(-x) * E(x)
+        * G(x+n) = c * (x+n)^(x+n-1/2) * exp(-(x+n)) * E(x+n)
+        *        = c * (x+n)^(x-1/2) * (x+n)^n * exp(-x) * exp(-n) * E(x+n)
+        *
+        * G(x+n)/G(x)
+        * = (1+n/x)^(x-1/2) * (x+n)^n * exp(-n) * E(x+n)/E(x)
+        * = (1+n/x)^x / sqrt(1+n/x) * (x+n)^n * exp(-n) * E(x+n)/E(x)
+        * = exp(x*log(1+n/x) - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
+        * = exp(x*log1p(n/x) - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
+        * = exp(x*(log1pmx(n/x)+n/x) - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
+        * = exp(x*log1pmx(n/x) + n - n) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
+        * = exp(x*log1pmx(n/x)) / sqrt(1+n/x) * (x+n)^n * E(x+n)/E(x)
+        */
+
+       gnm_quad_init (&qx, x);
+       gnm_quad_init (&qn, n);
+
+       gnm_quad_div (&qr, &qn, &qx);
+       r = gnm_quad_value (&qr);
+
+       gnm_quad_add (&qs, &qx, &qn);
+
+       gnm_quad_init (&qone, 1);
+
+       /* exp(x*log1pmx(n/x)) */
+       gnm_quad_mul12 (&f1, log1pmx (r), x);  /* sub-opt */
+       gnm_quad_exp (&f1, NULL, &f1);
+       if (debug) g_printerr ("f1=%.20g\n", gnm_quad_value (&f1));
+
+       /* sqrt(1+n/x) */
+       gnm_quad_add (&f2, &qone, &qr);
+       gnm_quad_sqrt (&f2, &f2);
+       if (debug) g_printerr ("f2=%.20g\n", gnm_quad_value (&f2));
+
+       /* (x+n)^n */
+       gnm_quad_pow (&f3, NULL, &qs, &qn);
+       if (debug) g_printerr ("f3=%.20g\n", gnm_quad_value (&f3));
+
+       /* E(x+n) */
+       gamma_error_factor (&f4, &qs);
+       if (debug) g_printerr ("f4=%.20g\n", gnm_quad_value (&f4));
+
+       /* E(x) */
+       gamma_error_factor (&f5, &qx);
+       if (debug) g_printerr ("f5=%.20g\n", gnm_quad_value (&f5));
+
+       gnm_quad_div (res, &f1, &f2);
+       gnm_quad_mul (res, res, &f3);
+       gnm_quad_mul (res, res, &f4);
+       gnm_quad_div (res, res, &f5);
+}
+
+static gnm_float
+pochhammer_naive (gnm_float x, int n)
+{
+       void *state = gnm_quad_start ();
+       GnmQuad qp, qx, qone;
+       gnm_float r;
+
+       gnm_quad_init (&qone, 1);
+       qp = qone;
+       gnm_quad_init (&qx, x);
+       while (n-- > 0) {
+               gnm_quad_mul (&qp, &qp, &qx);
+               gnm_quad_add (&qx, &qx, &qone);
+       }
+       r = gnm_quad_value (&qp);
+       gnm_quad_end (state);
+       return r;
+}
+
+
+
+/*
+ * Pochhammer's symbol: (x)_n = Gamma(x+n)/Gamma(x).
+ *
+ * While n is often an integer, that is not a requirement.
+ */
+
+gnm_float
+pochhammer (gnm_float x, gnm_float n, gboolean give_log)
+{
+       gnm_float rn, rx, lr;
+       GnmQuad m1, m2;
+       int e1, e2;
+
+       if (gnm_isnan (x) || gnm_isnan (n))
+               return gnm_nan;
+
+       /* This isn't a fundamental restriction, but one we impose.  */
+       if (x <= 0 || x + n <= 0)
+               return gnm_nan;
+
+       if (n == 0)
+               return give_log ? 0 : 1;
+
+       rx = gnm_floor (x + 0.5);
+       rn = gnm_floor (n + 0.5);
+
+       /*
+        * Use naive multiplication when n is a small integer.
+        * We don't want to use this if x is also an integer
+        * (but we might do so below if x is insanely large).
+        */
+       if (n == rn && x != rx && n >= 0 && n < 40) {
+               gnm_float r = pochhammer_naive (x, (int)n);
+               return give_log ? gnm_log (r) : r;
+       }
+
+       if (!qfactf (x + n - 1, &m1, &e1) &&
+           !qfactf (x - 1, &m2, &e2)) {
+               void *state = gnm_quad_start ();
+               int de = e1 - e2;
+               GnmQuad qr;
+               gnm_float r;
+
+               gnm_quad_div (&qr, &m1, &m2);
+               r = gnm_quad_value (&qr);
+               gnm_quad_end (state);
+
+               return give_log
+                       ? gnm_log (r) + M_LN2gnum * de
+                       : gnm_ldexp (r, de);
+       }
+
+       /*
+        * We have left the common cases.  One of x+n and x is
+        * insanely big, possibly both.
+        */
+
+       if (gnm_abs (x) < 1) {
+               /* n is big. */
+               if (give_log)
+                       goto via_log;
+               else
+                       return gnm_pinf;
+       }
+
+       if (n < 0) {
+               gnm_float r = pochhammer (x + n, -n, give_log);
+               return give_log ? -r : 1 / r;
+       }
+
+       if (n == rn && n >= 0 && n < 100) {
+               gnm_float r = pochhammer_naive (x, (int)n);
+               return give_log ? gnm_log (r) : r;
+       }
+
+       if (gnm_abs (n) < 1) {
+               /* x is big.  */
+               void *state = gnm_quad_start ();
+               GnmQuad qr;
+               gnm_float r;
+               pochhammer_small_n (x, n, &qr);
+               r = gnm_quad_value (&qr);
+               gnm_quad_end (state);
+               return give_log ? gnm_log (r) : r;
+       }
+
+       /* Panic mode.  */
+       g_printerr ("x=%.20g  n=%.20g\n", x, n);
+via_log:
+       lr = ((x - 0.5) * gnm_log1p (n / x) +
+             n * gnm_log (x + n) -
+             n +
+             (lgammacor (x + n) - lgammacor (x)));
+       return give_log ? lr : gnm_exp (lr);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+rescale_mant_exp (GnmQuad *mant, int *exp2)
+{
+       GnmQuad s;
+       int e;
+
+       (void)gnm_frexp (gnm_quad_value (mant), &e);
+       *exp2 += e;
+       gnm_quad_init (&s, gnm_ldexp (1.0, -e));
+       gnm_quad_mul (mant, mant, &s);
+}
+
+/* Tabulate up to, but not including, this number.  */
+#define QFACTI_LIMIT 10000
+
+static gboolean
+qfacti (int n, GnmQuad *mant, int *exp2)
+{
+       static GnmQuad mants[QFACTI_LIMIT];
+       static int exp2s[QFACTI_LIMIT];
+       static int init = 0;
+
+       if (n < 0 || n >= QFACTI_LIMIT) {
+               *mant = gnm_quad_zero;
+               *exp2 = 0;
+               return TRUE;
+       }
+
+       if (n >= init) {
+               void *state = gnm_quad_start ();
+
+               if (init == 0) {
+                       gnm_quad_init (&mants[0], 0.5);
+                       exp2s[0] = 1;
+                       init++;
+               }
+
+               while (n >= init) {
+                       GnmQuad m;
+
+                       gnm_quad_init (&m, init);
+                       gnm_quad_mul (&mants[init], &m, &mants[init - 1]);
+                       exp2s[init] = exp2s[init - 1];
+                       rescale_mant_exp (&mants[init], &exp2s[init]);
+
+                       init++;
+               }
+
+               gnm_quad_end (state);
+       }
+
+       *mant = mants[n];
+       *exp2 = exp2s[n];
+       return FALSE;
+}
+
+/* 0: ok, 1: overflow, 2: nan */
+int
+qfactf (gnm_float x, GnmQuad *mant, int *exp2)
+{
+       void *state;
+       gboolean res = 0;
+
+       if (gnm_isnan (x))
+               return 2;
+
+       if (x >= G_MAXINT / 2)
+               return 1;
+
+       if (x == gnm_floor (x)) {
+               /* Integer or infinite.  */
+               if (x < 0)
+                       return 2;
+
+               if (!qfacti ((int)x, mant, exp2))
+                       return 0;
+       }
+
+       state = gnm_quad_start ();
+
+       if (x < -1) {
+               if (qfactf (-x - 1, mant, exp2))
+                       res = 1;
+               else {
+                       GnmQuad b;
+
+                       gnm_quad_init (&b, -gnm_sinpi (x)); /* ? */
+                       gnm_quad_mul (&b, &b, mant);
+                       gnm_quad_div (mant, &gnm_quad_pi, &b);
+                       *exp2 = -*exp2;
+               }
+       } else if (x >= QFACTI_LIMIT - 0.5) {
+               /*
+                * Let y = x + 1 = m * 2^e; c = sqrt(2Pi).
+                *
+                * G(y) = c * y^(y-1/2) * exp(-y) * E(y)
+                *      = c * (y/e)^y / sqrt(y) * E(y)
+                */
+               GnmQuad y, f1, f2, f3, f4;
+               gnm_float ef2;
+               gboolean debug = FALSE;
+
+               if (debug) g_printerr ("x=%.20g\n", x);
+
+               gnm_quad_init (&y, x + 1);
+               *exp2 = 0;
+
+               /* sqrt(2Pi) */
+               gnm_quad_add (&f1, &gnm_quad_pi, &gnm_quad_pi);
+               gnm_quad_sqrt (&f1, &f1);
+               if (debug) g_printerr ("f1=%.20g\n", gnm_quad_value (&f1));
+
+               /* (y/e)^y */
+               gnm_quad_div (&f2, &y, &gnm_quad_e);
+               gnm_quad_pow (&f2, &ef2, &f2, &y);
+               if (ef2 > G_MAXINT || ef2 < G_MININT)
+                       res = 1;
+               else
+                       *exp2 += (int)ef2;
+               if (debug) g_printerr ("f2=%.20g\n", gnm_quad_value (&f2));
+
+               /* sqrt(y) */
+               gnm_quad_sqrt (&f3, &y);
+               if (debug) g_printerr ("f3=%.20g\n", gnm_quad_value (&f3));
+
+               /* E(x) */
+               gamma_error_factor (&f4, &y);
+               if (debug) g_printerr ("f4=%.20g\n", gnm_quad_value (&f4));
+
+               gnm_quad_mul (mant, &f1, &f2);
+               gnm_quad_div (mant, mant, &f3);
+               gnm_quad_mul (mant, mant, &f4);
+
+               if (debug) g_printerr ("G(x+1)=%.20g * 2^%d %s\n", gnm_quad_value (mant), *exp2, res ? 
"overflow" : "");
+       } else {
+               GnmQuad s, mFw;
+               gnm_float w, f;
+               int eFw;
+
+               /*
+                * w integer, |f|<=0.5, x=w+f.
+                *
+                * Do this before we do the stepping below which would kill
+                * up to 4 bits of accuracy of f.
+                */
+               w = gnm_floor (x + 0.5);
+               f = x - w;
+
+               gnm_quad_init (&s, 1);
+               while (x < 20) {
+                       GnmQuad a;
+                       x++;
+                       w++;
+                       gnm_quad_init (&a, x);
+                       gnm_quad_mul (&s, &s, &a);
+               }
+
+               if (qfacti ((int)w, &mFw, &eFw)) {
+                       res = 1;
+               } else {
+                       GnmQuad r;
+
+                       pochhammer_small_n (w + 1, f, &r);
+                       gnm_quad_mul (mant, &mFw, &r);
+                       gnm_quad_div (mant, mant, &s);
+                       *exp2 = eFw;
+               }
+       }
+
+       if (res == 0)
+               rescale_mant_exp (mant, exp2);
+
+       gnm_quad_end (state);
+       return res;
+}
+
+/* ------------------------------------------------------------------------- */
+
+gnm_float
+combin (gnm_float n, gnm_float k)
+{
+       GnmQuad m1, m2, m3;
+       int e1, e2, e3;
+       gboolean ok;
+
+       if (k < 0 || k > n || n != gnm_floor (n) || k != gnm_floor (k))
+               return gnm_nan;
+
+       k = MIN (k, n - k);
+       if (k == 0)
+               return 1;
+       if (k == 1)
+               return n;
+
+       ok = (n < G_MAXINT &&
+             !qfactf (n, &m1, &e1) &&
+             !qfactf (k, &m2, &e2) &&
+             !qfactf (n - k, &m3, &e3));
+
+       if (ok) {
+               void *state = gnm_quad_start ();
+               gnm_float c;
+               gnm_quad_mul (&m2, &m2, &m3);
+               gnm_quad_div (&m1, &m1, &m2);
+               c = gnm_ldexp (gnm_quad_value (&m1), e1 - e2 - e3);
+               gnm_quad_end (state);
+               return c;
+       }
+
+       if (k < 30) {
+               void *state = gnm_quad_start ();
+               GnmQuad p, a, b;
+               gnm_float c;
+               int i;
+
+               gnm_quad_init (&p, 1);
+               for (i = 0; i < k; i++) {
+                       gnm_quad_init (&a, n - i);
+                       gnm_quad_mul (&p, &p, &a);
+
+                       gnm_quad_init (&b, i + 1);
+                       gnm_quad_div (&p, &p, &b);
+               }
+
+               c = gnm_quad_value (&p);
+               gnm_quad_end (state);
+               return c;
+       }
+
+       {
+               gnm_float lp = pochhammer (n - k + 1, k, TRUE) -
+                       gnm_lgamma (k + 1);
+               return gnm_floor (0.5 + gnm_exp (lp));
+       }
+}
+
+gnm_float
+permut (gnm_float n, gnm_float k)
+{
+       if (k < 0 || k > n || n != gnm_floor (n) || k != gnm_floor (k))
+               return gnm_nan;
+
+       return pochhammer (n - k + 1, k, FALSE);
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/src/sf-gamma.h b/src/sf-gamma.h
new file mode 100644
index 0000000..1f8a6c6
--- /dev/null
+++ b/src/sf-gamma.h
@@ -0,0 +1,21 @@
+#ifndef GNM_SF_GAMMA_H_
+#define GNM_SF_GAMMA_H_
+
+#include <numbers.h>
+
+gnm_float lgamma1p (gnm_float a);
+gnm_float stirlerr(gnm_float n);
+
+gnm_float gnm_gamma (gnm_float x);
+gnm_float gnm_fact (gnm_float x);
+int       qfactf (gnm_float x, GnmQuad *mant, int *exp2);
+
+gnm_float gnm_lbeta (gnm_float a, gnm_float b);
+gnm_float beta (gnm_float a, gnm_float b);
+gnm_float lbeta3 (gnm_float a, gnm_float b, int *sign);
+
+gnm_float pochhammer (gnm_float x, gnm_float n, gboolean give_log);
+gnm_float combin (gnm_float n, gnm_float k);
+gnm_float permut (gnm_float n, gnm_float k);
+
+#endif


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